| ;*** | |
| ;ulldvrm.asm - unsigned long divide and remainder routine | |
| ; | |
| ; Copyright (c) Microsoft Corporation. All rights reserved. | |
| ; SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ; | |
| ;Purpose: | |
| ; defines the unsigned long divide and remainder routine | |
| ; __aulldvrm | |
| ; | |
| ;Original Implemenation: MSVC 14.29.30133 | |
| ; | |
| ;******************************************************************************* | |
| .686 | |
| .model flat,C | |
| .code | |
| ;*** | |
| ;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 | |
| HIWORD EQU [4] ; | |
| LOWORD EQU [0] | |
| 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 original 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 | |
| end |