Search code examples
assemblymultiplicationavratmega

Multiply two 8 bit number that gives 16bit number as result, with 8bit register and only add instruction


I'm new with assembly coding, I'm trying to learn for university purpose. We are using ATmel Studio to simulate ATmega structure, and we will work with ATmega328 instruction set. I have this exercise where I have to multiply two 8bit number (which are 0b00111010 and 0b01010101) with only add instruction (or even adc, no matter which one). I'm struggling a lot because my professor didn't give any paper, just the instruction set manual of the ATmega238, so I'm lost.

Please help me I'm getting crazy.

Following my chat-gpt helped code that doesn't work properly, it does only half of the task, because it correctly calculate only the LSB of the result, the MSB is wrong. I think the problem is in the line "adc r19, r1" but I don't know how to resolve it.

; Input r16 (00111010), r17 (01010101)
; Output r18:r19 (risultato)

    ldi r16, 0b00111010   ; Load first number in r16
    ldi r17, 0b01010101   ; Load second number in r17

    ldi r18, 0            ; Initialize LSB's resoult to 0
    ldi r19, 0            ; Initialize MSB's resoult to 0

    ldi r20, 8            ; 8 bit counter

multiply_loop:
    lsr r17               ; Shift to dx the first number
    brcc no_add           ; Skip if the LSB is 0

    add r18, r16          ; Add first number to the lower part of the resoult
    adc r19, r1           ; Add carry to the higher part of the resoult

no_add:
    lsl r16               ; Left shift of first number

    dec r20               ; Decrease counter
    brne multiply_loop    ; Repeat cycle until counter is 0

    ; Final resoult in r18:r19

Solution

  • First you need to understand what you are doing.

    ldi r16, 0b00111010 ; Load first number in r16 ldi r17, 0b01010101 ; Load second number in r17

    You can use R17 as a position adder, while R16 will be the value to be add to the pair R18, R19. Lets say R18 is the high order byte.

    You do the multiply as you do with pen and paper, every bit "1" of R17 will require an addition of R16 to R19:R18, but R16 will be shifted to represent the position of the bit "1" in the R17. As you can see, the first shift left of R16 you will lose the left bit to carry, so you really need two bytes to hold R16, since it will be shifted left 8 times. Lets use R15 and it will start with zero.

    So, R17 will tell you when to add, R15:R16 will be the value to add to R18:R19.

    Bit 0 (right) of R17 is "1", it means you need to add the bare R15:R16 to R18:R19. Bit 1 of R17 is "0" you don't add anything, but shift left R15:R16 left 1 bit. Bit 2 of R17 is "1" R15:R16 adds to R18:R19, shift R15:R16 left 1 bit. Bit 3 of R17 is "0" add nothing, shift R15:R16 left 1 bit. Bit 4 of R17 is "1" R16:R16 adds to R18:R19, shift R15:R16 left 1 bit. and so on until Bit 7 of R17 is "0" add nothing, result on R18:R19.

    In assembly, using better registers:

       ldi r16, 0b00111010   ; Load first number in r16 (LSB value to add)
       clr r17               ; the MSB of pair R17:R16 (value to add) 
    
       ldi r18, 0b01010101   ; Load second number in r18 (multiplier)
    
       clr r1               ; Initialize MSB's result to 0
       clr r0               ; Initialize LSB's result to 0
    
       ldi r20, 8            ; 8 bit counter
    
    multiply_loop: 
    
       lsr r18               ; Shift to carry the first number
       brcc no_add           ; Skip if the LSB is 0 (even number)
    
       add r0, r16          ; Add LSBs first 
       adc r1, r17          ; Add MSBs and carry to result MSB
    
    no_add: 
    
       lsl r16               ; left shift 16 bits of pair R17:R16
       rol r17               ; carry from lsl above into R17 bit 0
    
       dec r20               ; Decrease counter
       brne multiply_loop    ; Repeat cycle until counter is 0
    
    ; Final result in r1:r0