assembly x86 - 如何在变量之间移动字符串

YUV*_*RON 0 x86 assembly 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
Run Code Online (Sandbox Code Playgroud)

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

filename db ?
img1 db 'img1.bmp',0
img2 db 'img2.bmp',0
Run Code Online (Sandbox Code Playgroud)

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

Ped*_*d7g 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
Run Code Online (Sandbox Code Playgroud)

现在将img2"string" 复制到filename... img2是地址到内存中,其中以下img2length字节由db上面定义(包括零分隔符).目标地址是filename符号.将MOVS数据从ds:si(si作为"源")复制到es:di(di作为"目标").

...
; 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

...
Run Code Online (Sandbox Code Playgroud)

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

通常的做法是值传递给函数作为参数,在这种情况下,你可能会问呼叫者预先设定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
Run Code Online (Sandbox Code Playgroud)

然后在每次调用之前设置ds:dx,然后根据需要存储文件句柄:

    ...
    ; let's pretend the DS was already set before
    mov    dx, offset img1
    call   OpenFile
    mov    [img1FileHandle],ax
    ...
Run Code Online (Sandbox Code Playgroud)

数据设置如下:

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
Run Code Online (Sandbox Code Playgroud)

也可以将这些东西放入内存中的全局变量,然后从内存中读取它们OpenFile,但是如果你试着写它,你会发现它非常麻烦,在寄存器中传递参数更简单......到了一定程度,直到你的代码变得足够复杂,忘记了什么程序需要在哪个寄存器中,突然间它变得有点乱.

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