You are on page 1of 32

ASM Operations on the AVR

1. AVR ASM BIT MANIPULATION

THE "AND" OPERATION

The "AND" operation can be demonstrated with the following circuit of two switches and
a light in series:

Switch_1 Switch_2 LED


----/ ---------/ --------D

It is clear to see that the LED will only illuminate when both switches are closed to
produce a complete circuit. Switch one AND switch two both have to be closed before
the LED will work. This result can be displayed in a truth table where a zero means off
and a one means on:

SW1 SW2 LED


0 0 = 0
0 1 = 0
1 0 = 0
1 1 = 1

The "AND" operation can be used to clear a bit to zero. From the truth table above, you
can see that anything that is ANDed with a zero is zero. Lets say you wanted to clear the
high bit of a register, the following code will so just that:

LDI A,0b1111_1111 ;A = 11111111


ANDI A,0b0111_1111 ;A = 01111111

"AND" operations can also be used with a "bit-mask" to strip off bits we are interested in.
For example if we are only interested in the highest four bits of a byte. We can use the
binary number 0b1111_0000 to strip away the high nybble of that register and ignore the
remainder:

LDI A,0b1010_1111 ;A = 1010_1111


ANDI A,0b1111_0000 ;A = 1010_0000

THE "OR" OPERATION

The "OR" operation can be demonstrated with the following circuit with two switches in
parallel connected to a light:

Switch_1
-------------/ ---------+
| LED
+--------D
Switch_2 |
------------/ ----------+

It is clear to see that the LED will light when one "OR" the other switch is closed, and
even if both are closed. This can be represented by a truth table:

SW1 SW2 LED


0 0 = 0
0 1 = 1
1 0 = 1
1 1 = 1

The "OR" operation can be used to set a bit to one. From the truth table above, you can
see that anything that is ORed with a one is one. Lets say we need to set the high bit of a
register, the following code will do that:

LDI A,0b0101_0101 ;A = 0101_0101


ORI A,0b1000_0000 ;A = 1101_0101

THE EXCLUSIVE OR "EOR" OPERATION

The "EOR" operation is the same as the "OR" operation except that it is off when both
switches are closed. This means the LED is on if one "OR" the other is on, but not if both
are. This can be demonstrated with the following truth table:

SW1 SW2 LED


0 0 = 0
0 1 = 1
1 0 = 1
1 1 = 0

If we look at the truth table above, we will see that a one EORed with zero give a one,
and a one EORed with a one gives us zero. EORing something with a one gives us the
opposite or inverse. This gives us the property of flipping a bit. If you need to "blink" the
high bit of a register on and off, the following code will do that without disturbing the
other bits of the "A" register:

LDI B,0b1000_0000
LDI A,0b0101_0101 ;A = 0101_0101
EOR A,B ;A = 1101_0101
EOR A,B ;A = 0101_0101
EOR A,B ;A = 1101_0101

THE "NOT" OPERATION

The NOT or inverse operation means you want the opposite, ones become zero and zeros
become one. The truth table for this is:

A NOT_A
0 1
1 0
If we think back to the EOR command, we realize that when we EOR something with a
one, we flip that bit. So to get the inverse or NOT of an entire value, we can EOR it with
all ones:

LDI B,0b1111_1111 ;ALL ONES


LDI A,0b1010_1010 ;A=1010_1010
EOR A,B ;A=0101_0101

To do this with a constant we can use the negation operator ~ (tilde):

LDI A,~0b1010_1010 ;A=0101_0101

THE LEFT SHIFT OPERATIONS

The left-shift operation moves bits in a register, one place to the left. Why would we want
to do this? Lets look at the example of left-shifting the value of three:

LDI A,3 ;A=0b0000_0011=3


LSL A ;A=0b0000_0110=3x2=6
LSL A ;A=0b0000_1100=3x2x2=12

Since computers run on a Binary System, each time we shift a value one bit to the left, we
are multiplying that value by two. If we want to multiply something by eight, we would
left-shift it three times. What the Logical Shift Left command (LSL) does is shift the
highest bit out into the carry flag, shift the contents of the register to the left one bit and
shifts a zero into the lowest bit. The result is a multiplication by two:

CARRY A-REGISTER
[?] [0101_0101] BEFORE LEFT SHIFT VALUE = 85
[0] << [1010_1010] <<0 AFTER LEFT SHIFT VALUE = 85x2 = 170

CARRY AFTER LSL:


.----->[?] .----->[1]
| |
`--[1010_1010]<--0 `--[0101_0100]<--0

What if the highest bit was a one? If we are working with more than eight bits we can use
the rotate-left command ROL which rotates the value stored in the carry bit into the
lowest bit and then loads the carry flag with the value that was shifted out the high end:

CARRY AFTER ROL:


.----->[0]-------. .----->[1]-------.
| | | |
`--[1010_1010]<--' `--[0101_0100]<--'

In the example above the zero that was in the carry flag is shifted into the low end of the
register, the remaining bits are shifted one to the left and the high bit is shifted out and
into the carry flag.

LSL always shifts a zero into the lowest bit, while ROL shifts the contents of the carry
flag into the lowest bit. Using both we can multiply numbers larger than one byte.

To multiply a sixteen bit number by two, we first LSL the lower byte, then ROL the high
byte, this has the net effect of "rolling" the high bit of the lower byte into the first bit of
the 2nd byte. This technique can be expanded to multiply even larger numbers.

Multiplying a 32-bit number by two:

LSL A1 ;MULTIPLY VALUE IN A1 BY TWO


ROL A2 ;VALUE SHIFTED OUT OF A1 GETS SHIFTED INTO A2
ROL A3 ;VALUE SHIFTED OUT OF A2 GETS SHIFTED INTO A3
ROL A4 ;VALUE SHIFTED OUT OF A3 GETS SHIFTED INTO A4

RIGHT SHIFT OPERATIONS

The right-shift operation moves bits in a register, one place to the right. Why would we
want to do this? Lets look at the example of right-shifting the value of twelve:

LDI A,12 ;A = 0b0000_1100 = 12


LSR A ;A = 0b0000_0110 = 12/2=6
LSR A ;A = 0b0000_0011 = 12/4=3

Since computers run on a Binary System, each time we shift a value one bit to the right,
we are dividing that value by two. If we wanted to divide something by eight, we would
right-shift it three times.

What the Logical Shift Right command (LSL) does is shift a zero into the highest bit,
shift the contents to the right and shifts the lowest bit into the carry flag. The result is a
division by two:

A-REGISTER CARRY
[1010_1010] [?] BEFORE RIGHT SHIFT VALUE = 170
0>>[0101_0101]>>[0] AFTER RIGHT SHIFT VALUE = 170/2 = 85

CARRY AFTER LSL:


[?]<------. [0]<------.
| |
0-->[1010_1010]---' 0-->[0101_0101]---'

If we are working with more than eight bits and we wish to preserve the value of the
lowest bit, you can use the rotate-right command ROR which rotates the value stored in
the carry bit into the highest bit and then loads the carry flag with the value that was
shifted out the low end.

CARRY AFTER ROL:


.------[1]<------. .----->[0]<------.
| | | |
`->[1010_1010]---' `->[1101_0101]---'

In the example above the one that was in the carry flag is shifted into the low end of the
register, the remaining bits are shifted one to the right and the low bit is shifted out and
into the carry flag.

LSR always shifts a zero into the highest bit, while ROR shifts the contents of the carry
flag into the highest bit. Using both we can divide numbers larger than one byte.

To divide a sixteen bit number by two, you first LSR the highest byte, then ROR the
lower byte, this has the net effect of "rolling" the low bit of the high byte into the high bit
of the lower byte. This technique can be expanded to divide even larger numbers.

Dividing a 32-bit number by two:

LSR A4 ;DIVIDE VALUE IN A4 BY TWO


