Search code examples
linuxfasm

Displaying 64-bit register in ASM


This is my first attempt in 64-bit assembly under Linux. I am using FASM.

I am converting a 64-bit register hex value to string. It is working fine until it reaches the final digit. I can't figure out exactly what's wrong with my code. Maybe there is something about 64-programming that I don't know or with the syscall (I am a linux noob as well)

format ELF64 executable 3
entry start

segment readable executable
start:
    mov rax,3c5677h ;final '7' is not displayed
    push rax
    call REG
    ;call line

    xor edi,edi     ;exit
    mov eax,60
    syscall

;---------------------------------- 
REG:push rbp        ;stack frame setup  
    mov rbp,rsp
    sub rsp,8       ;space for local char
    mov rax,[rbp+16];arg
    lea r9,[rsp-8]  ;local char
    mov rcx,16      ;divisor
    mov rsi,16      ;16 hex digits for register 
.begin:             ;get the digit
    xor rdx,rdx       ;by division
    div rcx         ;of 16   
    push rdx        ;from back to front
    dec rsi
    test rsi,rsi
    jz .disp
    jmp .begin
.disp:              ;convert and display digit
    inc rsi          
    pop rax         ;In reverse order
    add rax,30h     ;convert digit to string
    cmp rax,39h     ;if alpha
    jbe .normal
    add rax,7       ;add 7
.normal:
    mov [r9],rax    ;copy the value
    push rsi        ;save RSI for syscall 
    mov rsi,r9      ;address of char
    mov edx,1       ;size
    mov edi,1       ;stdout
    mov eax,1       ;sys_write
    syscall
    pop rsi         ;restore RSI for index
    cmp rsi,16
    je .done
    jmp .disp
.done:  
    add rsp,8       ;stack balancing
    pop rbp 
    ret

Thanks in advance for your help.


Solution

  • I believe the problem printing the last digit comes from how you load r9. If on entry, rsp was 100. You subtract 8 (rsp = 92), then load r9 with rsp - 8 (r9 = 84). Presumably you meant r9 to 100, so try changing that to:

    lea r9, [rsp+8]
    

    For a more efficient solution, how about something more like this (assumes value in rbx):

        mov r9, 16 ; How many digits to print
        mov rsi, rsp ; memory to write digits to
        sub rsp, 8 ; protect our stack
    
        mov edx, 1 ; size is always 1
        mov edi, 1 ; stdout is always 1
    
    .disp:
        rol rbx, 4 ; Get the next nibble
        mov cl, bl ; copy it to scratch
        and cl, 15 ; mask out extra bits
        add cl, 0x30 ; Convert to char
        cmp cl, 0x39 ; if alpha
        jbe .normal
        add cl, 7 ; Adjust for letters
    
    .normal:
        mov [rsi], cl ; copy the value
        mov eax, 1 ; sys_write
        syscall ;  overwrites rcx, rax, r11
        dec r9 ; Finished a digit
        jnz .disp ; Are we done?
        add rsp, 8 ; Done with the memory