{ From: Michael Salem 31 May 1998 To: www.merlyn.demon.co.uk } {$IFDEF Documentation} Pascal has functions MOD and DIV, which do the same thing in machine language, but return different registers. It's often necessary to compute both the MOD and DIV of two numbers, which in Pascal means doing the same thing twice. As DIV plus MOD often occur in inner loops this is a worthwhile optimisation. Looking at the assembly code for MOD and DIV of 2-byte (WORD, INTEGER) and 4-byte (LongInt) numbers, we see that if we do a DIV followed IMMEDIATELY by appropriate assembly code we can retrieve the MOD with no extra work. Note that Remainder must be in the data segment (global VAR, or typed CONST) For 2-byte quantities: VAR Quotient, Dividend, Divisor, Remainder:WORD; Quotient:=Dividend div Divisor; asm mov word ptr Remainder,dx end; For 4-byte quantities: VAR Quotient, Dividend, Divisor, Remainder:LongInt; Quotient:=Dividend div Divisor; asm mov word ptr Remainder,cx mov word ptr Remainder+2,bx end; Note that this has been tested with ALL of Quotient, Dividend, Divisor, and Remainder of the same length. It probably works with mixed lengths so long as we know whether the compiler uses WORD or DWORD division. Tested OK with Borland Pascal 7 and Delphi 1. Not tested with other versions. Warnings: this is obviously not as safe as using Pascal code. (1) It depends upon implementation of the compiler. Future versions, patches, etc., may be incompatible (2) The ASM code must follow the associated DIV before processor registers get changed (immediately after is recommended) (3) It's vital to know whether Pascal executes its 2- or 4-byte DIV (4) The Remainder variable must be a global VAR or a CONST so that it is in the Data Segment {$ENDIF Documentation} {Following code both to disassemble to see what it does and demonstration} var DividendW,DivisorW,QuotientW:word; RemainderW:word; DividendL,DivisorL,QuotientL:longint; RemainderL:longint; begin { DividendW:=1234; DivisorW:=123; QuotientW:=DividendW mod DivisorW; commented out by JRS } DividendL:=12345678; DivisorL:=12345; RemainderL:=DividendL mod DivisorL; DividendW:=12345; DivisorW:=100; QuotientW:=DividendW div DivisorW; asm {MUST follow immediately after 2-byte "DIV" statement} mov word ptr RemainderW,dx end; writeln(DividendW,' = ',QuotientW,' * ',DivisorW,' + ',RemainderW); DividendL:=12345678; DivisorL:=100; QuotientL:=DividendL div DivisorL; asm {MUST follow immediately after 4-byte "DIV" statement} mov word ptr RemainderL,cx mov word ptr RemainderL+2,bx end; writeln(DividendL,' = ',QuotientL,' * ',DivisorL,' + ',RemainderL); end. Best wishes, -- Michael Salem ----- Notes by www.merlyn.demon.co.uk, 1998 ff. : ALSO : the divisor must not be a compile-time constant which is a power of two, since BP7 optimises to use "shr". AND : it seems also OK if the Remainder is declared in the current procedure. 1999-10-26 - trying this with Delphi 3 DCC32 -cc, the 2-byte is OK but the 4-byte is not. 1999-10-28 - It seems likely that D3 uses 32-bit division, presumed to leave the remainder in e?x; and inadequate testing shows that it is NOT eax ebx or ecx, and looks rather like edx. The following gives the right answers : {$I VERSION.PAS - sets PASCAL & DELPHI - JRS} DividendL:=12345678; DivisorL:=100; QuotientL:=DividendL div DivisorL; asm {MUST follow immediately after 4-byte "DIV" statement} {$IFDEF PASCAL} mov word ptr RemainderL,cx mov word ptr RemainderL+2,bx {$ENDIF} {$IFDEF DELPHI} mov dword ptr RemainderL,edx {$ENDIF} end; 1999-10-29 - On the other hand, it appears not to work in DATEPROX, where it's wanted - more thought needed ... 1999-10-30 - DATEPROX is OK now --- it's a matter of careful addressing; it appears that "word absolute" has some subtle difference between Pascal and Delphi. -----