Skip to content
Advertisement

Assembly NASM x86 – Simple Stack Project

I’m writing a subroutine to simply reprint decimal numbers as strings using the stack, but not getting the values I expected. When I run it through the debugger I see that I can’t get the value from esi into al. I suspect that I’m not allowed to use esi in the manner that I am, but I’m not sure on another way I can do this. Also, I am not allowed to push the elements I’m storing in edx onto the stack.

Subroutine code:

%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT  1
%define SYSCALL_READ  3
%define SYSCALL_WRITE 4
%define BUFLEN 256

        SECTION .bss                    ; uninitialized data section
src_str: resb BUFLEN            ; buffer for backwards number
dec_str: resb BUFLEN                    ; number will be converted and put in this buffer
rlen:    resb 4             ; length

    SECTION .text           ; code begins here
    global      prt_dec

    ; Begin subroutine

prt_dec:
    push    eax
    push    ebx         
    push    ecx
    push    edx
    push    esi
    push    edi
    mov     eax, [esp + 28]     ; store the decimal number 4 bytes each for each push, plus the eip
    mov esi, src_str        ; point esi to the backwards string buffer
    mov     edi, dec_str        ; point edi to the new buffer   
        mov     ebx, 10         ; stores the constant 10 in ebx

div_loop:
    mov edx, 0          ; clear out edx
    div     ebx         ; divide the number by 10
    add     edx, '0'                ; convert from decimal to char
    mov [esi], edx      ; store char in output buffer
    inc     esi         ; move to next spot in output buffer
    inc     ecx         ; keep track of how many chars are added
    cmp eax, 0          ; is there anything left to divide into?
    jne     div_loop        ; if so, continue the loop

output_loop:
    add esi, ecx        ; move 1 element beyond the end of the buffer
    mov al, [esi - 1]       ; move the last element in the buffer into al           
    mov [edi], al       ; move it into the first position of the converted output buffer
    inc edi         ; move to the next position of the converted output buffer
    dec ecx         ; decrement to move backwards through the output buffer
    cmp ecx, 0          ; if it doesn't equal 0, continue loop 
    jne output_loop 

print:  
    mov     eax, SYSCALL_WRITE      ; write out string
        mov     ebx, STDOUT
        mov     ecx, dec_str
    mov     edx, 0                
        mov     edx, rlen           
        int     080h

pop_end:
    pop     edi         ; move the saved values back into their original registers
    pop     esi
    pop edx         
    pop ecx
    pop ebx
    pop     eax
    ret

    ; End subroutine

Driver:

%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT  1
%define SYSCALL_READ  3
%define SYSCALL_WRITE 4


        SECTION .data                   ; initialized data section

lf:     db  10                  ; just a linefeed 

msg1:   db " plus " 
len1 equ $ - msg1

msg2:   db " minus "
len2 equ $ - msg2

msg3:   db " equals "
len3 equ $ - msg3

        SECTION .text                   ; Code section.
        global  _start                  ; let loader see entry point
    extern  prt_dec

_start: 
    mov ebx, 17
    mov edx, 214123
    mov edi, 2223187809
    mov ebp, 1555544444 


    push    dword 24
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    dword 0xFFFFFFFF
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    3413151
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    ebx
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    edx
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    edi
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    ebp
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    2
    call    prt_dec
    add esp, 4

        mov     eax, SYSCALL_WRITE      ; write message
        mov     ebx, STDOUT
        mov     ecx, msg1
        mov     edx, len1
        int     080h

    push    3
    call    prt_dec
    add esp, 4

        mov     eax, SYSCALL_WRITE      ; write message
        mov     ebx, STDOUT
        mov     ecx, msg3
        mov     edx, len3
        int     080h

    push    5
    call    prt_dec
    add esp, 4
    call    prt_lf

    push    7
    call    prt_dec
    add esp, 4

        mov     eax, SYSCALL_WRITE      ; write message
        mov     ebx, STDOUT
        mov     ecx, msg2
        mov     edx, len2
        int     080h

    push    4
    call    prt_dec
    add esp, 4

        mov     eax, SYSCALL_WRITE      ; write message
        mov     ebx, STDOUT
        mov     ecx, msg3
        mov     edx, len3
        int     080h

    push    3
    call    prt_dec
    add esp, 4
    call    prt_lf


        ; final exit
        ;
exit:   mov     EAX, SYSCALL_EXIT       ; exit function
        mov     EBX, 0                  ; exit code, 0=normal
        int     080h                    ; ask kernel to take over



    ; A subroutine to print a LF, all registers are preserved
prt_lf:
    push    eax
    push    ebx
    push    ecx
    push    edx

        mov     eax, SYSCALL_WRITE      ; write message
        mov     ebx, STDOUT
        mov     ecx, lf
        mov     edx, 1          ; LF is a single character
        int     080h

    pop edx
    pop ecx
    pop ebx
    pop eax
    ret

Advertisement

Answer

Fixes I had on mind (asterisk denotes lines I did touch), hopefully it will be clear from comments what I did:

    ...
div_loop:
*   xor     edx, edx    ; clear out edx
    div     ebx         ; divide the number by 10
*   add     dl, '0'     ; convert from decimal to char
*   mov     [esi], dl   ; store char in output buffer
    inc     esi         ; move to next spot in output buffer
    inc     ecx         ; keep track of how many chars are added
*   test    eax,eax     ; is there anything left to divide into?
*   jnz     div_loop    ; if so, continue the loop
* ; (jnz is same instruction as jne, but in this context I like "zero" more)

*   mov     [rlen], ecx ; store number of characters into variable

output_loop:
* ; esi already points beyond last digit, as product of div_loop (removed add)
*   dec     esi         ; point to last/previous digit
    mov     al, [esi]   ; move the char from the div_loop buffer into al
    mov     [edi], al   ; move it into the first position of the converted output buffer
    inc     edi         ; move to the next position of the converted output buffer
    dec     ecx         ; decrement to move backwards through the output buffer
*   jnz     output_loop ; if it doesn't equal 0, continue loop 

print:  
    mov     eax, SYSCALL_WRITE      ; write out string
    mov     ebx, STDOUT
    mov     ecx, dec_str
*   mov     edx, [rlen] ; read the number of digits from variable
    int     080h

    ...
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement