Skip to content

Instantly share code, notes, and snippets.

@bramhaag
Last active May 7, 2021 14:14
Show Gist options
  • Save bramhaag/c1185060a4bda825656aa73fafe284b9 to your computer and use it in GitHub Desktop.
Save bramhaag/c1185060a4bda825656aa73fafe284b9 to your computer and use it in GitHub Desktop.
Multiplication without the MUL instruction in 10 lines
; General explaination of the program:
; To replicate the MUL instruction, we came up with the following formula:
; aaaa * bbbb =
; (aaaa >> 0 & 1) * (bbbb << 0) +
; (aaaa >> 1 & 1) * (bbbb << 1) +
; (aaaa >> 2 & 1) * (bbbb << 2) +
; (aaaa >> 3 & 1) * (bbbb << 3)
; This formula still uses the multiply instruction, however since the result
; of (aaaa >> 3 & 1) will always be a 0 or a 1, we can use a branch instruction.
;
; The problem with this formula is that doing more than one shift at a time takes
; up a lot of instructions, since it it only possible to do one shift at a time with
; the LSL/LSR instruction
;
; To solve this problem we simplified the formula according to this rule:
; aaaa >> 3 & 1 = aaaa & (1 << 3) = aaaa & 8
;
; This results in the following formula:
; aaaa * bbbb =
; (aaaa & 1) * (bbbb << 0) +
; (aaaa & 2) * (bbbb << 1) +
; (aaaa & 4) * (bbbb << 2) +
; (aaaa & 8) * (bbbb << 3)
;
; This formula is no longer mathematically correct: (aaaa & n) can yield
; values larger than 1. However this is not an issue since we're using branches
; instead of multiplication.
;
; We keep track of the number used for the AND operation in CA (register 21).
; Every iteration of the loop, CA is shifted to the left using the LSL operation
; meaning the value of CA will go from 1 to 2 to 4 to 8.
;
; The result of the summations are stored in SUM (register 16)
;
; The total length of the multiplication calculation is 10 lines (line 49 to 61, excluding the empty lines)
.device atmega168
.def SUM = r16 ; Sum of calculations
.def A = r17 ; Multiplicand A
.def B = r18 ; Multiplicand B
.def TA = r19 ; Temporary place to store multiplicand A
.def TB = r20 ; Temporary place to store multiplicand B
.def CA = r21 ; And counter
ldi A, 5 ; Initialize multiplicand A. Try changing this value!
ldi B, 8 ; Initialize multiplicand B. Try changing this value!
ldi SUM, 0 ; Set the initial value of the sum. This is necessary because the Arduino does not
; clear its RAM on startup. In the case where the Arduino is rebooted
; (e.g when uploading code), the registers may still contain old values.
ldi CA, 1 ; Set the initial value of the number used for the and operation
loop: mov TA, A ; Loop 4 times. Initialize temporary multiplicand A
and TA, CA ; And operation between A and CA
breq skip ; Skip summation if the value of the operation is 0
add SUM, B ; Add multiplicand B to the result
skip: lsl B ; Shift bits of multiplicand B to the left
lsl CA ; Shift bits of the number used for the and operation to the left (values will be: 1, 2, 4, 8)
cpi CA, 16 ; Compare C to 4 (Loop has 4 iterations, but C starts at 0. The 4 is to compensate for the unneccesary increase in the last iteration)
brne loop ; Go back to the start of the loop if C is not 4
call sendr16tolaptop ; Send result (SUM) to the laptop
again: rjmp again ; Stop program by creating an infinite loop
.include "rs232link.inc"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment