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命令可能会有所帮助,但我无法理解它的工作方式.
只是一个简短的例子,如何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应用程序,可随意优化每个过程参数/结果,并且只需使用明确的描述对每个过程进行注释即可使用哪些寄存器.