Brief explanation for Forth newbies: UM* multiplies two 16-bit numbers to get a 32-bit result. X is the Forth data stack pointer in ZP. The stack grows downward, so the value in X will decrease as the stack gets deeper. Each 16-bit number has the low byte first, as is usual for 6502. The product will be 32-bit, occupying the same memory bytes that originally held the inputs. The product's high cell (most-significant 16 bits) will be at the top of the stack at 0,X and 1,X, and the low cell as next-on-stack at 2,X and 3,X. At the beginning, the second cell gets moved to scratchpad space called N, taking two bytes, N and N+1, and this stack cell gets initialized as 0000.
Code: Select all
CODE UM*
LDA 2,X ; Move NOS to N and then zero
STA N ; (ie, erase) NOS (2 bytes).
STZ 2,X ; STZ can be replaced with STY
; here since NEXT left 0 in Y.
LDA 3,X
STA N+1
STZ 3,X ; STZ can be replaced with STY
; here since NEXT left 0 in Y.
LDY #$10 ; Go through the loop 16 times.
1$: ASL 2,X ; Shift the 2 top stack cells
ROL 3,X ; (ie, four bytes) left one bit.
ROL 0,X ; The multiplier gets shifted out
ROL 1,X ; as the product gets shifted in.
BCC 2$
; If the high bit shifted out was 1,
CLC ; add the 2-byte value at N into NOS,
LDA N
ADC 2,X ; low byte,
STA 2,X
LDA N+1
ADC 3,X ; then high byte.
STA 3,X
; If there was a carry, then
BCC 2$ ; increment low byte of high cell.
INC 0,X ; Public-domain used LDA#0, ADC, STA.
; If incrementing the low byte of the
BNE 2$ ; high cell made it 0, you must also
INC 1,X ; increment the high byte of the high
; cell! Lacking this is where the
; bug lay in the public-domain UM*
2$: DEY
BNE 1$ ; Loop again if not done.
JMP NEXT
END_CODE