Linux x86 NASM - Subroutine: Print a dword from EAX
assembly, linux, nasm, x86
Solution
I'm very rusty at assembler, but it looks like ECX contains the value 48, when it should contain the address of the buffer to be written.
I presume what you intended in `print_eax_val` is to take the binary value of EAX, add the ASCII offset to digit `0` (which should have been 48, not 47) and then print that single character. To do this, add 48 before storing the value in `to_print`, put the address of `to_print` in ECX, and set the length (EDX) to 1, because you're writing only one character.
Now remember that this will work for EAX values between 0x0000 and 0x0009 only. When you go past 9 you will get other ASCII characters.
Explaining how to take an arbitrary binary value of EAX and convert it to a decimal string that can be printed is far beyond the scope of SO.
Problem
So I'm learning x86 Linux assembly with NASM syntax (Oh god, not this again, you're all thinking). I'm trying to make a subroutine that will simply print the value in EAX to stdout. The code runs and exits without error, but nothing prints. I can't figure out why. First of all, here is the file I'm working in: ``` segment .bss to_print: resd 1 segment .text global print_eax_val print_eax_val: ; (top) push dword ebx ;Stack: edx push dword ecx ; ecx push dword edx ; ebx ; (bot) mov ecx,eax ;ecx = eax mov [to_print],ecx ;to_print = ecx mov eax, 4 ;sys_write mov ebx, 1 ;to stdout add ecx, 47 ;add 47 for ASCII numbers mov edx, 2 ;double word = 2 bytes int 0x80 mov eax, [to_print] ;eax = original val pop edx ;pop the registers back from the stack pop ecx pop ebx ;Stack: empty ret ``` This is called from my main file, which looks like this (this is probably irrelevant, unless I'm missing something drastic). ``` segment .data hello db "Hello world!", 0 newline db 0xA len equ $ - hello len2 equ $ - newline segment .text extern print_nl extern print_eax_val global main main: enter 0,0 call print_nl mov eax, 1 call print_eax_val mov ebx, 0 ;exit code = 0 (normal) mov eax, 1 ;exit command int 0x80 ;ask kernel to quit ``` `print_nl` is just another subroutine that defines and prints a newline. This runs successfully and prints a new line as expected. Does the problem have to do with the length parameter for my `sys_write` call? I'm giving it 2, which is the size of a `dword`, which is the size of both the `EAX` register and my `to_print` label, which I reserved with `resd 1`. I tried changing the length to 1, 4, 8, 16, and 32 out of desperation... Nothing worked. EDIT: For anyone who is wondering, here is how I fixed the code: (I will put asterisks on lines that I changed): ``` segment .bss to_print: resd 1 segment .text global print_eax_val print_eax_val: ; (top) push dword ebx ;Stack: edx push dword ecx ; ecx push dword edx ; ebx ; (bot) mov ecx,eax ;ecx = eax mov [to_print],ecx ;to_print = ecx **** add dword [to_print], 48 mov eax, 4 ;sys_write mov ebx, 1 ;to stdout **** mov ecx, to_print mov edx, 2 int 0x80 **** sub dword [to_print], 48 mov eax, [to_print] ;eax = original val pop edx ;pop the registers back from the stack pop ecx pop ebx ;Stack: empty ret ``` Basically, `ecx` must contain the address of the block you want to print, NOT the value itself. As is pointed out in the selected answer, this will only work if eax is in the range 0-9. EDIT 2: So I was a little bit confused about the 2nd parameter for sys_write (the one stored in `edx`). I think it just refers to a number of bytes. So for a `dword`, as I was using, it would be proper to use 4 there, because a double word is 4 bytes, or 32 bits. I'm guessing it worked because x86 is little-endian. So in memory, the hex value of `to_print` would look like this: `90 00 00 00` And with a supplied length of two, sys_write gets: `90 00` So the value luckily doesn't get corrupted. I later changed the code to store `to_print` as a byte instead, using `resb 1` and accessing it using `byte` instead of `dword`... A byte is fine here, because I know I'm not going to give `to_print` a value above 9.