Experiment 8 - ATmega328 Working with Registers/Arithmetic Operations/Logical Operations

As has been pointed out before, most microprocessors do the same basic operations.  Knowing and understanding what these "basic operations" are, makes adapting to a new microprocessor easy.  In many cases it is simply a matter of identifying different instruction mnemonics.  In experiments 1 to 7 we explored Intel 8080's "basic operations".  In this and subsequent experiments, we will use this knowledge as a base to explore the capabilities of a more modern processor, the Atmel ATmega328.  Our approach in Experiments 8 to 10 will be to work our way through Intel 8080 experiments comparing and at times contrasting the instruction sets of the two processors.  In Experiment 11, we take a look at the ATmega328's built-in peripherals and explore how to write assembly code to use them.  In Experiment 12, we will play around with AVR Tiny BASIC and learn how to customize it for special applications.

The ATmega328 is one of a large family of microprocessors that are affordable and adaptable to a wide variety of applications including the popular Arduino computer boards.  Let's be clear. we are not suggesting that high level languages be abandoned in favor of assembly.  Rather, we do the study for its educational value striving for a better understanding of the internal operation of the microprocessor.  Of course, it may be that, in certain applications, assembly language or a combination of high-level language and assembly will provide a better solution.

Preparing the SparkFun Redboard

The SparkFun Inventor's Kit (SIK KIT-13969) contains many of the parts we will use in the following experiments.  Alternatively, the Redboard can be ordered alone (DEV-13975) and the remaining parts (breadboard, LEDs, jumper wires, etc.) obtained separately.  The kit includes some but all the parts we need.  The breadboard, LEDs, and jumper wires in the kit are fine.  The kit's 330 ohm resistor leads are too small in diameter to make good connection in the breadboard.   330 ohm 1/4 watt resistors purchased separately work much better. Pictured below is the SparkFun Redboard.

SparkFun Redboard

In order to match the ATmega32 experience as closely to the Intel 8080 experiments, we need Data LEDs and sense switches.  Pictured below is our version of sense switches and Data LEDs built on the SIK breadboard.  The short jumper wires are made from No. 22 hook-up wire.  The eight position DIP switch can be order from Mouser (Part no. 706-78B08ST).

Data LEDS and Sense Switches

The eight LEDs positive leads (rounded side) are patched from left to right to pins 7 to 0 of the Redboard (PORTD bits 7 to 0).  The LED leads are trimmed so that the LED fits snugly against the breadboard.  The chosen LED colors and spacing complement the octal orientation of the initial Intel 8080 experiments.  The negative (flat side) of each LED is connected to common ground through 330 ohm resistors.

The numericly labeled side of the DIP switch (top in image above) is patched as follows:  (1) the left most two switches (labeled 1 and 2) are patched to pins ADC1 and ADC0 (PORTC bits 1 and 0) respectively of the Redboard, and (2) right most six switches (labeled 3 to 8) are patched to pins 13 to 8 (PORTB bits 5 to 0) respectively of the Redboard.  All pins of the unnumbered side of the DIP switch are connected to common ground.

The DIP switches are open when positioned away from the numbers (down in image above).  This is important because the AVR programmer does not work if one or all of pins 11, 12, or 13 are shorted to ground. 

Note: Keep the switches positioned down (open) during flash programming. 

The Data LEDs light when the bits on PORTD are cleared, the opposite of the way they worked on the Altair simulation.  In addition, not all bits of the ATmega328's PORTB are available for use.  Bits 6 and 7 are used for the 16 MHz crystal connection.  This complicates using the sense switches, as they are split between PORTB and PORTC.  To overcome these complications, we use two subroutines to access Data LEDs and sense switches. 

To display a byte in the Data LEDs, move it to register r16 and use the code "call LEDout".  Similarly, to input from the sense switches, use the code "call ssinp".  The inputted byte is returned in register r16.

After wiring the LEDs and sense switches, work through Experiment 0 below to test.

Experiment 8-0 - Setting Up the AVR Programmer and Testing the LEDs and Sense Switches

The AVR ISP MK2 can be ordered from Mouser (Part no. 909-AVR-ISP-MK2). The programmer pictured below is an older version, but operational is the same.  The AVR ISP MK2 plugs into the ISP header on the Redboard as shown.  It is powered by the USB cable that also provides communication with ATMEL's Studio 7 downloadable here.

AVR Programmer and Sparkfun Redboard

Power up the SparkFun Redboard and then power up the attached AVR ISP MK2.  Follow these steps:

  1. Launch ATMEL Studio 7 and click "File-New-Project".
  2. Choose "Assembler" as the project type. Click "OK".  Name the project "Experiment_8"
  3. On the next screen under "Device Selection" select "ATmega328P". and click "OK".
  4. Replace the dummy "main.asm" shown in the editing screen with Experiment 8 main.asm found here.
  5. Click "Build-Build Solution" (or F7) - This assembles the assembly code and produces a "hex" code file.
  6. Click "Tools-Device Programming".
  7. Select AVR ISP MK2 as "Tool" and click "Apply". The "Interface Settings" should display.
  8. Click "Memories" and "Program" to program the "hex" code file into the ATmega328's flash memory.  Note: All sense switch DIP switches must be down (open).
  9. When complete, program will automatically execute at address 0x0000.
  10. Check that the sense switch(es) turn on the respective LEDs.  To restart the program, press the reset button located near the USB cable connector.

We are ready to begin examples in Experiment 8.

Memory Map for the AT mega328

The Intel 8080 program/data memory map was very simple: 0x0000 to 0xFFFF of external memory.  Program and data overlapped in this memory space.  The I/O map was separate covering 0x00 to 0xFF and like program/data memory, external.

With huge advances to component density, processors like the ATmega328 are able to incorporate program/data memory and peripherals internally.  Separate program and data memory maps are shown below.

ATmega328 Program Memory (16-bit) Addresses
Flash Memory (32K) 0x0000 to 0x07FF
EEPROM (1K) 0x0000 to 0x03FF
   
   
ATmega328 Data Memory (8-bit) Addresses
r0 to r31   32 working Registers 0x0000 to 0x001F
64 I/O Registers 0x0020 to 0x005F
160 Extended I/O Registers 0x0060 to 0x00FF
SRAM 0x0100 to 0x08FF

We describe the working registers in the next section.  The 64 I/O registers plus the 160 extended I/O registers provide for access to and control of ATmega328's internal peripherals.  SRAM (2048 bytes) is available for general data storage.   Other versions of the ATmega have varying amounts of program and data memory.

I/O registers have a wide range of function.  Some I/O registers provide for data transfer to and from the ATmega328.  Other are used to set options or read the the status of peripherals.  In this case, the individual bits, groups of bits, or entire bytes are used.  As we encounter peripherals, we will see how I/O registers are used.

Note: Program memory is non-volatile; it is retained after power down.  Data memory is volatile and lost when power is removed.

Experiment 8-1 - Working with AT mega328 Registers

Loading a Register with a Constant Value

Basic Operation Example Intel 8080 ATmega328
Load a register with a constant MVI B, 0x3F
Register B loaded with 0x3F
LDI R20, 0x3F
Register R20 loaded with 0x3F

Moving a Value from Register to Register

Basic Operation Example Intel 8080 ATmega328
Moving a value from register to register MOV B,H
 B register value acquires value of H register
MOV R2, R17
Register R2 value acquires value of register R17

Try it!

Note: The examples below are taken from Intel 8080 Experiment 1 and are, in most cases, simple translations to ATmega328 code that require little comment.  Where there are additional considerations, notes are provided.  I needed, use the link to refer back to the Intel 8080 experiment and example.  For each example, change the Target Jump located near the beginning of "main.asm" to the label of the code being tested.

Reminder: With the SparkFun Redboard, inputting from the Sense Switches and outputting to the Data LEDs is handled with utility subroutines "ssinp" and "LEDout" respectively.  In both cases, R16 is the input/output parameter passed between the subroutine and the calling program.  Refer to Experiment 9 Example 1 for details.

Experiment 8-1 Example 1. Move the value 10 to the a register and display the result in the Data LEDs.

;
;  Experiment 1 Example 1 - Load a register with an 8-bit value and display in Data LEDS

exmp1_1:
                ldi           r16, 0b00001010        ;012Q -> r16
                call         LEDout                        ;r16 -> LEDS
                rjmp       exmp1_1                      ;loop

Open the ATMEL STUDIO project Experiment_8. Change the Target Jump line in the assembly program "main.asm" to "exmp1_1".  Build the solution and program the ATmega328 flash memory to test the program.  The LEDs should display 00 001 010.

Note the following:

Experiment 8-1 Example 2. Load register R17 with 125Q and then move it to R16 for display in the Data LEDs.

;
; Experiment 1 Example 2 - Load a register (r17) with 125Q, move it another register (r16), and display in Data LEDS
;
exmp1_2:
                ldi             r17, 0b01010101       ;125Q -> r17
                mov          r16, r17                      ;r17 -> r16
                call           LEDout                       ;r16 -> LEDS
                rjmp         exmp1_2

 Change the Target Jump line to "exmp1_2".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 010 101.

Experiment 8-2 - Arithmetic Operations with the ATmega328

The ATmega328's arithmetic operators parallel very closely with the Intel 8080.   The table below provides a summary comparison.  The outstanding difference is that, unlike the Intel 8080, arithmetic operation can be performed with any ATmega328 register.  For this reason, the ATmega328 operational instructions must include as the first parameter, the destination register for the result.

