I have implemented an algorithm which converts a signed integer to its ASCII equivalent string with the corresponding sign as a first byte. I have tested it on several numbers and it worked correctly. But as a newbie I might have not noticed some logic mistakes or flaws.
I am asking for any ideas how to make this more efficient and elegant.
;;;;MACRO SECTION//////////////////////////////////////////////////////////////
%macro Int2ascii 2 ;1 - number, 2 - string
mov r12, %2 ;move ADDRESS of string
mov eax, dword[%1]
cmp eax, 0
jge %%placePlus ;check either number is negative or positive
%%placeMinus:
mov byte [r12], "-" ;fill the first (0th) byte with the sign
mov rdi, -1 ;remember the sign
;neg eax ;negate number and forgot the sign (deprecated: see EDIT 1)
jmp %%skipPlacePlus
%%placePlus:
mov byte [r12], "+"
mov rdi, 1 ;remember the sign
%%skipPlacePlus:
xor rcx, rcx ;digit counter here (used implicitely in %%fillLoop)
mov ebx, 10
%%divideLoop:
cdq ;eax -> edx:eax
idiv ebx ;signed division, reminder is also signed
imul rdx, rdi ;"unsign" the current reminder
;div ebx ;(deprecated: see EDIT 1)
push rdx ;push the reminder to stack
inc rcx ;increment digit counter
cmp eax, 0
jne %%divideLoop
mov rdi, 1 ;note that the first (0th) byte was number sign (+/-)
%%fillLoop:
pop rax ;pop back reminders
add al, "0" ;ascii code for digit
mov byte [r12 + rdi], al ;fill string
inc rdi
loop %%fillLoop
mov byte [r12 + rdi], 0x00 ;terminate string
%endmacro
Example of output
As @TobySpeight suggested I have tested the above algorithm on numbers: -1, 0, 1, 9, 10, 0x7fffffff, 0x80000000
. Output is (gdb
session):
(gdb) x/s &sNegOne
0x600377: "-1"
(gdb) x/s &sZero
0x600368: "+0"
(gdb) x/s &sPosOne
0x600386: "+1"
(gdb) x/s &sNine
0x600395: "+9"
(gdb) x/s &sTen
0x6003a4: "+10"
(gdb) x/s &sNumber1
0x6003b3: "+2147483647"
(gdb) x/s &sNumber2
0x6003c2: "-2147483648"
I have realized that trying to consider only unsigned positive numbers leads to double-word overflow for initially valid double-word number 0x80000000
but invalid after taking absolute value 0x7fffffff + 1
which is no longer signed double-word number. I have overcome it by signed division. The problem here is for signed division reminders are also signed and no more applicable for storing as digits without additional processing which is just multiplying by -1 or 1 depending on sign.
-1
,0
,+1
,9
,10
,0x7fffffff
,0x80000000
. \$\endgroup\$0x80000000
. This is because my negation line (when I'm "forgotting" the sign). Double-word overflows there. I will be thinking how to overcome this problem. Thank you. \$\endgroup\$cdq
. That sign extendsEAX
intoEDX
, but since you're treatingEAX
as unsigned at that point you just want to zero outEDX
. \$\endgroup\$