Multiplying, on a machine with no multiplier
a * b = qs[a + b] - qs[|a - b|] where qs[n] is n squared over four because (a+b)^2/4 minus (a-b)^2/4 is exactly a*b, and the halves the flooring throws away cancel between the two terms. A multiply is two lookups and a subtract. AND THE TABLE IS BUILT BY ADDING, which is the part that makes it fit a machine with no multiplier at all. A table of squares would need squaring to fill; this one does not, because qs[n] = qs[n-1] + n/2, and n/2 goes 0, 1, 1, 2, 2, 3 - a number that steps up on every even n. So the whole thing is a running total and a toggle, and nothing harder than an add appears anywhere in building the thing that does the multiplying. 511 entries of two bytes, because a byte plus a byte reaches 510. That is 1,022 bytes of Data Memory, and it is the price: a kilobyte traded for an operation the hardware has not got. The operands go in memory rather than in registers. B cannot be stored and a product does not fit in one byte anyway, so two in and two out would spend more instructions shuffling than the multiply costs. Checked against nought, the commutation both ways round, a square, and 255 times 255 - which is 0xFE01 and the largest product two bytes hold. The square is the case the identity leans on hardest: the difference term is nought and the whole answer comes out of one entry. Wanted for Lunar Porter's orbit, where the trade between height and speed has to be proportional to vx times vy and could not be. Useful well beyond it: this is the routine every fixed point sum on this machine has been doing without. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
a069ee7a00
commit
d896c9d433
@@ -0,0 +1,83 @@
|
||||
; The quarter square multiply, against numbers whose products are known.
|
||||
;
|
||||
; a * b = qs[a+b] - qs[|a-b|], and the table of quarter squares is built by adding, so nothing
|
||||
; in the whole arrangement needs a multiply to exist. What this checks is that the arrangement
|
||||
; actually multiplies: the cases below cover nought, one, the commutation, a square, and the
|
||||
; largest product two bytes can hold.
|
||||
;
|
||||
; Written by Anachronaut
|
||||
|
||||
#Include print.asm
|
||||
#Include math.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
CALL mulReady ; The table, once, before anything asks for a product.
|
||||
|
||||
RSTA
|
||||
CALL times ; 0 x 0
|
||||
INIA 0d7
|
||||
SETD.0 MulA
|
||||
STA.0
|
||||
RSTA
|
||||
SETD.0 MulB
|
||||
STA.0
|
||||
CALL show ; 7 x 0, which is the other way round from the last one.
|
||||
|
||||
INIA 0d1
|
||||
CALL both
|
||||
CALL show ; 1 x 1
|
||||
|
||||
INIA 0d12
|
||||
CALL both
|
||||
CALL show ; 12 x 12, a square, which is the case the identity leans on:
|
||||
; the difference term is nought and the whole answer is one entry.
|
||||
|
||||
INIA 0d3
|
||||
SETD.0 MulA
|
||||
STA.0
|
||||
INIA 0d200
|
||||
SETD.0 MulB
|
||||
STA.0
|
||||
CALL show ; 3 x 200
|
||||
|
||||
INIA 0d200
|
||||
SETD.0 MulA
|
||||
STA.0
|
||||
INIA 0d3
|
||||
SETD.0 MulB
|
||||
STA.0
|
||||
CALL show ; and 200 x 3, which had better agree.
|
||||
|
||||
INIA 0xFF
|
||||
CALL both
|
||||
CALL show ; 255 x 255, the largest a byte times a byte can be.
|
||||
|
||||
HALT
|
||||
|
||||
; A in both operands, for the square cases.
|
||||
both:
|
||||
SETD.0 MulA
|
||||
STA.0
|
||||
SETD.0 MulB
|
||||
STA.0
|
||||
RET
|
||||
|
||||
; A in both, then show it. The nought case wants this and nothing else does.
|
||||
times:
|
||||
CALL both
|
||||
CALL show
|
||||
RET
|
||||
|
||||
; The product, high byte then low, which is how a sixteen bit number reads.
|
||||
show:
|
||||
CALL mul8
|
||||
SETD.0 MulHigh
|
||||
LDA.0
|
||||
CALL printByteHex
|
||||
SETD.0 MulLow
|
||||
LDA.0
|
||||
CALL printByteHex
|
||||
CALL lineFeed
|
||||
RET
|
||||
Reference in New Issue
Block a user