NASM 程序集 while 循环计数器
NASM Assembly while loop counter
我正在汇编中编写一个 while 循环,以便在 Linux 终端中使用 nasm 和 gcc 进行编译。该程序比较 x 和 y 直到 y >= x 并在最后报告循环数。这是代码:
segment .data
out1 db "It took ", 10, 0
out2 db "iterations to complete loop. That seems like a lot.", 10, 0
x db 10
y db 2
count db 0
segment .bss
segment .text
global main
extern printf
main:
mov eax, x
mov ebx, y
mov ecx, count
jmp lp ;jump to loop lp
lp:
cmp ebx, eax ;compare x and y
jge end ;jump to end if y >= x
inc eax ;add 1 to x
inc ebx ;add 2 to y
inc ebx
inc ecx ;add 1 to count
jp lp ;repeat loop
end:
push out1 ;print message part 1
call printf
push count ;print count
call printf
push out2 ;print message part 2
call printf
;mov edx, out1 ;
;call print_string ;
;
;mov edx, ecx ;these were other attempts to print
;call print_int ;using an included file
;
;mov edx, out2 ;
;call print_string ;
这是在终端中编译和 运行 的:
nasm -f elf test.asm
gcc -o test test.o
./test
终端输出为:
It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)
我看不出逻辑有什么问题。我认为这是语法问题,但我们才刚刚开始学习汇编,我尝试了各种不同的语法,例如变量周围的括号和在段末尾使用 ret
,但似乎没有任何效果。我还搜索了分段错误,但没有发现任何真正有用的东西。任何帮助将不胜感激,因为我是一个绝对的初学者。
它崩溃的原因可能是您的 main
函数没有 ret
指令。还要确保将 eax
设置为 0 以表示成功:
xor eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret
此外,全局变量指定指针,而不是值。 mov eax, x
将 eax
设置为 x
的地址。如果您希望发生任何事情(或不使用全局变量),则需要写回它。
最后,您使用一个非字符串参数调用 printf
:
push count ;print count
call printf
第一个参数需要是格式字符串,如 "%i"
。这里,count
是一个指向空字节的指针,所以你什么也得不到。别想了,你应该试试这个:
out3 db "%i ", 0
; snip
push ecx
push out3
call printf
我认为您的问题可能只是您引用的是常量的地址,而不是它们的内在值。人们必须将 nasm 中的标签视为指针而不是值。要访问它,您只需要使用 [label]
:
segment .data
x dw 42
segment .text
global main
extern printf
main:
mov eax, x
push eax
call printf ; will print address of x (like doing cout<<&x in C++)
mov eax, [x]
push eax
call printf ; will print 42
sub esp, 8
xor eax, eax
ret
PS:我认为没有人提到它,但是在调用外部代码(C 或 C++ 或其他)时经常修改易失性寄存器,因为在编译时您使用的那些函数是 "translated" 组装,然后与您的 asm 文件链接。 PC 不是人,所以它不区分用高级或低级编写的内容,处理器只是读取存储在寄存器和内存中的操作码和操作数,因此为什么在使用低级语言时需要外部函数( call printf
) 将修改(或不修改!始终取决于编译器和体系结构)您也在使用的寄存器。
为了解决这个问题,有多种解决方案:
您使用 gcc your_c_file.c -S
检查哪些寄存器未被修改,然后在文件中 your_c_file.s
将是您的编译器从您的 C 生成的预先准备好的汇编代码文件。 (往往很难弄清楚什么是什么,如果您要使用此方法,请查看 Name Mangling,以了解函数名称将如何更改。)
将所有要保存的寄存器压入堆栈,然后在调用后将它们弹出回各自的寄存器,牢记后进先出法。
- 使用指令
PUSHA
和 POPA
分别压入或弹出所有寄存器。
这是 NASM 手册第 3 章,它解释了要使用的语言的基础:http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html
希望你能解决它。
我正在汇编中编写一个 while 循环,以便在 Linux 终端中使用 nasm 和 gcc 进行编译。该程序比较 x 和 y 直到 y >= x 并在最后报告循环数。这是代码:
segment .data
out1 db "It took ", 10, 0
out2 db "iterations to complete loop. That seems like a lot.", 10, 0
x db 10
y db 2
count db 0
segment .bss
segment .text
global main
extern printf
main:
mov eax, x
mov ebx, y
mov ecx, count
jmp lp ;jump to loop lp
lp:
cmp ebx, eax ;compare x and y
jge end ;jump to end if y >= x
inc eax ;add 1 to x
inc ebx ;add 2 to y
inc ebx
inc ecx ;add 1 to count
jp lp ;repeat loop
end:
push out1 ;print message part 1
call printf
push count ;print count
call printf
push out2 ;print message part 2
call printf
;mov edx, out1 ;
;call print_string ;
;
;mov edx, ecx ;these were other attempts to print
;call print_int ;using an included file
;
;mov edx, out2 ;
;call print_string ;
这是在终端中编译和 运行 的:
nasm -f elf test.asm
gcc -o test test.o
./test
终端输出为:
It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)
我看不出逻辑有什么问题。我认为这是语法问题,但我们才刚刚开始学习汇编,我尝试了各种不同的语法,例如变量周围的括号和在段末尾使用 ret
,但似乎没有任何效果。我还搜索了分段错误,但没有发现任何真正有用的东西。任何帮助将不胜感激,因为我是一个绝对的初学者。
它崩溃的原因可能是您的 main
函数没有 ret
指令。还要确保将 eax
设置为 0 以表示成功:
xor eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret
此外,全局变量指定指针,而不是值。 mov eax, x
将 eax
设置为 x
的地址。如果您希望发生任何事情(或不使用全局变量),则需要写回它。
最后,您使用一个非字符串参数调用 printf
:
push count ;print count
call printf
第一个参数需要是格式字符串,如 "%i"
。这里,count
是一个指向空字节的指针,所以你什么也得不到。别想了,你应该试试这个:
out3 db "%i ", 0
; snip
push ecx
push out3
call printf
我认为您的问题可能只是您引用的是常量的地址,而不是它们的内在值。人们必须将 nasm 中的标签视为指针而不是值。要访问它,您只需要使用 [label]
:
segment .data
x dw 42
segment .text
global main
extern printf
main:
mov eax, x
push eax
call printf ; will print address of x (like doing cout<<&x in C++)
mov eax, [x]
push eax
call printf ; will print 42
sub esp, 8
xor eax, eax
ret
PS:我认为没有人提到它,但是在调用外部代码(C 或 C++ 或其他)时经常修改易失性寄存器,因为在编译时您使用的那些函数是 "translated" 组装,然后与您的 asm 文件链接。 PC 不是人,所以它不区分用高级或低级编写的内容,处理器只是读取存储在寄存器和内存中的操作码和操作数,因此为什么在使用低级语言时需要外部函数( call printf
) 将修改(或不修改!始终取决于编译器和体系结构)您也在使用的寄存器。
为了解决这个问题,有多种解决方案:
您使用
gcc your_c_file.c -S
检查哪些寄存器未被修改,然后在文件中your_c_file.s
将是您的编译器从您的 C 生成的预先准备好的汇编代码文件。 (往往很难弄清楚什么是什么,如果您要使用此方法,请查看 Name Mangling,以了解函数名称将如何更改。)将所有要保存的寄存器压入堆栈,然后在调用后将它们弹出回各自的寄存器,牢记后进先出法。
- 使用指令
PUSHA
和POPA
分别压入或弹出所有寄存器。
这是 NASM 手册第 3 章,它解释了要使用的语言的基础:http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html
希望你能解决它。