}
}
8.5 Stack
A stack is a section of memory serving for temporaty storage of data (e.g. while calculating complex expressions). Its most important task is keeping the states of significant registers during jumps to subprograms, interrupts, traps, etc. During a jump to a subprogram in this part of the memory are kept the parameter values (if any) at the time of calling a function, value of the PC register (the place reached during the execution of a program), and the frame register W14. The values of the PC and W14 registers are copied to the stack automatically,
increasing the value of the W15 register by 6 (three times by 2). The compiler takes care to copy the parameters to the stack by adding a part of the code required for copying the parameters to the top-of-stack on each user call of a function.
A stack is a section of memory which is usually accessed sequentially. The access is possible, of course, to any memory location, even to the locations constituting the stack, but such concept is very seldom used. An increase or a decrease of the stack, however, can be done only by a sequential access. When a datum is copied to the stack, it is pushed to the top-of-stack. Only the value from the top-of-stack can be read by the W15 register. Thus a stack is a LIFO (Last In First Out) buffer. In order to know at each moment which address is read or which address is written, one of the registers is reserved as the stack pointer register. This is the W15 register. Therefore, when a data is copied to the stack, it is written to the location pointed by the W15 register and then the W15 register is increased by 2 to point at the next free location. When a data is read from the stack, the value of the W15 register is at first decreased by 2 and then the value from the top-of-stack is read.
How does this work in practice? The following example gives a program consisting of the main program and one void type function.
Example of a call of void type function:
int m;
void MyProc1(int a){
int i;
i = a+2;
}
void main(){
TRISB = 0;
m = 3;
asm nop;
MyProc1(m);
m = 2;
}
The main program begins by executing the instruction TRISB = 0;. After that, variable m is allocated the value 3. Then, the function is called. What happens at that moment? The compiler has generated a code for copying to the stack the variable m (Fig. 8-4a) and then jumps to the memory address where the subprogram is located.
Fig. 8-4a Stack before entering subprogram
The hardware automatically copies to the stack the program register PC (Program Counter) in order to determine the location from which the execution of the program continues after the subprogram is done, Fig. 8-4b. Since the width of the PC register is 24-bit, two memory locations are required for copying the PC register. The lower 16 bits (PCL) are copied first and then the higher (PCH) 8 bits (extended to 16 bits).
Fig. 8-4b Stack after jump to subprogram (W15=0x806, W14=xxxx)
After the PC register is saved, the hardware also copies and saves the W14 register (frame register) and then writes into it the current value of the W15 register, Fig. 8-4c. This saves the information where the last location used by the main progam is. Whatever the subprogram would do with the section of the stack after this address will have no influence on the
execution of the main program. Therefore, the W14 register is the boundary between the local variables of the subprogram and the parameters pushed in the stack from the main program. In addition, this allows to find the address where the parameter value valid at the time of calling the subprogram is. This is done simply by subtracting from W14 the number of locations occupied by the PC and W14 registers at the moment of jumping to the subprogram.
Fig. 8-4c Stack after entry to subprogram (W15=0x808, W14=0x808)
Upon jumping to the subprogram (void type function in this example), the compiler has the task of providing the locations required by the local variables. In this example the local variable is i of the type int. Fig. 8-4d shows the memory location 0x808 reserved for the local variable i. Value of the W15 register is increased to account for the reservation made for the local variables, but the value of the W14 register remains the same.
Fig. 8-4d Stack after entry to subprogram (W15=0x80A, W14=0x808)
How many locations have been occupied? The answer is three. The W14 register is 16-bit wide and occupies only one location, whereas the PC register is 24-bit wide thus it occupies two locations. The 4 bits unused for saving the PC register are used for saving the current priority level. The parameter address is claculated as W14-8. The register W14 points to the memory location next to those where the parameter, PC register, and W14 register are saved.
From this value one should subtract 2 because of the W14 register, 4 because of the PC register which occupies two memory locations, and 2 because of the parameter. For this reason from the value saved in the frame register W14 one should subtract 8. The compiler has generated additional code which reserves the place at the top-of-stack occupied by the local variable i. This has not influenced the value of the register W14 but did influence the value of the register W15 because it always has to point to the top-of-stack.
After entering the function, the register W15 points at the location 0x80A, the register W14 to location 0x808, and the parameter is at W14-8=0x800. After the function is comlpeted, it is not required to check how many data has been put on the stack and how many has been taken off the stack. The register W14 points to the location next to the locations where the important registers have been saved. The value of the register W14 is written into the W15 register, previous value of the register W14 is taken off the stack and immediatley after, so is the value of the PC register. This is done automatically by the hardware. The compiler has added a section of the code to the main program which then calls the functions on the stack and reads all parameters saved in it in order to recover the value of the W15 register and return the position of the stack to the previous state.
The following example gives a program consisting of one function and a program using this function. The difference between a function and a void type function lies in the result returned to the main program by the function.