Here are the source files for the P8A, the C assignment:
LDR/STR
with fixed
offsets from the frame pointer. However, you may use the PUSH/POP
instructions
that are a shortcut for pushing and popping values to/from the stack.
The stack pointer (SP) changes as the function is called and after the call
(and caller cleanup) returns to the same value as before the call started.
Since a stack is a LIFO data structure, the caller/callee code is symmetric. The caller setup/cleanup is the first/last code executed. And the callee setup/cleanup is the first/last code of the function.
JSR
is
executed.JSR
RET
divRem(), printNum(), getDigit()
These stack frames assume that there is a single local for divRem
,
two locals for printNum()
and no local for getDigit()
.
The notation (N/A
) represents a memory location that is not used.
lo divRem() printNum() getDigit()
^ +==================+ +==================+ +==================+
| FP --> 0 | negDenominator | <-- SP -1 | remainder | <-- SP FP --> 0 | N/A |
| |------------------| |------------------| +==================+
| +1 | caller's FP | FP --> 0 | quotient | +1 | caller's FP | <-- SP
| |------------------| |------------------| |------------------|
+2 | return address | +1 | caller's FP | +2 | return address |
|------------------| |------------------| |------------------|
mem +3 | numerator | +2 | return address | +3 | return val |
addr |------------------| |------------------| |------------------|
+4 | denominator | +3 | x | +4 | val |
| |------------------| |------------------| +==================+
| +5 | ptr to quotient | +4 | base |
| |------------------| +==================+
| +6 | ptr to remainder |
v +==================+
hi
printNum()
calls divRem()
and when it calls itself
recursively. In the box labeled caller's FP
, draw arrows to
show where the pointer points to. Similarly, draw arrow to show where the
pointer parameters in divRem()
point to. While this is not
required, making these drawings will help you understand how to write the code
and how to debug your code.
Here is a blank form you can print and fill in..
printnum.asm
.
Implement one function at a time. The easiest
one is getDigit()
, followed by divRem()
. You may
NOT add any additional
variables to the code beyond those in the supplied code. All data MUST
be stored on the stack. The only variables you may access by name are
negSign
and digits
.
Like previous LC3 programs, test values will be stored in memory locations. To execute the program, set data values as follows before stepping/continuing the program:
printNum()
(data1 = number, data2 = base). When the
program halts, the number is printed.getDigit()
(data1 = val). When the program
halts, the ASCII character is printed.divMod()
(data1 = numerator, data2 = denominator).
When the program halts, the quotient/remainder are in data1/data2
.
data1/data2
in your code.
Nor may you declare additional variables using .FILL/.BLKW
.
This is a complex program, though it does not contain that many lines of code.
The complexity comes from understanding how the stack is used in fucntion calls.
You should do incremental development and thoroughly test each section of code
as you write it. A good place to start is the function getDigit()
.
Since the callers setup/cleanup is provided for you in the testing section,
you might first write the callee's setup/cleanup code. This is very mechanical
if you follow the outline provided earlier. Then test it by stepping
through the code. You should observe the caller pushing arguments and getting
the return value off the stack. You should observe the callee completing the
setup and completing the cleanup. Your function doesn't do anything at this
point except the setup/cleanup. A check is to verify that the stack pointer
(R6
) has returned to its original value.
Now you can begin to write the actual code for getDigit()
. You
may find it useful to get a pointer to digits
using the LC3
instruction LEA
. Given the pointer and the parameter, you can
easily access the ASCII code corresponding to the parameter. Recall the
relationship between array access (array[index]
) and pointer
arithmetic (*(array + index)
). A reference implementation contined
17 lines of code including blank lines. Setup/cleanup code was 7 lines.
Next you will want to work on divRem()
. This code contains a loop
to subtract the divisor from the numerator until the quotient and remainder are
found. The tricky thing is the pointer variables. The value of a pointer
variable is the address where a value is located.
The C code *pointer
will generate two LC3 instructions. First, you
will need
a LDR
instruction to get the value of the pointer. This will be
followed by a LDR/STR
to get/set the value at that address.
A reference implementation contained 43 lines of code including blank lines.
Setup/cleanup code was 7 lines.
Finally, you work on printNum()
. You might first write the code to
print numbers that only require a single digit. Such test cases will not require
any recursive calls, nor a call to divRem()
. Once that code it
complete, add the call to divRem()
. Test with numbers where the
quotient is 0
(i.e. single digit numbers). Finally, add the
recursion to handle multiple digit numbers. A reference implementation required
49 lines of code including blank lines. Setup/cleanup was 17 lines.
For P8A, there will be no preliminary testing, but a test driver has been provided.
Final testing will verify getDigit (10 points), divRem (20 points), and printNum (20 points).
For P8B, there will be preliminary testing for divRem. Final testing will verify getDigit (20 points),
divRem (30 points), and printNum (40 points), and will make sure that the stack pointer is save and
restored correctly (10 points).
printnum.c
using the Checkin tab on the
course website. For P8B, submit the single file printnum.asm
using the
Checkin tab on the course website. Alternatively, from a terminal execute:
~cs270/bin/checkin P8A printnum.c
~cs270/bin/checkin P8B printnum.asm