Lab 6B: aMazing Solution/ Welcome Home

Your bear can now navigate the maze by himself. In this part of the lab, you will complete the Main loop routine by checking to see if the bear is in the forest. Plus, wouldn’t it be nice to welcome your bear home by reporting how many bees he encountered along the way or better yet play a song. In this second part of the lab and the bonus lab you will get the opportunity to do all this and more!

Figure 1: Main Loop

If you test drive the Lab06B.ino program you will see that it displays the number of bees along the way. This was a Lab 6 design challenge and is not a required part of Lab 6B. If you have not yet implemented this feature you can add it as part of the bonus lab.

Table of Contents

What is New?

Here are some of the new concepts you will be exploring in this lab.

  • How to test and branch for the opposite sense of conditional expression
  • How to put a Microcontroller to Sleep

No new assembly directives or instructions are introduced in this lab.

Update State_S2

Add the following comment blocks to the output decoder section of State S2. These are the two sections you will write in this lab. You are going to be responsible for adding the code at the correct location.

state_S2:

cpi r19, S2
brne end_switch

/* Output Decoder */
/*
* step
* 1 check to see if the bear is in the forest.
* if the answer is yes, welcome the bear home.
* 2 check to see if there are any bees in the room
* if the answer is yes, count the number of bees
*/

Part I – In the Forest

Test (tst) the Row

In this part of the lab, you will write the code to see if the bear is in the forest (i.e., home) and learn more about conditional branching. Your code accomplishes this by checking the row the bear is currently in. The rows and columns of the maze are numbered from 0 to 19 (0x13) starting in the upper left hand corner. When the bear has found his way out of the maze he is in row minus one (r24 < 0). When this occurs, your code should call the subroutine InForest. You can also see how I wrote the equivalent test in C++ by opening the lab06B.ino program within the Arduino IDE.

As shown in Figure 1 and in the Lab06B.ino program C++ example, the code to check to see if the bear is in the forest is placed after TakeAStep and before EnterRoom. This code placement makes sense, given that if the bear is in the forest, there is no room to enter! If the bear is in the forest, call the to-be-written subroutine InForest.

Use the test instruction (tst) to find out if the value in a register is less than or greater than zero. Choose the corresponding branch instruction that will make this conditional check work as intended. For example, brne will not work because positive values will also cause it to trigger. If you would like additional help in writing the test to check to see if the bear is in the room see Appendix A, specifically the discussion on the test instruction.

InForest Subroutine

When the bear has left the maze and is now in the forest (i.e., home), call the following InForest subroutine. The InForest subroutine, when called, displays a 0 on the 7 segment display and clears the discrete LEDs. It then turns off the timer and disables the external interrupt. Once the forest is entered, the only way to restart the program is to press the reset button.

; ————————–
; ——- InForest ———
; Input: none Outputs: none
; Warning: Subroutine enters power down mode.
; Press reset button to restart.
InForest:

// Display 0 and turn off LEDs
ldi r16, 0x3F
mov spi7SEG, r16 // write segments for zero
clr spiLEDS // all discrete LEDs off
call WriteDisplay
// Power-Down
ldi r16, 0x05 // When bits SM2..0 are written to 010 (Table 9-1),
out SMCR, r16 // and SE = 1 in the SMCR register (Section 9.11.1),
sleep // with SLEEP the MCU enters Powerdown(Section 9.5)
ret

How do I test my code?