Arithmetic Operation Intel 8080 ATmega328 Flags Affected
Register Add ADD  r ADD Rd, Rr Z,C,N,V,H
Register Add with Carry ADC r ADC Rd, Rr Z,C,N,V,H
Add Immediate ADI K N.A.  
Add Immediate with Carry ACI K N.A.  
Register Subtract SUB r SUB Rd, Rr Z,C,N,V,H
Register Subtract with Carry (Borrow) SBB r SBC Rd, Rr Z,C,N,V,H
Subtract Immediate SBI K SUBI Rd, K Z,C,N,V,H
Subtract Immediate with Carry (Borrow) SBI r SBCI Rd, K Z,C,N,V,H

The table below compares the status word of the Intel 8080 to the status register of the ATmega328.

Intel 8080
Processor Status Word (PSW) 
ATmega328
Status Register (SREG)
ATmega328
Bit No.
Comment
Carry (C) Carry (C Flag) 0 C - The Carry Flag indicates a carry in an arithmetic or logic operation.
Zero (Z) Zero (Z Flag) 1 Z - The Zero Flag indicates a zero result in an arithmetic or logic operation.
Sign (S) Negative (N Flag) 2 N - The Negative Flag indicates a negative result in an arithmetic or logic operation; i.e. bit 7 = 1).
N.A. Two's Complement Overflow (V Flag) 3 V - The Two’s Complement Overflow Flag supports two’s complement arithmetic.
N.A. Sign (S Flag) 4 S - The Sign Flag is exclusive-or between Negative Flag N and  Two’s Complement Overflow Flag V.
Auxiliary Carry (AC) Half Carry (H Flag) 5 H - The Half Carry Flag indicates a carry out of bit 3 in some arithmetic operations.
N.A. Copy Storage (T Flag) 6 T - The Temporary Flag provides 1-bit storage using BLD (Bit LoaD) and BST (Bit STore).
N.A. Global Interrupt Enable (I Flag) 7 I - The Global Interrupt Enable Flag must be set for the interrupts to be enabled.

The ATmega328 has additional arithmetic operations summarized in the table below.

Arithmetic Operation ATmega328 Comment Flags Affected
Register Twos Complement NEG Rd With the Intel 8080, we would use two instructions to accomplish this. E.g., CMA followed by INR A Z,C
Register Multiply Unsigned (0 to 255)        MUL Rd, Rr Result in R1 (high byte) and R0 (low byte). d, r = 0, 1 ...30, 31 Z,C
Register Multiply Signed (-128 to 127)          MULS Rd, Rr Result in R1 (high byte) and R0 (low byte). d, r = 16, 17 ... 30, 31 Z,C
Register Multiply Signed with Unsigned            MULSU Rd, Rr Result in R1 (high byte) and R0 (low byte). d, r = 16, 17 ... 22, 23 Z,C
Register Fractional Multiply   See the AVR Instruction Set Manual for details.   

Try it!

The examples below are taken from Intel 8080 Experiment 2 and are, in most cases, simple translations to ATmega328 code requiring little comment.  Where there are differences, notes are provided.  The link at the beginning of each example points back to the Intel 8080 example for reference. 

To test an example, change the "Target Jump" located near the beginning of the program to the start label of that example.

Experiment 8-2 Example 1. Create and test code that adds an immediate data value.

We overcome the absence of an add immediate instruction by subtracting the negative of 12 (highlighted in green).

;
; Experiment 2 Example 1a - Add 25 and 12 then display. The answer is 37 or 045Q.
;
; Using subtract immediate
;
exmp2_1a:
                ldi                r16, 25          ;25 -> r16
                subi             r16, -12         ;r16 - (-12) -> r16
                call             LEDout        ;r16 -> LEDs
                rjmp            exmp2_1a

Change the Target Jump line to "exmp2_1a".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 00 100 101.

Next redo the example replacing the subtract immediate instruction with an "add immediate" macro (highlighted in yellow).

. . .
;
; Add Immediate Macro
;
.macro addi
                    subi         @0, -@1         ;subtract the negative of an immediate value
.endmacro
;
. . .
;
; Experiment 2 Example 1b - Add 25 and 12 then display. The answer is 37 or 045Q.
;
; Using add immediate macro
;
exmp2_1b:
                ldi                 r16, 25             ;25 -> r16
                addi              r16, 12             ;r16 + 12 -> r16
                call             LEDout             ;r16 -> LEDs
                rjmp            exmp2_1b

 Change the Target Jump line to "exmp2_1b".  Build the solution and program the ATmega328 flash memory to test the program. The Data LEDs should display 00 100 101.

Note: Macro definitions like the "addi" should appear at the beginning of the assembly program before the macro is used.

Experiment 8-2 Example 2. Add 25 and 12 with so that the Carry Bit will be added into the sum.

