I am looking for a way to print an integer in assembler (the compiler I am using is NASM on Linux), however, after doing some research, I have not been able to find a truly viable solution. I was able to find a description for a basic algorithm to serve this purpose, and based on that I developed this code:
global _start
section .bss
digit: resb 16
count: resb 16
i: resb 16
section .data
section .text
_start:
mov dword[i], 108eh ; i = 4238
mov dword[count], 1
L01:
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[digit], edx
add dword[digit], 30h ; add 48 to digit to make it an ASCII char
call write_digit
inc dword[count]
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[i], eax
cmp dword[i], 0Ah
jg L01
add dword[i], 48 ; add 48 to i to make it an ASCII char
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, i ; store *address* of i into ecx
mov edx, 16 ; byte size of 16
int 80h
jmp exit
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
write_digit:
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, digit ; store *address* of digit into ecx
mov edx, 16 ; byte size of 16
int 80h
ret
C# version of what I want to achieve (for clarity):
static string int2string(int i)
{
Stack<char> stack = new Stack<char>();
string s = "";
do
{
stack.Push((char)((i % 10) + 48));
i = i / 10;
} while (i > 10);
stack.Push((char)(i + 48));
foreach (char c in stack)
{
s += c;
}
return s;
}
The issue is that it outputs the characters in reverse, so for 4238, the output is 8324. At first, I thought that I could use the x86 stack to solve this problem, push the digits in, and pop them out and print them at the end, however when I tried implementing that feature, it flopped and I could no longer get an output.
As a result, I am a little bit perplexed about how I can implement a stack in to this algorithm in order to accomplish my goal, aka printing an integer. I would also be interested in a simpler/better solution if one is available (as it's one of my first assembler programs).
One approach is to use recursion. In this case you divide the number by 10 (getting a quotient and a remainder) and then call yourself with the quotient as the number to display; and then display the digit corresponding to the remainder.
An example of this would be:
;Input
; eax = number to display
section .data
const10: dd 10
section .text
printNumber:
push eax
push edx
xor edx,edx ;edx:eax = number
div dword [const10] ;eax = quotient, edx = remainder
test eax,eax ;Is quotient zero?
je .l1 ; yes, don't display it
call printNumber ;Display the quotient
.l1:
lea eax,[edx+'0']
call printCharacter ;Display the remainder
pop edx
pop eax
ret
Another approach is to avoid recursion by changing the divisor. An example of this would be:
;Input
; eax = number to display
section .data
divisorTable:
dd 1000000000
dd 100000000
dd 10000000
dd 1000000
dd 100000
dd 10000
dd 1000
dd 100
dd 10
dd 1
dd 0
section .text
printNumber:
push eax
push ebx
push edx
mov ebx,divisorTable
.nextDigit:
xor edx,edx ;edx:eax = number
div dword [ebx] ;eax = quotient, edx = remainder
add eax,'0'
call printCharacter ;Display the quotient
mov eax,edx ;eax = remainder
add ebx,4 ;ebx = address of next divisor
cmp dword [ebx],0 ;Have all divisors been done?
jne .nextDigit
pop edx
pop ebx
pop eax
ret
This example doesn't suppress leading zeros, but that would be easy to add.
I think that maybe implementing a stack is not the best way to do this (and I really think you could figure out how to do that, saying as how pop is just a mov and a decrement of sp, so you can really set up a stack anywhere you like by just allocating memory for it and setting one of your registers as your new 'stack pointer').
I think this code could be made clearer and more modular if you actually allocated memory for a c-style null delimited string, then create a function to convert the int to string, by the same algorithm you use, then pass the result to another function capable of printing those strings. It will avoid some of the spaghetti code syndrome you are suffering from, and fix your problem to boot. If you want me to demonstrate, just ask, but if you wrote the thing above, I think you can figure out how with the more split up process.
; Input
; EAX = pointer to the int to convert
; EDI = address of the result
; Output:
; None
int_to_string:
xor ebx, ebx ; clear the ebx, I will use as counter for stack pushes
.push_chars:
xor edx, edx ; clear edx
mov ecx, 10 ; ecx is divisor, devide by 10
div ecx ; devide edx by ecx, result in eax remainder in edx
add edx, 0x30 ; add 0x30 to edx convert int => ascii
push edx ; push result to stack
inc ebx ; increment my stack push counter
test eax, eax ; is eax 0?
jnz .push_chars ; if eax not 0 repeat
.pop_chars:
pop eax ; pop result from stack into eax
stosb ; store contents of eax in at the address of num which is in EDI
dec ebx ; decrement my stack push counter
cmp ebx, 0 ; check if stack push counter is 0
jg .pop_chars ; not 0 repeat
mov eax, 0x0a
stosb ; add line feed
ret ; return to main
; eax = number to stringify/output
; edi = location of buffer
intToString:
push edx
push ecx
push edi
push ebp
mov ebp, esp
mov ecx, 10
.pushDigits:
xor edx, edx ; zero-extend eax
div ecx ; divide by 10; now edx = next digit
add edx, 30h ; decimal value + 30h => ascii digit
push edx ; push the whole dword, cause that's how x86 rolls
test eax, eax ; leading zeros suck
jnz .pushDigits
.popDigits:
pop eax
stosb ; don't write the whole dword, just the low byte
cmp esp, ebp ; if esp==ebp, we've popped all the digits
jne .popDigits
xor eax, eax ; add trailing nul
stosb
mov eax, edi
pop ebp
pop edi
pop ecx
pop edx
sub eax, edi ; return number of bytes written
ret
Related
I'm making new post about the same program - I'm sorry but I think that my question is much different than the previous one. My program gets 2 parameters at the start - number of repeats and a string. Number of repeats determines how many times should the last word from string be printed. For example:
./a.out 3 "ab cd"
shows in output
cdcdcd
I already made ( with Stack users help :-) ) a working program using call printf. It works for 0-9 number of repeats only but it's not as imporant as the main thing - my question is how to replace this "call printf" with sys_write calling.
I got information that I have to compile this using
-nostdlib
option but it doesn't matter if my code isn't correct. I tried my best and I also found some information about possible methods here but I can't make it work properly.
Printing new line works good but I have no idea how to deal with string from parameter #2 connected with sys_write. It would be great if someone more experienced find some time and point out what I need to change in the code. It took some time to get through the "call printf" version but then I was able to experiment and now I'm totally lost. Here it is:
.intel_syntax noprefix
.globl _start
.text
_start:
push ebp
mov ebp, esp
mov ecx, [ebp + 4] # arg1 int ECX
mov ebx, [ebp + 8] # arg2 string EBX
xor eax, eax
# ARG1 - FROM STRING TO INT
atoi:
movzx edx, byte ptr [ecx]
cmp edx, '0'
jb programend
sub edx, '0'
mov ecx, edx
## =========================== FUNCTION =========================== ##
# SEARCH FOR END OF STRING
findend:
mov dl, byte ptr [ebx + eax] # move through next letters
cmp dl, 0
jz findword
inc eax
jmp findend
# SEARCH FOR LAST SPACE
findword:
dec eax
mov dl, byte ptr [ebx + eax]
cmp dl, ' '
jz foundwordstart
jmp findword
# REMEMBER SPACE POSITION, CHECK COUNTER >0
foundwordstart:
push eax # remember space position
cmp ecx, 0 # check if counter > 0
jz theend
jmp foundword
# PRINT LAST WORD
foundword:
inc eax
mov dl, byte ptr [ebx + eax]
cmp dl, 0
jz checkcount
push ecx
push eax # save current position in word
push edx
push ebx
lea ecx, [ebx+eax] # char * to string
mov eax, 4 # sys_write
mov edx, 1; # how many chars will be printed
mov ebx, 1 # stdout
int 0x80
pop ebx
pop edx
pop eax
pop ecx
jmp foundword
# decrease counter and restore beginning of last word
checkcount:
dec ecx # count = count-1
pop eax # restore beginning of last word
jmp foundwordstart
theend:
pop eax # pop the space position from stack
jmp programend
# END OF PROGRAM
programend:
pop ebp
# new line
mov eax,4
mov ebx,1
mov ecx,offset msgn
mov edx,1
int 0x80
# return 0
mov eax, 1
mov ebx, 0
int 0x80
.data
msgn: .ascii "\n"
It's also really strange for me that I can run it with:
mov ecx, [ebp + 12]
mov ecx, [ecx + 8]
add ecx, eax # char * to string
mov eax, 4 # sys_write
mov edx, 1; # how many chars will be printed
mov ebx, 1 # stdout
int 0x80
and it works well - but only if I don't use -nostdlib (and of course I have to change _start to main)...
I'm trying to learn assembly. I wanted to write a simple program that counted to 20 and printed out the numbers. I know you have to subtract ascii '0' from a ascii representation of a number to turn it into it's digit, but my implementation just refuses to work. I still get 123456789:;<=>?#ABCD
Here is my code.
section .bss
num resb 1
section .text
global _start
_start:
mov eax, '1'
mov ebx, 1 ; Filehandler 1 = stdout
mov ecx, 20 ; The number we're counting to
mov edx, 1 ; Size of a number in bytes
l1:
mov [num], eax ; Put eax into the value of num
mov eax, 4 ; Put 4 into eax (write)
push ecx ; Save ecx on the stack
mov ecx, num ; print num
int 0x80 ; Do the print
pop ecx ; Bring ecx back from the stack
mov eax, [num] ; Put the value of num into eax
sub eax, '0' ; Convert to digit
inc eax ; Increment eax
add eax, '0' ; Convert back to ascii
loop l1
mov eax,1 ; System call number (sys_exit)
int 0x80 ; Call kernel
Can anyone see what the problem is? I'm totally hitting a brick wall. I'm using nasm to compile and ld to link.
code is as follows
getstr:
; get a LF terminated string from stdin
; in: EAX = dest buffer
; out: ax = bytes read
; EAX NOT preserved, all other registers preserved
;op mod opr1 opr2 comment
;--------------------------------------------------------
push ebx
push ecx
push edx
sub esp, 2 ; allocate memory
mov word [esp], 0x0000 ; zero memory
mov ecx, eax ; set the correct buffer
mov ebx, 0 ; stdin = 0
mov edx, 1 ; 1 byte reads
mov eax, 3 ; syscall read
.loop:
int 0x80 ; do read
test byte [ecx], 0xA
je .done
inc ecx
add word [esp], 1 ; increment the count
jmp .loop
.done:
mov byte [ecx],0x0
pop ax
pop edx
pop ecx
pop ebx
ret
gdb dump shows that 0 bytes were read
(gdb) info registers
eax 0x0 0
does anybody know what is going on here?
Two errors (assuming you use NASM):
First, int 80h / eax=3 changes eax. Thus, the next call to that function has not the wished eax, but the code 1 for exit. Move the label .loop just before the mov eax, 3 ; syscall read.
Second, test byte [ecx], 0xA doesn't compare the values. It performs an AND and sets the flags accordingly. The zero flag indicates that the result of the AND was zero. Change the line to cmp byte [ecx], 0xA.
I'm trying to implement insertion sort in 32bit assembly in linux using NASM and I get a segmentation fault mid-run (not to mention that for some reason 'printf' prints random garbage values, I'm not totally sure why), Here is the
code:
section .rodata
MSG: DB "welcome to sortMe, please sort me",10,0
S1: DB "%d",10,0 ; 10 = '\n' , 0 = '\0'
section .data
array DD 5,1,7,3,4,9,12,8,10,2,6,11 ; unsorted array
len DB 12
section .text
align 16
global main
extern printf
main:
push MSG ; print welcome message
call printf
add esp,4 ; clean the stack
call printArray ;print the unsorted array
;parameters
;push len
;push array
mov eax, len
mov ebx, array
push eax
push ebx
call myInsertionSort
call printArray ; print the sorted one
mov eax, 1 ;exit system call
int 0x80
printArray:
push ebp ;save old frame pointer
mov ebp,esp ;create new frame on stack
pushad ;save registers
mov eax,0
mov ebx,0
mov edi,0
mov esi,0 ;array index
mov bl, byte [len]
add edi,ebx ; edi = array size
print_loop:
cmp esi,edi
je print_end
push dword [array+esi*4]
push S1
call printf
add esp, 8 ;clean the stack
inc esi
jmp print_loop
print_end:
popa ;restore registers
mov esp,ebp ;clean the stack frame
pop ebp ;return to old stack frame
ret
myInsertionSort:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov ecx, [ebp+12]
movzx ecx, byte [ecx] ;put len in ecx, our loop variable
mov eax, 0
mov ebx, 0
mov esi, [ebp+8] ; the array
loop loop_1
loop_1:
cmp ecx, 0 ; if we're done
je done_1 ; then done with loop
mov edx, ecx
push ecx ; we save len, because loop command decrements ecx
sub edx, ecx
mov ecx, [esi+4*edx] ;;;;;; ecx now array[i] ? how do I access array[i] in a similar manner?
mov ebx, eax
shr ebx, 2 ; number of times for inner loop
loop_2:
cmp ebx, 0 ; we don't use loop to not affect ecx so we use ebx and compare it manually with 0
jl done_2
cmp [esi+ebx], ecx ;we see if array[ebx] os ecx so we can exit the loop
jle done_2
lea edx, [esi+ebx]
push dword [edx] ; pushing our array[ebx]
add edx, 4
pop dword [edx] ; popping the last one
dec ebx ; decrementing the loop iterator
jmp loop_2 ; looping again
done_2:
mov [esi+ebx+1], ecx
inc eax ; incrementing iterator
pop ecx ; len of array to compare now to eax and see if we're done
jmp loop_1
done_1:
pop edi
pop esi
pop ebx
pop ebp ; we pop them in opposite to how we pushed
ret
About the printf thing, I'm positive that I should push the parameters the opposite way (first S1 and then the integer so it'd be from left to right as we'd call it in C), and if I do switch them, nothing is printed at all while I'm getting a segmentation fault. I don't know what to do, it prints these as output:
welcome to sortMe, please sort me
5
16777216
65536
256
1
117440512
458752
1792
7
50331648
196608
768
mov ecx, [ebp+12] ;put len in ecx, our loop variab
This only moves the address of LEN into ECX not its value! You need to add movzx ecx, byte [ecx]
You also need to define LEN=48
loop loop_1
What's this bizare use of LOOP doing here?
You are mixing bytes and dwords on multiple occasions. You need to rework the code. p.e.
dec ebx ; ebx is now number of times we should go through inner loop
should become
shr ebx,2
This is not correct because you need the address and not the value. Change MOV into LEA.
jle done_2
mov edx, [esi+ebx]
Perhaps you can post your reworked code as an EDIT within your Original question.
Your edited code does not address ALL the problems signaled by user3144770!
The parameters to printf are correct but here are some additional problems with your printArray routine.
Since ESI is an index in an array of dwords you need to scale it up!
push dword [array+esi*4]
Are you sure pusha will save 32 bits ? Perhaps you'd better use pushad
ps Should you decide to rework your code and post the edit then please add the reworked code after the last line of the existing post. This way the original question will continue making sense to people viewing it the first time!
How do I write a variable to a file using NASM?
For example, if I execute some mathematical operation - how do I write the result of the operation to write a file?
My file results have remained empty.
My code:
%include "io.inc"
section .bss
result db 2
section .data
filename db "Downloads/output.txt", 0
section .text
global CMAIN
CMAIN:
mov eax,5
add eax,17
mov [result],eax
PRINT_DEC 2,[result]
jmp write
write:
mov EAX, 8
mov EBX, filename
mov ECX, 0700
int 0x80
mov EBX, EAX
mov EAX, 4
mov ECX, [result]
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
You have to implement ito (integer to ascii) subsequently len for this manner. This code tested and works properly in Ubuntu.
section .bss
answer resb 64
section .data
filename db "./output.txt", 0
section .text
global main
main:
mov eax,5
add eax,44412
push eax ; Push the new calculated number onto the stack
call itoa
mov EAX, 8
mov EBX, filename
mov ECX, 0x0700
int 0x80
push answer
call len
mov EBX, EAX
mov EAX, 4
mov ECX, answer
movzx EDX, di ; move with extended zero edi. length of the string
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
itoa:
; Recursive function. This is going to convert the integer to the character.
push ebp ; Setup a new stack frame
mov ebp, esp
push eax ; Save the registers
push ebx
push ecx
push edx
mov eax, [ebp + 8] ; eax is going to contain the integer
mov ebx, dword 10 ; This is our "stop" value as well as our value to divide with
mov ecx, answer ; Put a pointer to answer into ecx
push ebx ; Push ebx on the field for our "stop" value
itoa_loop:
cmp eax, ebx ; Compare eax, and ebx
jl itoa_unroll ; Jump if eax is less than ebx (which is 10)
xor edx, edx ; Clear edx
div ebx ; Divide by ebx (10)
push edx ; Push the remainder onto the stack
jmp itoa_loop ; Jump back to the top of the loop
itoa_unroll:
add al, 0x30 ; Add 0x30 to the bottom part of eax to make it an ASCII char
mov [ecx], byte al ; Move the ASCII char into the memory references by ecx
inc ecx ; Increment ecx
pop eax ; Pop the next variable from the stack
cmp eax, ebx ; Compare if eax is ebx
jne itoa_unroll ; If they are not equal, we jump back to the unroll loop
; else we are done, and we execute the next few commands
mov [ecx], byte 0xa ; Add a newline character to the end of the character array
inc ecx ; Increment ecx
mov [ecx], byte 0 ; Add a null byte to ecx, so that when we pass it to our
; len function it will properly give us a length
pop edx ; Restore registers
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
len:
; Returns the length of a string. The string has to be null terminated. Otherwise this function
; will fail miserably.
; Upon return. edi will contain the length of the string.
push ebp ; Save the previous stack pointer. We restore it on return
mov ebp, esp ; We setup a new stack frame
push eax ; Save registers we are going to use. edi returns the length of the string
push ecx
mov ecx, [ebp + 8] ; Move the pointer to eax; we want an offset of one, to jump over the return address
mov edi, 0 ; Set the counter to 0. We are going to increment this each loop
len_loop: ; Just a quick label to jump to
movzx eax, byte [ecx + edi] ; Move the character to eax.
movsx eax, al ; Move al to eax. al is part of eax.
inc di ; Increase di.
cmp eax, 0 ; Compare eax to 0.
jnz len_loop ; If it is not zero, we jump back to len_loop and repeat.
dec di ; Remove one from the count
pop ecx ; Restore registers
pop eax
mov esp, ebp ; Set esp back to what ebp used to be.
pop ebp ; Restore the stack frame
ret ; Return to caller