简体   繁体   English

程序集x86-如何在变量之间移动字符串

[英]assembly x86 - how to move strings between variables

(running assembly x86 through DOSBOX) (通过DOSBOX运行程序集x86)

I'm using procedures to load an image (bmp file) and the first one is to load the file (open it): 我正在使用过程加载图像(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

I want filename to be used as a global variable for a name of a file - string of its path so the only thing I have to do before running all the procs is to move the right path to the filename variable. 我希望将filename用作文件名的全局变量-文件路径的字符串,因此在运行所有proc之前,我要做的唯一一件事就是将正确的路径移至filename变量。

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

I thought it's some kind of string transferring and I found out that the MOVS command might be helpful but I didn't manage to understand the way it works. 我认为这是某种字符串传输方式,但我发现MOVS命令可能会有所帮助,但我无法理解它的工作方式。

Just a short example how MOVS works (and others from the "string" instruction family are similar). 只是一个简短的示例, MOVS是如何工作的(与“字符串”指令族的其他类似)。

First you need space in memory, so in your case that filename has to be extended. 首先,您需要在内存中留出空间,因此在这种情况下,必须扩展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

Now to copy img2 "string" into filename ... img2 is address into memory, where the following img2length bytes are defined by the db above (including the zero separator). 现在将img2 “字符串”复制到filename …… img2是内存中的地址,以下img2length字节由上面的db定义(包括零分隔符)。 And target address is the filename symbol. 目标地址是filename符号。 The MOVS copies data from ds:si ( si as "Source") to es:di ( di as "Destination"). MOVS数据从ds:sisi作为“源”)复制到es:didi作为“目标”)。

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

...

Another variant with pointers example: 带有指针示例的另一个变体:

Common practice is to pass value to function as an argument, in this case you may ask the caller to pre-set ds:dx ahead of OpenFile call, then you just omit the dx setup code in the procedure, and you are done, example: 通常的做法是将值传递为参数,在这种情况下,您可以要求调用方在OpenFile调用之前预先设置ds:dx ,然后只需在过程中省略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

Then ahead of each call you set up ds:dx, and after store the file handle as you wish: 然后在每次调用之前设置ds:dx,然后根据需要存储文件句柄:

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

Data being set up like this: 数据设置如下:

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

It's also possible to put those things into globals in memory, and then read them from memory inside OpenFile , but if you will try to write that, you will see it's quite cumbersome, passing arguments in registers is simpler... up to a point, until your code gets complex enough to forget what procedure needs what in which register, and suddenly it becomes a bit of mess. 也可以将这些内容放入内存中的全局变量中,然后从OpenFile内存中读取它们,但是如果您尝试编写该代码,则会发现它很麻烦,在寄存器中传递参数更简单了…… ,直到您的代码变得足够复杂而忘记了哪个过程需要哪个寄存器中的内容,突然之间它变得一团糟。

From there it's better to follow some official calling convention, like cdecl and similar, but most of calling conventions for 16b/32b modes did use stack for passing arguments, which is again a bit tedious to write by hand, and also performs worse than passing values by registers. 从那里最好遵循一些官方的调用约定,例如cdecl和类似的约定,但是大多数16b / 32b模式的调用约定确实使用了栈来传递参数,这再次手工编写有点繁琐,并且比传递更差按寄存器的值。 For small pure asm applications feel free to optimize each procedure arguments/results as they fit, and just comment each procedure with clear description which registers are used. 对于小型的纯asm应用程序,可以根据需要随意优化每个过程的参数/结果,只需注释每个过程并清楚说明所使用的寄存器。

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

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