我需要有关 emu8086 中字符串的帮助。我已经初始化了一个字符串:
str1 db "0neWord"
我有一个空字符串:
str2 db ?
现在我需要检查所有的字母str1
并复制到str2
,但是如果输入的字母str1
是0,我需要用O替换它。如果不是,我只需要复制这个字母。
我怎样才能做到这一点?
str2 db ?
不是空字符串。db
代表“定义字节”,这?
意味着单个未初始化的字节。
这db "0neWord"
是汇编程序的便利,它将编译成定义为的一系列字节'0', 'n', 'e', ..., 'd'
。汇编中没有“字符串”类型,一切都被编译成机器码,可以看作是一系列字节。存储在内存中的数据“类型”取决于用于访问它们的指令,但在内存中,所有内容都只是一系列字节,可以这样看待。
这可能是您检查 emu8086 调试器文档的好时机,并在str1
将代码加载到调试器后查看地址处的内存,看看它是如何编译的。
因此,一旦您将第二个字节从str1
to复制str2
,您将开始覆盖一些您没想到会覆盖的内存。
要分配一些固定大小的内存缓冲区,您可以使用例如对str2 db 100 DUP(?)
进行 100 次?
定义db
,从而在那里保留 100 字节的内存,同一节中的下一个字节的机器代码将被编译超出str2+100
地址。
要使用“字符串”做任何事情,str1
您需要知道:
1) 它在内存中的地址,x86 汇编器有很多方法可以得到它,但最简单的两种方法是:
mov <r16>,OFFSET str1
(r16 是任何 16b 寄存器)lea <r16>,[str1]
(在这种情况下做同样的事情)2)它的大小或结构。您没有在那里放置任何结构,例如以 nul 结尾的字符串0
在其末尾具有带有值的字节,或者int 21h, ah=9
显示字符串的 DOS 服务需要以美元符号结尾的字符串'$'
等。所以您至少需要大小。汇编程序的指令和EQU
“当前位置”可以用来计算str1
这样的大小:
str1 db "0neWord"
str1size EQU $-str1 ; "$" is assemblers "current_address" counter
嗯,我首先尝试通过阅读一些文档来验证这一点,但是我很难找到任何好的完整的 emu8086 文档(找到类似“参考”的东西,并且完全缺少对汇编程序指令的描述)。
我想知道为什么还有这么多人使用它,而不是完全免费、开源和文档化的 linux + nasm/similar。
所以让我们希望 emu8086 像 MASM/TASM 一样工作,并且我仍然正确地记得那个语法,那么上面提到的大小定义应该可以工作。否则请查阅您的示例/文档。
最后,当您拥有地址、大小和足够大的目标缓冲区(再次加载您可以使用的地址OFFSET
或lea
在 emu8086 中)时,您可以通过以下方式对您的任务进行编码:
; pseudo code follows, replace it by actual x86 instructions
; and registers as you wish
; ("r16_something" means one of 16b register, r8 is 8b register)
lea r16_str1,[str1] ; load CPU with address of str1
mov r16_counter,str1size ; load CPU with str1 size value
lea r16_str2,[str2] ; load address of target buffer
loop_per_character:
mov r8_char,[r16_str1] ; read single character
cmp r8_char,'0'
jne skip_non_ascii_zero_char
; the character is equal to ASCII '0' character (value 48)
mov r8_char,'O' ; replace it with 'O'
skip_non_ascii_zero_char:
; here the character was modified as needed, write it to str2 buffer
mov [r16_str2],r8_char
; make both str1/2 pointers to point to next character
inc r16_str1
inc r16_str2
; count down the counter, and loop until zero is reached
dec r16_counter
jnz loop_per_character
; the memory starting at "str2" should now contain
; modified copy of "str1"
; ... add exit instructions ...
嗯..原来“伪代码”是完整的 x86 代码,您只需将真实寄存器分配给伪寄存器,并在源代码中的任何地方替换它们。
我试图在那里发表非常广泛的评论(以我的观点),以便可以理解使用的每条指令。您应该使用 Intel 的指令参考指南来查阅每个指南,将其与您可用于组装的任何教程/课程进行交叉阅读,直到您觉得您了解什么是寄存器、内存等。
还要逐条调试代码指令,在每条指令之后检查 CPU 的状态(寄存器值、标志)和内存内容,以了解它是如何工作的。
有多种方法可以做到这一点。这里有些例子:
1)带字符串指令:
.model small
.data
str1 db "0neWord$"
size equ $-str1
str2 db size dup ('')
.code
main:
mov ax, @data
mov ds, ax
mov cx, size
cld ; DF might have been set, but we want lodsb to go forwards
lea si, str1
mov ax, 0
mov bx, 0
copyStr:
lodsb ;str1 to al
cmp al, '0'
je alterChar
mov str2[bx], al
jmp continue
alterChar:
mov str2[bx], 'o'
continue:
inc bx
loop copyStr
mov str2[bx], '$'
mov ah, 09h
lea dx, str2
int 21h
mov ah, 04ch
int 21h
end main
2)没有字符串指令:
.model small
.data
str1 db "0neWord$"
str2 db ?
.code
main:
mov ax, @data
mov ds, ax
mov si, 0
call copyStr
mov ah, 09h
lea dx, str2
int 21h
mov ah, 04ch
int 21h
copyStr proc
mov bx, 0
compute:
mov bl, str1 [si]
cmp bl, '0'
je alterChar
mov str2[si], bl
jmp continue
alterChar:
mov str2 [si], 'o'
continue:
inc si
cmp str1[si], '$'
je return
jmp compute
return:
mov str2[si], '$'
ret
copyStr endp
end main
LODSB 指令,您可以从此处了解有关字符串指令的更多信息
3) 使用 lodsb / stosb 并简化 / 优化:
.model small
.data
str1 db "0neWord$"
size equ $-str1
str2 db size dup ('')
.code
main:
mov ax, @data
mov ds, ax
mov es, ax ; stosb stores to [es:di]
mov si, OFFSET str1
mov di, OFFSET str2
cld ; make sure stosb/lodsb go forwards
; copy SI to DI, including the terminating '$'
copyStr: ; do {
lodsb ; str1 to al
cmp al, '0'
je alterChar
doneAlteration:
stosb ; al to str2
cmp al, '$'
jne copyStr ; } while(c != '$')
mov ah, 09h ; print implicit-length string
mov dx, OFFSET str2
int 21h
mov ah, 04ch ; exit
int 21h
alterChar:
mov al, 'o'
;jmp doneAlteration
stosb
jmp copyStr
end main