ROL A3 ;VALUE SHIFTED OUT OF A4 GETS SHIFTED INTO A3
ROL A2 ;VALUE SHIFTED OUT OF A3 GETS SHIFTED INTO A2
ROL A1 ;VALUE SHIFTED OUT OF A2 GETS SHIFTED INTO A1

THE "SWAP" COMMAND

The swap command exchanges the two nybbles of a byte. It exchanges the high four bits
with the lower four bits:

[1111_0000] ---> SWAP ---> [0000_1111]

SWAP A ;SWAPS HIGH NYBBLE IN "A" WITH LOW NYBBLE

For example if we want to isolate the high byte of register A: ANDI A,0b1111_0000
;MASK OFF HIGH NYBBLE LSR A ;RIGHT SHIFT = /16 LSR A LSR A LSR A

Or we could use the SWAP statement as follows:

ANDI A,0b1111_0000
SWAP A

THE SBI/CBI COMMANDS

The SBI & CBI commands can be used to set or clear bits in an output port respectively.
For example to set PORTB0 for output we need to set the zero-bit of the Data Direction
register for Port B to one:

SBI DDRB,0
To set PORTB0 for input we need to clear bit zero:

CBI DDRB,0

The number following the register DDRB is the bit position to set or clear, it is not the
value to write to the port. For example to set bit seven the command would be:

SBI DDRB,7 ;CORRECT

and not:

SBI DDRB,128 ;WRONG!!!

THE SBIS/SBIC COMMANDS

The SBIS and SBIC commands are used to branch based on if a bit is set or cleared. SBIS
will skip the next command if a selected bit in a port is set, and SBIC will skip the next
command if the bit is clear. For example if we are waiting for an ADC to complete, we
poll the ADIF Flag of the ADCSRA register to. We could read in the register and check if
the flag has been set with:

WAIT: IN A,ADCSRA ;READ THE STATUS


ANDI A,0b0001_0000 ;CHECK ADIF FLAG
BREQ WAIT

Or we can accomplish the same with the SBIS command that checks the status of the 4th
bit without the need to read the port into a register:

WAIT: SBIS ADCSRA,4


RJMP WAIT

The SBIC command is identical, except it checks if a bit is zero.

THE SBRS/SBRC COMMANDS

The SBRS and SBRC commands function the same as the SBIS/SBIC commands except
that they work on registers instead of ports.

2a. BASIC ARITHMETIC

A single register in the 8-Bit AVRs are 8 bits wide so they can hold values from zero to
255 (zero to $FF in Hex). To work with larger numbers we combine the use of several
registers. First we will examine commands that work with single registers and later we
will combine these commands to handle larger numbers.
1. ADDING AND SUBTRACTING ONE FROM A REGISTER:

Adding and subtracting one to a register is done so often, such as modifying counters in
loops, that there are specific commands that do just that. The increment (INC) and
decrement (DEC) will add or subtract a one from a register.

.DEF N = R0 ;Define “N” as Register Zero (R0)


INC N ;Increment N (R0) by adding one
DEC N ;Decrement N (R0) by subtracting one

Another advantage of using the increment (INC) and decrement (DEC) commands over
standard addition and subtraction is that they require less operands and work with all
registers R0 to R31. Some commands only work on registers R16 to R31.

For example to create a loop that goes from zero to 255:

.DEF N = R0 ;Define “N” as Register Zero

CLR N ;CLear Register N to all zeros


LOOP: ;A label for our loop
INC N ;INCrement N by one
BRNE LOOP ;BRanch if Not Equal to zero
;Keep looping until N wraps around from
;255 ($FF)to zero again
DONE: ;N (R0)=ZERO so we are done our loop

To create a loop that counts backward from 255 ($FF) to one:

.DEF N = R0
SER N ;SEt Register N to all ones = 255 ($FF)
LOOP: DEC N ;DECrement N by one
BRNE LOOP ;Keep looping until N equals zero
DONE: ;N (R0)=ZERO so we are done our loop

2. LOADING A REGISTER WITH A CONSTANT:

Instructions that work with constants are referred to as “immediate” mode instructions.
To load a constant value into a register we use the LoaD Immediate (LDI) command.

.DEF A = R16
LDI A,10 ;Set A (R16) to value of 10

Only registers in the range R16 to R31 can be loaded immediate. We cannot load a
constant into the registers R0 to R15 directly. It would have to be loaded into a valid
register first then copied. To load the value of 10 into register zero (R0):

.DEF N = R0
.DEF A = R16
LDI A,10 ;Set A (R16) to value of 10
MOV N,A ;Copy contents of A(R16) to N(R0)
3. ADDING AND SUBTRACTING A CONSTANT FROM A SINGLE
REGISTER:

Constants are values that do not change during the execution of a program. Since they
don't change in value, there is no need to use an additional registers to store them.

To subtract a constant from a register we will use the SUBtract Immediate instruction
(SUBI). Please note that the SUBI command does not work with Registers Zero to
Fifteen (R0-R15), so our working register must be in the range of (R16 to R31). In the
example below we use Register 16 (R16).

To subtract two from register A:

.DEF A = R16 ;Set “A” to Register 16 (R16)


SUBI A,2 ;Subtract two from A (decrement A by two)

Adding a constant to a register is a little tricky using AVR ASM because it does not have
an add immediate instruction. The way we can do it is to negate the constant we wish to
add and subtract that negative constant from the register. This takes advantage of the fact
that subtracting a negative number results in a double negative that is the same as
addition: X-(-2)=X+2.

.DEF A = R16 ;Set “A” to Register 16 (R16)


SUBI A,-2 ;Subtract negative two from A, A=A-(-2)=A+2
;The net result is adding two to A

4. ADDING TWO SINGLE-BYTE REGISTERS:

To add two registers together we simply load the registers with the values we want and
use the ADD instruction which works with all registers R0 to R31.

.DEF A = R16 ;Set “A” to Register 16 (R16)


.DEF B = R18 ;Set “B” to Register 18 (R18)

LDI A,1 ;Store value of one in R16


LDI B,48 ;Store value of 48 in R18
ADD A,B ;Add the value stored in B
;to the value stored in A (A=A+B)
;The result is that A=49 and
;the value stored in B remains unchanged

Since a single register can only hold 8 bits with a maximum value of 255. To be complete
we should check if our total is more than 255. When we ADD two registers together that
total more than 255 the “Carry Flag” will be set automatically.

ADD A,B ;Add the value stored in B to A


BRCS OVERFLOW ;BRanch if Carry Set to overflow code
5. SUBTRACTING TWO SINGLE-BYTE REGISTERS:

For us to subtract two registers we use the subtract (SUB) command. For example to
convert a numerical character to its numerical value we subtract 48.

LDI A,'1' ;Store value of 49 in R16


;49 is the ASCII value of the character 1
LDI B,48 ;Store value of 48 in R18
SUB A,B ;Subtract the value stored in B from A
;The value left in A is one.

If we need to check if the result of our subtraction is negative, the carry flag is
automatically set when our result goes below zero. We can check that flag with the
Branch if Carry Set (BRCS) command.

SUB A,B ;Subtract the value stored in B from A


BRCS UNDERFLOW ;BRanch if Carry Set to underflow code

6. ADDING MULTI-BYTE CONSTANTS:

If we wish to work with numbers greater than 255 we can use two registers together to
give us a range from zero to 65,535 ($FFFF). To automatically break a constant like 420
into two bytes we can use the HIGH and low functions.

.DEF AL = R16 ;To hold low byte of value


.DEF AH = R17 ;To hold HIGH byte of value

LDI AL,LOW(420) ;Copy low byte of 420


LDI AH,HIGH(420) ;Copy HIGH byte of 420

We can expand this method for numbers up to four bytes long with the BYTE2(),
BYTE3() and BYTE4() functions. This gives us a range from zero to 4,294,967,295
approximately 4.3 Billion.

.DEF A1 = R16 ;To hold low byte of value


