_alldiv and signed long long integer divides [SOLVED]

Programming, for all ages and all languages.
Post Reply
User avatar
mark3094
Member
Member
Posts: 164
Joined: Mon Feb 14, 2011 10:32 pm
Location: Australia
Contact:

_alldiv and signed long long integer divides [SOLVED]

Post by mark3094 »

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.

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;
}
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

Code: Select all

char * itoa(long long n) {
to

Code: Select all

char * itoa(unsigned long long n) {
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.

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
Last edited by mark3094 on Tue Jul 19, 2011 5:13 am, edited 1 time in total.
User avatar
mark3094
Member
Member
Posts: 164
Joined: Mon Feb 14, 2011 10:32 pm
Location: Australia
Contact:

Re: _alldiv and signed long long integer divides

Post by mark3094 »

I see part of where I have made my mistake...

It's not the divide that's causing the problem, it's the mod, which calls _allrem, not _alldiv

I got this code from the same place, and it would seem that there is something odd about the differences between _aullrem and _allrem

I'll have a look into this and post my findings
User avatar
mark3094
Member
Member
Posts: 164
Joined: Mon Feb 14, 2011 10:32 pm
Location: Australia
Contact:

Re: _alldiv and signed long long integer divides

Post by mark3094 »

Yes, it was indeed a problem with _allrem instead of _alldiv. I just had a few typos in there

I'm sorry if I have wasted anyones time on this one. At least I can sleep soundly tonight 8)
Post Reply