NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: djasy3 on February 26, 2014, 03:01:21 AM
-
hello! i'm a new user of nasm,
i'm trying to do an addition with 2 hex number and display them... i've been following a tutorial where they show how to do an addition with 2 decimals only, but i can't apply that template with hex number.
here are my numbers: 0x112233445566778899(DQ) and 0xFFEECCGG56AA89
i'm trying to do something like:
mov AX, '0x1122334455667788'
mov BX, '0xFFEECCGG56AA8'
add BX, AX
;and then save the result in a variable i declared above
mov [sum], bx
but when i try to display it, nothing is displayed, i don't even know if the result is correct
-
The result is not "correct". Well... Nasm will, with warnings, do what you asked it to do, but I doubt if it's what you want.
A 64-bit number (dq) will not fit in a 16-bit register (ax,bx), period.
Even if they would, 'G' is not a valid hex digit.
By surrounding your "numbers" with single quotes, you've declared it as a "character constant". That is, ignoring overflows, Nasm is adding the ascii codes for "0x" plus "0x". I ASSume, without checking, that Nasm gets this right (0xF060), but it doesn't seem useful.
Odd that nothing at all is displayed. Since you don't show how you do this, it's hard to say what might be wrong.
In my opinion... it is a mistake to think of "hex numbers" or "decimal numbers". A number is a number - "hex" and "decimal" (etc.) are ways we represent numbers as text. Given the number 10 (decimal), we can represent it as decimal ("10") or hex ("0xA") - it's the same number. When "getting a number from the user" (which you don't mention, but may want to do), what we get is a string of text characters representing (we hope) a number. To do anything with it, we need to convert that text to the number it represents. Then we can add two of them (you get that part right). Then to display the result, we need to convert that number back to text which we can display (same as any other text). The C library functions "scanf" and "printf" will do this for us - not clear if you're using them.
Your best bet might be to point us to the tutorial you're using. Either that or post more of your code (thanks for using "code tags", by the way!). The more you show us, the more we can help - and the more you show us that you've tried to do it yourself, the more willing we will be to help.
Welcome to assembly language and Nasm! It gets easier, honest! :)
Best,
Frank
-
here are my numbers: 0x112233445566778899(DQ) and 0xFFEECCGG56AA89
A 64-bit number (dq) will not fit in a 16-bit register (ax,bx), period.
Even if they would, 'G' is not a valid hex digit.
Yes, and these two numbers are not quad words (DQ) either.
Two hex digits represents one byte.
First number, divided by two, is equals to nine bytes, which is not quad word.
Second number, divided by two, is equals to seven bytes, which is not quad word.
You have to build, ascii-hex-string-stream-number-calculator, which sum's up two hex numbers.
; 1. Store each number as ascii-hex-string-stream-number (num0, num1, result).
num0: db "A123456789ABCDEF",0
num1: db "A122334455667788",0
result: times 100 db 0
; 2. Now you have to take one by one hex digit,
; from both numbers (num0, num1) and sum
; them up, and don't forget to respect carry flag
; and store sum in resulting number (result).
; ---------------------
; Something like this:
;
; num0: db "A...",0
; num1: db "A...",0
;
; A + A = 14
;
; Now result is: 4 and carry is 1
;
; What to do next? - Pick next hex digit from num0, num1, then respect carry and sum again further.
;
Well it's pretty easy and i'm not going to show you, how to do it. :D
Bye,
Encryptor256.
-
hello, i'm still trying to make the program better, but this time i have a segmentation fault, i added two functions to convert the input number to hexa and from hexa to ascii. @encryptor256: i know it's simple, but for now i'm still a beginner, so i'm doing all i can to understand. here is my code:
SYS_EXIT EQU 1
SYS_WRITE EQU 4
SYS_READ EQU 3 ;buffer
STDOUT EQU 1
STDIN EQU 2
;macro printf
%macro print 2
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, %1 ;expected number
mov edx, %2 ;length
int 0x80
%endmacro
;macro scanf
%macro read 2
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, %1
mov edx, %2
int 0x80
%endmacro
section .data
msg1 db "Entrer le premier nombre: ", 0xA, 0xD
len1 equ $-msg1
msg2 db "Entrer le deuxieme nombre: ", 0xA, 0xD
len2 equ $-msg2
msg3 db "La somme est : ", 0xA, 0xD
len3 equ $-msg3
section .bss
num0 resb 8
num1 resb 8
somme resb 17
section .code
global _start ;doit être déclaré pour utiliser gcc
_start:
print msg1, len1 ;on affiche le premier message
read num0, 8 ;le user entre le premier nombre
print msg2, len2 ;on affiche le deuxième message
read num1, 8 ;le user entre le deuxième nombre
; on effectue le calcul
mov ecx, [num0] ;on mov le premier nombre dans le registre eax
sub ecx, '0' ; convert ascii to number
call numb2hex ; convert number to hex
mov [num0], ecx
xor ecx, ecx
mov ecx, [num1]
sub ecx, '0'
call numb2hex
mov [num1], ecx
xor ecx, ecx
mov eax, [num0]
add [num1], eax
mov ecx, [num1] ;we use ecx register to store the number
;convert somme to ascii and display it
call hex2ascii
mov [somme], ecx
xor ecx, ecx
;on affiche la somme
print msg3, len3
print somme, 17
exit:
mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
;******************************************************* func NUMB2HEX ***********************************************************************
numb2hex:
;expect number in ecx, and buffer in eax
mov edx, 8 ;initialisation of a loop counter- doing 8 digits
.top:
rol ecx, 4 ;rotating the four leftmost bits to the bottom
mov ebx, ecx ;copying of it, cause, going to trash it
and ebx, 0Fh ;isolating the bits we want to convert
;convert those bits to a character
cmp bl, 9 ;is it bigger than 9
jbe .dec_digit
;if it's > 9, bump it up to A through F
add bl, 7 ;or 27h for lowercase
.dec_digit:
add bl, '0' ;add '0' to convert character representing that digit
mov[eax], bl ;we put it in EAX(our buffer)
inc eax ;prepare for the next character
dec edx ;done?
jnz .top ;if not, do more
ret
;********************************************** func hex2ascii****************************************
hex2ascii:
mov edx, 8
.lm:
rol ecx, 4
mov ebx, ecx
and ebx, 0Fh
cmp bl, 9
jbe .hex_digit
add bl, 7
.hex_digit:
add bl, '0'
mov [eax], bl
inc eax
loop .lm
ret
sorry some comments are in french, i'm a french speaker.
so i just tried with simple number, but it still doesn't work, i know for number with more than two digits i need check if there's an overflow as encryptor256 said, but for now it is a lil bit difficult for me. thanks
-
;********************************************** func hex2ascii****************************************
hex2ascii:
mov edx, 8
.lm:
rol ecx, 4 <- MODIFYING ECX
mov ebx, ecx
and ebx, 0Fh
cmp bl, 9
jbe .hex_digit
add bl, 7
.hex_digit:
add bl, '0'
mov [eax], bl <- EVENTUAL CRASH
inc eax
loop .lm <- CONTROLLED BY ECX!
ret
Those comments should point you in the right direction. And welcome to the forums! 8)
-
@Rob: thanks.
Sorry i don't understand you well, i found this code in some old post. can you,please, try to be more verbal so that i can try to figure out what's really wrong? for this instruction for example : rol ecx, 4 ;<- MODIFYING ECX
is the rotation correct? what should i do to make it right?
thanks.
-
You have a non-terminating loop. Let's look at the crux of the issue by examining slightly different code:
mov bl, 'A'
mov ecx, 4 ; initialize loop counter
.lm
mov [eax], bl
inc eax
loop .lm ; decrements ecx, if not zero jump to label .lm
That code will store the letter 'A' 4 times to the buffer pointed to by eax. Each time through the loop ecx will decrement. When ecx is zero the loop will exit. Make sure you understand what the opcode loop is actually doing.
In your version you are constantly rotating ecx inside the loop. If you run your code in a debugger you will see that ecx gets decremented by the loop opcode, jumps to the label .lm, but then at the start of the loop the contents of ecx are rotated. You most assuredly do not want to do that!
-
Bonjour djasy3,
(I "took" French in High School - a loooong time ago. "je m'appelle Francois" - don't remember much more)
Part of this confusion is partly my fault. Allow me to quote what I said in private mail, just to sort out which error is whose...
If you don't know where to start, write the comments first. I don't
always do this, but I always think it's a good idea - or would have
been a good idea. Maybe even before that, make sure you understand the
"specification" (assignment). The one to clarify that is the "customer"
(instructor). Break the assignment down into parts, and sort 'em out
into parts you know how to do and parts you're going to have to figure
out.
; prompt the user (like hello world but with different lyrics)
; read in text representing the first number
; we'll need a buffer for this. how big does it need to be?
; convert the text to a number
; what to do about invalid input?
; what to do about overflow?
; store the number someplace
; do it all again for the second number
; add em together
; overflow?
; convert the number to text
; dislpay "the result is: "?
; display the text representing the number
; don't forget to exit cleanly
The parts you're going to have trouble with are probably convertng
text<->number. You may want to write the number to text (and display
it) first, so you can use it to "debug" the earlier parts of the
program. You mentioned that you've got examples for converting a number
to a decimal representation. You can do hex the same way, dividing by
16 instead of ten. You'll have to deal with the fact that 'A' to 'F'
(not 'G"! : ) do not follow immediately after '0' to '9'. Converting to
hex is easier, in a way. Each four bits is going to be repesented by a
character. We can shift the bits we want into position and isolate
them. I like to use a rotate instruction, actually...
number_to_hex_ascii:
; expects: number in eax, buffer in... ecx? (where sys_write is going
to want it)
; returns... nothing useful
;
; initialize a loop counter - we're going to do 8 digits
mov edx, 8
.top:
; rotate the four leftmost bits to the bottom
rol eax, 4
; make a copy of it, 'cause we're going t trash it
mov ebx, eax
; isolate the bits we want to convert
and ebx, 0Fh
; convert those four bits to a character
; is it bigger than 9?
cmp bl, 9
jbe .dec_digit
; if it's bigger than 9, bump it up to 'A' thru 'F'
add bl, 7 ; 27h for lowercase
.dec_digit:
; add 48, 30h, or '0' to convert to character representing that digit
add bl, '0'
; put it in our buffer
mov [ecx], bl
; prepare for next character
inc ecx
; are we done?
dec edx
; if not, do more
jnz .top
; are we a subroutine?
ret
Something like that...
I don't know what you're looking at for a tutorial, or what textbook
you're using. Try http://www.drpaulcarter.com/pcasm for something in
Nasm syntax that covers 32-bit code and will work in Linux. There are
others, if that's what's not helping you much. We can discuss it more -
by email or (preferably) on the Forum.
In particular, I wasn't thinking too clearly when I suggested using ecx for the buffer (or the number, as you switched it to). Should have saved ecx for the loop counter. The "loop" instruction uses ecx as the loop counter, no choices. We can do the same thing with any register by doing "dec reg"/"jnz looptop". Obviously(?) we can't use a register as a loop counter and also alter it doing something else. Your "numb2hex" gets this right, but "hex2ascii" does not.
This brings us to a larger problem with your code. You don't actually have a "text to number" routine to handle inputs. Both of your routines are "number to text", and won't work for both purposes!
It may be worth discussing sys_read a bit first. When reading from the keyboard, sys_read does not return until the user hits "enter", no matter how many characters were typed. The linefeed (0xA) generated by hitting "enter" becomes part of the input, goes in the buffer, and is included in the count returned in eax - if there's room for it and edx allows. (if edx is bigger than the size of the buffer, that's a self-inflicted wound - just don't do it) This means that if you need 8 "good" bytes, you want 9 bytes in the buffer and in edx. Worse, if the pesky user types more (including "enter") than is in edx, it won't overflow "your" buffer but will remain in "the OS's input buffer" (what I think of as the "keyboard buffer") waiting for the next sys_read to come along. This could be the user trying to read the second number, or it could be read (and executed, if possible) by the shell. This could cause big problems if "rm -rf ." or something harmful were typed! You can "assume a well-behaved user" for now, but we really really ought to flush the (keyboard) buffer...
; sys_read
cmp byte [ecx + eax -1], 0xA ; got linefeed?
jz good_input
; else, keep reading into a dummy buffer
; until we find the damn linefeed!
Getting that to play nicely with your macro may be a challenge (haven't tried it) but it really ought to be done.
So... you've got input, presumably a text representation, in hex, of a number. What I like to do is...
; zero out a register to be "result" (eax is traditional)
top:
; get a single byte from input
; increment pointer into buffer
; make sure it's a valid hex digit
; I like to just quit if invalid - you can yell at the user if you want
; convert the character to a number
; subtract '0' if it's a decimal digit
; subtract another 7 if it's uppercase, 0x27 if lowercase
; shift the "result so far" left 4 bits
; "or" in the new number to the "result so far"
; go to top
Or something like that...
Minor nit: Linux doesn't use carriage returns (0xD). Doesn't seem to do any harm, but you might want to remove them from your prompt strings.
I skipped over the whole input part of your code, put a "known" number in ecx, put the buffer in eax (you forget to do this - another possible segfault), fixed the bug Rob identified... and it does print out a "hex number", so you're on the right track. :)
Best,
Frank
-
@frank: here is what i try to create a function to convert a string to hex, don't know if this totally work. please let me know if there's an error !
MAX_NUMBER equ 16
section .bss
buffer resb MAX_NUMBER
Lecture:
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, buffer ;
mov edx, MAX_NUMBER
int 0x80
;cmp byte [ecx + eax - 1], LF
cmp eax, '0'
je exit ;there an exit fonction in _start:
ascii2hex:
push ebx
mov edx, [esp + 8]
.top:
;mov ecx, byte [buffer]
inc edx
;on compare si le character est correcte
cmp byte[buffer], '0'
jb exit
cmp byte[buffer], 'f'
jb exit
;si le character est un nombre,on le traduit
cmp byte[buffer], '0'
ja .convNum
cmp byte[buffer], 'F' ; si la chaine commence avec une majuscule
jb .convNumMaj ;converti, le majuscule en hexa
cmp byte[buffer, 'a' ;si la chaine commence avec une miniscule
ja .convNumMin ;converti, le miniscule en hexa
;on fait un shift left de 4 bit pour déclaler les valeurs
mov ecx, byte[buffer]
sal ecx, 4
mov ebx, ecx
jmp short .top
.convNum:
sub byte[buffer], 0x00
.convNumMaj:
sub byte[buffer], 0x07
.convNumMin:
sub byte[buffer], 0x27
pop ebx
ret
And this is the function that helps me to do the addition of 2 8 bytes numbers with a 8 bit arithmetique:
calculate:
push bl
mov cl, [ebp + 2]
;we do the addition with lsb
mov al, [P]
add [Q], al
top:
inc bl
inc cl
cmp cl, 0x07 ;if we reach seven, that means we're at the end
je sum
mov al, [P]
adc [Q], al
jmp top
sum:
xor al, al
mov [edx:eax],[Q]
mov [R],[edx:eax] ;we put the result in the R variable, just above we did a resq 1
pop bl
-
Does it work? Does it even assemble? Doesn't look good to me - either of 'em
MAX_NUMBER equ 16
section .bss
buffer resb MAX_NUMBER
; still .data?
section .text
Lecture:
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, buffer ;
mov edx, MAX_NUMBER
int 0x80
;cmp byte [ecx + eax - 1], LF
; if this is true, all the input is in our buffer (including the linefeed)
; if this is not true, the pesky user has typed more than MAX_NUMBER characters
; and the "excess" remains to be read by the next sys_read
; we "probably" want to read it into a dummy buffer and discard it now
cmp eax, '0'
je exit ;there an exit fonction in _start:
; with "MAX_NUMBER equ 16", this should never happen
ascii2hex:
push ebx
mov edx, [esp + 8]
; some parameter was passed on the stack?
.top:
;mov ecx, byte [buffer]
inc edx
;on compare si le character est correcte
cmp byte[buffer], '0'
jb exit
cmp byte[buffer], 'f'
jb exit
; you probably want "ja" here - you're rejecting almost everything
;si le character est un nombre,on le traduit
cmp byte[buffer], '0'
ja .convNum
cmp byte[buffer], 'F' ; si la chaine commence avec une majuscule
jb .convNumMaj ;converti, le majuscule en hexa
cmp byte[buffer, 'a' ;si la chaine commence avec une miniscule
ja .convNumMin ;converti, le miniscule en hexa
; you've let a few invalid characters slip by here
; okay for a "well behaved user"
;on fait un shift left de 4 bit pour déclaler les valeurs
mov ecx, byte[buffer]
; I don't believe this will assemble
; "movzx" might work, if that's what you want
sal ecx, 4
; I doubt if this is the number you want to shift
mov ebx, ecx
jmp short .top
; doing this repeatedly to the first character in the buffer probably isn't useful
.convNum:
sub byte[buffer], 0x00
; This does nothing. You probably meant '0'
; and you probably don't want to "fall through" into other kinds of characters
.convNumMaj:
sub byte[buffer], 0x07
.convNumMin:
sub byte[buffer], 0x27
; you'll need to subtract an extra '0' in these two cases
pop ebx
ret
; we were called, I guess - not shown
I guess I didn't explain that too well. What I had in mind was... well, first we need to know where the buffer is that we're going to find this text. You do that "mov edx, [esp + 8]" - I suspect you copied that from somewhere without really understanding it, but it implies the address of the buffer was passed on the stack...
push buffer
call mythingie
add esp, 4
mov [this_num}, eax
Might be simpler just to pass it in edx...
mov edx, num1 ; I think that's a name you used
call mythingie
mov [this_num], eax
mov edx, num2
call mythingie
mov [that_num], eax
...
mythingie:
; expects: address of a buffer in edx
; returns: number in eax
; trashes: various...
; zero out a register to be "result" (eax is traditional)
xor eax, eax
.top:
; get a single byte from input
mov cl, [edx]
; increment pointer into buffer
inc edx
; make sure it's a valid hex digit
; I like to just quit if invalid - you can yell at the user if you want
cmp cl, '0'
jb .done
; this should catch both zero-terminated or LF-terminated strings
cmp cl, '9'
jb decimal_digit
; rather than check for both
; force it to uppercase (majuscule)
and cl, 0DFh
cmp cl, 'A'
jb .done
cmp cl, 'F"
ja .done
sub cl, 7 ; reduce it to just above '9'
; convert the character to a number
; subtract '0' if it's a decimal digit
sub cl, '0'
; at this point, cl should hold 0 to 15 - four bits
; shift the "result so far" left 4 bits
shl eax, 4 ; sal is okay, too - same thing (shr and sar differ, though)
; "or" in the new number to the "result so far"
or al, cl
; go to top
. done:
ret
That's untested - may not even assemble - but I think the idea is generally what you want.
I dunno, though... your second example (which won't even assemble the first line) seems to want to work with 64-bit numbers. Hmmm... and you want "MAX_NUMBER" to be 16... None of the routines we've been discussing will handle 64-bit numbers. That'll take quite a bit more work. I suggest you get 32-bit numbers working first!
I'll have to get back to ya on that second example. You've got "adc" right, but need to think about what you want to add the carry to.
Best,
Frank