We cannot use SUBI with the carry set, because this would subtract rather than add the carry from the sum.  Instead, we load a second working register (say R17) immediately with 12 and then use ADC (add registers with carry).

;
; Experiment 2 Example 2 - Add 25 and 12 with Carry then display. The answer is 38 or 046Q.
;
; Using subtract immediate
;
exmp2_2:
                sec                                   ;Set Carry
                ldi                r16, 25          ;25 -> r16
                ldi                r17, 12          ;12 -> r17
                adc              r16, r17         ;r16 + r17 -> r16
                call              LEDout          ;r16 -> LEDs
                rjmp            exmp2_2

Change the Target Jump line to "exmp2_2".  Build the solution and program the ATmega328 flash memory to test the program.   The Data LEDs should display 00 100 110.

Experiment 8-2 Example 3.  Create and test code that subtracts an immediate value.

;
; Experiment 2 Example 3 - Subtract 12 from 25 then display. The answer is 13 or 015Q.
;
; Using subtract immediate
;
exmp2_3:
                ldi             r16,25             ;25 -> r16
                subi          r16,12             ;r16 - 12 -> r16
                call          LEDout           ;r16 -> LEDs
                rjmp         exmp2_3

Change the Target Jump line to "exmp2_3".  Build the solution and program the ATmega328 flash memory to test the program. The Data LEDs should display 00 001 101.

Experiment 8-2 Example 4.  Subtract 12 from 25 with SBCI so that the Borrow (Carry) Bit will be subtracted along with the immediate value.

;
; Experiment 2 Example 4 - subtract 12 from 25 with borrow then display. The answer is 12 or 014Q.
;
; Using subtract immediate
;
exmp2_4:
                sec                                   ;Set Carry
                ldi             r16,25             ;25 -> r16
                sbci          r16,12             ;12 -> r17
                call          LEDout            ;r16 -> LEDs
                rjmp        exmp2_4

Change the Target Jump line to "exmp2_4".  Build the solution and program the ATmega328 flash memory to test the program. The Data LEDs should display 00 001 100.

Experiment 8-2 Example 5 - Create and test code that verifies that the negative of a negative is a positive. 

;
; Experiment 2 Example 5 - Show that -(-25) = 25.
;
; Using the ATmega328 negation instruction (NEG)
;
exmp2_5:
             ldi               r16,-25              ;-25 -> r16
             neg             r16                     ;-(-25) -> r16
             call            LEDout              ;r16 -> LEDs
             rjmp           exmp2_5

Change the Target Jump line to "exmp2_5".  Build the solution and program the ATmega328 flash memory to test the program. The Data LEDs should display 00 011 001.

Note: NEG is equivalent of the Intel 8080 instructions CMA followed by INR A (twos complement of A).

Experiment 8-2 Example 6 - Create and test code that verifies the variety of addition and subtraction examples shown in the table below.

 Use the code of Example 2 above (adding and subtracting with two registers) substituting the appropriate instruction (ADD or SUB) and the table values to verify the results indicated.

A Operation B = = (in Binary) = (in Octal)
25 + 18 43 00 101 011 053
25 + (-18) 7 00 000 111 007
-25 + 18 -7 11 111 001 371
-25 + -18 -43 11 010 101 325
25 - 18 7 00 000 111 007
-25 - 18 -43 11 010 101 325
-25 - -18 -7 11 111 001 371

;
; Experiment 2 Example 6a - Add two values then display. The answer is 43 or 053Q for first value in table.
;
; Using two registers
;
exmp2_6a:
                ldi             r16,25             ;25 -> r16
                ldi             r17,18             ;18 -> r17
                add           r16,r17            ;r16 + r17 -> r16
                call           LEDout            ;r16 -> LEDs
                rjmp         exmp2_6a
;
; Experiment 2 Example 6b - Subtract two values then display. The answer is 7 or 007Q for first values in table.
;
; Using two registers
;
exmp2_6b:
                ldi            r16,25                 ;25 -> r16
                ldi            r17,18                 ;18 -> r17
                sub          r16,r17                ;r16 - r17 -> r16
                call          LEDout               ;r16 -> LEDs
                rjmp        exmp2_6b

Change the Target Jump line to "exmp2_6a".  Build the solution and program the ATmega328 flash memory to test the program for the listed values.  Repeat for "exmp2_6b".

Experiment 8-3 - Logical Operations with the ATmega328

As with the ATmega328's arithmetic operators, the logical operators are very similar to the Intel 8080.   The table below provides a summary comparison. 

Arithmetic Operation 8080 ATmega328
Register And AND  r AND Rd, Rr
And Immediate ANI K ANDI Rd, K
Register Or ORA r OR Rd, Rr
Or immediate ORI K ORI Rd, K
Register Exclusive-Or XRA r EOR Rd, Rr
Exclusive-Or Immediate XRI K N.A.*

