汇编 - 如何使用更少的变量

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] 的结构(对于 ij 的任意但在范围内的值),您将获得基地址array,加上i * 32得到内部数组,然后加上j * 4得到实际的结构。在伪代码(不是真正的 C)中它看起来像

structure = array + i * 32 + j * 4

这为您提供了特定结构的地址。现在获取结构的成员是一样的。成员 x 位于偏移量 0,成员 y 位于偏移量 4(前一个成员的大小)。再次使用伪代码:

x = structure
y = structure + 4

所以要处理你的电路板,你只需要三个 "variables":外部数组的基地址,以及索引 ij

我的 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_x163_2_y19.

x 为 +0,y 为 +16(3_1_x83_2_y25)。

或者您能想到的任何您喜欢的映射。

假设您想要在 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.

此外,对于更新的特定部分,您通常只需要其中几个,例如计算用剑造成伤害的角色不需要 elocutioncharisma,但你还需要几个具有相同统计数据的不同角色(玩家对敌人),所以你仍然缺少寄存器。

在这种情况下,我通常只是模仿类 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]
    ; ...

(我没有调试我的代码,所以可能会有一些错误,但希望通过阅读它的原理很容易理解)