简体   繁体   English

如何在 x86 中保留数组或字符串

[英]How to preserve an array or string in x86

I'm programming the game "15 puzzle game" in x86 using tasm asm/dosbox.我正在使用 tasm asm/dosbox 在 x86 中编写游戏“15 益智游戏”。 I want to "save" or "preserve" my array called 'a' that is being declared in my data segment, so that after I swap the bytes around using upArrow, downArrow, etc... and then I press 'n' for new game, I can set my array 'a' back to it's original state as it was declared in my data segment.我想“保存”或“保留”在我的数据段中声明的名为“a”的数组,以便在使用 upArrow、downArrow 等交换字节后...然后按“n”新游戏,我可以将我的数组“a”设置回原来的 state,因为它在我的数据段中声明。 Here is my code:这是我的代码:

.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

The way I see it is there are 2 ways of doing this.我认为有两种方法可以做到这一点。 One way would be to create an empty array, and every time the user makes a move (up/down/left/right) I can push this to a stack, and then when the user hits 'n' for new game, I just pop through that stack and reverse all the moves.一种方法是创建一个空数组,每次用户移动(上/下/左/右)时,我都可以将其推送到堆栈中,然后当用户为新游戏点击“n”时,我只是弹出该堆栈并反转所有动作。

Another alternative would be to create 2 identical boards (arrays/string) 'a' and 'b', and then the user would manipulate board 'a' till they decide to hit 'n' for a new game, and then I would some how set 'a' = 'b'另一种选择是创建2个相同的板(数组/字符串)'a'和'b',然后用户将操纵板'a'直到他们决定为新游戏点击'n',然后我会一些如何设置'a' = 'b'

There's no magic way, you just have to copy the array like you would in C.没有什么神奇的方法,你只需要像在 C 中一样复制数组。 I'd suggest having one master copy of the data that you keep clean / untouched, never ever writing into it.我建议拥有一份您保持干净/未触及的数据的主副本,永远不要写入它。

Allocate uninitialized space for the working game state somewhere, eg on the stack or in the BSS 1 .为工作游戏 state 分配未初始化的空间,例如在堆栈上或 BSS 1中。

At the start of every game (including the first one), copy the preset whole array to the scratch space for the current game.在每场比赛开始时(包括第一场比赛),将预设的整个数组复制到当前比赛的暂存空间。 (eg with rep movsb or rep movsw after setting CX, SI, and DI. ES and DS segment regs are already equal segment because of.model tiny. ie implement a memcpy however you like.) (例如,在设置 CX、SI 和 DI 之后使用rep movsbrep movsw和 DS 段 regs 已经是相等的段,因为。model tiny。即实现一个 memcpy,但你喜欢。)

So you don't need to write the initializer data twice or anything like that, and only one copy of it needs to be in the file.所以你不需要写两次初始化数据或类似的东西,文件中只需要一个副本。

And no you don't need any exchanging, just a block copy.不,你不需要任何交换,只是一个块副本。 Get the assembler to calculate the size of the whole thing by putting a label at the end, or put datasize = $ - a right after the end of a to get the size in bytes.通过在末尾放置 label 或将datasize = $ - a放在a的末尾之后,让汇编器计算整个事物的大小,以获取以字节为单位的大小。


Footnote 1 : See @MichaelPetch's comment for details on how to declare a label in the .data?脚注 1 :有关如何在.data? segment so you can use normal label/symbol syntax.段,以便您可以使用正常的标签/符号语法。 Like gamestate datasize dup(?) , where datasize is an = or equ constant you got the assembler to calculate earlier from the size of the preset.gamestate datasize dup(?)一样,其中datasize是一个=equ常量,您可以让汇编器从预设的大小更早地进行计算。

I think .data?我认为.data? is just space past the end of where DOS loads your .com file, and you can use space from there out to near the end of a 64k segment where the program-loader put your stack.只是 DOS 加载您的.com文件的末尾之后的空间,您可以使用从那里到程序加载器放置堆栈的 64k 段末尾附近的空间。 (Unlike the.bss under a modern OS is not zeroed to start with. That's fine for this, you want to write the space you use before you read/modify it anyway.) (与现代操作系统下的 .bss 不同,一开始并没有归零。这很好,你想在阅读/修改之前写下你使用的空间。)


@DavidWohlferd also suggests you could re-read part of the executable from disk, with DOS file open/read system calls. @DavidWohlferd 还建议您可以使用 DOS 文件打开/读取系统调用从磁盘重新读取部分可执行文件。 That could be the start of a save-game mechanism.这可能是保存游戏机制的开始。 In its simplest form, if the first n bytes of your program are all read-only (code and constant data) apart from this game-state array, you could read the first n bytes of the raw .com file into memory at ds:100h (or cs or es, it's all the same in a tiny memory model).在最简单的形式中,如果您的程序的前n个字节都是只读的(代码和常量数据),除了这个游戏状态数组,您可以将原始.com文件的前n个字节读取到ds:100h (或cs或es,在微型memory模型中都是一样的)。 Overwriting instructions with themselves (like a jmp rel16 over the data) is fine.用自己覆盖指令(比如对数据的jmp rel16 )很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM