0
votes

I wrote a code for compute a triangle area loading the values of base and high in two registers (R16 and R18). These are 8 bit values

 .include "./m2560def.inc"  
;------------------------------------------------------------------
; Constants
;------------------------------------------------------------------
.def    base    = r16
.def    high    = r17

.equ    base_value  = 10
.equ    high_value = 20

    .cseg
        .org    0x0000
        rjmp    reset           ; reset intr
reset:
        LDI R16, HIGH(RAMEND)
        OUT SPH, R16
        LDI R16, LOW(RAMEND)
        OUT SPL, R16

RCALL   configure_ports

start:
        LDI base, base_value
        LDI high, high_value 
        MUL base, high
        MOVW R18, R0
        LSR  R18         ;divide by 2 
        MOV  R19, R18
        OUT  PORTB, R19  ;Output result in PORTB
        OUT  PORTD, R19  ;Output result in PORTD
        RJMP    start

configure_ports:
        ;Configurare B and C ports as outputs
        LDI R16, 0XFF
        OUT DDRB, R16
        OUT DDRC, R16
        ret

If I use 16 bit numbers for base and high how to load it in two registers and compute the (base x high) and (base x high) /2 operations for 16 bit?

1

1 Answers

3
votes

If you understand how decimal multiplication "on paper" is performed, then it will be easy to replace decimal digits by 8-bit numbers.

Let's say you have two decimal numbers: AB and XY (where A, B, X, Y - are decimal digits). You can express it as a sum of two multiplications of two-digit number by one-digit number:

AB * XY = (AB * Y) + ((AB * X) << 1d) (where << 1d means shifted 1 digit position left and equals to a multiplication by 10 in decimal)

The multiplication of two digits by one digit can be expressed in the same terms as

AB * Y = (B * Y) + ((A * Y) << 1d)

or whole experssion may be written as:

AB * XY = (B * Y) + ((A * Y) << 1d) + ((B * X) << 1d) + ((A * X) << 2d)

now you can just jump from the decimal system into the 256-based system, assuming every digit in the example above is a single byte.

So, to find multiplication of AB * XY you need:

  1. calculate B * Y, store it into 4-byte result (two upper bytes will be zeroes)
  2. calculate B * X, shift it left by 1 byte, add to result
  3. calculate A * Y, shift it left by 1 byte, add to result
  4. calculate A * X, shift it left by 2 bytes, add to result

In assmbler it may look as follows: Let's say we have:

  • r25:r24 - the first multiplier,
  • r23:r22 - the second
  • r21:r20:r19:r18 - result

The code would be like:

clr r16 // a zero register, we'll need it in the future

mul r24, r22 // r1:r0 = r24 * r22
movw r0, r18 // move result to r19:r18
clr r20 // clear r21 and r22
clr r21

mul r24, r23 // r1:r0 = r24 * r23
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry

mul r25, r22 // r1:r0 = r25 * r22
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry

mul r25, r23 // r1:r0 = r25 * r23
add r20, r0 // add to the result starting from the third from the right byte (r20)
adc r21, r1 // add next byte with carry

Well done! Now you have the four-byte result in r21:r20:r19:r18

To divide by 2 you can just move the result 1 binary position to the right. You need two instructions:

  • lsr - logical shift right. It moves bits one position to the right. Leftmost position filled by zero, while the pushed out rightmost position stored in the carry flag.
  • ror - rotate right through carry. It does the same: moves bits one position to the right and stores the pushed out rightmost position in the carry, but the leftmost position filled by the initial value of the carry flag

The code:

lsr r21
ror r20
ror r19
ror r18

now the four-byte value is divided by two (rounding downwards)