* The ATmega328 is missing the exclusive-or immediate instruction.  A simple substitution is to load the constant into a second working register and use the EOR instruction to exclusive-or it with the destination register.

Try it!

Experiment 8-3 Example 1 - Create and test code that performs each logic operation (or, and, and exclusive-or).  Assume one operand is 10101010B and the other is 00001111B.

;
; Experiment 3 Example 1a - Basic logical operations: OR. The result is 0b10101111.
;
; Using OR immediate to set bits
;
; Turns on lower four bits and leaves upper four bits unchanged; i.e., sets the lower four bits.
;
exmp3_1a:
            ldi              r16,0b10101010              ;0b010101010 -> r16
            ori             r16,0b00001111              ;r16 OR 0b00001111 -> r16
            call            LEDout                            ;r16 -> LEDs
            rjmp          exmp3_1a
;
; Experiment 3 Example 1b - Basic logical operations AND. The result is 0b00001010.
;
; Using AND immediate to clear bits
;
; Turns off upper four bits and leaves lower four bits unchanged; i.e., masks the lower four bits.
;
exmp3_1b:
            ldi              r16,0b10101010             ;0b010101010 -> r16
            andi           r16,0b00001111             ;r16 AND 0b00001111 -> r16
            call           LEDout                           ;r16 -> LEDs
            rjmp          exmp3_1b
;
; Experiment 3 Example 1c - Basic logical operations EOR (Exclusive-Or). The result is 0b10100101.
;
; Using register to register exclusive-or (EOR - no EOR immediate instruction)
;
; Flips the lower four bits and leaves the upper four bits unchanged.
;
exmp3_1c:
            ldi                r16,0b10101010             ;0b10101010 -> r16
            ldi                r17,0b00001111             ;0b00001111 -> r17
            eor               r16,r17                            ;r16 EOR r17 -> r16
            call             LEDout                            ;r16 -> LEDs
            rjmp            exmp3_1c

Change the Target Jump line to "exmp3_1a".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 10 101 111.  Repeat for "exmp3_1b" and "exmp3_1c".  The Data LEDs should display 00 001 010 and 10 100 101, respectively.

Recall that the logical operators can be used to (1) manipulate bits, (2) mask bits, and (3) test bits.  With the Intel 8080, we had no choice but to use 8-bit logical operators to accomplish these tasks.  The designers of the ATmega328 have provided additional bit level instructions. See the table below.

Bit   Operation ATmega328 Comment Flags Affected
Set Bits in Register SBR Rd,K Sets bits in register Rd (d=16,...,31) corresponding to set bits in K.
(Same as Rd ORI K)
Z,N,V
Clear Bits in Register CBR Rd,K Clears bits in register Rd (d=16,...,31) corresponding to set bits in K. (Same as Rd ANDI (0xFF-K)) Z,N,V
Set Bit in I/O Port SBI P,b Set bit (b = 0 to 7) on I/O Port1 (P = PORTA, PORTB, PORTC, or PORTD) None
Clear Bit in I/O Port CBI P,b Clear bit (b = 0 to 7) on I/O Port1 (P = PORTA, PORTB, PORTC, or PORTD) None
Swap nibbles in Register SWAP Rd Rd(bits 0...3) swapped with Rd(bits 4...7) None
Set All Bits in Register SER Rd Sets all bits in register Rd.
(Same a MOVI Rd, 0xFF)
None
Clear All Bits in Register CLR Rd Clears all bits in register Rd.
(Same a EOR Rd,Rd)
Z,N,V,S
Skip if Bit in Register Cleared SBRC Rr,b Skips next instruction if bit (b = 0 to 7) in register (r=0,...,31) is clear None
Skip if Bit in Register Set SBRS Rr,b Skips next instruction if bit (b = 0 to 7) in register (r = 0,...,31) is set None
Skip if Bit in I/O Port Cleared SBIC P,b Skips next instruction if bit (b = 0 to 7) on I/O port (P = 0,...,31) is clear None
Skip if Bit in I/O Port Set SBIS P,b Skips next instruction if bit (b = 0 to 7) on I/O  port (P = 0,...,31) is set None

In Experiment 3 Examples 2, 3, and 4, we give the direct translation from Intel 8080 code then the revised code using bit level operations where appropriate.  For some experiments, we use sense switches to represent input data and Data LEDs on PORTD to represent output data. 

Manipulating Bits with the ATmega328 - Try it!

Experiment 8-3 Example 2  In a security application, assume that the lower 6 bits of I/O PORTD control outdoor lights.  Setting a bit to one turns on a light; clearing a bit to zero turns the light off.  The table below shows the association of lights with particular bits on PORTD.

Light PORTD
Bit
Front Porch 0
Back Porch 1
West Front Flood 2
East Front Flood 3
West Back Flood 4
East Back Flood 5

