Versions of CamelForth prior to August 1999 will return an incorrect result if the division of two positive numbers yields a zero quotient and a nonzero remainder. For example, 3 0 5 FM/MOD should return a quotient of 0 and a remainder of 3; instead, it returns a quotient of -1 and a remainder of 8.
The original CamelForth FM/MOD was written in terms of SM/REM. After trying to fix the logic which adjusts the symmetric quotient and remainder for floored division, I realized that it would be faster and smaller to redefine FM/MOD in terms of the fundamental unsigned division operator, UM/MOD. Here is the corrected definition of FM/MOD, in Forth source and in 8051 assembly.
Note: if you install the 8051 fix yourself, be aware that the the new FM/MOD is slightly larger than the old, so some ACALLs will have to be changed to LCALLs.
;C FM/MOD d1 n1 -- n2 n3 floored signed div'n ; DUP >R divisor ; 2DUP XOR >R sign of quotient ; >R divisor ; DABS R@ ABS UM/MOD ; SWAP R> ?NEGATE SWAP apply sign to remainder ; R> 0< IF if quotient negative, ; NEGATE ; OVER IF if remainder nonzero, ; R@ ROT - SWAP 1- adjust rem,quot ; THEN ; THEN R> DROP ; ; Ref. dpANS-6 section 3.2.2.1.
.drw link
.set link,*+1
.db 0,6,"FM/MOD"
FMSLASHMOD: lcall DUP
lcall TOR
acall TWODUP
lcall XOR
lcall TOR
lcall TOR
acall DABS
lcall RFETCH
acall ABS
lcall UMSLASHMOD
lcall SWOP
lcall RFROM
acall QNEGATE
lcall SWOP
lcall RFROM
lcall ZEROLESS
lcall zerosense
jz fmmod1
lcall NEGATE
lcall OVER
lcall zerosense
jz fmmod1
lcall RFETCH
lcall ROT
lcall MINUS
lcall SWOP
lcall ONEMINUS
fmmod1: lcall RFROM
lcall DROP
ret