Write a Fizz program in assembly / Using C library

不打扰是莪最后的温柔 提交于 2019-11-29 16:40:56

I'm not going to give you a complete answer since this appears to be homework. I'll provide enough to get you started. The following code will simply count from 1 to 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. 

Primary changes from your code. We display the number using printf with a proper format string. I put the counter in EBX because you must be aware that EAX, ECX, and EDX are not preserved across a function call using CDECL calling convention:

In cdecl, subroutine arguments are passed on the stack. Integer values and memory addresses are returned in the EAX register, floating point values in the ST0 x87 register. Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved.

[snip]

The caller cleans the stack after the function call returns.

After making CDECL function calls, we must restore the stack pointer. We push 2 DWORDs (a total of 8 bytes) as parameters to printf therefore we must add 8 to the stack pointer to effectively remove them when the function returns.

The highlighted sentence is important. To simplify things I use EBX because its value is preserved across function calls and we don't have to save and restore it around the function calls we make (like printf).

Since main is being called as a C function using CDECL calling convention, we must preserve any callee registers (registers our function must preserve) so that we don't cause undefined behavior in the C library code that calls main. EBX is a callee saved register we did modify, so we use PUSH/POP around our function so that EBX is preserved when our main function returns.

The program has to be linked to a C library. The easiest way to do this is to link with GCC. The compile and link stage could look like:

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

This should assemble and link your code to a 32-bit program called count100 that can be executed with this command:

./count100

I leave the rest of the assignment up to you.


The assembly code in the sample would be equivalent to this code in C:

#include <stdio.h>

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

    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!