_alldiv and signed long long integer divides [SOLVED]
Posted: Tue Jul 19, 2011 3:55 am
Hello again,
I have a problem which I can't figure out. This partially follows on from two previous posts I posted, but is unique in it's own way too.
(http://forum.osdev.org/viewtopic.php?f=13&t=23858
http://forum.osdev.org/viewtopic.php?f=13&t=23878)
I have a C function which converts integers to ASCII, so printf or putchar can print them. It uses long long integers (on 32-bit x86) so it can be capable of converting 64-bit values.
This requires the use of the _alldiv function (as mentioned in the previous posts). I haven't yet figured out the specifics of how it works, so I have copied in some code I found on the net (see first post).
The problem is that it always converts the integers to 0. So, if I try to print 1234, it will print 0000. The interresting thing is that if I change to it works just fine. Also, if I change it to a long int instead of long long int, it is also OK.
This suggests to me that the _aulldiv function is fine, but the _alldiv isn't. I got both from the same source. I have included the code below. I have had to modify it to work with NASM which is what I'm using for all my asm files.
I know this one is really complicated, and I'm asking a lot, but this is starting to drive me insane.
I have a problem which I can't figure out. This partially follows on from two previous posts I posted, but is unique in it's own way too.
(http://forum.osdev.org/viewtopic.php?f=13&t=23858
http://forum.osdev.org/viewtopic.php?f=13&t=23878)
I have a C function which converts integers to ASCII, so printf or putchar can print them. It uses long long integers (on 32-bit x86) so it can be capable of converting 64-bit values.
Code: Select all
char * itoa(long long n) {
i = 0;
do {
itoastr[i++] = (n % 10) + 48;
} while ((n /= 10) != 0);
itoastr[i] = '\0';
reverse();
return itoastr;
}
The problem is that it always converts the integers to 0. So, if I try to print 1234, it will print 0000. The interresting thing is that if I change
Code: Select all
char * itoa(long long n) {
Code: Select all
char * itoa(unsigned long long n) {
This suggests to me that the _aulldiv function is fine, but the _alldiv isn't. I got both from the same source. I have included the code below. I have had to modify it to work with NASM which is what I'm using for all my asm files.
I know this one is really complicated, and I'm asking a lot, but this is starting to drive me insane.
Code: Select all
;
; alldiv.asm
[BITS 32]
[GLOBAL __alldiv]
[SECTION .TEXT]
__alldiv:
; Setup stack frame
PUSH EDI
PUSH ESI
PUSH EBX
xor edi,edi ; result sign assumed positive
mov eax,[esp+20] ; hi word of a
or eax,eax ; test to see if signed
jge short L1 ; skip rest if a is already positive
inc edi ; complement result sign flag
mov edx,[esp+16] ; lo word of a
neg eax ; make a positive
neg edx
sbb eax,0
mov [esp+20],eax ; save positive value
mov [esp+16],edx
L1:
mov eax,[esp+28] ; hi word of b
or eax,eax ; test to see if signed
jge short L2 ; skip rest if b is already positive
inc edi ; complement the result sign flag
mov edx,[esp+24] ; lo word of a
neg eax ; make b positive
neg edx
sbb eax,0
mov [esp+28],eax ; save positive value
mov [esp+24],edx
L2:
;
; Now do the divide. First look to see if the divisor is less than 4194304K.
; If so, then we can use a simple algorithm with word divides, otherwise
; things get a little more complex.
;
; NOTE - eax currently contains the high order word of DVSR
;
or eax,eax ; check to see if divisor < 4194304K
jnz short L3 ; nope, gotta do this the hard way
mov ecx,[esp+24] ; load divisor
mov eax,[esp+20] ; load high word of dividend
xor edx,edx
div ecx ; eax <- high order bits of quotient
mov ebx,eax ; save high bits of quotient
mov eax,[esp+16] ; edx:eax <- remainder:lo word of dividend
div ecx ; eax <- low order bits of quotient
mov edx,ebx ; edx:eax <- quotient
jmp short L4 ; set sign, restore stack and return
;
; Here we do it the hard way. Remember, eax contains the high word of DVSR
;
L3:
mov ebx,eax ; ebx:ecx <- divisor
mov ecx,[esp+24]
mov edx,[esp+20] ; edx:eax <- dividend
mov eax,[esp+16]
L5:
shr ebx,1 ; shift divisor right one bit
rcr ecx,1
shr edx,1 ; shift dividend right one bit
rcr eax,1
or ebx,ebx
jnz short L5 ; loop until divisor < 4194304K
div ecx ; now divide, ignore remainder
mov esi,eax ; save quotient
;
; We may be off by one, so to check, we will multiply the quotient
; by the divisor and check the result against the orignal dividend
; Note that we must also check for overflow, which can occur if the
; dividend is close to 2**64 and the quotient is off by 1.
;
mul dword [esp+28] ; QUOT * [esp+28]
mov ecx,eax
mov eax,[esp+24]
mul esi ; QUOT * [esp+24]
add edx,ecx ; EDX:EAX = QUOT * DVSR
jc short L6 ; carry means Quotient is off by 1
;
; do long compare here between original dividend and the result of the
; multiply in edx:eax. If original is larger or equal, we are ok, otherwise
; subtract one (1) from the quotient.
;
cmp edx,[esp+20] ; compare hi words of result and original
ja short L6 ; if result > original, do subtract
jb short L7 ; if result < original, we are ok
cmp eax,[esp+16] ; hi words are equal, compare lo words
jbe short L7 ; if less or equal we are ok, else subtract
L6:
dec esi ; subtract 1 from quotient
L7:
xor edx,edx ; edx:eax <- quotient
mov eax,esi
;
; Just the cleanup left to do. edx:eax contains the quotient. Set the sign
; according to the save value, cleanup the stack, and return.
;
L4:
dec edi ; check to see if result is negative
jnz short Clean ; if EDI == 0, result should be negative
neg edx ; otherwise, negate the result
neg eax
sbb edx,0
Clean:
; Clean up
POP EBX
POP ESI
POP EDI
RET