.DEF A2 = R17 ;To hold second byte of value
.DEF A3 = R18 ;To hold third byte of value
.DEF A4 = R19 ;To hold fourth byte of value

LDI A1,LOW(420420) ;Copy low byte of 420420


LDI A2,BYTE2(420420) ;Copy second byte of 420420
LDI A3,BYTE3(420420) ;Copy third byte of 420420
LDI A4,BYTE4(420420) ;Copy fourth byte of 420420

7. SUBTRACTING MULTI-BYTE CONSTANTS:

To subtract a two byte constant from a two byte value stored in the register pair AL/AH
we first subtract the low byte of the constant from the low byte register (AL). If the result
of the subtraction is negative the carry flag gets set. This carry flag is used by the
SuBtract with Carry Immediate command (SBCI) below to indicate that the result of
subtracting the two lower bytes resulted in a negative number so a one needs to be
“borrowed” from the result of subtracting the two high bytes.

SUBI AL,LOW(420) ;Subtract two low bytes: AL=AL-LOW(420)


SBCI AH,HIGH(420);Subtract high bytes and include Carry Flag
;AH=AH-HIGH(420)-CarryFlag
BRCS UNDERFLOW ;If the result of the complete subtraction
;is negative then the Carry Flag is set

A similar method can be used to subtract four-byte numbers.

SUBI A1,LOW(420420) ;Subtract two low bytes: AL=AL-LOW(420)


SBCI A2,BYTE2(420420);Subtract second bytes and include Carry
SBCI A3,BYTE3(420420);Subtract third bytes and include Carry
SBCI A4,BYTE4(420420);Subtract fourth bytes and include Carry

Since the AVRs do not have a Add Immediate instruction we have to subtract the
negative of the constant we wish to add. Once again we use the low() and high()
functions to break our constant into two bytes.

SUBI AL,LOW(-420) ;Add two low bytes: AL=AL+LOW(420)


SBCI AH,HIGH(-420);Add high bytes and include Carry Flag
;AH=AH+HIGH(420)+CarryFlag
BRCS OVERFLOW ;If the result of the complete addition
;is greater than 65,535 then the
;Carry Flag is set

We can use the same method to add four byte numbers.

SUBI A1,LOW(-420420) ;Add two low bytes


SBCI A2,BYTE2(-420420);Add second bytes and include Carry Flag
SBCI A3,BYTE3(-420420);Add third bytes and include Carry Flag
SBCI A4,BYTE4(-420420);Add fourth bytes including Carry Flag

8. ADDING MULTI-BYTE REGISTERS:

To add two 16 bit registers we first add the two lower bytes together with the ADD
command, then we add the two high bytes together and include the carry bit which
contains any overflow from the addition of the lower bits. This is accomplished with the
Add with Carry command (ADC).

.DEF AL = R16 ;To hold low byte of value1


.DEF AH = R17 ;To hold high byte of value1
.DEF BL = R18 ;To hold low byte of value2
.DEF BH = R19 ;To hold high byte of value2

ADD AL,BL ;Add the lower bytes together


ADC AH,BH ;Add the higher bytes and include carry bit
BRCS OVERFLOW
We can expand this method to add together two four-byte registers.

.DEF A1 = R16 ;To hold low byte of value1


.DEF A2 = R17 ;To hold second byte of value1
.DEF A3 = R18 ;To hold third byte of value1
.DEF A4 = R19 ;To hold fourth byte of value1
.DEF B1 = R20 ;To hold low byte of value2
.DEF B2 = R21 ;To hold second byte of value2
.DEF B3 = R22 ;To hold third byte of value2
.DEF B4 = R23 ;To hold fourth byte of value2

ADD A1,B1 ;Add the lower bytes together


ADC A2,B2 ;Add the second bytes and include carry bit
ADC A3,B3 ;Add the third bytes and include carry bit
ADC A4,B4 ;Add the fourth bytes and include carry bit

9. SUBTRACTING MULTI-BYTE REGISTERS:

For subtraction we use a similar procedure to addition, we first subtract the lower bits
with the subtract command (SUB) then we subtract the two high bytes including the carry
bit with the subtract with carry command (SBC). The carry flag will indicate that the
addition of the lower bytes resulted in a negative number and that a one needs to be
“borrowed” from the high bytes. If the carry flag is one, by subtracting its value from the
high bytes we accomplish the needed borrowing of one.

SUB AL,BL ;Subtract two low bytes


SBC AH,BH ;Subtract two high bytes and include carry
BRCS UNDERFLOW

The same procedure can be used to subtract larger multi-byte numbers.

SUB A1,B1 ;Subtract the lower bytes


SBC A2,B2 ;Subtract the second bytes and include carry
SBC A3,B3 ;Subtract the third bytes and include carry
SBC A4,B4 ;Subtract the fourth bytes and include carry
2b. BASIC MATH

1. MULTIPLlYING TWO SINGLE-BYTE NUMBERS WITH MUL


COMMAND

If your AVR chip supports the multiplication command (MUL) then multiplying two
eight-bit numbers is quite simple. MUL will work on all 32 registers R0 to R31 and leave
the low-byte of the result in R0 and the high-byte in R1. The registers for multiplicand
and multiplier remain unchanged. The routine takes about three cycles.

.DEF ANSL = R0 ;To hold low-byte of answer


.DEF ANSH = R1 ;To hold high-byte of answer
.DEF A = R16 ;To hold multiplicand
.DEF B = R18 ;To hold multiplier

LDI A,42 ;Load multiplicand into A


LDI B,10 ;Load multiplier into B
MUL A,B ;Multiply contents of A and B
;Result 420 left in ANSL and ANSH

2. MULTIPLYING A SINGLE-BYTE NUMBER BY POWER OF TWO

If our AVR does not support the hardware MUL command we will have to compute
multiplications manually. If we need to multiply by a power of two such as 2,4,8 etc. The
result can be achived by shifting bits to the left. Each shift to the left is a multiplication
by two.

The Logical Shift Left (LSL) command is used on the lower byte because it will shift the
contents one bit to the left, a zero is shifted into the lowest bit and the highest bit is
shifted into the carry flag.

10101010
Carry [1] <-- 01010100 <--0 (LSL)

We use the Rotate Left though Carry (ROL) command on the high byte because it will
also shift contents one bit to the left, but it will shift the contents of the Carry Flag into
the lowest bit.

00000000
Carry [0] <-- 00000001 <--[1] Carry (ROL)

Every time we shift the multiplicand to the left we are multiplying it by two. So to
multiply by eight we simply shift the multiplicand to the left three times. The routine
takes about ten cycles.

.DEF ANSL = R0 ;To hold low-byte of answer


.DEF ANSH = R1 ;To hold high-byte of answer
.DEF AL = R16 ;To hold low-byte of multiplicand
.DEF AH = R17 ;To hold high-byte of multiplicand
LDI AL,LOW(42) ;Load multiplicand into AH:AL
LDI AH,HIGH(42) ;
MUL8:
MOV ANSL,AL ;Copy multiplicand into R1:R0
MOV ANSH,AH ;
LSL ANSL ;Multiply by 2
ROL ANSH ;Shift the Carry into R1
LSL ANSL ;Multiply by 2x2=4
ROL ANSH ;Shift the Carry into R1
LSL ANSL ;Multiply by 2x2x2=8
ROL ANSH ;Shift the Carry into R1
;Result 42x8=336 left in ANSL and ANSH

3. MANUALLY MULTIPLYING TWO SINGLE-BYTE NUMBERS

To do standard multiplication we examine how binary multiplication is achieved, we


notice that when a digit in the multiplier is a one we add a shifted version of the
multiplicand to our result. When the multiplier digit is a zero we need to add zero, which
means we do nothing.

00101010 = 42 multiplicand
x00001010 = 10 multiplier
--------
00000000
00101010
00000000
00101010
00000000
00000000
00000000
00000000
----------------
0000000110100100 = 420 result
================

The routine below mimics the hardware multiply (MUL) by leaving the multiplicand and
multiplier untouched, and the result appears in the register pair R1 and R0. It shifts the
bits of the multiplier into the carry bit and uses the contents of the carry to add the
multiplicand if it is a one or skip it if the carry is a zero. The routine takes about sixty
cycles.

.DEF ANSL = R0 ;To hold low-byte of answer


.DEF ANSH = R1 ;To hold high-byte of answer
.DEF A = R16 ;To hold multiplicand
.DEF B = R18 ;To hold multiplier
.DEF C = R20 ;To hold bit counter

LDI A,42 ;Load multiplicand into A


LDI B,10 ;Load multiplier into B
MUL8x8:
LDI C,8 ;Load bit counter into C
CLR ANSH ;Clear high-byte of answer
MOV ANSL,B ;Copy multiplier into low-byte of answer
LSR ANSL ;Shift low-bit of multiplier into Carry
LOOP: BRCC SKIP ;If carry is zero then skip addition
ADD ANSH,A ;Add multiplicand to answer
SKIP: ROR ANSH ;Shift low-bit of high-byte
ROR ANSL ;of answer into low-byte
DEC C ;Decrement bit-counter
BRNE LOOP ;Check if done all eight bits
;Result 420 left in ANSL and ANSH

4. MULTIPLYING TWO 16-BIT NUMBERS WITH THE MUL


COMMAND

Multiplying two 16-bit numbers can result in a four-byte result. We use the hardware
multiply (MUL) command to create all four cross products and add them to the 32-bit
result. The MUL command leaves it results each time in R1:R0 which we then add into
our result. The routine takes about twenty cycles.

.DEF ZERO = R2 ;To hold Zero


.DEF AL = R16 ;To hold multiplicand
.DEF AH = R17
.DEF BL = R18 ;To hold multiplier
.DEF BH = R19
.DEF ANS1 = R20 ;To hold 32 bit answer
.DEF ANS2 = R21
.DEF ANS3 = R22
.DEF ANS4 = R23

LDI AL,LOW(42) ;Load multiplicand into AH:AL


LDI AH,HIGH(42) ;
LDI BL,LOW(10) ;Load multiplier into BH:BL
LDI BH,HIGH(10) ;

MUL16x16:
CLR ZERO ;Set R2 to zero
MUL AH,BH ;Multiply high bytes AHxBH
MOVW ANS4:ANS3,R1:R0 ;Move two-byte result into answer

MUL AL,BL ;Multiply low bytes ALxBL


MOVW ANS2:ANS1,R1:R0 ;Move two-byte result into answer

MUL AH,BL ;Multiply AHxBL


ADD ANS2,R0 ;Add result to answer
ADC ANS3,R1 ;
ADC ANS4,ZERO ;Add the Carry Bit

MUL BH,AL ;Multiply BHxAL


ADD ANS2,R0 ;Add result to answer
ADC ANS3,R1 ;
ADC ANS4,ZERO ;Add the Carry Bit

5. MULTIPLYING TWO 16-BIT NUMBERS MANUALLY


Multiplying Two-Byte numbers together can leave a result that is four-bytes wide (32-
bits). With this routine we add the multiplicand to the high-bytes of our result for each
one that appears in our 16-bit multiplier, then shift the result into the lower bytes of our
result sixteen times, once for each bit of our multiplier. The routine takes about 180
cycles.

.DEF AL = R16 ;To hold multiplicand


.DEF AH = R17
.DEF BL = R18 ;To hold multiplier
.DEF BH = R19
.DEF ANS1 = R20 ;To hold 32 bit answer
.DEF ANS2 = R21
.DEF ANS3 = R22
.DEF ANS4 = R23
.DEF C = R24 ;Bit Counter

LDI AL,LOW(42) ;Load multiplicand into AH:AL


LDI AH,HIGH(42) ;
LDI BL,LOW(10) ;Load multiplier into BH:BL
LDI BH,HIGH(10) ;

MUL16x16:
CLR ANS3 ;Set high bytes of result to zero
CLR ANS4 ;
LDI C,16 ;Bit Counter
LOOP: LSR BH ;Shift Multiplier to the right
ROR BL ;Shift lowest bit into Carry Flag
BRCC SKIP ;If carry is zero skip addition
ADD ANS3,AL ;Add Multiplicand into high bytes
ADC ANS4,AH ;of the Result
SKIP: ROR ANS4 ;Rotate high bytes of result into
ROR ANS3 ;the lower bytes
ROR ANS2 ;
ROR ANS1 ;
DEC C ;Check if all 16 bits handled
BRNE LOOP ;If not then loop back

6. MULTIPLYING TWO 32-BIT NUMBERS MANUALLY

The following routine will multiply two 32-bit numbers with a 64-Bit (8 Byte) result. The
routine takes about 500 clock cycles.

.DEF ANS1 = R0 ;64-Bit Answer


.DEF ANS2 = R1 ;
.DEF ANS3 = R2 ;
.DEF ANS4 = R3 ;
.DEF ANS5 = R4 ;
.DEF ANS6 = R5 ;
.DEF ANS7 = R6 ;
.DEF ANS8 = R7 ;
.DEF A1 = R16 ;Multiplicand
.DEF A2 = R17 ;
.DEF A3 = R18 ;
.DEF A4 = R19 ;
.DEF B1 = R20 ;Multiplier
.DEF B2 = R21 ;
.DEF B3 = R22 ;
.DEF B4 = R23 ;
.DEF C = R24 ;Loop Counter

LDI A1, LOW($FFFFFFFF)


LDI A2,BYTE2($FFFFFFFF)
LDI A3,BYTE3($FFFFFFFF)
LDI A4,BYTE4($FFFFFFFF)
LDI B1, LOW($FFFFFFFF)
LDI B2,BYTE2($FFFFFFFF)
LDI B3,BYTE3($FFFFFFFF)
LDI B4,BYTE4($FFFFFFFF)
MUL3232:
CLR ANS1 ;Initialize Answer to zero
CLR ANS2 ;
CLR ANS3 ;
CLR ANS4 ;
CLR ANS5 ;
CLR ANS6 ;
CLR ANS7 ;
SUB ANS8,ANS8 ;Clear ANS8 and Carry Flag
MOV ANS1,B1 ;Copy Multiplier to Answer
MOV ANS2,B2 ;
MOV ANS3,B3 ;
MOV ANS4,B4 ;
LDI C,33 ;Set Loop Counter to 33
LOOP:
ROR ANS4 ;Shift Multiplier to right
ROR ANS3 ;
ROR ANS2 ;
ROR ANS1 ;
DEC C ;Decrement Loop Counter
BREQ DONE ;Check if all bits processed
BRCC SKIP ;If Carry Clear skip addition
ADD ANS5,A1 ;Add Multipicand into Answer
ADC ANS6,A2 ;
ADC ANS7,A3 ;
ADC ANS8,A4 ;
SKIP:
ROR ANS8 ;Shift high bytes of Answer
ROR ANS7 ;
ROR ANS6 ;
ROR ANS5 ;
RJMP LOOP
DONE:

7. DIVIDING BY A POWER OF TWO

Since there is no hardware divide command, we will have to do it manually. If we need to


divide by a power of two such as 2,4,8 etc. The result can be achieved by shifting bits to
the right. Each shift to the right is a division by two.

The Logical Shift Right (LSR) command is used on the higher byte because it will shift
the contents one bit to the right, a zero is shifted into the highest bit and the lowest bit is
shifted into the carry flag.

01010101
0--> 00101010 -->[1] Carry

We use the Rotate Right though Carry (ROR) command on the low byte because it will
also shift contents one bit to the right, but it will shift the contents of the Carry Flag into
the highest bit.

00000000
Carry [1] --> 10000000 -->[0] Carry (ROL)

Every time we shift the multiplicand to the right we are dividing it by two. So to divide
by eight we simply shift the multiplicand to the right three times. The routine takes about
ten cycles.

.DEF ANSL = R0 ;To hold low-byte of answer


.DEF ANSH = R1 ;To hold high-byte of answer
.DEF AL = R16 ;To hold low-byte of multiplicand
.DEF AH = R17 ;To hold high-byte of multiplicand

LDI AL,LOW(416) ;Load multiplicand into A


LDI AH,HIGH(416) ;
DIV8:
MOVW ANSH:ANSL,AH:AL ;Copy multiplicand into Result
LSR ANSH ;Divide by 2
ROR ANSL ;Shift Carry Flag to R0
LSR ANSH ;Divide by 4 (2x2)
ROR ANSL ;Shift Carry Flag into R0
LSR ANSH ;Divide by 8 (2x2x2)
ROR ANSL ;Shift Carry Flag into R0
;Result 416/8=52 left in ANSL and ANSH

8. DIVIDING TWO SINGLE-BYTE NUMBERS

Just as multiplication can be achieved with shifting and addition. Dividing can be
accomplished by shifting and subtraction. The routine below tries to repeatedly subtract
the divisor. If the result is negative it reverses the process and shifts the dividend to the
left to try again. The routine takes about 90 cycles.

.DEF ANS = R0 ;To hold answer


.DEF REM = R2 ;To hold remainder
.DEF A = R16 ;To hold dividend
.DEF B = R18 ;To hold divisor
.DEF C = R20 ;Bit Counter

LDI A,255 ;Load dividend into A


LDI B,5 ;Load divisor into B
DIV88:
LDI C,9 ;Load bit counter
SUB REM,REM ;Clear Remainder and Carry
MOV ANS,A ;Copy Dividend to Answer
LOOP: ROL ANS ;Shift the answer to the left
DEC C ;Decrement Counter
BREQ DONE ;Exit if eight bits done
ROL REM ;Shift the remainder to the left
SUB REM,B ;Try to Subtract divisor from remainder
BRCC SKIP ;If the result was negative then
ADD REM,B ;reverse the subtraction to try again
CLC ;Clear Carry Flag so zero shifted into A
RJMP LOOP ;Loop Back
SKIP: SEC ;Set Carry Flag to be shifted into A
RJMP LOOP
DONE:

9. DIVIDING TWO 16-BIT NUMBERS

The previous routine can be expanded to handle divide two-byte numbers in the range of
zero to 65,535. The routine takes about 230 cycles.

.DEF ANSL = R0 ;To hold low-byte of answer


.DEF ANSH = R1 ;To hold high-byte of answer
.DEF REML = R2 ;To hold low-byte of remainder
.DEF REMH = R3 ;To hold high-byte of remainder
.DEF AL = R16 ;To hold low-byte of dividend
.DEF AH = R17 ;To hold high-byte of dividend
.DEF BL = R18 ;To hold low-byte of divisor
.DEF BH = R19 ;To hold high-byte of divisor
.DEF C = R20 ;Bit Counter

LDI AL,LOW(420) ;Load low-byte of dividend into AL


LDI AH,HIGH(420) ;Load HIGH-byte of dividend into AH
LDI BL,LOW(10) ;Load low-byte of divisor into BL
LDI BH,HIGH(10) ;Load high-byte of divisor into BH
DIV1616:
MOVW ANSH:ANSL,AH:AL ;Copy dividend into answer
LDI C,17 ;Load bit counter
SUB REML,REML ;Clear Remainder and Carry
CLR REMH ;
LOOP: ROL ANSL ;Shift the answer to the left
ROL ANSH ;
DEC C ;Decrement Counter
BREQ DONE ;Exit if sixteen bits done
ROL REML ;Shift remainder to the left
ROL REMH ;
SUB REML,BL ;Try to subtract divisor from remainder
SBC REMH,BH
BRCC SKIP ;If the result was negative then
ADD REML,BL ;reverse the subtraction to try again
ADC REMH,BH ;
CLC ;Clear Carry Flag so zero shifted into A
RJMP LOOP ;Loop Back
SKIP: SEC ;Set Carry Flag to be shifted into A
RJMP LOOP
DONE:
10. DIVIDING A 32-BIT NUMBER

The previous routine can be futher expanded to handle 32-bit number divided by a 16-bits
number, This means numbers in the range of zero to 4,294,967,295 (4.3 billion) divided
by numbers in the range zero to 65,535. The routine takes about 700 cycles.

.DEF ANS1 = R0 ;To hold low-byte of answer


.DEF ANS2 = R1 ;To hold second-byte of answer
.DEF ANS3 = R2 ;To hold third-byte of answer
.DEF ANS4 = R3 ;To hold fourth-byte of answer

.DEF REM1 = R4 ;To hold first-byte of remainder


.DEF REM2 = R5 ;To hold second-byte of remainder
.DEF REM3 = R6 ;To hold third-byte of remainder
.DEF REM4 = R7 ;To hold fourth-byte of remainder

.DEF ZERO = R8 ;To hold the value zero

.DEF A1 = R16 ;To hold low-byte of dividend


.DEF A2 = R17 ;To hold second-byte of dividend
.DEF A3 = R18 ;To hold third-byte of dividend
.DEF A4 = R19 ;To hold fourth-byte of dividend

.DEF BL = R20 ;To hold low-byte of divisor


.DEF BH = R21 ;To hold high-byte of divisor

.DEF C = R22 ;Bit Counter

LDI A1,LOW(420) ;Load low-byte of dividend into A1


LDI A2,BYTE2(420) ;Load second-byte of dividend into A2
LDI A3,BYTE3(420) ;Load third-byte of dividend into A3
LDI A4,BYTE4(420) ;Load fourth-byte of dividend into A4

LDI BL,LOW(10) ;Load low-byte of divisor into BL


LDI BH,HIGH(10) ;Load high-byte of divisor into BH

DIV3216:
CLR ZERO
MOVW ANS2:ANS1,A2:A1 ;Copy dividend into answer
MOVW ANS4:ANS3,A4:A3 ;
LDI C,33 ;Load bit counter
SUB REM1,REM1 ;Clear Remainder and Carry
CLR REM2 ;
CLR REM3 ;
CLR REM4 ;
LOOP: ROL ANS1 ;Shift the answer to the left
ROL ANS2 ;
ROL ANS3 ;
ROL ANS4 ;
DEC C ;Decrement Counter
BREQ DONE ;Exit if 32 bits done
ROL REM1 ;Shift remainder to the left
ROL REM2 ;
ROL REM3 ;
ROL REM4 ;
SUB REM1,BL ;Try to subtract divisor from remainder
SBC REM2,BH ;
SBC REM3,ZERO ;
SBC REM4,ZERO ;
BRCC SKIP ;If the result was negative then
ADD REM1,BL ;reverse the subtraction to try again
ADC REM2,BH ;
ADC REM3,ZERO ;
ADC REM4,ZERO ;
CLC ;Clear Carry Flag so zero shifted into A
RJMP LOOP ;Loop Back
SKIP: SEC ;Set Carry Flag to be shifted into A
RJMP LOOP
DONE:

11. SQUARE-ROOT OF A SINGLE-BYTE NUMBER

To compute the square of an eight-byte number we can take advantage of the fact that the
sum of the odd numbers create square numbers:

1 = 1^2 = 1
1+3 = 2^2 = 4
1+3+5 = 3^2 = 9
1+3+5+7 = 4^2 = 16
1+3+5+7+9 = 5^2 = 25

If we study the above table we notice that the square-root of the number equals the
number of odd numbers we have summed. We simply create a routine that keeps track of
the total of odd numbers that have been subtracted. The routine takes fifteen to one
hunded cycles.

.DEF ANS = R0 ;To hold answer


.DEF A = R16 ;To hold the square
.DEF B = R18 ;Sum, Work space

LDI A,100 ;Load the square into A

