NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: Anonymous on June 11, 2014, 06:31:40 PM
-
I have been trying to write a program that takes in a value and divides it by 16 and prints out the result, I soon found out that I needed to convert from ascii to integer to actually use the input value and with a 64 bit value I needed to iterate through each byte and subtract 48 or 0x30 in hex and so I did and then I went ahead and multiplied it by 10 each time it went through the loop and divided it by 10 at the very end to correct the error that the loop multipled too many times by 10 and I still do not get the desired result I am sorry if this might seem like a stupid question I am very new to assembly and just started a couple days ago. here is my code:
section .bss
n_number resb 255
section .data
newLine : db "",10
newLine_L : equ $ - newLine
section .text
atoi:
mov rbx, 10
mov qword [n_number], rax
mov rdi, 0
.NxtDgt:
cmp byte [n_number+rdi], 0
je next
sub byte [n_number +rdi], 0x30
mov rax, qword[n_number]
mul rbx
mov qword[n_number], rax
inc rdi
jmp .NxtDgt
next:
mov rax, qword[n_number]
mov rdx, 0
mov rcx, 10
div rcx
mov rdx, 0
mov rcx, 16
div rcx
mov rbx, 10
mov qword [n_number], rax
mov rdi, 0
.NxtDgt2:
cmp byte [n_number+rdi], 0
je next2
add byte [n_number +rdi], 0x30
mov rax, qword[n_number]
div rbx
mov qword[n_number], rax
inc rdi
jmp .NxtDgt2
next2:
mov rax, qword [n_number]
mov rdx, 0
mov rcx, 10
mul rcx
mov qword[n_number], rax
done:
mov rdx, 255 ;length
mov rsi, n_number;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
mov rdx, newLine_L;length
mov rsi, newLine;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
ret
-
My 64-bit machine died, so I'm back to 32-bits... so I can't test this or provide a tested version. It's not a stupid question. You seem to have the general idea right, but I don't think some of the details are going to work.
section .bss
n_number resb 255
section .data
newLine : db "",10
newLine_L : equ $ - newLine
section .text
atoi:
mov rbx, 10
mov qword [n_number], rax
mov rdi, 0
.NxtDgt:
cmp byte [n_number+rdi], 0
je next
I guess you're calling this with the address of the ASCII string in rax? (and I take it it's zero-terminated) You put the address of the string in [n_number]. What we would like to do to get to the next digit is:
cmp byte [ [n_number] + rdi], 0
... but there's no such instruction. What I think I would do is put the address of the string in some register, and just use that. rdi would do (rsi might be more conventional). I think I'd get the byte from the string into a register and do my comparisons and manipulation there.
atoi:
mov rdi, rax ; if you wish to pass the parameter in rax
mov rbx, 10 ; to multiply by
xor rax, rax ; to use as "result so far"
xor rcx, rcx ; our character/digit (high bits zero)
.top:
mov cl, [rdi] ; get a character
add rdi, 1 ; get ready for the next one
cmp cl, 0 ; end of string?
je .done
cmp cl, '0'
jb .invalid
cmp cl, '9'
ja .invalid
sub cl, '0' ; or 48 or 30h
; now that we know we have a valid digit...
; multiply "result so far" by 10
mul rbx
jc .overflow ; ?
; and add in the new digit
add rax, rcx
jmp .top
; I'm not going to do anything different for overflow or invalid
; just return what we've got
.overflow:
.invalid:
.done:
ret ; number is in rax
I think you'll find - if I haven't screwed it up (untested code!) - that this eliminates having to divide 'cause you multiplied too many times.
One last nit...
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
ret
... you've got the comments backwards.
Best,
Frank
-
Thank you I now get the logic but as you feared the code does not work and it seems like it should. It is giving me a seg fault
-
Hmmm... Do I understand correctly that you're passing the address of the string in rax? What's the caller look like? I should probably have "global atoi" in there...
Best,
Frank
-
EDIT here is the new code turns out I was just calling it wrong:
;------------------------------------------
; int atoi(Integer number)
; Ascii to integer function (atoi)
;%include "functions.asm"
section .data
MSG: db "Input number : ",10
MSG_L: equ $ - MSG
section .bss
x_number resb 255
section .text
global _start:
_start:
mov rdx, MSG_L ;length
mov rsi, MSG;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
mov rdx, 255
mov rsi, x_number
mov rdi, 0
mov rax, 0
syscall
mov rdi, x_number
call atoi
mov rdx, 0
mov rcx, 16
div rcx
mov rdi, [rax]
call itoa
mov rdi, 255
mov rsi, rax
mov rdi, 1
mov rax, 1
syscall
mov rax, 60
mov rdi, 0
syscall
ret
atoi:
push rbx;if you are going to use rbx you must preserve it by pushing it onto the stack
;~ Address is passed in rdi
mov rbx, 10 ; to multiply by
xor rax, rax; to use as "result so far"
xor rcx, rcx ; our character/digit (high bits zero)
.top:
mov cl, byte [rdi] ; get a character
add rdi, 1 ; get ready for the next one
cmp cl, 0 ; end of string?
je .done
cmp cl, '0'
jb .invalid
cmp cl, '9'
ja .invalid
sub cl, '0' ; or 48 or 30h
; now that we know we have a valid digit...
; multiply "result so far" by 10
mul rbx
jc .overflow ; ?
; and add in the new digit
add rax, rcx
jmp .top
; I'm not going to do anything different for overflow or invalid
; just return what we've got
.overflow:
.invalid:
.done:
pop rbx;restore rbx to its original value
ret ; number is in rax
Now I have another question how would one go about Converting from integer to ASCII so I can print out the result I haven't at all looked at what algorithm to use so I have no idea how to do this I have tried doing the inverse of atoi by adding 48 and dividing by 10 instead of the other way around and it didn't work. Also how do I make code blocks in this forum?
-
Code blocks are easy. Just the word "code" in square brackets (like a Nasm memory reference) at the top, and "/code" in square brackets at the end. This might make it easier to read, and definitely makes it easier to cut and paste.
You've got the right idea for itoa. Divide by ten, then add '0' (48 or 30h) to the remainder (in rdx). Unfortunately, we get these remainders in the opposite order from which we're going to want to print them. There are different ways to deal with this. You could just put 'em in the buffer "backwards" and then do a "string reverse" on it. You could push 'em on the stack and then pop 'em off in the "right" order. This is what I usually show beginners, since I think it's easier to understand. Maybe it is and maybe it isn't. Or, you could put 'em in the buffer starting at the "end" and working towards the "front". You'll probably run out of digits (indicated by rax being zero) before you get all the way to the start of the buffer, so keep track of the "start position" to tell sys_write... or space-pad (or zero-pad) the buffer to the beginning. Right justified numbers look nicer (IMO) if you're going to print them in a column. You'll need to tell sys_write how many characters to print, of course.
I may be able to come up with an example (later), but give it a shot.
Best,
Frank
-
I tried it and I think this should work but I think I am calling it wrong I have no Idea where my error is . Here is my code :
;------------------------------------------
; int atoi(Integer number)
; Ascii to integer function (atoi)
;%include "functions.asm"
section .data
MSG: db "Input number : ",10
MSG_L: equ $ - MSG
section .bss
x_number resb 255
section .text
global _start:
_start:
mov rdx, MSG_L ;length
mov rsi, MSG;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
mov rdx, 255
mov rsi, x_number
mov rdi, 0
mov rax, 0
syscall
mov rdi, x_number
call atoi
mov rdx, 0
mov rcx, 16
div rcx
mov qword [x_number], rax
mov rdi, x_number
call itoa
mov rax, 60
mov rdi, 0
syscall
ret
atoi:
push rbx;if you are going to use rbx you must preserve it by pushing it onto the stack
;~ Address is passed in rdi
mov rbx, 10 ; to multiply by
xor rax, rax; to use as "result so far"
xor rcx, rcx ; our character/digit (high bits zero)
.top:
mov cl, byte [rdi] ; get a character
add rdi, 1 ; get ready for the next one
cmp cl, 0 ; end of string?
je .done
cmp cl, '0'
jb .invalid
cmp cl, '9'
ja .invalid
sub cl, '0' ; or 48 or 30h
; now that we know we have a valid digit...
; multiply "result so far" by 10
mul rbx
jc .overflow ; ?
; and add in the new digit
add rax, rcx
jmp .top
; I'm not going to do anything different for overflow or invalid
; just return what we've got
.overflow:
.invalid:
.done:
pop rbx;restore rbx to its original value
ret ; number is in rax
itoa:
push rbx
mov rbx, 10
;puts a zero before everything so that in the next loop it will know when to stop
dec rdi
mov byte [rdi], 0
inc rdi
.top:;loops through to the end so that rdi points to the end
mov cl, byte [rdi]
inc rdi
cmp rdi, 0
je .next
jmp .top
xor rax, rax;clears out rax just in case
xor rdx, rdx;clearing out rdx just incase
.next:
mov al, byte [rdi];moves the next character into al
div rbx;divide by ten
add rdx, '0';convert to ASCII
mov rdx, 1 ;length
mov rsi, rdx;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
cmp byte [rdi], 0;is it the end?
je .done;f so jup to done
dec rdi;get ready for the next character
jmp .next
.done:
pop rbx
ret
It is seg faulting , looks like I cause a lot of those lol
-
Is this your first foray into Assembly language? Do you know a high level language such as C? You haven't read the Linux 64 bit /SYSTEM V/ AMD64 ABI have you? (Google it) The 64 bit ABI is MUCH different than the 32 bit ABI! The AMD64 ABI is MUCH different than the Windows 64 bit ABI (Notice I am using ABI and NOT API, which is different) The ABI is a set of rules that dictate when what registers need to be saved across a function call, which don't need to be saved, stack alignment, return values etc... It would behoove you to read it!!! Unlike the 32 bit ABI; rdi and rsi are volatile registers, meaning their value does not have to be preserved across calls.
In your itoa call, you have the address of a string in rdi, you then make a system call in your loop which then trashes the value (address) in rdi! At the start of your function, you should move the address that is in rdi into a non-volatile register r12 - r15, and of course, save the register at the start and restore it at the end of the proc.
add rdx, '0';convert to ASCII
mov rdx, 1 ;length
mov rsi, rdx;variable
mov rdi,1;sys_write
mov rax, 1;stdout
syscall
Your converting to ASCII and trying to print a character with a syscall, they do not work like this, they need an address of a char or string. Easiest way is to put this char into a local var (think rsp) and pass the address of rsp to the syscall.
-
Hi Gunner,
Yeah, he says right up top he's only been at it for a few days. I think he's doing pretty well, but there are a few problems...
add rdx, '0';convert to ASCII
; so now we've got a character ready to print
; so we overwrite it with 1...
mov rdx, 1 ;length
; and then tell rsi that it's our address of buffer...
mov rsi, rdx;variable
; rdi is stdout (our file descriptor/handle)
mov rdi,1;sys_write
; rax is sys_write
mov rax, 1;stdout
; I know you knew that. :)
syscall
I have read the ABI (Application Binary Interface) - a long time ago. I've apparently forgotten a lot. I'll have to "use" it before I "get" it, anyway. They trash rsi and rdi? That's really rude!
But we may not be ready for a system call quite yet. Printing the characters one at a time at this point will get 'em backwards anyway.
Let's poke at this. I think you're okay up to here...
mov rdi, x_number
call atoi
; if this works as promised, our number is in rax
mov rdx, 0
mov rcx, 16
div rcx
; remainder is in rdx, which is discarded
; I guess that's okay...
; itoa needs two parameters, at least
; the number, and a buffer to put the text in
; you cleverly pass the number in the buffer
; neat trick!
mov qword [x_number], rax
mov rdi, x_number
call itoa
; [snip]
itoa:
push rbx
mov rbx, 10
mov rax, [rdi] ; do this now
;puts a zero before everything so that in the next loop it will know when to stop
dec rdi
mov byte [rdi], 0
inc rdi
; you've put a zero BEFORE the buffer!
.top:;loops through to the end so that rdi points to the end
mov cl, byte [rdi]
inc rdi
cmp rdi, 0
je .next
jmp .top
; I guess this is to find the end of the buffer?
; but the buffer holds... I guess it still holds
; our text input (after our number).
; this might just work!
; xor rax, rax;clears out rax just in case
; don't do this! rax has our number
xor rdx, rdx;clearing out rdx just incase
; shouldn't be needed here
.next:
; what we want here is the whole number in rax
; not just a "character"
; mov al, byte [rdi];moves the next character into al
; mov rax, [rdi]
; but we wanted to do this while rdi pointed to it!
; we do not want to reload rax in our "div" loop
; we do need to clear rdx here
xor rdx, rdx
div rbx;divide by ten
add rdx, '0';convert to ASCII
; now we want to put it in our buffer...
mov [rdi], dl
sub rdi, 1
; (I'm used to using inc and dec, but in
; long mode (64-bit) the short encodings
; are used for the "rex prefix" I believe.
; not yet...
; mov rdx, 1 ;length
; mov rsi, rdx;variable
; mov rdi,1;sys_write
; mov rax, 1;stdout
; syscall
; what tells us we're done is rax!
; cmp byte [rdi], 0;is it the end?
test rax, rax ; or cmp rax, 0
jne .next
; je .done;f so jup to done
; dec rdi;get ready for the next character
; jmp .next
.done:
pop rbx
ret
We probably need to return something intelligent here. rdi points a byte before where we're going to want sys_write to start (maybe it would have been better to decrement it later as you did), and sys_write is going to need the length - it will NOT stop on a zero-terminated string. We can calculate the length of a zero-terminated string (have to be careful to zero-terminate the string and not pre-zero it :) ). Or... we could count characters and return two values. Not compatible with any calling convention I know of, but we can do it. Works great, but probably not a good idea. :(
Or we could rename the function from "itoa" to "print_int", put the sys_write back into it, and not return anything. Since we're not dealing with the complexities of a minus sign yet, they should be named "utoa" and "atou" anyway...
Best,
Frank
-
Yup Frank!
Windows 64:
Volatile: rax, rcx, rdx, r8-r11
Non-Volatile: rbx, rsi, rdi, rbp, r12-r15
Linux 64:
Volatile: rax, rcx, rdx, rsi, rdi, r8-r11
Non-Volatile: rbx, rbp, r12-r15
http://www.agner.org/optimize/calling_conventions.pdf
-
Hi Frank , I was poking around at the code you posted and when I ran it initially I got a seg fault still but as a I hacked at it in my attempt to understand I found this block of code,
.top:;loops through to the end so that rdi points to the end
;mov al, byte[rdi] this somehow was causing a seg fault
inc rdi
cmp rdi, 0
je .next
jmp .top
but once I took it out it seemed like it was in an infinite loop which wouldn't end the program I am still looking at it to figure it out perhaps you could shed some light on this. Also Gunner yes I do know some higher level languages C and java this is my first time doing assembly and I need to go reed the ABI thanks for the tip.
-
Araaagh! Did I write that? No, no, no. That's what comes of posting untested code! :(
.top:;loops through to the end so that rdi points to the end
;mov al, byte[rdi] this somehow was causing a seg fault
inc rdi
cmp rdi, 0
; Yikes! That won't be true for a long, long time!
; I must have meant:
cmp al, 0
je .next
jmp .top
Apparently... the fantasy was that we had a zero-terminated string in a buffer pointed to by rdi. (the text representation of the number we input) This probably was not true in the first place. sys_read does not return a zero-terminated string(!). We can zero-terminate it easily enough...
mov rdx, max_length
mov rsi, buffer
mov rdi, file_descriptor ; probably stdin
mov rax, sys_read
syscall
; at this point, rax is number of bytes actually read
; including the linefeed which terminates input
; but wait! it could indicate an error
; probably not, reading from stdin
; but we should check!
; (this is a sloppy way, but close enough)
test rax, rax
js error ; if it's negative, get out!
; okay, if we get here, rax is number read
; overwrite the linefeed with a zero
mov byte [rsi + rax - 1], 0
That may not even work. The 32-bit equivalent does, but if they're going to trash rsi on me... I may not know how to do this!
Even if we had a zero-terminated string, the length of the (text representation) number we input may not be the same as the length of the number (text representation) we wish to display. We divided by 16, so it won't be. ("shr rax, 4" would be a quicker way to divide by 16, but that isn't the point) Forget about that section of code entirely!
I know, off the top of my head, how many digits might be in a 32-bit number - 10. I know I need to have a byte for the zero terminator (if I want one), and I might want a minus sign (when we get to that), so 12 bytes. I usually reserve 16, just because it's a "nice round number". I'm going to have to count how many digits in 64-bits (text representation) (could do it with logarithms, but I forget how). Fortunately, the "extras" required should be the same.
I've seen code (from Randy Hyde) which actually does an entire separate divide loop just to find out how many characters we're going to have. This has always seemed horribly inefficient to me, but it's one way to do it.
If we push remainders as we get 'em, and count 'em, we can put 'em in the buffer starting from the "front" as we pop 'em off. This isn't very efficient either.
Starting at the "back" of the buffer and working towards the "front" is probably the way to go, but that "problem snippit" isn't a good way to find the "back" of the buffer. I'd add some arbitrary amount to rdi - since you've got a 256 byte buffer, 80 should be plenty - and work from there.
I can print a 64-bit number on this 32-bit machine, but I've gotta get that 64-bit machine back in service. Not right now...
Best,
Frank
-
The have "borrowed" code from: http://www.cs.usfca.edu/~cruse/cs210s09/uint2rax.s modified it into INTEL syntax, made it handle signed numbers, and added an errno variable since I use this in a shared library. You can take out/ignore the errno2
;~ #########################################
;~ atodw64 - Convert a string into a binary interger
;~ in rdi - address of string to convert
;~ out rax - converted number
;~ #########################################
atodw64:
push rbx
mov qword [errno2], 0
xor eax, eax
mov rbx, 10
xor rsi, rsi
.CheckSign:
mov cl, [rdi]
cmp cl, "-"
jne .NxtDgt
mov rsi, 1
inc rdi
.NxtDgt:
mov cl, [rdi]
test cl, cl
je .StringEnd
cmp cl, "0"
jb .invdgt
cmp cl, "9"
ja .invdgt
mul rbx
jc .invdgt
and rcx, 0x0F
add rax, rcx
test rax, rax
inc rdi
jmp .NxtDgt
.StringEnd:
test rsi, rsi
jz .Done
neg rax
.Done:
pop rbx
ret
.invdgt:
xor rax, rax
not rax
mov qword [errno2], ERANGE
pop rbx
ret
-
I have modified my code to use a general purpose register instead of rdi and I have changed it so now it wont seg fault and am attempting to print out the characters now but nothing is printing out at all Here is my code
itoa:
push rbx
mov rbx, 10
mov rax, [r8] ; do this now
;puts a zero before everything so that in the next loop it will know when to stop
dec r8
mov byte [r8], 0
inc r8
; you've put a zero BEFORE the buffer!
.top:;loops through to the end so that rdi points to the end
mov al, byte[r8] ;this somehow was causing a seg fault
inc r8
cmp al, 0
je .next
jmp .top
; I guess this is to find the end of the buffer?
; but the buffer holds... I guess it still holds
; our text input (after our number).
; this might just work!
; xor rax, rax;clears out rax just in case
; don't do this! rax has our number
xor rdx, rdx;clearing out rdx just incase
; shouldn't be needed here
.next:
; what we want here is the whole number in rax
; not just a "character"
; mov al, byte [rdi];moves the next character into al
; mov rax, [rdi]
; but we wanted to do this while rdi pointed to it!
; we do not want to reload rax in our "div" loop
; we do need to clear rdx here
xor rdx, rdx
div rbx;divide by ten
test rax, rax ; or cmp rax, 0
jne .next
add rdx, '0';convert to ASCII
; now we want to put it in our buffer...
mov byte [r8], dl
mov qword[x_number],rax
call print
mov rax, qword[x_number]
sub r8, 1
; (I'm used to using inc and dec, but in
; long mode (64-bit) the short encodings
; are used for the "rex prefix" I believe.
; not yet...
; mov rdx, 1 ;length
; mov rsi, rdx;variable
; mov rdi,1;sys_write
; mov rax, 1;stdout
; syscall
; what tells us we're done is rax!
; cmp byte [rdi], 0;is it the end?
; je .done;f so jup to done
; dec rdi;get ready for the next character
; jmp .next
.done:
pop rbx
ret
print:
;I think this is where I am going wrong any ideas?
mov rdx, 255
mov rsi, qword [r8]
mov rdi,1
mov rax, 1
syscall
ret
-
Dangerous to be flailing around blindly like this, but "fools rush in...".
itoa:
; expects: buffer in r8
; number to convert in buffer (unusual, but should work)
;
; returns: "start print" position in rax
; zero-terminated string in buffer
; miscellaneous registers trashed
push rbx
mov rbx, 10
mov rax, [r8] ; our number was in buffer
add r8, 80 ; this is "too much", but should work
mov byte [r8], 0 ; we want a zero-terminated string, right?
sub r8, 1
.next:
; we do need to clear rdx here - "div" uses it
xor rdx, rdx
div rbx;divide by ten
; quotient in rax, remainder in rdx
add rdx, '0';convert to ASCII
; now we want to put it in our buffer...
mov byte [r8], dl
call print ; for every digit? okay...
test rax, rax ; or cmp rax, 0
jz .done
sub r8, 1
jmp .top
.done:
; return "start print" position in rax
mov rax, r8
pop rbx
ret
;-----------------
;-------------------
print:
;I think this is where I am going wrong any ideas?
; yeah...
; we're trashing registers we were using
; save 'em
push rax
push rdx
push rsi
push rdi
push r8
; mov rdx, 255
; this is way too much
; we have a zero-terminated string,
; find its length
xor rdx, rdx
.getlen:
cmp byte [r8 + rdx], 0
jz .gotlen
add rdx, 1
jmp .getlen
.gotlen:
; mov rsi, qword [r8]
; no, we want the address, not [contents]
mov rsi, r8
mov rdi,1
mov rax, 1
syscall
; restore caller's registers
pop r8
pop rdi
pop rsi
pop rdx
pop rax
ret
;-----------------------
Am I even close, Gunner? I think that's what I'd try. Something "like" that, anyway... This isn't going to "look good" since we print each digit as we get it. If the answer (after dividing by 16) were "1234", we would be printing "4342341234" (I think). That's probably not what you really want to do - not after getting it "debugged" anyway... I guess if it prints anything at all it's an "improvement". :)
Best,
Frank
-
Thank you so much it works very good for untested code frank only one mistake but I fixed it for future reference if anyone looks at this page here it is:
.next:
; we do need to clear rdx here - "div" uses it
xor rdx, rdx
div rbx;divide by ten
; quotient in rax, remainder in rdx
add rdx, '0';convert to ASCII
; now we want to put it in our buffer...
mov byte [r8], dl
call print ; for every digit? okay...
test rax, rax ; or cmp rax, 0
jz .done
sub r8, 1
jmp .next;instead of jmp top but I got what you ment :)