汇编 - 如何使用更少的变量
Assembly - how to use fewer variables
我正在制作游戏"Connect Four"。我有一块 4x4 的方板。对于板上的每个瓷砖,我必须绘制一个圆盘(方形),它具有 x 值和 y 值。问题是我必须制作 32 个变量或很多过程,这些过程会一直更改这两个变量,但这些选项看起来效率很低,而且代码会很长。如果您有什么更好的建议,请告诉我。
*注意 - 我正在使用 x86 程序集和 TASM
这是代码:
proc CheckPlayer1Number
mov ah, 7
int 21h
cmp al, 31h
je CheckColumn1
jmp CheckPlayer1Number
endp CheckPlayer1Number
proc CheckColumn1
cmp [FirstColumnArray], 0
je ChangeColumnNumber1
endp CheckColumn1
proc ChangeColumnNumber1
inc [FirstColumnArray]
mov [Player1Drawx], 25h
mov [Player1Drawy], 87h
jmp DrawPlayer1Disc
endp ChangeColumnNumber1
DrawPlayer1Loop:
mov bh,0h
mov cx,[Player1Drawx]
mov dx,[Player1Drawy]
mov al,[player1disccolor]
mov ah,0ch
int 10h
inc [Player1Drawx]
cmp cx, [Player1Drawx + 14h]
jl DrawPlayer1Loop
DrawPlayer1Disc:
mov bh, 0h
inc [Player1Drawy]
cmp dx, [Player1Drawy + 14h]
jl DrawPlayer1Loop
我只编写了一些程序,只是为了看看代码是否有效。
感谢您的帮助。
使用数组而不是单独的变量?就像四个 (x,y) 值的四个数组的数组。然后你只需要数组的基地址,然后你可以通过偏移量得到数组的所有元素。
在 C 中它会是这样的
struct Disc
{
int x;
int y;
};
struct Disc array[4][4];
C 是一种非常低级的语言,接近 "to the metal",并且在某些情况下(尤其是在其历史的早期)可以被视为一种非常高级的宏汇编程序。
C 中的数组是连续且连续的内存块,每个元素一个接着一个,因此计算数组中特定元素的偏移量非常容易。事实上,这就是 C 编译器为您所做的,这也是数组索引在 C 中以零开始而不是像同一时代的许多其他高级语言中的 1 的原因。
对于像这样的简单数组
int a[4];
数组中每个元素的偏移量是索引乘以数据大小。要获取元素 0(即 a[0]
),您需要执行 0 * sizeof(int)
,要获取第二个元素,您需要执行 1 * sizeof(int)
.
现在采用我上面显示的结构数组的数组,您使用相同的原理来计算从内存开始的偏移量:索引乘以元素大小。
如果我们向后开始,结构是两个 int
成员,在典型的 32 位机器上总共是 8 个字节(每个成员是一个 4 字节(32 位)整数,两个成员) .这意味着内部数组是 8 个字节乘以 4 个元素,或 32 个字节。要获取数组中的特定元素,请将索引乘以 8(结构大小)并将其作为偏移量添加到数组的开头。
外层数组每个元素为32字节(每个元素是4个结构的数组),这意味着上面显示的array
的总大小为128字节。要获取该数组中的元素,您需要将从零开始的索引乘以 32。
使用上面的方法,获取位于 array[i][j]
的结构(对于 i
和 j
的任意但在范围内的值),您将获得基地址array
,加上i * 32
得到内部数组,然后加上j * 4
得到实际的结构。在伪代码(不是真正的 C)中它看起来像
structure = array + i * 32 + j * 4
这为您提供了特定结构的地址。现在获取结构的成员是一样的。成员 x
位于偏移量 0,成员 y
位于偏移量 4(前一个成员的大小)。再次使用伪代码:
x = structure
y = structure + 4
所以要处理你的电路板,你只需要三个 "variables":外部数组的基地址,以及索引 i
和 j
。
我的 x86 程序集生锈了,我很久以前就用过它,但是计算偏移量并将正确的结构变量移动到寄存器中很容易 IIRC。
32 个变量通常相当于 32 个项目大小的单个变量(数组)。
而不是使用变量名称 square_3_1_x
,您想到的是 "mapping function",例如,您将 4x4 正方形作为每行 0,1,2.. 来处理。
所以 square(row,column)
索引是 ((row
-1)*4+(column
-1)) for row/column in 1..4 range.
然后对 x
使用 *2 和 +0 / y
使用 +1:3_1_x
是 16
,3_2_y
是 19
.
或 x
为 +0,y
为 +16(3_1_x
为 8
,3_2_y
为 25
)。
或者您能想到的任何您喜欢的映射。
假设您想要在 32b x86 汇编程序(NASM 语法)中的第 3 行 (1..4) 和第 2 列 (1..4) 处获取光盘的 [x,y] 位置(我喜欢你没有指定你的平台和语法,所以人们回答可以选择他们想要的任何东西,然后你将不得不理解完全陌生的语法,并将其转换为你的最终来源,你非常慷慨):
discPositions: times 32 dd 0 ; 16+16 for x+y couples (16 = 4x4).
GetDiscPosition:
; input: row (1..4) in eax, column (1..4) in ebx
; output: x position in eax, y position in ebx
dec eax ; row-1
dec ebx ; column-1
shl eax,2 ; (row-1)*4
add eax,ebx ; (row-1)*4+(column-1)
shl eax,3 ; ((row-1)*4+(column-1))*2*4
; *2 to cover [x,y] pairs, *4 to respect data size (DWORD)
; so here eax is byte offset to [x,y] couple in discPositions array
; it would make sense to have that offset calculation in another
; subroutine, so you can reuse it for SetDiscPosition routine
; Now just return the [x,y] stored in the 128B memory array labeled discPositions
mov ebx,[discPositions+eax+4] ; fetch y position
mov eax,[discPositions+eax+0] ; fetch x position
ret
; ... now somewhere in your code, where you want to get disc's x,y for square(3,4)
mov eax,3
mov ebx,4
call GetDiscPosition
; eax now has x position, ebx has y position
; ...
编辑:
好吧,既然这个答案得到了两次投票,看起来这些东西可能对其他人有帮助(多亏了问题的慷慨措辞?)。
所以我将从第二个共同的角度来解决这个问题。假设您确实有 32 个变量,例如角色扮演游戏中角色的属性,有些只需要 byte
(强度) , 一些 qword
(experience), 记住 health 有索引 4
很快就会变得非常烦人,并且 prestidigitation 具有索引 29
.
此外,对于更新的特定部分,您通常只需要其中几个,例如计算用剑造成伤害的角色不需要 elocution 和 charisma,但你还需要几个具有相同统计数据的不同角色(玩家对敌人),所以你仍然缺少寄存器。
在这种情况下,我通常只是模仿类 C 的结构:
; defining "PERSON" structure
PERSON_X equ 0 ;2B
PERSON_Y equ PERSON_X+2 ;2B
PERSON_HP equ PERSON_Y+2 ;4B
PERSON_MANA equ PERSON_HP+4 ;4B
PERSON_LEVEL equ PERSON_MANA+4 ;2B
;...
PERSON_PRESTIDIGITATION equ PERSON_ELOCUTION+1 ;1B
PERSON_SIZE equ PERSON_PRESTIDIGITATION+1
; reserving array for 4 player's characters
playersParty: resb 4*PERSON_SIZE
; ... somewhere in the code:
; teleporting whole party at x,y=(32,64)
MOV ax,32 ; new x
MOV bx,64 ; new y
MOV ecx,4
MOV edi,playersParty
setAllPartyMembers:
MOV [edi+PERSON_X],ax
MOV [edi+PERSON_Y],bx
ADD edi,PERSON_SIZE
LOOP setAllPartyMembers
NASM 实际上有 "struc" 宏,这会为您节省一些手动计算正确大小的麻烦(就像我上面所做的那样):
; defining "PERSON" structure with base offset 13 in NASM
; (but you want particular attributes aligned to their size boundary)
STRUC person, 13
alignb 2
.x: resw 1
.y: resw 1
alignb 4
.hp: resd 1
.mana: resd 1
.level: resw 1
;...
.prestidigitation: resb 1
.size:
ENDSTRUC
; player single person with some basic init (e.g. in .data segment)
SEGMENT .data
playerPerson1: ISTRUC person
AT person.hp, dd 100
AT person.mana, dd 100
AT person.level, dw 1
IEND
; .. somewhere in the code:
; moving player by ax on x, bx on y
; and decreasing hp by 1 (it's a trap!)
MOV edi,playerPerson1
ADD [edi+person.x],ax
ADD [edi+person.y],bx
DEC DWORD [edi+person.hp]
; ...
(我没有调试我的代码,所以可能会有一些错误,但希望通过阅读它的原理很容易理解)
我正在制作游戏"Connect Four"。我有一块 4x4 的方板。对于板上的每个瓷砖,我必须绘制一个圆盘(方形),它具有 x 值和 y 值。问题是我必须制作 32 个变量或很多过程,这些过程会一直更改这两个变量,但这些选项看起来效率很低,而且代码会很长。如果您有什么更好的建议,请告诉我。
*注意 - 我正在使用 x86 程序集和 TASM 这是代码:
proc CheckPlayer1Number
mov ah, 7
int 21h
cmp al, 31h
je CheckColumn1
jmp CheckPlayer1Number
endp CheckPlayer1Number
proc CheckColumn1
cmp [FirstColumnArray], 0
je ChangeColumnNumber1
endp CheckColumn1
proc ChangeColumnNumber1
inc [FirstColumnArray]
mov [Player1Drawx], 25h
mov [Player1Drawy], 87h
jmp DrawPlayer1Disc
endp ChangeColumnNumber1
DrawPlayer1Loop:
mov bh,0h
mov cx,[Player1Drawx]
mov dx,[Player1Drawy]
mov al,[player1disccolor]
mov ah,0ch
int 10h
inc [Player1Drawx]
cmp cx, [Player1Drawx + 14h]
jl DrawPlayer1Loop
DrawPlayer1Disc:
mov bh, 0h
inc [Player1Drawy]
cmp dx, [Player1Drawy + 14h]
jl DrawPlayer1Loop
我只编写了一些程序,只是为了看看代码是否有效。
感谢您的帮助。
使用数组而不是单独的变量?就像四个 (x,y) 值的四个数组的数组。然后你只需要数组的基地址,然后你可以通过偏移量得到数组的所有元素。
在 C 中它会是这样的
struct Disc
{
int x;
int y;
};
struct Disc array[4][4];
C 是一种非常低级的语言,接近 "to the metal",并且在某些情况下(尤其是在其历史的早期)可以被视为一种非常高级的宏汇编程序。
C 中的数组是连续且连续的内存块,每个元素一个接着一个,因此计算数组中特定元素的偏移量非常容易。事实上,这就是 C 编译器为您所做的,这也是数组索引在 C 中以零开始而不是像同一时代的许多其他高级语言中的 1 的原因。
对于像这样的简单数组
int a[4];
数组中每个元素的偏移量是索引乘以数据大小。要获取元素 0(即 a[0]
),您需要执行 0 * sizeof(int)
,要获取第二个元素,您需要执行 1 * sizeof(int)
.
现在采用我上面显示的结构数组的数组,您使用相同的原理来计算从内存开始的偏移量:索引乘以元素大小。
如果我们向后开始,结构是两个 int
成员,在典型的 32 位机器上总共是 8 个字节(每个成员是一个 4 字节(32 位)整数,两个成员) .这意味着内部数组是 8 个字节乘以 4 个元素,或 32 个字节。要获取数组中的特定元素,请将索引乘以 8(结构大小)并将其作为偏移量添加到数组的开头。
外层数组每个元素为32字节(每个元素是4个结构的数组),这意味着上面显示的array
的总大小为128字节。要获取该数组中的元素,您需要将从零开始的索引乘以 32。
使用上面的方法,获取位于 array[i][j]
的结构(对于 i
和 j
的任意但在范围内的值),您将获得基地址array
,加上i * 32
得到内部数组,然后加上j * 4
得到实际的结构。在伪代码(不是真正的 C)中它看起来像
structure = array + i * 32 + j * 4
这为您提供了特定结构的地址。现在获取结构的成员是一样的。成员 x
位于偏移量 0,成员 y
位于偏移量 4(前一个成员的大小)。再次使用伪代码:
x = structure
y = structure + 4
所以要处理你的电路板,你只需要三个 "variables":外部数组的基地址,以及索引 i
和 j
。
我的 x86 程序集生锈了,我很久以前就用过它,但是计算偏移量并将正确的结构变量移动到寄存器中很容易 IIRC。
32 个变量通常相当于 32 个项目大小的单个变量(数组)。
而不是使用变量名称 square_3_1_x
,您想到的是 "mapping function",例如,您将 4x4 正方形作为每行 0,1,2.. 来处理。
所以 square(row,column)
索引是 ((row
-1)*4+(column
-1)) for row/column in 1..4 range.
然后对 x
使用 *2 和 +0 / y
使用 +1:3_1_x
是 16
,3_2_y
是 19
.
或 x
为 +0,y
为 +16(3_1_x
为 8
,3_2_y
为 25
)。
或者您能想到的任何您喜欢的映射。
假设您想要在 32b x86 汇编程序(NASM 语法)中的第 3 行 (1..4) 和第 2 列 (1..4) 处获取光盘的 [x,y] 位置(我喜欢你没有指定你的平台和语法,所以人们回答可以选择他们想要的任何东西,然后你将不得不理解完全陌生的语法,并将其转换为你的最终来源,你非常慷慨):
discPositions: times 32 dd 0 ; 16+16 for x+y couples (16 = 4x4).
GetDiscPosition:
; input: row (1..4) in eax, column (1..4) in ebx
; output: x position in eax, y position in ebx
dec eax ; row-1
dec ebx ; column-1
shl eax,2 ; (row-1)*4
add eax,ebx ; (row-1)*4+(column-1)
shl eax,3 ; ((row-1)*4+(column-1))*2*4
; *2 to cover [x,y] pairs, *4 to respect data size (DWORD)
; so here eax is byte offset to [x,y] couple in discPositions array
; it would make sense to have that offset calculation in another
; subroutine, so you can reuse it for SetDiscPosition routine
; Now just return the [x,y] stored in the 128B memory array labeled discPositions
mov ebx,[discPositions+eax+4] ; fetch y position
mov eax,[discPositions+eax+0] ; fetch x position
ret
; ... now somewhere in your code, where you want to get disc's x,y for square(3,4)
mov eax,3
mov ebx,4
call GetDiscPosition
; eax now has x position, ebx has y position
; ...
编辑: 好吧,既然这个答案得到了两次投票,看起来这些东西可能对其他人有帮助(多亏了问题的慷慨措辞?)。
所以我将从第二个共同的角度来解决这个问题。假设您确实有 32 个变量,例如角色扮演游戏中角色的属性,有些只需要 byte
(强度) , 一些 qword
(experience), 记住 health 有索引 4
很快就会变得非常烦人,并且 prestidigitation 具有索引 29
.
此外,对于更新的特定部分,您通常只需要其中几个,例如计算用剑造成伤害的角色不需要 elocution 和 charisma,但你还需要几个具有相同统计数据的不同角色(玩家对敌人),所以你仍然缺少寄存器。
在这种情况下,我通常只是模仿类 C 的结构:
; defining "PERSON" structure
PERSON_X equ 0 ;2B
PERSON_Y equ PERSON_X+2 ;2B
PERSON_HP equ PERSON_Y+2 ;4B
PERSON_MANA equ PERSON_HP+4 ;4B
PERSON_LEVEL equ PERSON_MANA+4 ;2B
;...
PERSON_PRESTIDIGITATION equ PERSON_ELOCUTION+1 ;1B
PERSON_SIZE equ PERSON_PRESTIDIGITATION+1
; reserving array for 4 player's characters
playersParty: resb 4*PERSON_SIZE
; ... somewhere in the code:
; teleporting whole party at x,y=(32,64)
MOV ax,32 ; new x
MOV bx,64 ; new y
MOV ecx,4
MOV edi,playersParty
setAllPartyMembers:
MOV [edi+PERSON_X],ax
MOV [edi+PERSON_Y],bx
ADD edi,PERSON_SIZE
LOOP setAllPartyMembers
NASM 实际上有 "struc" 宏,这会为您节省一些手动计算正确大小的麻烦(就像我上面所做的那样):
; defining "PERSON" structure with base offset 13 in NASM
; (but you want particular attributes aligned to their size boundary)
STRUC person, 13
alignb 2
.x: resw 1
.y: resw 1
alignb 4
.hp: resd 1
.mana: resd 1
.level: resw 1
;...
.prestidigitation: resb 1
.size:
ENDSTRUC
; player single person with some basic init (e.g. in .data segment)
SEGMENT .data
playerPerson1: ISTRUC person
AT person.hp, dd 100
AT person.mana, dd 100
AT person.level, dw 1
IEND
; .. somewhere in the code:
; moving player by ax on x, bx on y
; and decreasing hp by 1 (it's a trap!)
MOV edi,playerPerson1
ADD [edi+person.x],ax
ADD [edi+person.y],bx
DEC DWORD [edi+person.hp]
; ...
(我没有调试我的代码,所以可能会有一些错误,但希望通过阅读它的原理很容易理解)