Is there some way to use printf to print a horizontal list of decrementing hex digits in NASM assembly on Linux

42 views Asked by At

I essentially want to write a program that takes F and decrements it until its 0, displaying the results like : F E D C B A 9 8 7 6 5 4 3 2 1 0. I specifically have to use a loop to write these out, i can't just say "db 'F E D C B A 9 8 7 6 5 4 3 2 1' , 10, 0". Since I can't use system calls to print (write) hex digits I abandoned that way of thinking. Wherever I go I'm told to use printf since it doesn't require a conversion from number to character. However, printf seems to require a parameter that's usually something along the lines of "db '%x', 10, 0" within a label. That 10 usually being a LF makes it seemingly impossible to write a horizontal list and instead writes a vertical list which I really don't want.

So I tried to take this program:

extern printf
global main

section .data

format_specifier:
  db '%x', 10, 0 ;format specifier for printf with LF

section .text

main:

mov rbx, qword 16

loop1: ;loop to decrement and print number in rsi 

dec rbx
mov rdi, format_specifier
mov rsi, rbx
xor rax, rax
call printf


cmp rbx, qword 0
jne loop1

mov rax, 60
syscall

and replace the 10 in the format specifier with 0x20 (the space ASCII) to separate the hex digits as they printed. This, of course, resulted in nothing outputting whatsoever.

I have seen that LF flushes the output buffer and I have tried using fflush to do the same with no avail as seen:

extern printf
extern fflush
global main

section .data

format_specifier:
  db '%x', 0x20, 0 ;format specifier for printf with LF

section .text

main:

mov rbx, qword 16

loop1: ;loop to decrement and print number in rsi 

dec rbx
mov rdi, format_specifier
mov rsi, rbx
xor rax, rax
xor rsi, rsi
call fflush
call printf


cmp rbx, qword 0
jne loop1

mov rax, 60
syscall

I'm unsure whether it is even the right idea to try and use fflush for my program or if I'm just going in the wrong direction. Please help.

1

There are 1 answers

1
Jester On BEST ANSWER

You can use printf with the space and then a separate line feed. Also putchar is a thing. Furthermore you should keep stack 16 byte aligned and using the exit syscall is bad practice.

A possible solution:

extern printf
extern putchar
global main

section .data

format_specifier:
  db '%X', 0x20, 0 ;format specifier for printf with trailing space

section .text

main:
    push rbx ; rbx is callee-saved. Also aligns stack.
    mov ebx, 15 ; 32 bits is enough

loop1: ;loop to decrement and print number in ebx 
    lea rdi, [rel format_specifier] ; use position independent code
    mov esi, ebx
    xor eax, eax ; 32 bit ops zero extend
    call printf wrt ..plt ; PIC
    dec ebx
    jns loop1
    mov edi, 10 ; LF
    call putchar wrt ..plt

    xor eax, eax ; exit code
    pop rbx
    ret

Caveat: this will print a space after the final 0.

Or, you can avoid printf altogether:

extern putchar
global main

main:
    push rbx ; rbx is callee-saved. Also aligns stack.
    mov ebx, 'F'

loop1: ;loop to decrement and print char in ebx
    mov edi, ebx
    call putchar wrt ..plt ; PIC
    dec ebx
    cmp ebx, '0'
    jb done
    cmp ebx, 'A' - 1
    jne space
    mov ebx, '9'
space:
    mov edi, ' '
    call putchar wrt ..plt
    jmp loop1

done:
    mov edi, 10 ; LF
    call putchar wrt ..plt

    xor eax, eax ; exit code
    pop rbx
    ret