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 ...