循环计数器

时间:2015-04-29 02:25:04

标签: c loops gcc assembly nasm

我在程序集中编写了一个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,但是似乎没什么用。我也搜索了分段错误,但我还没有发现任何真正有用的东西。任何帮助都会受到赞赏,因为我是绝对的初学者。

2 个答案:

答案 0 :(得分:6)

崩溃的原因可能是您的main函数没有ret指令。另外,请务必将eax设置为0以表示成功:

xor     eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret

此外,全局变量指定指针,而不是值。 mov eax, xeax设置为x的地址。如果您想要发生任何事情(或不使用全局变量),您需要回写它。

最后,您使用一个非字符串参数调用printf

push    count     ;print count
call    printf

第一个参数需要是格式字符串,如"%i"。这里,count是一个指向空字节的指针,所以你什么也得不到。在我的头上,你应该试试这个:

out3    db "%i ", 0

; snip

push    ecx
push    out3
call    printf

答案 1 :(得分:0)

我认为你的问题可能只是你引用了常量的地址而不是它们的内在价值。必须将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 ++或其他代码)时,常常会修改易失性寄存器,因为在编译时,您使用的那些函数是&#34;翻译的&#34;组装然后与您的asm文件链接。 PC不是人类,所以它不区分高级或低级写入,处理器只是读取存储在寄存器和存储器中的操作码和操作数,因此在使用低级语言时为什么是外部函数( call printf)将修改(或不是!总是依赖于编译器和体系结构)您正在使用的寄存器。 要解决这个问题,有各种解决方案:

  1. 使用gcc your_c_file.c -S检查哪些寄存器未被修改,然后在文件your_c_file.s中将是编译器从C文件生成的预先准备好的汇编代码。 (通常很难弄清楚什么是什么,如果你打算使用这种方法,请查看名称管理,看看如何更改功能名称。)

  2. 将要保存的所有寄存器推送到堆栈,然后在调用后将它们弹回寄存器,并记住LIFO方法。

  3. 使用分别推送或弹出所有寄存器的PUSHAPOPA说明。
  4. 这是NASM手册第3章,它解释了使用语言的基础:http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html

    希望你设法解决它。