SQRT:
LOOP:
SUB A,B ;Subtract B from Square
BRCS DONE ;If bigger than sqaure we are done
INC ANS ;Increment the answer
SUBI B,-2 ;Increment B by two
RJMP LOOP

12. SQUARE-ROOT OF A 16-BIT NUMBER

We could expand the routine above to handle the square-root of a sixteen-bit number but
the number of clock cycles can be as high as 3750. The routine below processes two bits
at a time starting with the highest bits and also provides the remainder. It uses about 160
clock cycles.

.DEF ANSL = R0 ;Square Root (answer)


.DEF ANSH = R1 ;
.DEF REML = R2 ;Remainder
.DEF REMH = R3 ;
.DEF AL = R16 ;Square to take root (input)
.DEF AH = R17 ;
.DEF C = R20 ;Loop Counter

LDI AL,LOW($FFFF)
LDI AH,HIGH($FFFF)
SQRT16:
PUSH AL ;Save Square for later restore
PUSH AH ;
CLR REML ;Initialize Remainder to zero
CLR REMH ;
CLR ANSL ;Initialize Root to zero
CLR ANSH ;
LDI C,8 ;Set Loop Counter to eight
LOOP:
LSL ANSL ;Multiply Root by two
ROL ANSH ;
LSL AL ;Shift two high-bits of Square
ROL AH ;into Remainder
ROL REML ;
ROL REMH ;
LSL AL ;Shift second high bit of Sqaure
ROL AH ;into Remainder
ROL REML ;
ROL REMH ;
CP ANSL,REML ;Compare Root to Remainder
CPC ANSH,REMH ;
BRCC SKIP ;If Remainder less or equal than Root
INC ANSL ;Increment Root
SUB REML,ANSL ;Subtract Root from Remainder
SBC REMH,ANSH ;
INC ANSL ;Increment Root
SKIP:
DEC C ;Decrement Loop Counter
BRNE LOOP ;Check if all bits processed
LSR ANSH ;Divide Root by two
ROR ANSL ;
POP AH ;Restore Original Square
POP AL
RJMP LOOP

13. SQUARE-ROOT OF A 32-BIT NUMBER

We can expand the previous routine to handle 32-bit numbers. It takes between 500 to
580 clock cycles to complete.

.DEF ANS1 = R0 ;Square Root (answer)


.DEF ANS2 = R1 ;
.DEF ANS3 = R2 ;
.DEF ANS4 = R3 ;
.DEF REM1 = R4 ;Remainder
.DEF REM2 = R5 ;
.DEF REM3 = R6 ;
.DEF REM4 = R7 ;
.DEF A1 = R16 ;Square (input)
.DEF A2 = R17 ;
.DEF A3 = R18 ;
.DEF A4 = R19 ;
.DEF C = R20 ;Loop Counter

LDI A1, LOW($FFFFFFFF)


LDI A2,BYTE2($FFFFFFFF)
LDI A3,BYTE3($FFFFFFFF)
LDI A4,BYTE4($FFFFFFFF)
sqrt16:
PUSH A1 ;Save Square for later restore
PUSH A2 ;
PUSH A3 ;
PUSH A4 ;
CLR REM1 ;Initialize Remainder to zero
CLR REM2 ;
CLR REM3 ;
CLR REM4 ;
CLR ANS1 ;Initialize Root to zero
CLR ANS2 ;
CLR ANS3 ;
CLR ANS4 ;
LDI C,16 ;Set Loop Counter to sixteen
LOOP:
LSL ANS1 ;Multiply Root by two
ROL ANS2 ;
ROL ANS3 ;
ROL ANS4 ;
LSL A1 ;Shift two high-bits of Square
ROL A2 ;into Remainder
ROL A3 ;
ROL A4 ;
ROL REM1 ;
ROL REM2 ;
ROL REM3 ;
ROL REM3 ;
LSL A1 ;Shift second high bit of Sqaure
ROL A2 ;into Remainder
ROL A3 ;
ROL A4 ;
ROL REM1 ;
ROL REM2 ;
ROL REM3 ;
ROL REM4 ;
CP ANS1,REM1 ;Compare Root to Remainder
CPC ANS2,REM2 ;
CPC ANS3,REM3 ;
CPC ANS4,REM4 ;
BRCC SKIP ;If Remainder less or equal than Root
INC ANS1 ;Increment Root
SUB REM1,ANS1 ;Subtract Root from Remainder
SBC REM2,ANS2 ;
SBC REM3,ANS3 ;
SBC REM4,ANS4 ;
INC ANS1 ;Increment Root
SKIP:
DEC C ;Decrement Loop Counter
BRNE LOOP ;Check if all bits processed
LSR ANS4 ;Divide Root by two
ROR ANS3 ;
ROR ANS2 ;
ROR ANS1 ;
POP A4 ;Restore Original Square
POP A3
POP A2
POP A1

14. CUBE ROOT OF A SINGLE-BYTE NUMBER

Since there are only seven possible cube roots of a single-byte number (0 to 6) this
routine simply cycles through them until the correct root is found. The routine takes from
20 to 90 clock cycles.

.DEF ANS = R0 ;Cube Root (answer)


.DEF REM = R2 ;Remainder
.DEF A = R16 ;Cube
.DEF C = R17 ;Loop Counter

LDI A,$FF ;Load Cube into A


CLR C ;Start Loop Counter at Zero
LOOP: MUL C,C ;R0 = C^3
MUL C,R0 ;
CP A,R0 ;Check if gone too far
BRCS FINI ;If so then Finish
MOV REM,A ;Calculate Remainder
SUB REM,R0 ;
INC C ;Increment Loop Counter
CPI C,7 ;Check if Done
BRNE LOOP ;Go back
FINI: MOV ANS,C ;Answer = Counter - 1
DEC ANS

15. CUBE ROOT OF A 16-BIT NUMBER

The following routine finds the cube root by using two successive approximations, one
high and one low and waits until they merge. The routine uses 150 to 250 clock cycles.

.DEF ANS = R0 ;Answer (Cube Root)


.DEF REML = R2 ;Remainder = A^3 - INT(CUBEROOT(ANS))^3
.DEF REMH = R3 ;
.DEF CUB1 = R4 ;Answer from CUBE Routine
.DEF CUB2 = R5 ;
.DEF CUB3 = R6 ;
.DEF TMP1 = R8 ;Temporary Workspace
.DEF TMP2 = R9 ;
.DEF ZERO = R10 ;To hold value Zero
.DEF ONE = R11 ;To hold value One
.DEF LES = R12 ;Low Estimate
.DEF HES = R13 ;High Estimate
.DEF AVG = R14 ;Average Low & High Estimate

.DEF AL = R16 ;Original Cube


.DEF AH = R17 ;
.DEF B = R18 ;General Purpose

LDI AL, LOW(1000) ;Load Original Cube into AH:AL


LDI AH,HIGH(1000) ;
CLR REML ;Clear Remainder
CLR REMH ;
CLR ZERO ;Set Zero
CLR ONE ;Set One
INC ONE ;
CLR LES ;Start Low Estimate at Zero
LDI B,42 ;Start High Estimate at Forty-Two
MOV HES,B
LOOP: MOV AVG,LES ;AVG = (LowEst+HighEst+1)/2
ADD AVG,HES ;
ADC AVG,ONE ;
LSR AVG ;
MOV B,AVG ;Calculate AVG^3
RCALL CUBE ;
CP CUB1,AL ;Compare AVG^3 to Original CUBE
CPC CUB2,AH ;
CPC CUB3,ZERO ;
BRNE SKIP1 ;Check if AVG^3 = Original CUBE
MOV LES,AVG ;AVG^3 = CUBE so Low-Est = AVG (Finished)
RJMP FINI2 ;
SKIP1: BRCC ISHIGH ;
MOV LES,AVG ;AVG^3 < CUBE so Low-Est = AVG
RJMP SKIP2 ;
ISHIGH: MOV HES,AVG ;AVG^3 > CUBE so High-Est = AVG
SKIP2: MOV B,HES ;B = HighEst - LowEst
SUB B,LES ;
BREQ FINI ;LowEst = HighEst so we are Finished
CPI B,1 ;
BRNE LOOP ;If HighEst-LowEst > 1 then try again
FINI:
MOV B,LES ;Calculate Remainder
RCALL CUBE ;Remainder = Cube - LowEst^3
MOVW REMH:REML,AH:AL ;
SUB REML,CUB1 ;
SBC REMH,CUB2 ;
FINI2: MOV ANS,LES ;Store Result = LowEst
DONE: RJMP DONE

