1

I was programming an ASCII to decimal converter in ASM and after an edit, it just didn't add to rax, this caused an infinite loop. GDB says that rax wasn't accepting ANY numbers, even when moved from memory and other registers.

I have tried moving things into rax with mov and I have also tried the inc operation.

Here is the asciiToDec function, well commented, I push the location of the ASCII numbers then the amount of them onto the stack then call this function.

    asciiToDec:                     ; Converts a number in ASCII to decimal that the computer can understand
        pop rax                     ; Remove the ret pointer so that it isnt popped off later when the other values are popped
        pop qword[amount]           ; Remove the amount of characters from the stack
        pop qword[location]         ; Remove the location from the stack
        push rax                    ; Push the ret pointer back onto the stack
        mov rax,1                   ; Move 1 into rax since 0*x==0 so 1*x==10 where x=10
        mov rbx,10                  ; Move 10 into rbx
        mov rcx,[amount]            ; Move the amount of stuff into rcx
        sub rcx,1                   ; Subtract one since the one's digit needs to be multiplied by 1 not 10
;-----------------------------------;
        loop1:                      ; Stage 1: Counts the power of 10 that is needed for the first number, highest digit
            mul rbx                 ; Multiply rax by rbx, rax*10
            sub rcx,1               ; Subtract 1 from rcx
            cmp rcx,0               ; Test if rcx==0
            jne loop1               ; Repeat if rcx>0
            mov [power10],rax       ; Move the power of ten into the power10 variable
            xor rax,rax             ; Set rax to 0 via xoring the bits to 0
            mov rbx,[location]      ; Move the location of the ASCII into rbx
;-----------------------------------;
        loop2:                      ; Stage 2: Actually converts the ASCII to decimal by subtracting ASCII '0'
            xor rcx,rcx             ; Remove previous data
            mov cl,byte[rbx+rax]    ; Copy new data into the low byte of rcx
            cmp cl,10               ; Next 4 lines: test for newlines and carrige returns, shouldn't have any but just making sure
            je  loop2               ; /\
            cmp cl,13               ; /\
            je  loop2               ; /\
            add rax,1               ; INC rax so we have the next byte
            sub cl,'0'              ; Make it decimal and not ASCII
            cmp cl,9                ; Test if the value in cl is equal to 9
            jg  failFun             ; If greater than 9 then the ASCII value in this index was not 0-9
            push rax                ; Get the data in rax out of the way so we can do some calculations with rax
            mov al,cl               ; Move the byte in cl to al, 0-9
            mul qword[power10]      ; Multiply rax (al is the lowest 8 bits of rax) by the power of 10
            mov rcx,rax             ; Move the val in rax back to rcx for later use
            mov rax,[power10]       ; Move the power of 10 to rax
            push rbx                ; Get the data in rbx out of the way, like rax
            mov rbx,10              ; Move 10 into rbx
            div rbx                 ; Divide rax (power of ten) by rbx (10) to get the next lower digit's power
            pop rbx                 ; Get back the data that was in rbx
            mov [power10],rax       ; Move the power of 10 back into the `power10` variable from rax
            pop rax                 ; Get rax's data back
            add [total],rcx         ; Add rcx to the total
            cmp rax,[amount]        ; Compare rax to the amount of characters
            jne loop2               ; If we haven't gone through all the characters, loop again
        pop rax                     ; Get the ret pointer out of the stack so I can have it on the top later
        push qword[total]           ; Move the total into the stack
        push rax                    ; and push the ret pointer back on top
        jmp clean                   ; Jump to clean
;-----------------------------------;
        failFun:                    ; Stage 3: An option for the function to go through if stage 2 fails
            push qword fail
            push qword failLen
            call print
            pop rax
            push qword 0
            push rax
            jmp clean

I expect it to push the dec number onto the stack for popping later but instead, rax is never above 0 even after the add operation and the mov rax,[power10] which results in an infinite loop because rax never reaches [amount] EDIT: GDB output:

Starting program: /path/to/executable/a.out 
1234

Breakpoint 1, loop2 () at common.asm:114
114             xor rcx,rcx             ; Remove previous data
(gdb) info reg rax
rax            0x0  0
(gdb) nexti
115             mov cl,byte[rbx+rax]    ; Copy new data into the low byte of rcx
(gdb) 
116             cmp cl,10               ; Next 4 lines: test for newlines and carrige returns, shouldn't have any but just making sure
(gdb) 
120             add rax,1               ; INC rax so we have the next byte
(gdb) 
121             sub cl,'0'              ; Make it decimal and not ASCII
(gdb) info reg rax
rax            0x0  0
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Clinery
  • 33
  • 4
  • It's unclear what your problem is. Post gdb log of an instruction that is supposed to load `rax` but doesn't. PS: popping and pushing return address is not a good practice. Consider following standard calling conventions. – Jester Sep 11 '19 at 12:31
  • whoops! changed that and it did not effect the outcome of `rax` in loop2. Ill fix that, thanks for pointing that out. – Clinery Sep 11 '19 at 12:31
  • Please post `x/i $rip` at the `add rax, 1`. I suspect you have a mismatch between your binary and the source. – Jester Sep 11 '19 at 12:38
  • `120 add rax,1 ; INC rax so we have the next byte (gdb) 121 sub cl,'0' ; Make it decimal and not ASCII (gdb) x/i $rip => 0x400176 : sub $0x30,%cl ` is the output of `x/i $rip` – Clinery Sep 11 '19 at 12:43
  • That's for the last line. Post `x/i $rip` **at the add rax, 1** – Jester Sep 11 '19 at 12:45
  • `=> 0x400174 : je 0x40016b ` – Clinery Sep 11 '19 at 12:47
  • If that is really the `add` instruction then you can see you are not running the binary corresponding to the source which is why your rax is not changing. Verify you have built your binary properly and you loaded the one you intended and not some old version. – Jester Sep 11 '19 at 12:50
  • I can confirm that it is the newest executable, I just recompiled and linked everything again and ran the GDB commands, resulted in the same output. – Clinery Sep 11 '19 at 12:54
  • If gdb says `x/i $rip` is **not** an `add` then it's the wrong code. Simple as that. The cpu is running what `x/i $rip` shows not what your source shows. – Jester Sep 11 '19 at 13:04
  • 2
    Okay, found the problem. Nasm is confused by your `/\\` comments and ignores the lines. Remove those comments. *Actually backslash is line continuation so nasm thinks the following lines are the rest of your comment. – Jester Sep 11 '19 at 13:23

2 Answers2

2

NASM is confused by your /\ comments and ignores the lines.Remove those comments. *Actually backslash is line continuation so nasm thinks the following lines are the rest of your comment - Jester

Removing the comment with a \ fixes the issue since NASM thinks the next lines are comments. VIM NASM syntax highlighting does not follow this rule.

From the NASM manual, 3.1 Layout of a NASM Source Line:

NASM uses backslash (\) as the line continuation character; if a line ends with backslash, the next line is considered to be a part of the backslash-ended line.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Clinery
  • 33
  • 4
1

Just a side note, you can use xor eax, eax instead of xor rax, rax. The former zeros out the 64-bit high half as well, but has 1 byte shorter encoding. Same applies to mov eax, 1 vs mov rax, 1.

In general, 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register (otherwise 32-bit operations would have a dependency on the 64-bit higher half of the register). See x86-64 Tour of Intel Manuals for more details.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271