assembly x86 - 如何在变量

时间:2017-08-21 10:45:45

标签: assembly x86 tasm dosbox

(通过DOSBOX运行程序集x86)

我使用程序加载图像(bmp文件),第一个是加载文件(打开文件):

proc OpenFile
    mov ah, 3Dh
    xor al, al
    mov dx, offset filename
    int 21h
    jc openerror1
    mov [filehandle], ax
    ret
    openerror1:
    mov dx, offset ErrorMsg
    mov ah, 9h
    int 21h
    ret
endp

我希望将filename用作文件名的全局变量 - 其路径的字符串,因此在运行所有过程之前我唯一需要做的就是将正确的路径移动到文件名变量。

filename db ?
img1 db 'img1.bmp',0
img2 db 'img2.bmp',0

我认为它是某种字符串传输,我发现MOVS命令可能会有所帮助,但我没有理解它的工作方式。

1 个答案:

答案 0 :(得分:5)

一个简短的例子MOVS如何工作("字符串"指令系列中的其他人类似)。

首先你需要在内存中占用空间,所以在你的情况下必须扩展filename

MAX_FILE_NAME_LENGTH    EQU  128
filename    db MAX_FILE_NAME_LENGTH dup (?)   ; reserving enough space for "string"
img1        db 'img1.bmp',0
img1length  equ $-img1
img2        db 'img2.bmp',0
img2length  equ $-img2

现在复制img2" string" into filename ... img2是内存中的地址,其中以下img2length字节由上面的db定义(包括零分隔符)。目标地址是filename符号。 MOVSds:si的数据从si(&{34;来源")复制到es:didi为"目的地&#34 ;)

...
; make sure the DS and ES are set up correctly, if you use data segment, then
; (this can be done just once, if you don't change ds/es in your code any more)
mov ax, @data
mov ds, ax
mov es, ax
cld             ; DF=0, if you don't plan to use DF=1 elsewhere
  ; DF=0 means, that the "string" instructions increment SI/DI
  ; DF=1 would make them run backwards, decrementing addresses
  ; (that's handy when implementing "memmove" with overlapping areas)
...

...
; now prepare registers (except ds+es) for `rep movsb` variant
mov cx, img2length   ; how many bytes to copy (numeric value)
lea si, [img2]       ; offset img2 into SI
lea di, [filename]   ; offset filename into DI
rep movsb            ; copy "img2length" bytes from ds:si to es:di

; check memory in debugger, the reserved area at "filename" should
; now contain the copied string, including the zero terminator

; WARNING, if you forget about reserved space limitations, and define
; img2 string longer than MAX_FILE_NAME_LENGTH, the code above will
; merrily overwrite more memory than expected, destroying values in memory
; beyond the filename buffer

...

另一个带有指针示例的变体:

通常的做法是将值传递给函数作为参数,在这种情况下,您可以要求调用者在ds:dx调用之前预先设置OpenFile,然后您只需省略dx在程序中设置代码,你就完成了,例如:

; arguments: ds:dx = pointer to ASCIIZ file name
; returns (and modifies): ax = file handle
; in case of file error "..." happens
proc OpenFile
    mov ax, 3D00h    ; ah = 3D "open file", al = 0 "read-only"
    int 21h
    jc openerror1
    ret
    openerror1:
    ... ; probably terminate app any way in case of error

然后在每个调用之前设置ds:dx,并在存储文件句柄之后:

    ...
    ; let's pretend the DS was already set before
    mov    dx, offset img1
    call   OpenFile
    mov    [img1FileHandle],ax
    ...

数据设置如下:

img1 db 'img1.bmp',0
img1FileHandle dw 2    ; DW, because handle is 16 bit "wide" (AX = 16 bits)
  ; 2 == STDERR, until the code will run OpenFile and store real handle

也可以将这些东西放入内存中的全局变量中,然后在OpenFile内的内存中读取它们,但是如果你试着写它,你会看到它相当繁琐的,在寄存器中传递参数更简单......直到你的代码变得复杂到足以忘记什么程序需要什么在哪个寄存器中,突然间它变得有点乱。

从那里开始遵循一些官方调用约定更好,比如cdecl和类似的,但16b / 32b模式的大多数调用约定确实使用堆栈来传递参数,这也是手工编写的有点乏味,并且比通过寄存器传递值更糟糕。对于小型纯asm应用程序,可以随意优化每个过程参数/结果,只需使用明确的描述对每个过程进行注释即可使用哪些寄存器。