[Solved] CSC 230 Lab 6-Safe Subroutines (functions)

$25

File Name: CSC_230_Lab_6-Safe_Subroutines_(functions).zip
File Size: 395.64 KB

SKU: [Solved] CSC 230 Lab 6-Safe Subroutines (functions) Category: Tag:
5/5 - (1 vote)

Recall from the previous lab, a function call implies a transfer (branch, jump) to an address representing the entry point of the function, causing execution of the body of the function. When the function is finished, another transfer occurs to resume execution at the statement following the original call. The first transfer is the function call (for invocation); the second transfer is the return. Together, this constitutes the processors call-return mechanism for subroutine execution.

During the subroutine execution, the corresponding code would likely need to use some registers. We would NOT want that code to override the information stored in the registers by the caller.

How to determine which registers are safe to use?

The best practice is to protect the callers registers by saving the information stored there on the stack. After the subroutine completes execution, that information can be easily restored. In other words, we can back-up the registers using the stack before executing the subroutine and restore them afterwards.

Who protects the registers, the caller or the subroutine?

Arguably, the most convenient option is for the subroutine to protect any registers that it uses.

Following is an example of a safe subroutine which does not produce any undesirable side effects. This subroutine copies a c-string from the program memory location labeled msg to the data memory location labeled msg_copy.

Example: Create an Assembler project in Atmel Studio 7 and replace the contents of its main.asm with the program provided in copy_string.asm file on conneX. Build the program and debug it. Observe that the initial values in r16 and r30 are changed while the subroutine executes, but then they are restored before the program reaches the end-loop (done: rjmp done). Verify the contents of the stack as the program executes and the values are pushed onto it.

Below is the diagram of the internal data memory (SRAM) when lpm r16, Z+ is executed for the first time.

Address content details notes
0x0000 ~0x001F General Purpose Registers
0x0020 ~0x005F 64 I/O Registers
0x0060 ~0x01FF 416 extended I/ORegisters
0x0200 .DSEG
0x21F7 <-SP 0x21F7 is stored in the stack pointer register
0x21F8 R16 saved register In the subroutine copy_string, the registers to be used by the subroutine are pushed onto the stack to protect them from being changed.
0x21F9 XL (R26) saved register
0x21FA XH (R27) saved register
0x21FB ZL (R30) saved register
0x21FC ZH (R31) saved register
0x21FD high(ret) return address The CPU pushes the return address onto the stack automatically when call copy_string is executed.
0x21FE mid(ret) return address
0x21FF low(ret) return address

Recall that ATmega2060 has 256KB of word-addressable program memory, and thus the program counter (PC) needs to count as high as 128K word addresses (217 words) (refer to page 7, Table 2-1 of the datasheet). Therefore, the PC register uses 3 bytes, which are stored on the stack when a call instruction executes. Then, the called subroutine stores the protected registers on top of the return address. After the registers are restored, the return address is on the top of the stack again when the ret instruction executes and writes the top three bytes on the stack to the PC register, thereby returning the control flow back to the instruction which immediately follows the initial call instruction.

  1. Passing (and returning) parameters via the stack.

There are two ways to pass parameters to a subroutine: by value and by reference. Below are two examples illustrating each method.

Example 1 (pass by value):

Consider the following C function that takes two 8-bit unsigned integers as parameters, multiplies them, and returns the result.

uint16_t multiply(uint8_t multiplicand, uint8_t multiplier) {

uint16 result = multiplicand * multiplier;

return result;

}

An equivalent Assembly function is provided in the multiply_by_value.asm file on conneX. Here, the caller (main program) pushes the 8-bit values of the two parameters onto the stack and also reserves the required 16-bit space on the stack for the return value. The function protects all registers that it uses, retrieves the parameters from the stack, multiplies the integers, and stores the result in the reserved location on the stack before returning.

Download the multiply_by_value.asm file and put it in the same location as main.asm for the project that you started earlier in this lab. Then, add the file to your project by right-clicking the lab in the Solution Explorer window and choosing Add->Existing item:

Then, right-click the file and choose Set As EntryFile:

Only one file can be set as EntryFile at any time; this indicates which Assembly file will be built and debugged by the Atmel Studio.

