在汇编/使用C库中编写Fizz程序

时间:2016-03-18 18:30:14

标签: assembly x86 printf nasm calling-convention

有人可以帮我解决这个程序集:首先打印数字1到100.然后按照儿童计数游戏Fizz的规则:每当数字可以被5整除时,或包含 数字5,用“Fizz”一词代替数字。到目前为止,这是我的计划:

extern printf
section .data
msg db "Hello, world!",0xa
len equ $ - msg
fmt: db "a=%d, eax=%d", 10, 0 ; The printf format, "\n",'0'
section .text
global main
main:
L1:
    mov eax,1
    push eax
    call printf
    cmp eax,100
    jae end
    inc eax
end:

1 个答案:

答案 0 :(得分:3)

我不打算给你一个完整的答案,因为这似乎是作业。我会提供足够的帮助你。以下代码将从1到100进行计数:

extern printf
section .data
fmt: db "number = %d", 10, 0 ; printf format string

section .text
global main
main:
    push ebx      ; EBX is callee saved so we need to save it so that it 
                  ;     can be restored when we RETurn from main
    mov ebx, 1    ; ebx = 1 (counter)
L1:
    push ebx      ; 2nd parameter is our number(counter) to print
    push fmt      ; 1st parameter is the address of the format string
    call printf
    add sp, 8     ; We pushed 8 bytes prior to printf call, we must adjust the stack
                  ;     by effectively removing those bytes.
    inc ebx       ; counter += 1
    cmp ebx,100
    jle L1        ; If counter is <= 100 go back and print again
end:
    xor eax,eax   ; Return 0 when our program exits
    pop ebx       ; Restore EBX before exiting main per calling convention
    ret           ; RETurn from main will cause program to gracefully exit
                  ;     because we are linked to the C runtime code and main was 
                  ;     called by that C runtime code when our program started. 

代码中的主要更改。我们使用printf使用正确的格式字符串显示数字。我把计数器放在 EBX 中,因为您必须知道 EAX ECX EDX 不会被保留使用CDECL调用约定的函数调用:

  

在cdecl中,子程序参数在堆栈上传递。整数值和存储器地址在EAX寄存器中返回,ST0 x87寄存器中的浮点值。 注册EAX,ECX和EDX被呼叫者保存,其余的被呼叫者保存。

     

[剪断]

     

调用程序在函数调用返回后清理堆栈。

在进行 CDECL 函数调用之后,我们必须恢复堆栈指针。我们将2 DWORD s(总共8个字节)作为参数推送到printf因此我们必须在堆栈指针中添加8到有效当函数删除它们时回报。

突出显示的句子很重要。为了简化我使用 EBX 的事情,因为它的值在函数调用中保留,我们不必围绕我们进行的函数调用保存和恢复它(如printf)。 / p>

由于使用 CDECL 调用约定将main称为 C 函数,我们必须保留所有被调用者寄存器(我们的函数必须保留的寄存器),以便我们不会在调用main C 库代码中导致未定义的行为。 EBX 是我们修改过的被调用者保存的寄存器,因此我们在函数周围使用 PUSH / POP ,以便 EBX 在我们的main函数返回时保留。

该程序必须链接到 C 库。最简单的方法是使用 GCC 进行链接。编译和链接阶段可能如下所示:

nasm -f elf32 count100.asm -o count100.o
gcc -m32 count100.o -o count100 

这应该将您的代码汇编并链接到一个名为count100的32位程序,该程序可以使用此命令执行:

./count100

我将剩下的任务留给你了。

示例中的汇编代码与 C 中的代码相同:

#include <stdio.h>

int main()
{
    int counter = 1;
    do {
        printf ("number = %d\n", counter);
    } while (++counter <= 100);

    return 0;
}