首页 > 解决方案 > 如何在 x86 中保留数组或字符串

问题描述

我正在使用 tasm asm/dosbox 在 x86 中编写游戏“15 益智游戏”。我想“保存”或“保留”在我的数据段中声明的名为“a”的数组,以便在我使用 upArrow、downArrow 等交换字节之后......然后按“n”新游戏,我可以将我的数组“a”设置回它在我的数据段中声明的原始状态。这是我的代码:

.MODEL TINY
.386

.DATA
PROMPT DB "Enter q to quit, arrow keys to move, n to restart (new game) HAPPY HOLIDAYS!! :)$"

;this is board preset A
a       DB 201,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,187,
    DB 186,255,48,50,255,186,255,48,49,255,186,255,48,51,255,186,255,48,52,255,186,255,48,53,255,186,255,48,54,255,186,
    DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185,
    DB 186,255,48,55,255,186,255,48,56,255,186,255,48,57,255,186,255,49,48,255,186,255,49,49,255,186,255,49,50,255,186,
    DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185, 
    DB 186,255,49,51,255,186,255,49,52,255,186,255,49,53,255,186,255,49,54,255,186,255,49,55,255,186,255,255,255,255,186,
    DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185, 
    DB 186,255,49,56,255,186,255,49,57,255,186,255,50,48,255,186,255,50,49,255,186,255,50,50,255,186,255,50,51,255,186,
    DB 200,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,188,"$"


col DB 0
row DB 0
board DB 0

.CODE
org 100h
MAIN PROC

;initialize display step 0
MOV AH, 00h
MOV AL, 03h
INT 10h

MOV AX, 0B800h
MOV ES, AX
XOR di,di


;initialize configuration and dislay prompt step 1
newGame:
call initConfig

;display board step 2


;wait for key step 3
game:
;print board
call printBoard1

;get cursor x y
call SetCursor

MOV AH, 0
INT 16H

MOV BL, AL

;up arrow
CMP AH, 48h
jz upArrow

;down arrow
CMP AH, 50h
jz downArrow

;right arrow
CMP AH, 4dh
jz rightArrow

;left arrow
CMP AH, 4bh
jz leftArrow

;lowercase q
CMP AL, 71h
jz EXIT

;uppercase q
CMP AL, 51h
jz EXIT

;lowercase n
CMP AL, 6eh
jz newGame

;uppercase n
CMP AL, 4eh
jz newGame

jmp game

MAIN ENDP

EXIT:
MOV AH, 4CH                  ; return control to DOS
INT 21H

SetCursor:
mov dl, col
mov dh, row
mov bh, 0
mov ah, 02h
int 10h
ret

getStrlen:
lodsb
cmp al, 24h
je strlen_end
inc bx
jmp getStrlen
strlen_end:
ret

loadAndDisplayStr:
lodsb
stosw
dec cx
jne loadAndDisplayStr
ret

printBoard1:
MOV board, 1
xor dx,dx
xor ax,ax
mov cx, 9
myloop1:
push cx
lea si, a
add si, dx
mov cx, 31
mov di, ax
push ax
mov ah, 4
call loadAndDisplayStr
pop ax
add ax, 160
add dx, 32
pop cx
dec cx
jnz myloop1
ret

upArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
sub row, 2
mul row
add al, col
mov di, ax
mov ax, 32
add row, 2
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
sub row, 2
jmp game

downArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
add row, 2
mul row
add al, col
mov di, ax
mov ax, 32
sub row, 2
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
add row, 2
jmp game

leftArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
sub col, 5
mul row
add al, col
mov di, ax
mov ax, 32
add col, 5
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
sub col, 5
jmp game

rightArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
add col, 5
mul row
add al, col
mov di, ax
mov ax, 32
sub col, 5
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
add col, 5
jmp game

initConfig:
;put the cursor at 00
mov col, 27
mov row, 5

;gets strlen of prompt and stores it in BX
lea si, PROMPT
xor bx, bx
call getStrlen

;display prompt
lea si, PROMPT
mov cx, bx
mov ah, 2
mov di, 5a0h
call loadAndDisplayStr
ret

END MAIN

我认为有两种方法可以做到这一点。一种方法是创建一个空数组,每次用户移动(上/下/左/右)时,我都可以将其推送到堆栈中,然后当用户为新游戏点击“n”时,我只是弹出该堆栈并反转所有动作。

另一种选择是创建2个相同的板(数组/字符串)'a'和'b',然后用户将操纵板'a'直到他们决定为新游戏点击'n',然后我会一些如何设置'a' = 'b'

标签: arraysassemblymemoryx86x86-16

解决方案


没有什么神奇的方法,您只需要像在 C 中一样复制数组。 我建议您拥有一份保持干净/未触及的数据主副本,永远不要写入它。

在某处为工作游戏状态分配未初始化的空间,例如在堆栈上或在 BSS 1中。

在每场比赛开始时(包括第一场比赛),将预设的整个数组复制到当前比赛的暂存空间。(例如,在设置 CX、SI 和 DI的情况下rep movsb或之后。ES 和 DS 段 regs 已经是相等的段,因为 .model 很小。即实现一个 memcpy,但是你喜欢。)rep movsw

所以你不需要写两次初始化数据或类似的东西,文件中只需要一个副本。

不,你不需要任何交换,只是一个块副本。让汇编程序通过在末尾放置一个标签来计算整个事物的大小,或者放在datasize = $ - a末尾之后a以获取以字节为单位的大小。


脚注 1.data? :有关如何在段中声明标签以便您可以使用普通标签/符号语法的详细信息,请参阅@MichaelPetch 的评论。像gamestate datasize dup(?),哪里datasize是一个=equ常数,你让汇编器从预设的大小更早地计算出来。

我认为.data?只是DOS加载.com文件末尾的空间,您可以使用从那里到程序加载器放置堆栈的64k段末尾附近的空间。(与现代操作系统下的 .bss 不同,一开始并没有归零。这很好,你想在阅读/修改之前写下你使用的空间。)


@DavidWohlferd 还建议您可以使用 DOS 文件打开/读取系统调用从磁盘重新读取部分可执行文件。这可能是保存游戏机制的开始。以最简单的形式,如果n您的程序的第一个字节除了这个游戏状态数组之外都是只读的(代码和常量数据),那么您可以n将原始.com文件的第一个字节读入内存ds:100h(或 cs 或 es,在一个微小的内存模型中都是一样的)。用自己覆盖指令(如覆盖jmp rel16数据)很好。


推荐阅读