Author Topic: Addition of 2 hex numbers and displaying them[LINUX] 32 bit  (Read 22842 times)

Offline djasy3

  • Jr. Member
  • *
  • Posts: 8
Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« 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:

Code: [Select]
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

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #1 on: February 26, 2014, 05:17:25 AM »
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


Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #2 on: February 26, 2014, 06:39:49 AM »
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.

Code: [Select]

; 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.
Encryptor256's Investigation \ Research Department.

Offline djasy3

  • Jr. Member
  • *
  • Posts: 8
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #3 on: February 27, 2014, 02:03:35 AM »
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:

Code: [Select]
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

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #4 on: February 27, 2014, 02:37:59 AM »
Code: [Select]
;********************************************** 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)

Offline djasy3

  • Jr. Member
  • *
  • Posts: 8
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #5 on: February 27, 2014, 03:29:34 AM »
@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 :
Code: [Select]
rol ecx, 4            ;<- MODIFYING ECX is the rotation correct? what should i do to make it right?
thanks.

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #6 on: February 27, 2014, 03:57:46 AM »
You have a non-terminating loop.  Let's look at the crux of the issue by examining slightly different code:
Code: [Select]
   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!

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #7 on: February 27, 2014, 07:03:28 AM »
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...
Quote

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...
Code: [Select]
; 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...
Code: [Select]
; 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


Offline djasy3

  • Jr. Member
  • *
  • Posts: 8
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #8 on: February 28, 2014, 06:41:41 PM »
@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 !
Code: [Select]
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:
Code: [Select]
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
« Last Edit: March 01, 2014, 06:14:09 AM by djasy3 »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Addition of 2 hex numbers and displaying them[LINUX] 32 bit
« Reply #9 on: March 01, 2014, 12:19:12 PM »
Does it work? Does it even assemble? Doesn't look good to me - either of 'em

Code: [Select]
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...

Code: [Select]
push buffer
call mythingie
add esp, 4
mov [this_num}, eax
Might be simpler just to pass it in edx...
Code: [Select]
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
...

Code: [Select]
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