The code below implements in sequence parts "a" through "d" of the Example 2 using a 2 second delay between each part; i.e., (1) Turn off all lights, (2) Turn on all lights, (3) Turn off all light except front and back porch. We show in the first example ATmega328 code simply translated from the Intel 8080 example.  We follow in the second example with code revised to take advantage of the ATmega328's additional bit level instructions.

 Using the assembler's equate directive ".equ", we can associate labels with light bit numbers (highlighted in yellow).  Now we can refer to bit numbers by name.  Besides providing better readability, a second advantage of assigning names is that we can reassign light bits once in the equates and avoid having to change in multiple places throughout the program.

To create the name/bit equivalents, we used the assembler's shift left (<<) operator to shift the left operand (1 in this case) left the number of positions given by the right operand (the bit number for each light).  As an example, the West Porch Light directive is 1<<2 producing 0b00000100 (only bit 2 set).  The last directive equates the label "All_Lights" with the preceding individual lights ORed together to produce 0b00111111.

;
; Experiment 3 Example 2
;
; Assign lights to bit positions in PORTD
;
.equ     Front_Porch = 1
.equ     Back_Porch = 1<<1
.equ     West_Front_Flood = 1<<2
.equ     East_Front_Flood = 1<<3
.equ     West_Back_Flood = 1<<4
.equ     East_Back_Flood = 1<<5
.equ     All_Lights = East_Back_Flood | West_Back_Flood | East_Front_Flood | West_Front_Flood | Back_Porch | Front_Porch
;
; Experiment 3 Example 2 Part a - Bit Manipulation
;
; Using OR and AND immediate as in Intel 8080 experiment
;
; Turn all lights off. Turn all lights on. Turn off all except front and back porch. Repeat.
;
;
exmp3_2a:
        ldi         r16,0 ;clear r16
exmp3_2a_loop:
        andi         r16,0                         ;turn off any lights that are on
        out          portd,r16                   ;update PORTD
        call         delay                          ;wait 2 seconds
        ori          r16,All_Lights            ;turn on all lights
        out         portd,r16                   ;update PORTD
        call         delay                         ;wait 2 seconds
        andi        r16,(Front_Porch | Back_Porch)     ;clear all bits except 0 and 1
        out         portd,r16                   ;turn off all lights except front and back porch
        call         delay                         ;wait 2 seconds
        rjmp       exmp3_2a_loop      ;repeat
;

Change the Target Jump line to "exmp3_2a". Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 00 000 000, 00 111 111, and 00 000 011.

In Example 2b below, we made these code changes to take advantage of the ATmega328's bit manipulating capabilities.

 ;
; Experiment 3 Example 2 Part b - Bit Manipulation
;
; Using ATmega328 bit level instructions
;
; Turn all lights off. Turn all lights on. Turn off all except front and back porch. Repeat.
;
.def Lights = r16
;
exmp3_2b:
        clr             Lights ;clear lights register
exmp3_2b_loop:
        cbr            Lights, All_Lights             ;turn off any lights that are on
        out            portd, Lights                    ;update PORTD
        call           delay                               ;wait 2 seconds
        sbr            Lights, All_Lights             ;turn on all lights
        out            portd, Lights                    ;update PORTD
        call          delay                               ;wait 2 second
        clr             Lights                             ;clear light register
        sbr            Lights,(Front_Porch | Back_Porch)     ;turn on front and back porch lights
        out            portd, Lights                   ;update PORTD
        call          delay                              ;wait 2 second
        rjmp         exmp3_2b_loop           ;repeat full sequence
;

Change the Target Jump line to "exmp3_2b". Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 00 000 000, 00 111 111, and 00 000 011.

An additional advantage can be gained by operating directly on individual I/O port bits.  The code below demonstrates this using CBI and SBI instructions to turn off and on the back porch light (highlighted in yellow).  These instructions clear and set respectively specified bits in an I/O register.  The this example, we clear and then set back porch bit 1 on PORTD.  To clear and set multiple bits, requires several lines of code.  While the previous approach using a working register is more efficient, it could be argued that setting or clearing bits one at a time produces more readable code.

;
; Experiment 3 Example 2a Part c- Bit Manipulation
;
; Using ATmega328 I/O port direct bit level instructions
;
; Turn on and off back porch.
;
exmp3_2c:
        clr              r16                        ;clear r16
exmp3_2c_loop:
        cbi             portd,1                  ;turn off back porch (bit 1)
        call            delay                      ;wait 2 seconds
        sbi             portd,1                  ;turn on back porch (bit 1)
        call           delay                     ;wait 2 second
        rjmp          exmp3_2c_loop  ;repeat full sequence

Change the Target Jump line to "exmp3_2c". Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 00 000 000 and 00 000 010.

Masking Bits with the ATmega328 - Try it!

