GoSub
Syntax
GoSub Label
or
GoSub Label [Variable, {Variable, Variable... etc}], Receipt Variable
Overview
GoSub jumps the program to a defined label and continues execution from there. Once the program hits a Return command the program returns to the instruction following the GoSub that called it and continues execution from that point.
If using an 18F device, parameters can be pushed onto a software stack before the call is made, and a variable can be popped from the stack before continuing execution of the next commands. Only the 18F devices have this mechanism, because they contain an FSR2 register that is used as a stack pointer. The other 16F devices do not contain this SFR.
Parameters
Label is a user-defined label placed at the beginning of a line which must have a colon ':' directly after it. Variable is a user defined variable of type Bit, Byte, Long, Word, Dword, Float, String, Array or Constant value, that will be pushed onto the stack before the call to a subroutine is performed. Receipt Variable is a user defined variable of type Bit, Byte, Word, Long, Dword, Float, String or Array that will hold a value popped from the stack after the subroutine has returned.
Example 1
' Implement a standard subroutine call
GoTo Start
' Jump over the subroutines
SubA: { subroutine A code
……
……
}
Return
SubB: { subroutine B code
……
……
}
Return
' Actual start of the main program
Start:
GoSub SubA
GoSub SubB
Stop
Example 2
' Call a subroutine with parameters
Device = 18F25K20
' Stack only suitable for 18F devices
Declare Xtal = 16
' Tell the compiler the device is operating at 16MHz
Declare Stack_Size = 20
' Create a small stack capable of holding 20 bytes
Dim Wrd1 as Word
' Create a Word variable
Dim Wrd2 as Word
' Create another Word variable
Dim Receipt as Word
' Create a variable to hold result
Wrd1 = 1234
' Load the Word variable with a value
Wrd2 = 567
' Load the other Word variable with a value
' Call the subroutine and return a value
GoSub AddThem [Wrd1, Wrd2], Receipt
Print Dec Receipt
' Display the result as decimal
Stop
' Subroutine starts here. Add two parameters passed and return the result
AddThem:
Dim AddWrd1 as Word
' Create two uniquely named variables
Dim AddWrd2 as Word
Pop AddWrd2
' Pop the last variable pushed
Pop AddWrd1
' Pop the first variable pushed
AddWrd1 = AddWrd1 + AddWrd2
' Add the values together
Return AddWrd1
' Return the result of the addition
In reality, what's happening with the GoSub in the above program is simple, if we break it into its constituent events: -
Push Wrd1
Push Wrd2
GoSub AddThem
Pop Receipt
Notes
Now that the Positron8 compiler has true procedures, the parameters used with GoSub are legacy and should not be used in new programs.
Only one parameter can be returned from the subroutine, any others will be ignored.
If a parameter is to be returned from a subroutine but no parameters passed to the subroutine, simply issue a pair of empty square braces: -
GoSub Label [ ], Receipt
The same rules apply for the parameters as they do for Push, which is after all, what is happening.
Positron8 allows any amount of GoSubs in a program, but the 14-bit PICmicro™ architecture only has an 8-level return address stack, which only allows 8 Gosubs to be nested. The compiler only ever uses a maximum of 4-levels for its library subroutines, therefore do not use more than 4 GoSubs within subroutines. The 18F devices however, have a 28-level return address stack which allows any combination of up to 28 GoSubs to occur.
A subroutine must always end with a Return command.
What is a Stack?
All microprocessors and most microcontrollers have access to a Stack, which is an area of RAM allocated for temporary data storage. But this is sadly lacking on a PICmicro™ device. However, the 18F devices have an architecture and low-level mnemonics that allow a Stack to be created and used very efficiently.
A stack is first created in high memory by issuing the Stack_Size Declare.
Declare Stack_Size = 40
The above line of code will reserve 40 bytes at the top of RAM. This means that it is a safe place for temporary variable storage.
Taking the above line of code as an example, we can examine what happens when a variable is pushed on to the 40 byte stack, and then popped off again.
First the RAM is allocated as a byte array above all Dimmed variables so it does not interfere with anything in the BASIC program. For this explanation we will assume that a 18F452 PICmicro™ device is being used. Reserving a stack of 40 bytes may create a byte array at address 1495.
Pushing.
When a Word variable is pushed onto the stack, the memory map would look like the diagram below: -
Top of Memory |..............Empty RAM.............. | Address 1535
~ ~
~ ~
|..............Empty RAM.............. | Address 1502
|..............Empty RAM.............. | Address 1501
| Low Byte address of Word variable | Address 1496
Start of Stack | High Byte address of Word variable | Address 1495
The high byte of the variable is first pushed on to the stack, then the low byte. And as you can see, the stack grows in an upward direction whenever a Push is implemented, which means it shrinks back down whenever a Pop is implemented.
If we were to Push a Dword variable on to the stack as well as the Word variable, the stack memory would look like: -
Top of Memory |................Empty RAM.............| Address 1535
~ ~
~ ~
|................Empty RAM.............| Address 1502
|................Empty RAM.............| Address 1501
| Low Byte address of Dword variable | Address 1500
| Mid1 Byte address of Dword variable | Address 1499
| Mid2 Byte address of Dword variable | Address 1498
| High Byte address of Dword variable | Address 1497
| Low Byte address of Word variable | Address 1496
Start of Stack | High Byte address of Word variable | Address 1495
Popping.
When using the Pop command, the same variable type that was pushed last must be popped first, or the stack will become out of phase and any variables that are subsequently popped will contain invalid data. For example, using the above analogy, we need to Pop a Dword variable first. The Dword variable will be popped Low Byte first, then MID1 Byte, then MID2 Byte, then lastly the High Byte. This will ensure that the same value pushed will be reconstructed correctly when placed into its recipient variable. After the Pop, the stack memory map will look like: -
Top of Memory |..............Empty RAM.............. | Address 1535
~ ~
~ ~
|..............Empty RAM.............. | Address 1502
|..............Empty RAM.............. | Address 1501
| Low Byte address of Word variable | Address 1496
Start of Stack | High Byte address of Word variable | Address 1495
If a Word variable was then popped, the stack will be empty, however, what if we popped a Byte variable instead? the stack would contain the remnants of the Word variable previously pushed. Now what if we popped a Dword variable instead of the required Word variable? the stack would underflow by two bytes and corrupt any variables using those address's . The compiler cannot warn you of this occurring, so it is up to you, the programmer, to ensure that proper stack management is carried out. The same is true if the stack overflows. i.e. goes beyond the top of RAM. The compiler cannot give a warning.
Technical Details of Stack implementation.
The stack implemented by the compiler is known as an Incrementing Last-In First-Out Stack. Incrementing because it grows upwards in memory. Last-In First-Out because the last variable pushed, will be the first variable popped.
The stack is not circular in operation, so that a stack overflow will rollover into the PICmicro's hardware register, and an underflow will simply overwrite RAM immediately below the Start of Stack memory. If a circular operating stack is required, it will need to be coded in the main BA- SIC program, by examination and manipulation of the stack pointer (see below).
Indirect register pair FSR2L and FSR2H are used as a 16-bit stack pointer, and are incremented for every Byte pushed, and decremented for every Byte popped. Therefore checking the FSR2 registers in the BASIC program will give an indication of the stack's condition if required. This also means that the BASIC program cannot use the FSR2 register pair as part of its code, unless for manipulating the stack. Note that none of the compiler's commands, other than Push and Pop, use FSR2.
Whenever a variable is popped from the stack, the stack's memory is not actually cleared, only the stack pointer is moved. Therefore, the above diagrams are not quite true when they show empty RAM, but unless you have use of the remnants of the variable, it should be considered as empty, and will be overwritten by the next Push command.