;----------------------------;
; Calculates Cube of B ;
; Results in CUB3:CUB2:CUB1 ;
;----------------------------;
CUBE:
MUL B,B ;Calc B*B
MOVW TMP2:TMP1,R1:R0 ;
MUL TMP1,B ;Calc B*B*B
MOVW CUB2:CUB1,R1:R0 ;
MUL TMP2,B ;
ADD CUB2,R0 ;
CLR CUB3 ;
ADC CUB3,R1 ;
RET

2c. LOGARITHMS

BASIC AVR MATH III v1.2


LOGARITHMS & e
by RetroDan@GMail.Com

CONTENTS:

1. LOG BASE TWO OF A SINGLE-BYTE NUMBER


2. LOG BASE TWO OF A SIXTEEN-BIT NUMBER
3. MULTIPLYING A SINGLE-BYTE NUMBER BY e
4. MULTIPLYING A SIXTEEN-BIT NUMBER BY e
5. MULTIPLYING A SINGLE-BYTE NUMBER BY 1/e
6. MULTIPLYING A SIXTEEN-BIT NUMBER BY 1/e
7. NATURAL LOGARITHM OF A SINGLE-BYTE NUMBER
8. NATURAL LOGARITHM OF A SIXTEEN-BIT NUMBER
9. LOG BASE 10 OF A SINGLE-BYTE NUMBER
10. LOG BASE 10 OF A SIXTEEN-BIT NUMBER

The advantage of using integer math is the speed as versus the much slower speed and
large size of floating-point math, however answers can be subject to larger rounding
errors with integer math.

1. LOG BASE 2 OF A SINGLE-BYTE NUMBER

The integer value of logarithm base two of a number is useful in determining the number
of bits required to store the number. The routine works by shifting the number left until a
one falls out. The routine yields an error of about 1% and uses 10 to 40 clock cycles.

.DEF ANSF = R0 ;Fractional Part of Answer


.DEF ANS = R1 ;Integer Part of Answer
.DEF A = R16 ;Original Value
.DEF N = R20 ;Counter/Index

LDI A,100 ;Load Original Value


LDI N,7 ;Initial log2 Value
PUSH A ;Store Original Value
LOOP: LSL A ;Shift Left until a one falls out
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,A ;Store Fractional Part
POP A ;Restore Original Value

2. LOG BASE TWO OF A SIXTEEN-BIT NUMBER

We can expand the previous routine to handle 16-bit numbers. The routine uses 20 to 100
clock cycles and should have an error of about 0.1%.

.DEF ANSF = R0 ;Fractional Part of Answer


.DEF ANS = R1 ;Integer Part of Answer
.DEF AL = R16 ;Original Value
.DEF AH = R17 ;
.DEF N = R20 ;Counter/Index

LDI AL,LOW(10000) ;Load Original Value


LDI AH,HIGH(10000) ;
LDI N,15 ;Initial log2 Value
PUSH AL ;Store Original Value
PUSH AH ;
LOOP: LSL AL ;Shift Left until a one falls out
ROL AH ;
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,AH ;Store Fractional Part
POP AH ;Restore Original Value
POP AL ;

3. MULTIPLYING A SINGLE-BYTE NUMBER BY e

The value of e is 2.718281828. To estimate this with integer math we use the ratio 87/32
which is 2.71875 yielding an error of 0.017%. We first multiply our number by 87 then
shift the result five times to the right for a division of 32. The routine is about 16 bytes
long and takes about 28 clock cycles.

.DEF ANSL = R0 ;Answer Low Byte


.DEF ANSH = R1 ;Answer High Byte
.DEF A = R16 ;Original Value
.DEF B = R18 ;Workspace
.DEF N = R20 ;Counter

LDI A,100 ;Load Multiplier into A


LDI B,87 ;Load Est of e = 87/32
MUL A,B ;
LDI N,5 ;32 = 2^5
LOOP: LSR ANSH ;Divide result by 32
ROR ANSL ;
DEC N ;
BRNE LOOP ;

4. MULTIPLYING A SIXTEEN-BIT NUMBER BY e

To estimate the value of e = 2.718281828 we use the ratio 5567/2048 = 2.71826 which
yields an error of 0.001%. The routine is about 40 bytes long and takes about 24 clock
cycles.

.DEF ANS1 = R0 ;Two Byte Answer


.DEF ANS2 = R1 ;
.DEF ZERO = R10 ;To hold Zero
.DEF AL = R16 ;To hold multiplicand
.DEF AH = R17 ;
.DEF BL = R18 ;To hold multiplier
.DEF BH = R19 ;
.DEF WRK1 = R20 ;Workspace
.DEF WRK2 = R21 ;
.DEF WRK3 = R22 ;
.DEF WRK4 = R23 ;

LDI AL,LOW(1000) ;Load Original Value into AH:AL


LDI AH,HIGH(1000) ;
LDI BL,LOW(5567) ;Load multiplier into BH:BL
LDI BH,HIGH(5567) ;
MUL16x16:
CLR ZERO ;Set Zero
MUL AH,BH ;Multiply AH:AL by 5567
MOVW WRK4:WRK3,R1:R0 ;
MUL AL,BL ;
MOVW WRK2:WRK1,R1:R0 ;
MUL AH,BL ;
ADD WRK2,R0 ;
ADC WRK3,R1 ;
ADC WRK4,ZERO ;
MUL BH,AL ;
ADD WRK2,R0 ;
ADC WRK3,R1 ;
ADC WRK4,ZERO ;
LSR WRK4 ;Ignore lower two bytes is division
ROR WRK3 ;by 1024 and a shift makes it 2048
MOV ANS2,WRK4 ;Store Answer
MOV ANS1,WRK3 ;By ignoring the lower two bytes we
;get a division by 65536

5. MULTIPLYING A SINGLE-BYTE NUMBER BY 1/e

The number 1/e = 0.367879441 can be estimated with the ratio 94/256 which is 0.36719
with an error of 0.19%. The routine is about 6 bytes long and takes about 2 clock cycles.

.DEF ANSL = R0 ;Fractional Low Byte (Ignore)


.DEF ANS = R1 ;Answer
.DEF A = R16 ;Original Value
.DEF B = R18 ;Workspace
.DEF N = R20 ;Counter

LDI A,100 ;Original Value


LDI B,94 ;Load Est of e = 94/256
MUL A,B ;Multiply Original Value by 94

6. MULTIPLYING A SIXTEEN-BIT NUMBER BY 1/e

The constant 1/e = 0.367879441 can be estimated by the ratio 24109/65536 = 0.36787
with an error of 0.001%. The routine is about 36 bytes long and takes about 20 clock
cycles.

.DEF ANS1 = R0 ;Two Byte Answer


.DEF ANS2 = R1
.DEF ZERO = R10 ;To hold Zero
.DEF AL = R16 ;Original Value
.DEF AH = R17 ;
.DEF BL = R18 ;To hold multiplier
.DEF BH = R19 ;
.DEF WRK1 = R20 ;Workspace
.DEF WRK2 = R21 ;
.DEF WRK3 = R22 ;
.DEF WRK4 = R23 ;

LDI AL,LOW(1000) ;Load Original Value into AH:AL


LDI AH,HIGH(1000) ;
LDI BL,LOW(24109) ;Load multiplier into BH:BL
LDI BH,HIGH(24109) ;