Experiment 8-3 Example 3.  Suppose that the lower five bits of a data byte indicate room temperature in degrees C; i.e., 0 to 31 °C.  Suppose further that the upper three bits indicate the room in which the temperature measurement is made.  Up to eight rooms numbered 0 to 7 can be designated.  Use masking to isolate the two distinct pieces of data.

The code below extracts the lower fives bits using AND immediate following closely the original Intel 8080 code.

;
; Experiment 3 Example 3 Part a - Masking
;
; Using AND immediate
;
; Masks off upper three bits leaving lower five bits as temperature.
;
exmp3_3a:
        ldi           r16,0b01011011              ;preset room and temperature
        out          portd,r16                         ;update PORTD
        call         delay                                ;wait 2 seconds
        andi        r16,0b00011111             ;mask off upper three bits
        out         portd,r16                         ;update port d
        call         delay                               ;wait 2 seconds
        rjmp       exmp3_3a

Change the Target Jump line to "exmp3_3a".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 011 011 before and 00 011 011 after masking.

In the revised example, we use the "Clear Bits in Register" CBR instruction to clear the upper three bits instead of masking with AND immediate.  It could be argued that the codes purpose is better documented with the "Clear Bits in Register" instruction.

;
; Experiment 3 Example 3 Part b - Masking
;
; Using CBR bit level instruction
;
; Masks off (clear) upper three bits leaving lower five bits as temperature.
;
exmp3_3b:
        ldi           r16,0b01011011             ;preset room and temperature
        out         portd,r16                         ;update PORTD
        call         delay                               ;wait 2 seconds
        cbr         r16,0b11100000             ;mask off (clear) upper three bits leaving temperature
        out         portd,r16                         ;update PORTD
        call        delay                                ;wait 2 seconds
        rjmp      exmp3_3b

Change the Target Jump line to "exmp3_3b".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 011 011 before and 00 011 011 after masking.

In the following three examples, we extract the room number in the upper three bits.  The first example "3c" uses five right rotates just as the Intel 8080 code.  The next example "3d" saves a few instruction bytes by swapping the upper and lower nibbles thereby bringing the room number within one right rotation.  Finally, in example "3e", we use the fractional multiply instruction to multiply by 1/32 completing the rotation in a single instruction. 

;
; Experiment 3 Example 3 Part c - Masking
;
; Using CBR bit level and rotate
;
; Masks off (clear) lower five bits leaving upper three bits. Rotate five right to get room number.
;
exmp3_3c:
        ldi            r16,0b01011011             ;preset room and temperature
        out          portd,r16                         ;update PORTD
        call         delay                                ;wait 2 seconds
        cbr         r16,0b00011111              ;mask off (clear) lower five bits leaving room number
        ror         r16                                   ;rotate right
        ror         r16                                   ;rotate right
        ror         r16                                   ;rotate right
        ror         r16                                   ;rotate right
        ror         r16                                   ;rotate right
        out

        portd,r16                           ;update PORTD
        call        delay                                 ;wait 2 seconds
        rjmp      exmp3_3c

Change the Target Jump line to "exmp3_3c".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 011 011 before and 00 000 010 after extracting the room number.

;
; Experiment 3 Example 3 Part d - Masking
;
; Using CBR bit level, swap, and rotate
;
; Masks off lower five bits leaving upper three bits. Swap nibbles and one rotate right.
;
exmp3_3d:
        ldi          r16,0b01011011             ;preset room and temperature
        out         portd,r16                         ;update PORTD
        call         delay                               ;wait 2 seconds
        cbr         r16,0b00011111             ;mask off (clear) lower five bits leaving room number
        swap      r16                                  ;swap upper and lower nibble
        ror         r16                                   ;rotate right
        out         portd,r16                         ;update PORTD
        call        delay                                ;wait 2 seconds
        rjmp       exmp3_3d

Change the Target Jump line to "exmp3_3d".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 011 011 before and 00 000 010 after extracting the room number.

;
; Experiment 3 Example 3 Part e - Masking
;
; Using CBR bit level and fractional multiply
;
; Masks off (clear) lower five bits leaving upper three bits. Multiply by 1/32 to get room number.
;
exmp3_3e:
        ldi         r16,0b01011011             ;preset room and temperature
        out        portd,r16                         ;update LEDs
        call        delay                               ;wait 2 seconds
        cbr        r16,0b00011111             ;mask off (clear) lower five bits leaving room number
        ldi         r17,0b00000100              ;prepare to multiply by 1/32 (assumes 1.7 binary format i.e.  0.0000100)
        fmul      r16,r17                             ;fractional unsigned multiply
        out       portd,r1                             ;MSB of product is room number
        call       delay                                 ;wait 2 seconds
        rjmp     exmp3_3e

Change the Target Jump line to "exmp3_3e".  Build the solution and program the ATmega328 flash memory to test the program.  The Data LEDs should display 01 011 011 before and 00 000 010 after extracting the room number.

