Multiplication with Q8

As a gentle introduction to the Quine8 (Q8) tool, we'll start by multiplying two numbers. Because the Q8 instruction set does not contain a multiply operation, we'll have to write our own. The algorithm for multiplication is pretty easy, you probably learned it in grade school.

6 * 4 = 24

That can be expanded in one of two ways:

6 + 6 + 6 + 6 = 24
6, 4 times is 24
4 + 4 + 4 + 4 + 4 + 4 = 24
4, 6 times is 24

In a C-like language, they look like this:

int x = 0;
for (int i = 4; i > 0; i--) {
    x += 6;
}
int x = 0;
for (int i = 6; i > 0; i--) {
    x += 4;
}

If you look at the two examples carefully, you'll notice that one of the 2 will resolve faster than the other. 4 times through the loop is faster than 6, and the larger the difference between the two numbers, the more this will effect runtime. If we ran 2 * 50, it would either run through the incremental loop twice or fifty times. That makes a huge difference, especially for the Q8 bytecode.

Loop ops: 10
Add Increment tiles: 11

1 time through the loop: 10 + 11 = 21 tiles
2 times through the loop: 21 * 2 = 42 tiles
50 times through the loop: 21 * 50 = 1,050 tiles

Assuming each tile takes 1 second, that's the difference between running in 42 seconds, and running for 17 and a half minutes. This is an optimization definitely worth having.

Bytecode Program

Assembly Program

>0
start:
LOAD A x ; load x
LOAD B y ; load y
CMP A B ; is x > y?
JG swap ; goto swap

>9
save_var:
STORE A i ; save min(x, y) as i
STORE B inc ; save max(x, y) as increment
JMP loop

>32
swap:
SWAP A B ; (x, y) -> (y, x)
JMP save_var

>36
loop:
LOAD A i ; get i
ISZERO A ; is i == 0?
JZ exit
DEC A ; i--
STORE A i ; save new i
JMP add_increment

>67
add_increment:
LOAD A cumulative ; read in cumulative value
LOAD B inc ; read in increment
ADD A B ; cumulative = cumulative + increment
JERR exit ; if overflow, exit
STORE A cumulative ; save new cumulative
JMP loop

>255
exit:
HALT

; DATA
>80
x: $6
y: $4
>96
i: $0
cumulative: $0
>112
inc: $0