The TakeAStep subroutine updates variables row and col as your bear travels through the maze. You may use TakeAStep to directly test if your new code is working – however, it may take a while to run each test. Instead, you will be using AVR Studio’s simulator/debugger to verify the operation of your new code snip-it.

  1. Open the debug window . Run the program  to allow the reset section to be completed.
  2. Stop program execution . In the I/O View window select PORTD. In the bottom window check PIND bit 2. This simulates pressing the button down.

    Figure 2: Port D Registers Simulating Button Pressed

  3. Single step the program  once or twice. Then uncheck PIND bit 2. This simulates releasing the button. The external interrupt is configured to trigger on the falling edge of the input signal.

    Figure 3: Port D Registers Simulating Button Released

  4. Single step the program  at least twice to initiate the call to the interrupt. You should now see the program counter go to the INT0 address (INT0addr) within the interrupt vector table (IVT).
  5. Although you could easily accomplish the following using the watch window as described in the last lab, I will now show you an alternative method. Open the Memory window (View and Memory). From the drop-down memory select Data. Find the address in SRAM Data Memory assigned to row by the assembler. In debug mode, you can find the address of the row variable by simply rolling over any instance of row in your program.

    Figure 4: Rollover Pop-up Window Showing Variable Address in SRAM

    If this does not work, you can also find row in your list (LST) or map files. For me row was located at memory location 0x101.

  6. In the memory window’s Address text entry field, enter the Address of your row variable. You can now manually insert row values in the memory window by double clicking this location in memory and entering the value you want in the pop-up window.

    Figure 5: Setting Data Values in Memory

    Make sure that the Data memory is selected from the drop down menu (upper-left hand corner of the Memory window. In Figure 5, I have set the row so the bear is about to leave the maze.

  7. Once you have entered your test value set a Breakpoint  (F9) at the start of your new code segment and run your program . The program should then break at the start of the in forest check. While single stepping your program  verify in the Processor window that the Zero flag Z is set to the correct value for the row number you entered. Test both true and false conditions.

Part II – Count and Display Bees

In the previous section, you checked to see if the bear is in the forest (i.e., home). In the following sections, you are going to check to see if the bear is in a room with bees. If the answer is yes, then you will add the number of bees in the room to the current total number of bees encountered up to that time. When the bear enters the forest you will display this number; specifically, the total number of times the bear has been stung.

Check Room for Bees

In lab 5A, you defined a new variable named bees to record the number of bees in a room. The subroutine EnterRoom returned the number of bees and the room number in register r24. You then wrote the code to separate these two values, saving them in variables room and bees. In my version, the number of bees is also held in register r22. Up to this point we have not done anything with the information on the number of bees in a room. In this section, as shown in Figure 1, you will be checking for and adding up the number of bees encountered in the maze in a block of code located after EnterRoom and before WhichWay. This block of code will be placed in-line (i.e, no subroutine) with the main loop. The C++ version of the lab (Lab06B.ino) also checks for and adds the number of bees with code placed inside (“in-line”) with the main loop.

The number of bees in a room was returned by EnterRoom. Using a compare instruction (cp) followed by a conditional branch instruction, check to see if this register is greater than zero. If the answer is yes, then do not count the number of bees, otherwise count the number of bees. This can get a little bit tricky, if you need additional help please see Appendix A.

Count Bees

You know the bear is in a room with bees, so all you need to do now is add the bees in the room to the total number of bees encountered in the maze up to this point. To hold this later value define a new variable named total_bees. As with all variables you define your variable using the BYTE assembly directive. Place the following assembly directive at the beginning of your program in the data segment (.DSEG).

total_bees: .BYTE 1 // total number of times bear was stung

Remember to always initialize variables at the same time you define them in the initialization (reset) section of your program.

; Initialize total number of bees
clr r1 // assumed to always be zero by C compilers
sts total_bees, r1 // no bees

Warning: If you do not initialize your variable, the number of bees encountered will not be reinitialized the second time you have your bear navigate the maze.

Now write a new subroutine named CountBees which counts the number of bees that have been encountered as the bear walks through the maze.

Display the Number of Bees

In this section, you will update the InForest subroutine (Part I) to display the number of bees encountered in place of simply displaying the number 0.

Modify your call to inForest to include the total number of bees as a parameter (see Lab06B.ino). If you could look “under the hood” as the Arduino C++ compiler translates the InForest(total_bees) subroutine call into machine code, you would see the total_bees (the first parameter) placed in register r24. We follow this convention in our assembly equivalent of this call. Specifically, load the total number of bees (total_bees) into register r24 before your call to inForest.

Next, delete the following two lines in my InForest subroutine (Part I).

InForest:

ldi r16, 0x3F
mov spi7SEG, r16 // zero
clr spiLEDS // all off
call WriteDisplay

Replace these two lines with assembly code to call a to-be-written subroutine named Hex_to_7SEG and make sure to provide the proper input. The total number of bees (total_bees), the calling argument to InForest (sent in r24) is now sent to Hex_to_7SEG, which also takes as its calling argument the contents of register r24. The Hex_to_7SEG subroutine returns in r24 the segments to be turned on to represent this hexadecimal digit. Do not forget to move this return value into spi7SEG before calling WriteDisplay.

Hex_to_7SEG

This subroutine converts a hexadecimal value from zero (0x00) to fifteen (0x0F), into its visual representation on a 7 Segment display. To help you write this subroutine, I would recommend reviewing my lecture notes on the Indirect Addressing Mode. Remember to move the output from this subroutine to the correct register to display the number on the 7 segment display.

Warning: The Bytes Defined (.DB) in table are saved sequentially in the 16-bit wide Flash Program memory. Because a 16-bit wide word contains 2 bytes, the number of bytes defined on each line must be an even number.

A simple way to test if Hex_to_7SEG is working is discussed in “A Bug Story” (included in the next section).

Test Your Program

You may upload your program to quickly check to see if it works.The only change in the owner’s manual is the number of bees is now displayed when the bear enters the forest (leaves the maze). I would be surprised if your program works the first time in which case you will need to simulate the steps defined in the owner’s manual. See “Simulation and Debugging” in Lab 6 Part I for help here.

A Bug Story – Continued

Again I downloaded my program hoping it would work immediately and of course it did not. When my bear reached the forest segments a and b turned on but nothing else. It appeared that InForest was being called but the incorrect segments were being displayed. From this observation, I suspected that Hex_to_7SEG was not working. To test my theory I saved the binary number to be decoded into spiLEDS before calling Hex_to_7SEG. Now I could see what value was being sent to Hex_to_SEG to be decoded. Assembling, uploading and running my program I discovered that the binary number 00001000 (810) was being sent to subroutine as expected (the bear had encountered 8 bees). This seemed to show that I was correct and the decoder was not working. To further isolate the bug, I needed to use the debugger/simulator. To simplify the debugging process, I copied the subroutine call to the now empty main loop and set a breakpoint at the call. Now I could immediately test Hex_to_7SEG without having to run the rest of the program. In simulation at the break point, I set r24 equal to 8 to simulate the calling argument to Hex_to_7SEG. Stepping through the subroutine I discovered everything worked fine and that Hex-to_7SEG was returning 0x7F (segments to display 8) as expected. Very strange. It then hit me that I had not sent r24 to spi7SEG so although the answer was being returned to the calling program the return value had never made it to the display. After adding the missing move instruction everything worked as intended.

Share your Story

If you have a similar story of debugging your code, send it to me so I can add it to A Bug Story and help future students.

Lab 6 Deliverable(s)

Make sure to turn in the formatted list file with everything necessary for Lab 6. The new subroutines that should be in the report are WhichWay, InForest, CountBees, and Hex_to_7SEG. Remove all sections that are from include files. Do not turn in any other material. Please read the “Typical Lab Deliverable(s)” section of the Lab Grading Policy document contained in the Lab folder for detailed instructions for how to format your lab report.

All labs should represent your own work – DO NOT COPY.

Lab Notebook Your lab notebook should show your work done as part of this lab. To demonstrate your level of effort, record the time intervals over which you worked on each lab section. Specifically, note the current date and time at the top of each page. Lab notebooks are used to show the thought processes that went into the solution of a problem. More credit will be given to solution attempts than the actual solution.

Appendix A: The Test (tst) and Compare (cp and cpi) Instructions.

The following section is provided for those students who want additional assistance writing the in forest and bee test sections of the program.

For the inforest test we only need to determine if register (r24) is positive or equal to zero (r24 >= 0). Where the value in r24 equals the row coordinate of the bear. If the row is greater than or equal to zero then no action needs to be taken, otherwise we need to call the in forest subroutine. To find the answer to this type of question we use the test (tst) instruction.

The test instruction performs the logical and operation on the operand with itself (Rd ← Rd & Rd). This operation is done in order to set and or clear SREG ALU flag bits Z and N. Looking at the “Conditional Branch Summary” Table below, we see that knowing the value of these two SREG bits allows us to quickly test to see if the value held in this register is negative (N = 1), positive (N = 0), and equal (Z = 1) or not equal to zero (Z = 0).

For the inForest test we need to test if the number is positive (N = 0), in which case the bear is still in the maze.

For the bees test we want to count the bees if the room has bees (r22 > 0). Where r22 holds the number of bees in the room. When we translate high level if expressions into assembly we invariably use the opposite sense of the test, in this case (r22 <= 0). This is done to allow the block of code to be execute if the original test is true to immediately follow the test. In other words if the r22 > 0 then run the code following, otherwise (r22 <= 0) jump over this code. Looking at the table we anticipate that this test will involve flag bits C and Z. The test instruction does not help with the carry bit so we must look to the compare (cp and cpi) instruction.

A compare instruction performs a subtract operation without a destination. You may be asking yourself, what is the point of subtracting two numbers if you don’t save the answer? Most of the time when you compare two items, for example in a C++ if instruction, you do not want the items being compared to be modified. You only want to know the relationship between the two numbers. For example, are the two numbers equal, is one greater than or equal to the other, or is one less than the other? In assembly, while the compare instruction does not save the result it does set all the SREG status flags  associated with the subtract operation. When combined with the AVR’s extensive suite of conditional branch instructions (see “Conditional Branch Summary” table) designed to work with the compare instruction, you have a quick way of comparing two values and taking action.

Assuming the use of a compare immediate instruction (cpi Rd, Rs), where Rs is a constant, in this case 0, for our bees test problem, we want to find the assembly instruction corresponding to Rd <= Rs. From the table we see that the instruction is Branch if Same or Higher (brsh), with a note attached. The note tells us to “Interchange Rd and Rr in the operation before the test. We therefore would want to make Rd = 0 and Rs = r22. Unfortunately, for a compare immediate instruction Rs is a constant and so we must switch to a compare instruction (cp) and use some arbitrary register set to 0 for Rd. Here is how the final test would look.

// if(bees > 0); // C++ test, with equivalent Assembly Code
clr r16 // see Conditional Branch Instruction Summary Table
cp r16, r22 // Note: 1 Interchange Rd and Rr in the operation

// before the test

brsh no_bees

block of code to be executed if the room has bees.

no_bees:

Appendix B: How do I divide a number by 2?

In assembly language we always divide in multiples of 2 by shifting right 1 place for each factor of 2. The logical shift right instruction shifts an unsigned (counting) number by one place to the right – just what we need. For example, 4 = 01002 shifted right one place gives you 2 = 00102. Never use the div instruction to divide by two.
In most microprocessors a single shift right logical instruction is included as part of the instruction set. For the AVR processor this is the LSR (Logical Shift Right) instruction.

The AVR also includes an Arithmetic Shift Right (ASR) instruction. While the logical version (LSR) of the shift instruction always inserts a zero into the most significant bit, the arithmetic version (ASR) recycles the sign bit. In this way the sign of the number (plus or minus) is preserved.

In summary, for unsigned numbers to divide by 2 use the logical shift right instruction. For signed numbers always use the arithmetic shift right instruction. Either shift instruction will work in our application. Do you know why?