;++ ; ; Copyright (c) Microsoft Corporation ; ; Module Name: ; ; blcrt.asm ; ; Abstract: ; ; This module implements utility functions for boot loader C runtime. ; ; Environment: ; ; Boot loader. ; ;-- include bl.inc .686p .model flat .code assume ds:flat assume es:flat assume ss:flat assume fs:flat LOWORD equ [0] HIWORD equ [4] ;*** ;lldiv.asm - signed long divide routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the signed long divide routine ; __alldiv ; ;Revision History: ; 11-29-83 DFW initial version ; 06-01-84 RN modified to use cmacros ; 10-24-87 SKS fixed off-by-1 error for dividend close to 2**32. ; 05-18-89 SKS Remove redundant "MOV SP,BP" from epilog ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-22-94 GJF Use esp-relative addressing for args. Shortened ; conditional jumps. Also, don't use xchg to do a ; simple move between regs. ; ;******************************************************************************* ;*** ;lldiv - signed long divide ; ;Purpose: ; Does a signed long divide of the arguments. Arguments are ; not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the quotient (dividend/divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __alldiv PROC NEAR push edi push esi push ebx ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a/b will ; generate a call to lldiv(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; | EDI | ; |---------------| ; | ESI | ; |---------------| ; ESP---->| EBX | ; ----------------- ; DVND equ [esp + 16] ; stack address of dividend (a) DVSR equ [esp + 24] ; stack address of divisor (b) ; Determine sign of the result (edi = 0 if result is positive, non-zero ; otherwise) and make operands positive. xor edi,edi ; result sign assumed positive mov eax,HIWORD(DVND) ; 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,LOWORD(DVND) ; lo word of a neg eax ; make a positive neg edx sbb eax,0 mov HIWORD(DVND),eax ; save positive value mov LOWORD(DVND),edx L1: mov eax,HIWORD(DVSR) ; 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,LOWORD(DVSR) ; lo word of a neg eax ; make b positive neg edx sbb eax,0 mov HIWORD(DVSR),eax ; save positive value mov LOWORD(DVSR),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,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; 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,LOWORD(DVND) ; 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,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) 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 ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) mov ecx,eax mov eax,LOWORD(DVSR) mul esi ; QUOT * LOWORD(DVSR) 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,HIWORD(DVND) ; 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,LOWORD(DVND) ; 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 L8 ; if EDI == 0, result should be negative neg edx ; otherwise, negate the result neg eax sbb edx,0 ; ; Restore the saved registers and return. ; L8: pop ebx pop esi pop edi ret 16 __alldiv ENDP ;*** ;lldvrm.asm - signed long divide and remainder routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the signed long divide and remainder routine ; __alldvrm ; ;Revision History: ; 10-06-98 SMK Initial version. ; ;******************************************************************************* ;*** ;lldvrm - signed long divide and remainder ; ;Purpose: ; Does a signed long divide and remainder of the arguments. Arguments are ; not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the quotient (dividend/divisor) ; EBX:ECX contains the remainder (divided % divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __alldvrm PROC NEAR push edi push esi push ebp ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a/b will ; generate a call to alldvrm(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; | EDI | ; |---------------| ; | ESI | ; |---------------| ; ESP---->| EBP | ; ----------------- ; DVND equ [esp + 16] ; stack address of dividend (a) DVSR equ [esp + 24] ; stack address of divisor (b) ; Determine sign of the quotient (edi = 0 if result is positive, non-zero ; otherwise) and make operands positive. ; Sign of the remainder is kept in ebp. xor edi,edi ; result sign assumed positive xor ebp,ebp ; result sign assumed positive mov eax,HIWORD(DVND) ; 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 inc ebp ; complement result sign flag mov edx,LOWORD(DVND) ; lo word of a neg eax ; make a positive neg edx sbb eax,0 mov HIWORD(DVND),eax ; save positive value mov LOWORD(DVND),edx L1: mov eax,HIWORD(DVSR) ; 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,LOWORD(DVSR) ; lo word of a neg eax ; make b positive neg edx sbb eax,0 mov HIWORD(DVSR),eax ; save positive value mov LOWORD(DVSR),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,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; 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,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend div ecx ; eax <- low order bits of quotient mov esi,eax ; ebx:esi <- quotient ; ; Now we need to do a multiply so that we can compute the remainder. ; mov eax,ebx ; set up high word of quotient mul dword ptr LOWORD(DVSR) ; HIWORD(QUOT) * DVSR mov ecx,eax ; save the result in ecx mov eax,esi ; set up low word of quotient mul dword ptr LOWORD(DVSR) ; LOWORD(QUOT) * DVSR add edx,ecx ; EDX:EAX = QUOT * DVSR jmp short L4 ; complete remainder calculation ; ; Here we do it the hard way. Remember, eax contains the high word of DVSR ; L3: mov ebx,eax ; ebx:ecx <- divisor mov ecx,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) 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 ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) mov ecx,eax mov eax,LOWORD(DVSR) mul esi ; QUOT * LOWORD(DVSR) 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,HIWORD(DVND) ; 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,LOWORD(DVND) ; 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 sub eax,LOWORD(DVSR) ; subtract divisor from result sbb edx,HIWORD(DVSR) L7: xor ebx,ebx ; ebx:esi <- quotient L4: ; ; Calculate remainder by subtracting the result from the original dividend. ; Since the result is already in a register, we will do the subtract in the ; opposite direction and negate the result if necessary. ; sub eax,LOWORD(DVND) ; subtract dividend from result sbb edx,HIWORD(DVND) ; ; Now check the result sign flag to see if the result is supposed to be positive ; or negative. It is currently negated (because we subtracted in the 'wrong' ; direction), so if the sign flag is set we are done, otherwise we must negate ; the result to make it positive again. ; dec ebp ; check result sign flag jns short L9 ; result is ok, set up the quotient neg edx ; otherwise, negate the result neg eax sbb edx,0 ; ; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx. ; L9: mov ecx,edx mov edx,ebx mov ebx,ecx mov ecx,eax 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. ; dec edi ; check to see if result is negative jnz short L8 ; if EDI == 0, result should be negative neg edx ; otherwise, negate the result neg eax sbb edx,0 ; ; Restore the saved registers and return. ; L8: pop ebp pop esi pop edi ret 16 __alldvrm ENDP ;*** ;llmul.asm - long multiply routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; Defines long multiply routine ; Both signed and unsigned routines are the same, since multiply's ; work out the same in 2's complement ; creates the following routine: ; __allmul ; ;Revision History: ; 11-29-83 DFW initial version ; 06-01-84 RN modified to use cmacros ; 04-17-85 TC ignore signs since they take care of themselves ; do a fast multiply if both hiwords of arguments are 0 ; 10-10-86 MH slightly faster implementation, for 0 in upper words ; 03-20-89 SKS Remove redundant "MOV SP,BP" from epilogs ; 05-18-89 SKS Preserve BX ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-22-94 GJF Use esp-relative addressing for args. Shortened ; conditional jump. ; ;******************************************************************************* ;*** ;llmul - long multiply routine ; ;Purpose: ; Does a long multiply (same for signed/unsigned) ; Parameters are not changed. ; ;Entry: ; Parameters are passed on the stack: ; 1st pushed: multiplier (QWORD) ; 2nd pushed: multiplicand (QWORD) ; ;Exit: ; EDX:EAX - product of multiplier and multiplicand ; NOTE: parameters are removed from the stack ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __allmul PROC NEAR A EQU [esp + 4] ; stack address of a B EQU [esp + 12] ; stack address of b ; ; AHI, BHI : upper 32 bits of A and B ; ALO, BLO : lower 32 bits of A and B ; ; ALO * BLO ; ALO * BHI ; + BLO * AHI ; --------------------- ; mov eax,HIWORD(A) mov ecx,HIWORD(B) or ecx,eax ;test for both hiwords zero. mov ecx,LOWORD(B) jnz short hard ;both are zero, just mult ALO and BLO mov eax,LOWORD(A) mul ecx ret 16 ; callee restores the stack hard: push ebx ; must redefine A and B since esp has been altered A2 EQU [esp + 8] ; stack address of a B2 EQU [esp + 16] ; stack address of b mul ecx ;eax has AHI, ecx has BLO, so AHI * BLO mov ebx,eax ;save result mov eax,LOWORD(A2) mul dword ptr HIWORD(B2) ;ALO * BHI add ebx,eax ;ebx = ((ALO * BHI) + (AHI * BLO)) mov eax,LOWORD(A2) ;ecx = BLO mul ecx ;so edx:eax = ALO*BLO add edx,ebx ;now edx has all the LO*HI stuff pop ebx ret 16 ; callee restores the stack __allmul ENDP ;*** ;llrem.asm - signed long remainder routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the signed long remainder routine ; __allrem ; ;Revision History: ; 11-29-83 DFW initial version ; 06-01-84 RN modified to use cmacros ; 10-23-87 SKS fixed off-by-1 error for dividend close to 2**32. ; 05-18-89 SKS Remove redundant "MOV SP,BP" from epilog ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-22-94 GJF Use esp-relative addressing for args. Shortened ; conditional jumps. ; ;******************************************************************************* ;*** ;llrem - signed long remainder ; ;Purpose: ; Does a signed long remainder of the arguments. Arguments are ; not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the remainder (dividend%divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __allrem PROC NEAR push ebx push edi ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a%b will ; generate a call to lrem(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; | EBX | ; |---------------| ; ESP---->| EDI | ; ----------------- ; DVND equ [esp + 12] ; stack address of dividend (a) DVSR equ [esp + 20] ; stack address of divisor (b) ; Determine sign of the result (edi = 0 if result is positive, non-zero ; otherwise) and make operands positive. xor edi,edi ; result sign assumed positive mov eax,HIWORD(DVND) ; 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 bit mov edx,LOWORD(DVND) ; lo word of a neg eax ; make a positive neg edx sbb eax,0 mov HIWORD(DVND),eax ; save positive value mov LOWORD(DVND),edx L1: mov eax,HIWORD(DVSR) ; hi word of b or eax,eax ; test to see if signed jge short L2 ; skip rest if b is already positive mov edx,LOWORD(DVSR) ; lo word of b neg eax ; make b positive neg edx sbb eax,0 mov HIWORD(DVSR),eax ; save positive value mov LOWORD(DVSR),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,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; load high word of dividend xor edx,edx div ecx ; edx <- remainder mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend div ecx ; edx <- final remainder mov eax,edx ; edx:eax <- remainder xor edx,edx dec edi ; check result sign flag jns short L4 ; negate result, restore stack and return jmp short L8 ; result sign ok, 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,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) 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 ; ; 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. ; mov ecx,eax ; save a copy of quotient in ECX mul dword ptr HIWORD(DVSR) xchg ecx,eax ; save product, get quotient in EAX mul dword ptr LOWORD(DVSR) 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 the original divisor from the result. ; cmp edx,HIWORD(DVND) ; 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,LOWORD(DVND) ; hi words are equal, compare lo words jbe short L7 ; if less or equal we are ok, else subtract L6: sub eax,LOWORD(DVSR) ; subtract divisor from result sbb edx,HIWORD(DVSR) L7: ; ; Calculate remainder by subtracting the result from the original dividend. ; Since the result is already in a register, we will do the subtract in the ; opposite direction and negate the result if necessary. ; sub eax,LOWORD(DVND) ; subtract dividend from result sbb edx,HIWORD(DVND) ; ; Now check the result sign flag to see if the result is supposed to be positive ; or negative. It is currently negated (because we subtracted in the 'wrong' ; direction), so if the sign flag is set we are done, otherwise we must negate ; the result to make it positive again. ; dec edi ; check result sign flag jns short L8 ; result is ok, restore stack and return L4: neg edx ; otherwise, negate the result neg eax sbb edx,0 ; ; Just the cleanup left to do. edx:eax contains the quotient. ; Restore the saved registers and return. ; L8: pop edi pop ebx ret 16 __allrem ENDP ;*** ;llshl.asm - long shift left ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; define long shift left routine (signed and unsigned are same) ; __allshl ; ;Revision History: ; 11-??-83 HS initial version ; 11-30-83 DFW added medium model support ; 03-12-84 DFW broke apart; added long model support ; 06-01-84 RN modified to use cmacros ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-08-94 GJF Faster, fatter version for NT. ; 07-13-94 GJF Further improvements from JonM. ; ;******************************************************************************* ;*** ;llshl - long shift left ; ;Purpose: ; Does a Long Shift Left (signed and unsigned are identical) ; Shifts a long left any number of bits. ; ;Entry: ; EDX:EAX - long value to be shifted ; CL - number of bits to shift by ; ;Exit: ; EDX:EAX - shifted value ; ;Uses: ; CL is destroyed. ; ;Exceptions: ; ;******************************************************************************* __allshl PROC NEAR ; ; Handle shifts of 64 or more bits (all get 0) ; cmp cl, 64 jae short RETZERO ; ; Handle shifts of between 0 and 31 bits ; cmp cl, 32 jae short MORE32 shld edx,eax,cl shl eax,cl ret ; ; Handle shifts of between 32 and 63 bits ; MORE32: mov edx,eax xor eax,eax and cl,31 shl edx,cl ret ; ; return 0 in edx:eax ; RETZERO: xor eax,eax xor edx,edx ret __allshl ENDP ;*** ;llshr.asm - long shift right ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; define signed long shift right routine ; __allshr ; ;Revision History: ; 11-??-83 HS initial version ; 11-30-83 DFW added medium model support ; 03-12-84 DFW broke apart; added long model support ; 06-01-84 RN modified to use cmacros ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-08-94 GJF Faster, fatter version for NT. ; 07-13-94 GJF Further improvements from JonM. ; ;******************************************************************************* ;*** ;llshr - long shift right ; ;Purpose: ; Does a signed Long Shift Right ; Shifts a long right any number of bits. ; ;Entry: ; EDX:EAX - long value to be shifted ; CL - number of bits to shift by ; ;Exit: ; EDX:EAX - shifted value ; ;Uses: ; CL is destroyed. ; ;Exceptions: ; ;******************************************************************************* __allshr PROC NEAR ; ; Handle shifts of 64 bits or more (if shifting 64 bits or more, the result ; depends only on the high order bit of edx). ; cmp cl,64 jae short RETSIGN ; ; Handle shifts of between 0 and 31 bits ; cmp cl, 32 jae short MORE32 shrd eax,edx,cl sar edx,cl ret ; ; Handle shifts of between 32 and 63 bits ; MORE32: mov eax,edx sar edx,31 and cl,31 sar eax,cl ret ; ; Return double precision 0 or -1, depending on the sign of edx ; RETSIGN: sar edx,31 mov eax,edx ret __allshr ENDP ;*** ;ulldiv.asm - unsigned long divide routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the unsigned long divide routine ; __aulldiv ; ;Revision History: ; 11-29-83 DFW initial version ; 06-01-84 RN modified to use cmacros ; 10-23-87 SKS fixed off-by-1 error for dividend close to 2**32. ; 05-18-89 SKS Remove redundant "MOV SP,BP" from epilog ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-22-94 GJF Use esp-relative addressing for args. Shortened ; conditional jumps. Also, don't use xchg to do a ; simple move between regs. ; ;******************************************************************************* ;*** ;ulldiv - unsigned long divide ; ;Purpose: ; Does a unsigned long divide of the arguments. Arguments are ; not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the quotient (dividend/divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __aulldiv PROC NEAR push ebx push esi ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a/b will ; generate a call to uldiv(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; | EBX | ; |---------------| ; ESP---->| ESI | ; ----------------- ; DVND equ [esp + 12] ; stack address of dividend (a) DVSR equ [esp + 20] ; stack address of divisor (b) ; ; 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. ; mov eax,HIWORD(DVSR) ; check to see if divisor < 4194304K or eax,eax jnz short L1 ; nope, gotta do this the hard way mov ecx,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; load high word of dividend xor edx,edx div ecx ; get high order bits of quotient mov ebx,eax ; save high bits of quotient mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend div ecx ; get low order bits of quotient mov edx,ebx ; edx:eax <- quotient hi:quotient lo jmp short L2 ; restore stack and return ; ; Here we do it the hard way. Remember, eax contains DVSRHI ; L1: mov ecx,eax ; ecx:ebx <- divisor mov ebx,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) L3: shr ecx,1 ; shift divisor right one bit; hi bit <- 0 rcr ebx,1 shr edx,1 ; shift dividend right one bit; hi bit <- 0 rcr eax,1 or ecx,ecx jnz short L3 ; loop until divisor < 4194304K div ebx ; 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 ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) mov ecx,eax mov eax,LOWORD(DVSR) mul esi ; QUOT * LOWORD(DVSR) add edx,ecx ; EDX:EAX = QUOT * DVSR jc short L4 ; 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,HIWORD(DVND) ; compare hi words of result and original ja short L4 ; if result > original, do subtract jb short L5 ; if result < original, we are ok cmp eax,LOWORD(DVND) ; hi words are equal, compare lo words jbe short L5 ; if less or equal we are ok, else subtract L4: dec esi ; subtract 1 from quotient L5: xor edx,edx ; edx:eax <- quotient mov eax,esi ; ; Just the cleanup left to do. edx:eax contains the quotient. ; Restore the saved registers and return. ; L2: pop esi pop ebx ret 16 __aulldiv ENDP ;*** ;ulldvrm.asm - unsigned long divide and remainder routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the unsigned long divide and remainder routine ; __aulldvrm ; ;Revision History: ; 10-06-98 SMK Initial version. ; ;******************************************************************************* ;*** ;ulldvrm - unsigned long divide and remainder ; ;Purpose: ; Does a unsigned long divide and remainder of the arguments. Arguments ; are not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the quotient (dividend/divisor) ; EBX:ECX contains the remainder (divided % divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __aulldvrm PROC NEAR push esi ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a/b will ; generate a call to aulldvrm(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; ESP---->| ESI | ; ----------------- ; DVND equ [esp + 8] ; stack address of dividend (a) DVSR equ [esp + 16] ; stack address of divisor (b) ; ; 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. ; mov eax,HIWORD(DVSR) ; check to see if divisor < 4194304K or eax,eax jnz short L1 ; nope, gotta do this the hard way mov ecx,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; load high word of dividend xor edx,edx div ecx ; get high order bits of quotient mov ebx,eax ; save high bits of quotient mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend div ecx ; get low order bits of quotient mov esi,eax ; ebx:esi <- quotient ; ; Now we need to do a multiply so that we can compute the remainder. ; mov eax,ebx ; set up high word of quotient mul dword ptr LOWORD(DVSR) ; HIWORD(QUOT) * DVSR mov ecx,eax ; save the result in ecx mov eax,esi ; set up low word of quotient mul dword ptr LOWORD(DVSR) ; LOWORD(QUOT) * DVSR add edx,ecx ; EDX:EAX = QUOT * DVSR jmp short L2 ; complete remainder calculation ; ; Here we do it the hard way. Remember, eax contains DVSRHI ; L1: mov ecx,eax ; ecx:ebx <- divisor mov ebx,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) L3: shr ecx,1 ; shift divisor right one bit; hi bit <- 0 rcr ebx,1 shr edx,1 ; shift dividend right one bit; hi bit <- 0 rcr eax,1 or ecx,ecx jnz short L3 ; loop until divisor < 4194304K div ebx ; 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 ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) mov ecx,eax mov eax,LOWORD(DVSR) mul esi ; QUOT * LOWORD(DVSR) add edx,ecx ; EDX:EAX = QUOT * DVSR jc short L4 ; 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,HIWORD(DVND) ; compare hi words of result and original ja short L4 ; if result > original, do subtract jb short L5 ; if result < original, we are ok cmp eax,LOWORD(DVND) ; hi words are equal, compare lo words jbe short L5 ; if less or equal we are ok, else subtract L4: dec esi ; subtract 1 from quotient sub eax,LOWORD(DVSR) ; subtract divisor from result sbb edx,HIWORD(DVSR) L5: xor ebx,ebx ; ebx:esi <- quotient L2: ; ; Calculate remainder by subtracting the result from the original dividend. ; Since the result is already in a register, we will do the subtract in the ; opposite direction and negate the result. ; sub eax,LOWORD(DVND) ; subtract dividend from result sbb edx,HIWORD(DVND) neg edx ; otherwise, negate the result neg eax sbb edx,0 ; ; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx. ; mov ecx,edx mov edx,ebx mov ebx,ecx mov ecx,eax mov eax,esi ; ; Just the cleanup left to do. edx:eax contains the quotient. ; Restore the saved registers and return. ; pop esi ret 16 __aulldvrm ENDP ;*** ;ullrem.asm - unsigned long remainder routine ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; defines the unsigned long remainder routine ; __aullrem ; ;Revision History: ; 11-29-83 DFW initial version ; 06-01-84 RN modified to use cmacros ; 10-23-87 SKS fixed off-by-1 error for dividend close to 2**32. ; 05-18-89 SKS Remove redundant "MOV SP,BP" from epilog ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-22-94 GJF Use esp-relative addressing for args. Shortened ; conditional jumps. ; ;******************************************************************************* ;*** ;ullrem - unsigned long remainder ; ;Purpose: ; Does a unsigned long remainder of the arguments. Arguments are ; not changed. ; ;Entry: ; Arguments are passed on the stack: ; 1st pushed: divisor (QWORD) ; 2nd pushed: dividend (QWORD) ; ;Exit: ; EDX:EAX contains the remainder (dividend%divisor) ; NOTE: this routine removes the parameters from the stack. ; ;Uses: ; ECX ; ;Exceptions: ; ;******************************************************************************* __aullrem PROC NEAR push ebx ; Set up the local stack and save the index registers. When this is done ; the stack frame will look as follows (assuming that the expression a%b will ; generate a call to ullrem(a, b)): ; ; ----------------- ; | | ; |---------------| ; | | ; |--divisor (b)--| ; | | ; |---------------| ; | | ; |--dividend (a)-| ; | | ; |---------------| ; | return addr** | ; |---------------| ; ESP---->| EBX | ; ----------------- ; DVND equ [esp + 8] ; stack address of dividend (a) DVSR equ [esp + 16] ; stack address of divisor (b) ; 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. ; mov eax,HIWORD(DVSR) ; check to see if divisor < 4194304K or eax,eax jnz short L1 ; nope, gotta do this the hard way mov ecx,LOWORD(DVSR) ; load divisor mov eax,HIWORD(DVND) ; load high word of dividend xor edx,edx div ecx ; edx <- remainder, eax <- quotient mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend div ecx ; edx <- final remainder mov eax,edx ; edx:eax <- remainder xor edx,edx jmp short L2 ; restore stack and return ; ; Here we do it the hard way. Remember, eax contains DVSRHI ; L1: mov ecx,eax ; ecx:ebx <- divisor mov ebx,LOWORD(DVSR) mov edx,HIWORD(DVND) ; edx:eax <- dividend mov eax,LOWORD(DVND) L3: shr ecx,1 ; shift divisor right one bit; hi bit <- 0 rcr ebx,1 shr edx,1 ; shift dividend right one bit; hi bit <- 0 rcr eax,1 or ecx,ecx jnz short L3 ; loop until divisor < 4194304K div ebx ; now divide, ignore remainder ; ; 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. ; mov ecx,eax ; save a copy of quotient in ECX mul dword ptr HIWORD(DVSR) xchg ecx,eax ; put partial product in ECX, get quotient in EAX mul dword ptr LOWORD(DVSR) add edx,ecx ; EDX:EAX = QUOT * DVSR jc short L4 ; 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're ok, otherwise ; subtract the original divisor from the result. ; cmp edx,HIWORD(DVND) ; compare hi words of result and original ja short L4 ; if result > original, do subtract jb short L5 ; if result < original, we're ok cmp eax,LOWORD(DVND) ; hi words are equal, compare lo words jbe short L5 ; if less or equal we're ok, else subtract L4: sub eax,LOWORD(DVSR) ; subtract divisor from result sbb edx,HIWORD(DVSR) L5: ; ; Calculate remainder by subtracting the result from the original dividend. ; Since the result is already in a register, we will perform the subtract in ; the opposite direction and negate the result to make it positive. ; sub eax,LOWORD(DVND) ; subtract original dividend from result sbb edx,HIWORD(DVND) neg edx ; and negate it neg eax sbb edx,0 ; ; Just the cleanup left to do. dx:ax contains the remainder. ; Restore the saved registers and return. ; L2: pop ebx ret 16 __aullrem ENDP ;*** ;ullshr.asm - long shift right ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; define unsigned long shift right routine ; __aullshr ; ;Revision History: ; 11-??-83 HS initial version ; 11-30-83 DFW added medium model support ; 03-12-84 DFW broke apart; added long model support ; 06-01-84 RN modified to use cmacros ; 11-28-89 GJF Fixed copyright ; 11-19-93 SMK Modified to work on 64 bit integers ; 01-17-94 GJF Minor changes to build with NT's masm386. ; 07-08-94 GJF Faster, fatter version for NT. ; 07-13-94 GJF Further improvements from JonM. ; ;******************************************************************************* ;*** ;ullshr - long shift right ; ;Purpose: ; Does a unsigned Long Shift Right ; Shifts a long right any number of bits. ; ;Entry: ; EDX:EAX - long value to be shifted ; CL - number of bits to shift by ; ;Exit: ; EDX:EAX - shifted value ; ;Uses: ; CL is destroyed. ; ;Exceptions: ; ;******************************************************************************* __aullshr PROC NEAR ; ; Handle shifts of 64 bits or more (if shifting 64 bits or more, the result ; depends only on the high order bit of edx). ; cmp cl,64 jae short RETZERO ; ; Handle shifts of between 0 and 31 bits ; cmp cl, 32 jae short MORE32 shrd eax,edx,cl shr edx,cl ret ; ; Handle shifts of between 32 and 63 bits ; MORE32: mov eax,edx xor edx,edx and cl,31 shr eax,cl ret ; ; return 0 in edx:eax ; RETZERO: xor eax,eax xor edx,edx ret __aullshr ENDP end