Build and debug the solution. Observe the stack memory and SP as you step through the program. For convenience, use a breakpoint at the instruction immediately after the multiply loop and the debuggers run command to skip over the loop execution. Below is the stack frame diagram of the stack contents when the instruction in ZH, SPH is executed. An equivalent diagram is also available in the comments right before the function code in the multiply_by_value.asm file.

Address content details notes
0x0200 .DSEG
0x21F1 <-SP 0x21F1 is stored in the stack pointer register
0x21F2 zero saved register R0 The registers to be used by the multiply subroutine are pushed onto the stack at the very beginning of the subroutine to protect their values from being changed by the subroutine.
0x21F3 result_low saved register R4
0x21F4 result_high saved register R3
0x21F5 multiplier saved register R2
0x21F6 multiplicand saved register R1
0x21F7 ZH saved register R30
0x21F8 ZL saved register R31
0x21F9 high(ret) return address The CPU pushes the return address (the address of the next command) onto the stack automatically when call add_num is executed.
0x21FA mid(ret) return address
0x21FB low(ret) return address
0x21FC 0x00 result_high
0x21FD 0x00 result_low
0x21FE 0xCD parameter 2 The caller (in this case the main program) pushes these parameters onto the stack.
0x21FF 0xAB parameter 1

Example 2 (pass by reference):

Below is the stack frame diagram for a very similar multiplication function as above, except instead of the values, their corresponding memory addresses are passed to the multiply subroutine.

Address content details notes
0x0200 .DSEG
<-SP 0x21F1 is stored in the stack pointer register
0x21EE zero saved register R0 The registers to be used by the multiply subroutine are pushed onto the stack at the very beginning of the subroutine to protect their values from being changed by the subroutine.
0x21EF result_low saved register R4
0x21F0 result_high saved register R3
0x21F1 multiplier saved register R2
0x21F2 multiplicand saved register R1
0x21F3 YH saved register R28
0x21F4 YL saved register R29
0x21F5 ZH saved register R30
0x21F6 ZL saved register R31
0x21F7 high(ret) return address The CPU pushes the return address (the address of the next command) onto the stack automatically when call add_num is executed.
0x21F8 mid(ret) return address
0x21F9 low(ret) return address
0x21FA 0x02 address of answer The caller (in this case the main program) pushes these addresses onto the stack in big-endian format (most significant byte in the lower memory location than the least significant byte).
0x21FB 0x02 address of answer
0x21FC 0x02 address num2
0x21FD 0x01 address num2
0x21FE 0x02 address num1
0x21FF 0x00 address num1

As in the previous example, download the multiply_by_reference.asm file, put it in the same location as main.asm and the multiply_by_value.asm files, add it to your Atmel Studio solution, and set it as EntryFile. Build and debug the solution. Observe the stack memory and SP as you step through the program. The stack frame diagram above illustrates the stack contents when the instruction in ZH, SPH is executed. An equivalent diagram is also available in the comments right before the function code in the multiply_by_value.asm file.

You may find it helpful to use the stack_frame.docx file provided on conneX or a piece of scrap paper for designing and tracing the stack when answering the questions below.

  1. For this exercise you will write a modified version of the copy_string function in the main.asm file (from the first section of this lab). First, design the stack for it and then modify this function to take the program memory address and the data memory address via the stack instead of using a pre-defined memory location as it does now. This way you can re-use this code in the future to copy any string(s) from program memory to data memory.
  2. Extend the program from the previous exercise. First, design the stack for a new function called string_length, and then write the function. This function should take a parameter via the stack (in big-endian format) which contains the data memory address where a c-string resides. The function should count the number of characters in that string and return it by via the stack. Remember, the caller has to reserve the space on the stack for the return value.
  3. Use the Word file provided on conneX or a blank piece of paper and trace the stack for the recursive function in the triangle_number.asm file, which is located on conneX, from the beginning until the program finishes by reaching the end-loop (done: rjmp done). What does the program do?

Reviews

There are no reviews yet.

Only logged in customers who have purchased this product may leave a review.

Shopping Cart
[Solved] CSC 230 Lab 6-Safe Subroutines (functions)
$25