Wednesday, February 28, 2018 Topics for today C functions and Pep/9 subroutines Introduction Location of subprograms in a program Translating functions (a) Void functions (b) Void functions with parameters C Functions and Pep/9 Subroutines (Section 63) The Structured Programming Theorem tells us that "if" and "while" are sufficient to implement any algorithm but for programs of any size, we need a subprogram/function/subroutine mechanism: (1) so that we don't have a huge "main" program (2) so we have logical pieces to write and test independently (and also reuse in other programs) Section 63 of the text covers the basics of subprograms In places it has too few examples so we will add our own We can almost implement a subprogram at the assembly code level using branch instructions br subx br subx ; invoke the subroutine ; then urn here ; invoke the subroutine ; then urn here subx: ; subroutine code is here ; problem is here how do we know which location to urn to? Comp 162 Notes Page 1 of 14 February 28, 2018
However, as the above illustrates, there is the problem of "remembering" what address to branch back to at the end of the subprogram For a subroutine mechanism to work, we need a place to store that urn address Some options: (a) (b) (c) a single fixed address used by all subroutines, eg Memory[61000] Problem: this would not allow nested calls the inner call would overwrite the urn address of the outer one an address within the subroutine itself This is feasible and some systems have used this approach but having one address per subroutine would prohibit recursion the next free space in some global area this would enable nesting including recursion Option (c) is what is usually used The same stack we use for local variables is where: the calling environment pushes the urn address the called environment (subroutine) pops the urn address off when it is time to urn control back to the calling environment Pep/9 provides an instruction to transfer control to a subroutine CALL subroutinename Pep/9 provides an instructions to urn from a subroutine RET Here is how our example above could be written subx: call subx call subx ; invoke the subroutine ; then urn here ; invoke the subroutine ; then urn here ; subroutine code is here ; urn to appropriate location Comp 162 Notes Page 2 of 14 February 28, 2018
Here are details of what the call and instructions do CALL X (a) decrease SP by 2 Decrease because the stack grows towards location 0 decreasing thus makes the stack bigger By 2 because the urn address is a 2-byte number (b) (c) RET (a) (b) copy the PC to mem[sp] and mem[sp+1] ie, the top 2 stack bytes At this point in the fetch-decode-execute cycle the PC is already pointing to the instruction following the CALL, ie, is pointing to the one we want to execute next after the subroutine put address of subroutine X into the PC so the next instruction fetched is the first one in the subroutine put contents of top 2 stack bytes into PC This is the address saved earlier Because of the fetch-decode-execute cycle, the instruction at this address will be the next one fetched after RET add 2 to SP (no need for the urn address any more) Consider the following program (instruction addresses are arbitrary but realistic) Hex Address 100 Main: deco N,d 103 call X 106 deco M,d 109 stop 150 X: ldwa A,d 153 call Y 156 stwa B,d 159 180 Y: ldwa M,d 183 adda N,d 186 stwa M,d 189 Here is a trace of the program showing the PC and stack PC 100 103 150 153 180 183 186 189 156 159 106 109 01 56 01 56 01 56 01 56 Stack 01 01 01 01 01 01 01 01 06 06 06 06 06 06 06 06 Comp 162 Notes Page 3 of 14 February 28, 2018
This is the basis of the subroutine mechanism There are variations depending on parameters passed, results urned, local variables and so on Section 63 of the book is short on examples so we will have additional ones Probably a good idea not to read too far ahead at this point Location of subprograms in program We saw earlier that we have to be careful where we put data variables in a program Similarly, subprograms can go anywhere as long as you make sure that it is not possible to "fall into" a subprogram, that the only way to enter one is by means of CALL Otherwise, when it comes time to do the RET, there is no appropriate address on the stack to urn to The following two "maps" show the most usual options for locating subroutines: Option 1 Option 2 main: br main sub1: stop sub1: sub2: main: sub2: stop end end The stack will be used for local variables, urn addresses, parameters and for urn values Comp 162 Notes Page 4 of 14 February 28, 2018
Given that "main" in a C program is a function, the middle column in the following is probably the closest translation though Warford doesn t do it this way C #include <globals> <function one> <function two> main() { } Pep/9 Should be call main stop one: two: main: end <globals> Pep/9 Warford one: two: main: stop end br main <globals> Translating functions There are different types of high-level language functions that we have to be able to translate into Pep/9: functions with and without parameters functions with and without local variables functions that do and do not urn a value functions that are or are not recursive We will start with the simplest ones and gradually work towards the more complex As noted earlier, the book is a little short on examples In what follows the "calling environment" of a function is the place from which it is called, ie, where the CALL instruction is It might be (a) the main program (b) another (non-main) function (c) function itself (ie, the function might be recursive) The called environment is the function being called Comp 162 Notes Page 5 of 14 February 28, 2018
(a) Translating void functions In C terminology, a "void" function is one that does not urn a value An example is void printhello() { printf( hello ); } The following is a general template for translating void functions that will be useful in writing code (derived from Warford s discussion on page 263) It shows which actions belong in the calling environment and which in the called environment Not every subroutine and call will need all 8 steps Calling environment (1) allocate stack space for actual parameters (2) push actual parameters on stack (3) do CALL (8) deallocate space for parameters Called environment (subroutine) (4) allocate space for local variables (5) do action of the subroutine (6) deallocate space for local variables (7) do RET The instructions needed to perform step (2) will be slightly different depending on whether the actual parameter is a global or a local You will not go too far wrong in translating functions and calls if you use this checklist and keep an accurate map of the stack contents For now, we will just look at parameters passed by value Later, we will see how parameters are passed by reference Comp 162 Notes Page 6 of 14 February 28, 2018
We will have are eight examples of high-level language function (in a C-like language) with a typical call and the translation of the call and function into Pep/9 assembly language The book has few simple examples before a very complex one (Fig 625 a doubly-recursive non-void function) Here is a guide to our 8 examples Example Locals? Parameters? Returns a value? Recursive? Similar book examples 1 N N N N Fig 618 2 Y N N N 3 N Y N N 4 Y Y N N Fig 621, Fig 623 5 N Y Y N 6 Y Y Y N 7 N Y N Y 8 N Y Y Y Fig 625 Example 1 - no local variables, no parameters Stack just used to hold the urn address (RA) C Pep/9 Maximum Stack Function void ex1() { output("hi"); } ex1: ldba 'H',i stba charout,d ldba 'i',i stba charout,d RA Call ex1(); call ex1 Comp 162 Notes Page 7 of 14 February 28, 2018
Example 2 - function has local variables but no parameters The subroutine allocates space for locals on entry That space needs to be deallocated before the RET instruction picks up the urn address If there are 4 bytes of local space as in the example we can use addsp 4,i C Pep/9 Maximum Stack Function void ex2() { int A,B; read(a); read(b); B = A+B; output(b); } ex2: subsp 4,i ; for locals deci 0,s ; read values into locals deci 2,s ldwa 0,s ; A adda 2,s ; add B stwa 2,s ; store sum in B deco 2,s ; and output it addsp 4,I ; deallocate locals ; urn A B RA Call ex2(); call ex2 Comp 162 Notes Page 8 of 14 February 28, 2018
Example 3 function has parameters but no local variables Our template shows that the calling environment is responsible for putting the parameters on the stack and removing them afterwards Function C void min (int A, int B) { if (A<B) output(a); else output(b); } Pep/9 min: ldwa 2,s ; A cpwa 4,s ; B brlt outa deco 4,s ; output B outa: deco 2,s ; output A Call /* assume P and Q are globals */ min(p,q); subsp 4,i ; for parameters ldwa P,d stwa 0,s ldwa Q,d stwa 2,s call min addsp 4,i ; done with params Maximum Stack RA P Q Comp 162 Notes Page 9 of 14 February 28, 2018
Parameters: local or global? Warford points out that actual parameters might be global variables, as in our Example 3, or they might be locals in the current environment Consider the two calls in the following What are the differences between their translations? int P,Q; // global int main() { int X,Y,Z; // local to main } min (P,Q); // Parameters are global variables min (X,Y); // Parameters are local variables The translation of the function min itself is not affected The translations of the calls of the function are different in the two cases because in the call min(x,y) the actual parameters are themselves on the stack If we picture the stack before the call as Z Y X then to implement the call we make space for the parameters subsp 4,i Z Y X then copy appropriate values from elsewhere in the stack Comp 162 Notes Page 10 of 14 February 28, 2018
ldwa 8,s stwa 2,s ldwa 6,s stwa 0,s ; get value of X - local variable, on the stack ; put a copy as a parameter of the call ; get value of Y = local on the stack ; put a copy as the other parameter Copy of Y Copy of X Z Y X then call the function call min Comp 162 Notes Page 11 of 14 February 28, 2018
Example 4 - a void function having both parameters and local variables Function C void ex4 (int low, int high) { int N; read(n); if (N<low)output ( * ); if (N>high) output('*'); } Pep/9 ex4: subsp 2,i ; for local deci 0,s ; read N ldwa 0,s cpwa 4,s ; compare local (N) with low brlt outstar ; branch if N<low cpwa 6,s ; compare with high brle exit ; branch if N <=high outstar:ldba '*',i stba charout,d exit: addsp 2,i ; deallocate local space Call /* limit is global */ ex4(50,limit) subsp 4,i ; parameter space lda 50,i sta 0,s lda limit,d sta 2,s call ex4 addsp 4,i ; finished with parameters Maximum Stack N RA 50 limit Reading We are up to about page 320 We will look next at functions that urn values Comp 162 Notes Page 12 of 14 February 28, 2018
Review Questions 1 Is there a limit to the number of times a recursive subroutine can call itself? If so, why? 2 Do bad things happen if we forget the RET instruction as the end of a subroutine or will the program behave as expected? 3 Can the calling environment be responsible for allocating and deallocating local variables? Pros and cons? 4 Can we have indirection recursion, for example A calls B and B calls A? 5 Why is it a problem if we forget to deallocate local variables at the end of a subroutine? 6 Does the calling environment need to be concerned with the mapping of local variables onto stack locations? 7 Does the calling environment need to be concerned about the order in which parameters are placed on the stack? Comp 162 Notes Page 13 of 14 February 28, 2018
Review Answers 1 Yes Each new call will add at least 2 bytes to the stack (the urn address) so eventually the stack will run into the program code 2 It will probably not behave as expected Look at our two options for placing subroutines in a program If we omit the RET from the end of sub1 it will just execute sub2 and, when it encounters the RET at the end of sub2, control will urn to the instruction following the call to sub1 3 Allocation and deallocation could be done in the calling environment but there would be a lot of code duplication and the subroutine would not be as self-contained 4 Yes, but we have to have a non-recursive exit option 5 RET will use the top 2 bytes of the local variable space as the urn address 6 No The calling environment does not even need to know whether there are local variables 7 Yes It is critical that the calling environment know where the subroutine expects particular parameters to be placed For example, if subroutine PRBLOCK (M,N) prints a block of stars with M rows and N columns, the calling environment needs to know which of M and N is placed on the stack first Comp 162 Notes Page 14 of 14 February 28, 2018