I hope the stack effect of these simple words is obvious. If not, I'll include a stack comment. But multiply and divide leave the addend on the stack. It is often unnecessary and always a nuisance to remove. Call it an abandoned number, which I'll indicate by . (and multiple such by ..) in comments.
The ALU must be in add-with-carry mode, which means the code must be compiled at an address with the 200 bit set. The words +cy and -cy set and reset this bit in the current code address. Carry must be clear on first entry. It will remain clear if the high-order sum doesn't overflow (and no other + sets it).
Remember that the + instruction must have time for internal carry to propagate. This usually requires a . instruction to preceed it. But not if it's the first instruction following a call/jump.
Clear carry (in +cy mode): Add an unsigned 18-bit number to a 36-bit number: Negate a 36-bit number: Double a 36-bit number: Sign-extend an 18-bit number to 36 bits: In this code, the addend is left on the stack. The carry bit is not used or changed by the +* instruction.
Unsigned multiplier, signed addend, 36-bit signed product: If the multiplier is signed, its high-order bit indicates a subtract: If both multiplier and addend are unsigned: code TBD Replacing the 0 above with an positive number will add that number to the low-order part of the product.
A faster 18-bit product can be computed from 2 numbers whose bits add up to 18; say 9 and 9. the multiplier (top of stack) is a normal right-justified unsigned integer. The signed addend is expected to be left-shifted the number of bits in the multiplier. The product will be an 18-bit signed integer. Divide is slower than multiply, since there is no divide step instruction. The divisor must be made negative.
With the dividend positive and in carry mode with carry clear If the quotient will be small, this is shorter and faster: without carry The quotient is on top, so: */ is a classic Forth word. It multiplies a number by a ratio. With 3 numbers on the stack, the top is the divisor (negative).
The 36-bit intermediate product preserves precision: For fractional arithmetic it's useful to define 1. In this context: If the multiplier is positive: If the multiplier is signed, its high-order bit indicates a subtract: 16-bit code requires an additional left shift to the 17-bit code: With b a on the stack, b is added into a. Both addresses are discarded. n is the number-1 of 18-bit words in such numbers:
-d can fall into +u, if it's compiled immediately before.
2*d dup . + push dup . + pop ;
2*d -if push - 2* - pop 2* ;
then push 2* pop 2* ;
Add 2 36-bit numbers (uses register A):
Add a signed 18-bit number to a 36-bit number:
+s can fall into +d, if it's compiled immediately before.
Integer Multiply
Multiply is implemented with the +* instruction, with the multiplier in register A and addend in S. It adds S to T if A0 is 1, then does a 37-bit shift of T and A right.
then 0 17 for . . . next a ;
Integer Divide
Dividing a double-precision dividend by a positive (17-bit) divisor produces a positive remainder and quotient.
This code is unobvious: the loop starts with a 36-bit left shift. The carry of a . + will be used by the following + , thus building the quotient in T. Carry is clear upon exit if the numbers were such as produce a reasonable result.
dup a . + -if drop pop *next dup . + ;
then over or or pop next dup . + ;
No ; since next never stops. Putting push drop pop in front of next will remove the abandoned numbers.
If the dividend is signed:
Please don't use mod with negative numbers. It isn't useful and there is no agreement about signs.
Integer Square-Root
An algorithm similar to divide produces a 16-bit square-root from a 32-bit number:
loop if a! over 2* over - . + a . +
-if - push drop a or pop dup
then drop pop 2*d push a 2/ loop ;
then drop drop pop drop ;Fraction Multiply
The simplest fractions have 17 fraction bits and a sign bit. Thus fractions from .1ffff to -1.00000 can be represented. The addend can be either fraction or integer.
Then to convert fractions to/from decimal numbers:
.0 10000 1. */ ;
*. a! 0 17 for *+ unext a -if drop - 2* - ; then drop 2* ;
The most convenient fractions have 16 fraction bits, an integer bit and a sign bit. This allows 1.0000 to be represented. However, the product of 2 fractions must be less than 1.ffff.
Fraction Divide
Dividing an 18-bit number by a 16-bit fraction requires expanding the dividend to 36-bits with a low-order 0:
Fraction Square-Root
Multiple-precision Add
Multiple-precision numbers are stored as an array in RAM, low-order at low address. The largest number is 32 words, since 2 such will fit in 64-word RAM. 2^18^32 is a large number, roughly 10^173.
Defining the word +c to add with carry, allows using ordinary + to increment an address, without changing the carry:
n+ n for push a! @+ push a pop pop a! @ . + !+ a next drop drop ;
-cy
Set a number to 0:
n+ a! n for dup b! @b @ +c !+ 1 + next drop ;