Testing Bits with the ATmega328 - Try it!

With the Intel 8080 we relied on using the AND to logically test individual bits in A register.  A similar approach works with the ATmega328, but we also demonstrate the use of the ATmega328's special bit level instructions to make test bits.

Experiment 8-3 Example 4.  Suppose that the lower six bits of a data byte indicate the status of a vehicle's four doors, its cargo door, and its hood.  A zero bit value indicates the door is open while a one indicates the door is closed.  Note that we have changed this from the original Intel 8080 example to accommodate direct connection of Sense Switches to ATmega328's internal I/O digital port (PORTB in this case).  The chart below shows the specific association of doors with particular bits.

Door Bit
Front Driver 0
Front Passenger 1
Rear Driver 2
Rear Passenger 3
Cargo 4
Hood 5

Write and test code to answer these questions.

a)  Is the cargo door open?   To facilitate testing the code, we use the Sense Switches to simulate the doors.  A switch down is door closed (bit set); a switch up is door open (bit cleared).

;
; Experiment 3 Example 4
;
; Assign door switches to bit positions in PORTB (bit = 1 door closed)
;
.equ Front_Driver = 0
.equ Front_Passenger = 1
.equ Rear_Driver = 2
.equ Rear_Passenger = 3
.equ Cargo = 4
.equ Hood = 5
.equ All_Doors = 1<<Front_Driver | 1<<Front_Passenger | 1<<Rear_Driver | 1<<Rear_Passenger | 1<<Cargo | 1<<Hood
;
; Experiment 3 Example 4 Part a - Bit Testing
;
; Using AND immediate to test bit
;
; Set sense switches to 0b00000011 (front driver & passenger doors open; all others closed)
; or
; Set sense switches to 0b00010011 (front driver, passenger, and cargo doors open; all others closed)

; A displayed one indicates the cargo door is ajar. All other doors ignored.
;
; Note: Reading sense switch PORTB directly, "up" produces cleared bit (door open) while down a set bit (door closed)
;
exmp3_4a:
    ldi             r16,0                    ;preset r16 to zero (false)
    in              r17,pinb               ;get lower six bits of sense switches
    andi          r17, 1<<Cargo     ;is cargo door closed (bit 5 set)?
    brne         exmp3_4_a_cont  ;if so, do nothing (branch on not equal to zero to exmp3_4_a_cont)
    ldi             r16,1                   ;otherwise, set r16 to 1 (true)
exmp3_4a_cont:
    call          LEDout               ;update Data LEDs and check zero flag
    rjmp         exmp3_4a

Change the Target Jump line to "exmp3_4a".  Build the solution and program the ATmega328 flash memory to test the program.

The code below demonstrates how to use the ATmega328 SBIS bit level instruction to check Cargo Door (bit 4) directly to see if it is open.

;
; Experiment 3 Example 4 Part b - Bit Testing
;
; Using I/O bit test and conditional skip
;
; Set sense switches to 0b00000011 (front driver & passenger doors open; all others closed)
; or
; Set sense switches to 0b00010011 (front driver, passenger, and cargo doors open; all others closed)
;
; A displayed one indicates the cargo door is open. All other doors ignored.
;
; Note: Reading sense switch PORTB directly, "up" produces cleared bit (door open) while "down" a set bit (door closed)
;
exmp3_4b:
    ldi             r16,0                ;preset r16 to zero (false)
    sbis          pinb,Cargo        ;is cargo door closed? (bit 5 cleared?)
    ldi             r16,1                ;if so, skip loading r16 with one (true).
    call          LEDout             ;update Data LEDs
    rjmp         exmp3_4b

b) Is any door open?  No  ATmega328 bit level instruction exists to test multiple bits, so we have to revert to ANDI masking method for the code below.

;
; Experiment 3 Example 4 Part c - Bit Testing
;
; Using AND immediate to test bit
;
; Set sense switches to 0b00000000 (all others closed)
; or
; Set sense switches to 0b00xxxxxx (any one or more x's set and all others closed)
;
; A displayed one indicates the cargo door is open. All other doors ignored.
;
; Note: Reading sense switch PORTB directly, "up" produces cleared bit (door open) while "down" a set bit (door closed)
;
exmp3_4c:
    ldi             r16,0                      ;preset r16 to zero (false)
    in              r17,pinb                 ;get lower six bits of sense switches
    com          r17                         ;ones complement r17
    andi           r17, All_Lights       ;is any door open? (bit 5 set?)
    breq         exmp3_4c_cont   ;if not, do nothing
    ldi             r16,1                     ;otherwise, set r16 to 1 (true)
exmp3_4c_cont:
    call          LEDout                  ;update Data LEDs
    rjmp         exmp3_4c

Change the Target Jump line to "exmp3_4c".  Build the solution and program the ATmega328 flash memory to test the program.

Continue to next Experiment - Click Here