TITLE imul.asm
;; imulnt.asm Copyright (C) Codemist Ltd, 1993-2002
;;
;; This code implements Imultiply and Idivide in 80386 (and up) assembly
;; code. In fact two versions of the functions are provided, one for
;; the usual "__cdecl" calling conventions (all args passed on the stack)
;; and one for the Microsoft so-called "fastcall" where two args are passed
;; in registers. Name conventions permit the two versions to co-exist.
;;
.386P
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
;
; unsigned32 Imultiply(unsigned32 *rlow,
; unsigned32 a, unsigned32 b, unsigned32 c)
; {
; /* uns64 is a 64-bit integral type here */
; uns64 r = (uns64)a*(uns64)b + (uns64)c;
; *rlow = (unsigned32)(r & 0x7fffffff);
; return (unsigned32)(r >> 31);
; }
;
PUBLIC _Imultiply
_TEXT SEGMENT
_rlow$ = 8
_a$ = 12
_b$ = 16
_c$ = 20
_Imultiply PROC NEAR
mov eax, DWORD PTR _b$[esp-4]
mul DWORD PTR _a$[esp-4]
mov ecx, DWORD PTR _rlow$[esp-4]
add eax, DWORD PTR _c$[esp-4]
adc edx, 0 ; carry into top half
add eax, eax
adc edx, edx
shr eax, 1
mov DWORD PTR [ecx], eax
mov eax, edx
ret 0
nop
nop
_Imultiply ENDP
_TEXT ENDS
; unsigned32 Idiv10_9(unsigned32 *qp, unsigned32 high, unsigned32 low)
; {
; uns64 p = ((uns64)high << 31) | (uns64)low;
; *qp = (unsigned32)(p / (uns64)1000000000U);
; return (unsigned32)(p % (uns64)1000000000U);
; }
;
PUBLIC _Idiv10_9
_TEXT SEGMENT
_qp$ = 8
_high$ = 12
_low$ = 16
_Idiv10_9 PROC NEAR
mov edx, DWORD PTR _high$[esp-4]
mov eax, DWORD PTR _low$[esp-4]
shl eax, 1
shr edx, 1
rcr eax, 1 ; That glued together 31+31 bits
mov ecx, 1000000000
div ecx
mov ecx, DWORD PTR _qp$[esp-4]
mov DWORD PTR [ecx], eax
mov eax, edx
ret 0
nop
nop
_Idiv10_9 ENDP
_TEXT ENDS
; unsigned32 Idivide(unsigned32 *qp, unsigned32 a, unsigned32 b, unsigned32 c)
; {
; uns64 p = ((uns64)a << 31) | (uns64)b;
; *qp = (unsigned32)(p / (uns64)c);
; return (unsigned32)(p % (uns64)c);
; }
;
PUBLIC _Idivide
_TEXT SEGMENT
_qp$ = 8
_a$ = 12
_b$ = 16
_c$ = 20
_Idivide PROC NEAR
mov edx, DWORD PTR _high$[esp-4]
mov eax, DWORD PTR _low$[esp-4]
shl eax, 1
shr edx, 1
rcr eax, 1 ; That glued together 31+31 bits
mov ecx, DWORD PTR _c$[esp-4]
div ecx
mov ecx, DWORD PTR _qp$[esp-4]
mov DWORD PTR [ecx], eax
mov eax, edx
ret 0
nop
nop
nop
_Idivide ENDP
;;
;; Now for the versions that take args in registers.
;;
_TEXT ENDS
;
; unsigned32 Imultiply(unsigned32 *rlow,
; unsigned32 a, unsigned32 b, unsigned32 c)
; {
; /* uns64 is a 64-bit integral type here */
; uns64 r = (uns64)a*(uns64)b + (uns64)c;
; *rlow = (unsigned32)(r & 0x7fffffff);
; return (unsigned32)(r >> 31);
; }
;
PUBLIC @Imultiply@16
_TEXT SEGMENT
@Imultiply@16 PROC NEAR
mov eax, DWORD PTR 4[esp]
mul edx
add eax, DWORD PTR 8[esp]
adc edx, 0 ; carry into top half
add eax, eax
adc edx, edx
shr eax, 1
mov DWORD PTR [ecx], eax
mov eax, edx
ret 8
nop
nop
nop
nop
nop
nop
@Imultiply@16 ENDP
_TEXT ENDS
; unsigned32 Idiv10_9(unsigned32 *qp, unsigned32 high, unsigned32 low)
; {
; uns64 p = ((uns64)high << 31) | (uns64)low;
; *qp = (unsigned32)(p / (uns64)1000000000U);
; return (unsigned32)(p % (uns64)1000000000U);
; }
;
PUBLIC @Idiv10_9@12
_TEXT SEGMENT
@Idiv10_9@12 PROC NEAR
mov eax, DWORD PTR 4 [esp]
shl eax, 1
shr edx, 1
rcr eax, 1 ; That glued together 31+31 bits
push 1000000000
div DWORD PTR [esp]
mov DWORD PTR [ecx], eax
pop eax
mov eax, edx
ret 4
nop
nop
nop
nop
nop
nop
@Idiv10_9@12 ENDP
_TEXT ENDS
; unsigned32 Idivide(unsigned32 *qp, unsigned32 a, unsigned32 b, unsigned32 c)
; {
; uns64 p = ((uns64)a << 31) | (uns64)b;
; *qp = (unsigned32)(p / (uns64)c);
; return (unsigned32)(p % (uns64)c);
; }
;
PUBLIC @Idivide@16
_TEXT SEGMENT
@Idivide@16 PROC NEAR
mov eax, DWORD PTR 4 [esp]
shl eax, 1
shr edx, 1
rcr eax, 1 ; That glued together 31+31 bits
div DWORD PTR 8[esp]
mov DWORD PTR [ecx], eax
mov eax, edx
ret 8
nop
nop
nop
@Idivide@16 ENDP
_TEXT ENDS
END