Lab 5: Pointers, Subroutines
This lab has you convert a C program that uses pointers and subroutines into PIC24 assembly language. You will also use debugging techniques to examine the workings of a C program.
Basics
Download the ZIP archive containing the lab and extract it directly into C:\ece3724.
As shown above, erase lab_assem3 from the filepath when extracting, or you'll get a folder inside a folder.
You should now have a folder called C:\ece3724\lab_assem3.
Inside this folder, there are three different exercises, called strdcase, strflip, and strxchg. You will only be completing one of these.
If the last digit of your MSU student ID number is:
3,5,7 then use strxchg
2,4,6 then use strdcase
8,9,0,1 then use strflip
Be sure you do the correct exercise or you will get a zero for the lab.
For each exercise, there's an MPLAB project folder containing a C program, strxchg.c, strdcase.c, or strflip.c. (For the sake of brevity, for the rest of the lab, this C program will be referred to as strXXXX.c and the corresponding assembly code as strXXXX.s.)
The C programs are all similar. They each:
initialize two null-terminated strings, sz_1 and sz_2
print the strings
call a function, dostr, that alters the strings:
strxchg exchanges sz_1 and sz_2, and flips the case of the letters of sz_1 (changes capitals to lowercase and vice versa)
strdcase copies sz_2 to sz_1, and downcases all letters of sz_1 (makes capitals lowercase)
strflip copies sz_1 to sz_2 and flips the case of the letters of sz_2
print the strings after they've been altered
For Task 1, you will translate your C program into assembly.
There's also a folder called mplab with two example projects: upcase_c_version, which contains a C program with a function called upcase that converts two strings to all uppercase; and upcase_asm_version, which as you could probably guess, contains an assembly program that does the same thing.
For Task 2, you will use debugging techniques to determine where various parts of upcase_c_version are located in program memory.
Prelab
The prelab assignment is expected to be completed before you walk into lab for checkoff by the TA. If you arrive more than fifteen minutes late to your assigned lab, you are not eligible for prelab credit unless you have made prior arrangements with your TA.
Copy upcase_asmversion.s from C:\ece3724\lab_assem3\mplab to your strXXXX project folder, C:\ece3724\lab_assem3\strXXXX, and rename the file strXXXX.s (do not literally use "Xs" - see above)
Open MPLAB. Open your strXXXX project. Run (Debug Project)** and examine the output in the UART 1 Output window. You should see something like this, depending on your particular project:
If there's nothing in the window, or your UART window is missing, here's how to fix it:
Finish Debugger Session (Shift-F5).
Right click on your project name in the Project window. Select Properties (all the way at the bottom of the context menu in MPLAB X 6.15).
3. Under Categories on the left, select Simulator:
4. In the dropdown under Options for Simulator/ Option categories, select "Uart1 IO Options":
5. Check the "Enable Uart1 IO" box
6. If it doesn't work, restart MPLAB. If it still doesn't work, close all projects, close MPLAB, delete your entire lab_assem3 folder (after saving any work you may have done in Notepad), and re-extract the archive. If the UART window still doesn't show up, don't worry, you don't need it to complete the lab. You can see the desired results of your program by running it and examining the variables sz_1 and sz_2, or by consulting the reference at the bottom of this page.
3. Open your strXXXX.c file. Change the block comments in and around the dostr function to single-line comments. When you're done, it should look like this:
5. Right-click on Source Files under your project name in the Projects window. Select Add Existing Files, and add your strXXXX.s file to the project.
6. Open your strXXXX.s file, then switch back to strXXXX.c in the editor.
7. Select and copy dostr, highlighted below:
8. Paste it into your strXXXX.s file near the top, right above the line .include "xc.inc"
9. Copy paste your UART window output above dostr
10. Enclose everything you just pasted with a block comment.
11. Change the name of the file and the file description. (Feel free to copy this from the Basics section above).
12. Put a header on the top of the file with your name, course number, lab section number, lab number, and the filename. When you're done, it should look something like this:
His friends just call him Mike.
It doesn't have to look exactly the same: You could use less asterisks, or include the date, for example. And you should use your own name and section number. But it should contain the essential information and have a tidy appearance.
13. There are two places in the program - one in code and one in comments - that contain the strings used in upcase: "Hello" and "UPPER/Lower". Find them and replace them with the correct strings: sz_1 = "Upper/LOWER." and sz_2 = "mIXeD CaSe.." Fix the typo in the comment that calls them both sz_1.
14. Finally, temporarily remove strXXXX.c from your project (Right-click > Exclude file(s) from current configuration). Run the project. It should build without errors, but won't seem to do anything.
How to exclude
15. Watch the video of Dr. Jones performing tasks similar to those of Task 2.
** Using Debug Project to execute your project/program is referred to in the vernacular as "running it" to avoid confusion with "debugging it" which is used to denote finding and removing errors.
Prelab Checkoff
strXXXX project has correctly named strXXXX.s file set as source file (5)
strXXXX.s has a block comment at the top containing correct title, file description, C code, and example output (5)
There's a heading on top of strXXXX.s with your name, course number, lab section number, lab number, and filename (5)
The strings in strXXXX.s have been changed in both code and comments to sz_1 = "Upper/LOWER." and sz_2 = "mIXeD CaSe.." (5)
If you do not have a prelab component (distance student or excused absence), these tasks are to be completed as the first part of Task 1.
Task 1
Your job is to manually convert your assigned C program into PIC24 assembly language.
There are no printf statements in the assembly code to test if your program is working. To see the output, examine the data memory (File Registers) at the addresses where the sz_1 and sz_2 variables are stored. (Window > Target Memory View > File Registers)
What you should see if you've followed the steps above: The code in your file left over from upcase is making the strings uppercase.
A different view of the File Registers. Symbol format makes the strings harder to read, but gives us a better understanding of exactly what is at each memory location. For example, the ASCII code for 'P' is 0x50 and we see this character is at address 0x1001.
You may also use the Watches window to monitor sz_1 and sz_2. To make reading the strings possible, right-click the variables and select Display Value Column As > Character. You may also need to increase User Defined Size if the entire string is not visible. The strings may appear to be backwards because they are stored in memory with their first letters in the least significant bytes.
Watches window
The upcase subroutine (the code that's in your program right now) only takes one parameter. Main puts sz_1 into W0, then calls upcase. Then it puts sz_2 into W0 and calls upcase again:
Calling upcase twice.
However the C function you are translating takes two parameters, so your dostr subroutine must also. Edit main to call dostr instead of upcase. Move #sz_1 to W0, move #sz_2 to W1, then call dostr. An example of a subroutine with two parameters is given below (str_swap). You must translate the C code exactly as written. If you don't have a subroutine called dostr and pass it two parameters in W0 and W1, you will lose 40 points from your lab grade.
The next thing you must do is replace the C code in upcase with the C code you want to translate (at the top of your file). Be sure to rename upcase to dostr as well! I would just comment everything in upcase out so you can use it as a reference, but it's up to you. If you do comment it out, make sure to remove it before you submit your code.
Finally, translate each line of C and provide register assignments. You don't need to use the Input/Process/Output method anymore, but if you're not using those comments, remove them.
dostr pointers
Instead of initializing a new variable for u8_c / c / temp, just use a register like in str_swap (see below). Be sure to note it in the comments so it's clear what you're doing.
psz_1 is a pointer (memory address). Since it is the first parameter, we can access it by using W0. psz_1++ means "go to the next memory address" (Point to the next character in the string)
*psz_1 means we are dereferencing the pointer. (Getting whatever value is at the memory address it points to, i.e. *psz_1 is the actual letter). We translate it to [W0] in assembly.
In PIC24 assembly, all pointers are 16 bits no matter what they point to. They are like telephone numbers - you don't need a longer phone number because you have a big house. psz_1 is an unsigned 16-bit variable.
*psz_1 (what psz_1 points to, aka what's at the address stored in psz_1) is a char, an unsigned 8-bit variable.
Keeping in mind the last two points, be sure you're using .b in the correct places.
You should not use labels with names like upcase_anything: in your subroutine, since it's now named dostr.
Make sure you use the exact strings (periods and all). Double check below.
You don't need to move the parameters into new registers to use them. To get you started:
dostr:
;; [w0]
;; while (*psz_1 != 0) {
cp0.b [w0]
bra Z, dostr_end
Your PIC24 assembly program should have the same values for the strings when main is finished as the values printed by the original C program. When it does, take a screenshot of the Files Register window showing sz_1 and sz_2 with format set to Hex in the dropdown at the bottom.
Open the Program Memory window (Window > Target Memory View > Program Memory). Set Format to Code in the dropdown at the bottom. Right click in the window and check Symbolic Mode and Verbose Labels.
Find the first line of your dostr subroutine. There's a variety of ways to do this:
set a breakpoint and step through until you reach that point
scroll through and look for it
use the Find function (magnifying glass on the left border of the Program Memory window).
Note the memory address of the first line of dostr and take a screenshot.
There's upcase! (note: intentionally cropped screenshot)
TA Checkoff
Your program correctly transforms the two strings (as verified by file registers window) (30).
You have found the first line of dostr in program memory (10).
Task 2
Your job is to find various locations in a C program, upcase_cversion.c
Open the project upcase_c_version. Run it.
You can set breakpoints in program memory as well as in your code. Set one on the first line of program memory:
Single step through program memory, observing where you are in the C code. Experiment with the following commands:
Step Into (F7)
Step Over (F8)
Step Out (Ctrl-F7)
Continue
Reset
For more information about these and other debugging methods, consult the MPLAB X IDE User's Guide.
With Program Memory settings of Format>Code and Symbolic Mode, step through the program until you reach the point where the program puts the first "H" into sz_1:
You will need to use Step Into, since Step Over will jump right through whole subroutines.
Note the line, address, opcode, and (dis)assembly code of the instruction that caused the "H" to appear. Take a screenshot of the Program Memory that includes this line.
You need to know the exact line and state it in your report, and it needs to be in the screenshot, but you don't need to have stopped the program counter exactly on it. In fact, the program counter (green arrow) points to the line that is about to execute.
The screenshot should look similar to this but much different.
Still using upcase_c_version.c switch the Program Memory view format to Hex, which will display ASCII values (and make text strings readable).
The PIC has a 24-bit instruction:
From Microchip DS30009715D.
In the first section of program memory, we see:
Program memory of upcase_c_version
A diagram of how the addresses are numerically arranged in this view would look like this, with xx representing phantom bytes:
Instructions have even-numbered addresses. So at address 0x00000, the instruction is 0x040200. At address 0x00002, the instruction is 0x000000.... At address 0x0000E, the instruction is 0x0002FA and etc. See the diagram below.
Odd-numbered memory addresses refer to the upper word of the instruction. The upper byte of the upper word is not implemented. So at address 0x00005 (and 0x00007, 0x00009, 0x0000B, 0x0000D, and 0x0000F) we see 0xFA.
Program memory at beginning of upcase_c_version (with Format set to Code).
Locate the area in program memory which contains the constant strings used to initialize sz_1 and sz_2 . Take a screenshot and note the address(es) where they are stored. To determine the address(es) of the string constants, locate the characters in the strings by finding their ASCII values. For instance, "H" is 0x48, "e" is 0x65, etc. The two strings both end with a "/0" 0x00 null character.
(Note: There is a bug in MPLAB where the Go To function does not behave properly in Hex Format when the Hex Display Width is set to One Byte.)
TA Checkoff
You can show the TA which instruction "put the H in sz_1". (10)
You can show the TA where the constant strings used to initialize sz_1 and sz_2 are located. (10)
Report
Your report must have a title page.
Label the first section Task 1.
Briefly describe what you did for Task 1. Mention things like what language you used, what your program did, and what the results were.
Include the screenshot of data memory along with a brief explanation of what it is and what it signifies.
Include the screenshot of program memory along with a brief explanation of what it is and what it signifies. (Memory address of first line of dostr)
Label the second section Task 2.
Briefly describe what you did for Task 2.
Include the first screenshot of program memory along with a brief explanation of what it is and what it signifies. (address, etc of instruction)
Include the second screenshot of program memory along with a brief explanation of what it is and what it signifies. (address of string constants)
Attach your strXXXX.s file.
It must be properly commented. The comments should indicate which assembly language source lines implement which C statements and give a register assignment for each line of C. It may contain additional comments helping you to understand what the program is doing.
Make sure it has the header you put on during the prelab.
Remove any stray comments that don't pertain to the program (e.g. remnants from upcase or str_swap)
Tidy it up - align indentations and use a sensible/consistent number of blank lines to make it easy to read.
Run it again to make sure it still works.
Grading
The report is worth 20 points: 10 for the quality of your code/comments, and 10 for neatly and coherently presenting your information to a reader.
The tasks are worth 60 points. If your report indicates that you did not complete or do not understand a task, you will lose credit, even if you performed it during the lab. The same is true for tasks performed during the prelab.
There are four subtasks. The following non-exhaustive list of errors will result in losing full credit for a subtask:
missing screenshot
screenshot of the wrong thing
screenshot showing incorrect values
awful screenshot (didn't crop, tiny, blurry, etc)
missing text description of subtask
blatantly erroneous text
"This is a screenshot of the program memory" (next to a picture of file registers)
"The value of u16_a is..." (while showing sz_1)
"sz_2 contains 'mixed case'" (while it contains "Hello")
text is "lab jargon" ("I did strdcase. I swapped my psz2/1") rather than an intelligible description ("I translated a C program, which...")
copy/pasting part of the lab writeup rather than using your own words
no comments/almost no comments/wrong comments in code
missing all/most register assignments in code
didn't bother to remove upcase comments from strXXXX.s
didn't follow basic instructions of programming task (didn't use pointers, pass 2 parameters to dostr subroutine, translate exact C code, etc)
code doesn't run / doesn't transform strings properly
used wrong strings - double check against the list below!
The following non-exhaustive list of errors will result in losing partial credit for a subtask:
bad screenshot (lazy cropping, small, giant, on a page break)
garbled / confusing text
repeatedly using vague or incorrect terminology ("I pushed the strings into memory storage at location 00x001000")
excessive text (Each subtask requires approximately 1-3 sentences, not paragraphs or pages)
multiple mistakes in comments/missing some comments in code
missing some register assignments
a few wrong/misleading comments carelessly left in code
untidy code (random spacing, no spacing, random indentations)
minor errors in programming task (used registers W8-W15, didn't use .b when you should have, etc)
Lab reports that flagrantly violate submission policy (wrong lab, no screenshots, no title page, no text besides headings/labels, mostly blank, code pasted into pdf, paragraphs of lab text pasted in, did wrong case, etc.) will not be accepted. The student will receive a zero for the lab and may resubmit with late penalty.
str_swap
The following source code is from exercise 6.27 of B.A. Jones, R. Reese, and JW Bruce, Microcontrollers: From Assembly Language to C Using the PIC24 Family, 2nd ed. Cengage Learning, 2015. Comments have been added/modified.
// this subroutine implements a string swap.
void str_swap(char* psz_1, char* psz_2){
char c_char;
while (*psz_1 != 0) {
c_char = *psz_1;
*psz_1 = *psz_2;
*psz_2 = c_char;
psz_1++; psz_2++;
}
}
; W0 = psz_1, W1 = psz_2, W2 used for u8_char
str_swap:
; while *psz_1 != 0
cp0.b [W0]
bra Z, str_swap_exit
; u8_char = *psz_1
mov.b [W0], W2
; *psz_1 = *psz_2
mov.b [W1],[W0]
; *psz_2 = u8_char, psz_2++
mov.b W2, [W1++]
; psz_1++
inc W0, W0
bra str_swap
str_swap_exit:
return
; we could call str_swap like this:
;; W0 W1
;; str_swap(sz_1, sz_2);
mov #sz_1, W0
mov #sz_2, W1
rcall str_swap
Reference (for TAs or the UART-less)
****** strdcase ******
Before...
sz_1: Upper/LOWER.
sz_2: mIXeD CaSe..
After...
sz_1: mixed case..
sz_2: mIXeD CaSe..
****** strxchg ******
Before...
sz_1: Upper/LOWER.
sz_2: mIXeD CaSe..
After...
sz_1: MixEd cAsE..
sz_2: Upper/LOWER.
****** strflip ******
Before...
sz_1: Upper/LOWER.
sz_2: mIXeD CaSe..
After...
sz_1: Upper/LOWER.
sz_2: uPPER/lower.
****** upcase ******
Before...
sz_1: Hello
sz_2: UPPER/lower
After...
sz_1: HELLO
sz_2: UPPER/LOWER