arrays - 如何在 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'
解决方案
没有什么神奇的方法,您只需要像在 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
数据)很好。
推荐阅读
- javascript - 使用 Redux 从 DB 获取 EditorState(DraftJS)
- html - 具有可滚动内容的 Flexbox 完整父级的宽度和高度
- python - 基于列合并 DataFrame
- android - google play 是否会修改或优化已发布的 APK?
- plugins - 是否可以使用没有与文件名相关的链接的 mkdocs
- ios - iOS/macOS app distribution failed while using framework after upgrading Xcode12 and macOS BigSur
- ibm-doors - 为了练习 DXL 的使用,有没有办法在本地安装 IBM DOORS?
- ruby-on-rails - 如何从 Rails API 查询对象列表?
- spring-cloud-stream - 使用 Azure Eventhub 的 Spring 云流绑定器
- python - 如何使用 OpenCV 读取货架产品条形码