我在一段时间后涉足大会(NASM风味),我正试图再次学习它。 (我知道我可以先计算字符串长度,但这是一个练习)
无论如何,我编写了这段代码,使用stdout
将空终止字符串打印到sys_write
。 (我打算概括一下,我现在正在测试。)
似乎它会起作用,因为如果我在调用i
之前递增sys_write
,那么它会打印'e'
,但如果我之后递增它,那么它会打印'H'
as预期。但是,只要遇到jne print_loop
,就会产生运行时错误,错误代码为-1
。我尝试了几个跳转指令,它们都崩溃了,但是一旦我删除了跳转,程序就会运行而没有任何错误。
SECTION .data
hello:
db "Hello World!/n",0
SECTION .bss
i:
resb 1
SECTION .text
global _start
_start:
mov ecx, hello
call print
mov eax, 1
mov ebx, 0
int 80h
print:
mov eax, 4
mov ebx, 1
mov edx, 1
print_loop:
push eax
mov eax, [i]
lea ecx, [ecx+eax]
pop eax
int 80h
inc dword [i]
cmp ecx, 0
jne print_loop ; if I comment this out, it runs without error.
ret
这是固定版本:
%macro print_ 1
mov eax, 4
mov ebx, 1
mov ecx, %1
call print
%endmacro
%macro exit_ 1
mov eax, 1
mov ebx, %1
int 80h
%endmacro
SECTION .data
hello:
db "Hello World!/n",0
SECTION .bss
i:
resb 1
SECTION .text
global _start
_start:
print_ hello
exit_ 0
;print code called by print_ macro
print:
push ecx
count:
inc ecx
cmp byte [ecx], 0
jne count
mov edx, ecx
pop ecx
sub edx, ecx
int 80h
ret
答案 0 :(得分:1)
代码有几个问题:
i:
resb 1
这里为一个字节保留空格,但在打印功能中,您将i
视为双字。
int 80h
这会废弃eax
的旧值,因为eax
包含系统调用的返回值。因此,您需要在每次迭代时使用系统调用号(4)重新加载eax
。
lea ecx, [ecx+eax]
在这里,您使用&string[i]
覆盖字符串的基址,这将导致后续迭代中的地址不正确,因为您不保留原始ecx
值。
cmp ecx, 0
我不确定为什么你期望ecx
为0.更有意义的是加载存储在[ecx]
的字节并将 与零进行比较(之前)系统调用)。
答案 1 :(得分:0)
有几个问题。
首先:
mov eax, [i]
i
是一个字节变量,但该指令从其位置读取4个字节。
inc dword [i]
i
是一个字节变量,但该指令在其位置增加一个4字节的值。
第二(我认为):
在此指示之后ecx
将会是什么
lea ecx, [ecx+eax]
第二次执行?它仍然是指向hello
的有效地址吗?
顺便说一下,学会使用调试器。现在是时候了。