MUL16x16:
CLR ZERO ;Set Zero
MUL AH,BH ;Multiply Original Value by 24109
MOVW WRK4:WRK3,R1:R0 ;
MUL AL,BL ;
MOVW WRK2:WRK1,R1:R0 ;
MUL AH,BL ;
ADD WRK2,R0 ;
ADC WRK3,R1 ;
ADC WRK4,ZERO ;
MUL BH,AL ;
ADD WRK2,R0 ;
ADC WRK3,R1 ;
ADC WRK4,ZERO ;
MOV ANS2,WRK4 ;Store Answer
MOV ANS1,WRK3 ;By ignoring the lower two bytes we
;get a division by 65536

7. NATURAL LOGARITHM OF A SINGLE-BYTE NUMBER

Previously we created routines to calculate the log base two of a number. From the
equations below we see that by multiplying the log2 of a number by 0.6931472 we can
calculate the natural logarithm.
ln(x) = log2(x)/log2(e)
= log2(x) * 1/1.442695
= log2(x) * 0.6931472
= C * Log2(x) ; where C = 0.6931472

We will use the following ratios to estimate the constant above.

C = 177/256 = 0.69141 Error 0.24%

The routine is about 40 bytes long and uses about 50 to 80 clock cycles the error should
be less than 1%.

.DEF ANSF = R2 ;Fractional Part of Answer


.DEF ANS = R3 ;Integer Part of Answer
.DEF A = R16 ;Original Value
.DEF N = R20 ;Counter/Index

LDI A,250 ;Load Original Value


LDI N,7 ;Initial log2 Value
PUSH A ;Store Original Value
LOOP: LSL A ;Shift Left until a one falls out
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,A ;Store Fractional Part
LDI A,177 ;Multiply by 177 then
MUL16x8:MUL ANSF,A ;divide by 256 by ignoring
MOV ANSF,R1 ;lowest byte of the result
MUL ANS,A ;
CLR ANS ;
ADD ANSF,R0 ;
ADC ANS,R1 ;
POP A ;Restore Original Value

8. NATURAL LOGARITHM OF A SIXTEEN-BIT NUMBER

Previously we created routines to calculate the log base two of a number. From the
equations below we see that by multiplying the log2 of a number by 0.6931472 we can
calculate the natural logarithm.

ln(x) = log2(x)/log2(e)
= log2(x) * 1/1.442695
= log2(x) * 0.6931472
= C * Log2(x) ; where C = 0.6931472

We will use the following ratio to estimate the constant above.

C = 45426/65536 = 0.69315 Error = 0.0004%

The routine is about 70 bytes long and takes about 40 to 120 clock cycles.
.DEF ANSF = R2 ;Fractional Part of Answer
.DEF ANS = R3 ;Integer Part of Answer
.DEF TMP1 = R4 ;Temporary Workspace
.DEF TMP2 = R5 ;
.DEF TMP3 = R6 ;
.DEF TMP4 = R7 ;
.DEF ZERO = R10 ;Hold Zero
.DEF AL = R16 ;Original Value
.DEF AH = R17 ;
.DEF N = R20 ;Counter/Index

LDI AL,LOW(65000) ;Load Original Value


LDI AH,HIGH(65000) ;
LDI N,15 ;Initial log2 Value
PUSH AL ;Store Original Value
PUSH AH ;
LOOP: LSL AL ;Shift Left until a one falls out
ROL AH ;
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,AH ;Store Fractional Part
LDI AL,LOW(45426) ;Multiply by 45426
LDI AH,HIGH(45426) ;Then divide by 65536 by ignoring
MUL16x16: ;the lowest two bytes
CLR ZERO
MUL ANS,AH ;
MOVW TMP4:TMP3,R1:R0 ;
MUL ANSF,AL ;
MOVW TMP2:TMP1,R1:R0 ;
MUL ANS,AL ;
ADD TMP2,R0 ;
ADC TMP3,R1 ;
ADC TMP4,ZERO ;
MUL ANSF,AH ;
ADD TMP2,R0 ;
ADC TMP3,R1 ;
ADC TMP4,ZERO ;
MOVW ANS:ANSF,TMP4:TMP3 ;Store Answer
POP AH ;Restore Original Value
POP AL ;

9. LOG BASE 10 OF A SINGLE-BYTE NUMBER

Previously we created routines to calculate the log base two of a number. From the
equations below we see that by multiplying the log2 of a number by 0.301030004 we can
calculate the logarithm base 10.

log10(x) = log2(x)/log2(10)
= log2(x) * 1/3.321928
= log2(x) * 0.301030004
= C * Log2(x) ; where C = 0.0.301030004
We will use the following ratio to estimate the constant above.

C = 77/256 = 0.30078 Error = 0.08%

The routine is about 40 bytes long and takes about 20 to 50 clock cycles.

.DEF ANSF = R2 ;Fractional Part of Answer


.DEF ANS = R3 ;Integer Part of Answer
.DEF A = R16 ;Original Value
.DEF N = R20 ;Counter/Index

LDI A,250 ;Load Original Value


LDI N,7 ;Initial log2 Value
PUSH A ;Store Original Value
LOOP: LSL A ;Shift Left until a one falls out
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,A ;Store Fractional Part
LDI A,77 ;Multiply by 77 then
MUL16x8:MUL ANSF,A ;divide by 256 by ignoring
MOV ANSF,R1 ;lowest byte of the result
MUL ANS,A ;
CLR ANS ;
ADD ANSF,R0 ;
ADC ANS,R1 ;
POP A ;Restore Original Value

10. LOG BASE 10 OF A SIXTEEN-BIT NUMBER

Previously we created routines to calculate the log base two of a number. From the
equations below we see that by multiplying the log2 of a number by 0.301030004 we can
calculate the logarithm base 10.

log10(x) = log2(x)/log2(10)
= log2(x) * 1/3.321928
= log2(x) * 0.301030004
= C * Log2(x) ; where C = 0.0.301030004

We will use the following ratio to estimate the constant above.

C = 19728/65536 = 0.30103 Error = 0.000001%

The routine is about 70 bytes long and takes about 40 to 125 clock cycles.

.DEF ANSF = R2 ;Fractional Part of Answer


.DEF ANS = R3 ;Integer Part of Answer
.DEF TMP1 = R4 ;Workspace
.DEF TMP2 = R5 ;
.DEF TMP3 = R6 ;
.DEF TMP4 = R7 ;
.DEF ZERO = R10 ;Hold Zero
.DEF AL = R16 ;Original Value
.DEF AH = R17 ;
.DEF N = R20 ;Counter/Index

LDI AL,LOW(65000) ;Load Original Value


LDI AH,HIGH(65000) ;
LDI N,15 ;Initial log2 Value
PUSH AL ;Store Original Value
PUSH AH ;
LOOP: LSL AL ;Shift Left until a one falls out
ROL AH ;
BRCS FINI ;
SKIP: DEC N ;
BRNE LOOP ;
FINI: MOV ANS,N ;Store the Integer Part of Answer
MOV ANSF,AH ;Store Fractional Part
LDI AL,LOW(19728) ;Multiply by 19728
LDI AH,HIGH(19728) ;Then divide by 65536 by ignoring
MUL16x16: ;the two lowest bytes
CLR ZERO ;
MUL ANS,AH ;
MOVW TMP4:TMP3,R1:R0 ;
MUL ANSF,AL ;
MOVW TMP2:TMP1,R1:R0 ;
MUL ANS,AL ;
ADD TMP2,R0 ;
ADC TMP3,R1 ;
ADC TMP4,ZERO ;
MUL ANSF,AH ;
ADD TMP2,R0 ;
ADC TMP3,R1 ;
ADC TMP4,ZERO ;
MOVW ANS:ANSF,TMP4:TMP3 ;Store Answer
POP AH ;Restore Original Value
POP AL ;

You might also like