GOSUB ... RETURN | Examples |
GOSUB Label ... RETURN { Value }
Function
Store the address of the next instruction after GOSUB, then go to the point
in the program specified by Label; with the intention of returning to the
stored address.
Quick Facts
SX18, SX20, SX28, SX48, SX52 | |
Maximum Nested GOSUBs | 8 |
Explanation
GOSUB is a close relative of GOTO, in fact, its name means, "GO to
a SUBroutine". When a program reaches a GOSUB, the program executes the
code beginning at the specified Label. Unlike GOTO, GOSUB also
stores the address of the instruction immediately following itself. When the program
encounters the RETURN instruction, it interprets it to mean, "go to the
instruction that follows the most recent GOSUB." In other words, a
GOSUB makes the program do a similar operation as you do when you see a
table or figure reference in this manual; 1) you remember where you are, 2) you go
to the table or figure and read the information there, and 3) when you've reached
the end of it, you "return" to the place you were reading originally.
GOSUB is mainly used to execute the same piece of code from multiple locations. If you have, for example, a block of three lines of code that need to be run from 10 different locations in your entire program you could simple copy and paste those three lines to each of those 10 locations. This would amount to a total of 30 lines of repetitive code (and extra space wasted in the program memory). A better solution is to place those three lines in a separate routine, complete with it's own label and followed by a RETURN instruction, then just use a GOSUB instruction at each of the 10 locations to access it. Since SX/B compiles instructions inline (no optimization) this technique can save a lot of program space.
SX/B simplifies subroutine use and error trapping with the declararing of subroutines (SUB directive) and required/possible parameters. When a subroutine is declared, the GOSUB keyword is no longer required and any parameters passed with be checked against the user declaration. The following examples demonstrate the differences in code style.
Version 1.1 (This style is still valid but not recommended)
Get_Char: SERIN Sio, Baud, char ' wait for character RETURN ' ------------------------------ Start: TRIS_B = %00000000 ' make RB pins outputs Main: GOSUB Get_Char IF char <> "!" THEN Main ' wait for "!"
Version 1.2+ (applies to SUB and FUNC)
GET_CHAR SUB ' subroutine (no parameters) ' ------------------------------ Start: TRIS_B = %00000000 ' make RB pins outputs Main: char = GET_CHAR ' no "GOSUB" required IF char <> "!" THEN Main ' wait for "!" END ' ------------------------------ GET_CHAR: SERIN Sio, Baud, temp1 ' wait for character RETURN temp1 ' return character to caller
Declared subroutines simplify SX/B programming by removing the necessity of the GOSUB keyword (which, in effect, allows the programmer to extend the language by creating new commands), it allows the compiler to validate the number of parameters being passed, and -- most valuable to the programmer -- it allows subroutine code to be placed anywhere in memory without concern of code page boundaries (now handled automatically).
Passing Parameters To/From a Subroutine
SX/B allows the programmer to pass up to four parameters to
subroutines. The parameter may hold a value (bit or byte) or the
address of a byte-variable (when prefaced with '@'). When used in
subroutines, passed parameters must be saved before any SX/B instructions
are called.
For example:
TX_BYTE SUB ' subroutine with no parameters SEND_CHAR SUB 2 ' subroutine with 2 parameters ' ------------------------------ Main: ' less convenient theChar = "*" ' byte to send idx = 10 ' times to send TXBYTE theChar = 13 idx = 1 TXBYTE ' much more convenient SENDCHAR "*", 10 ' ********** SENDCHAR 13, 1 ' <CR> PAUSE 1000 GOTO Main ' ------------------------------ SEND_CHAR: theChar = __PARAM1 ' save character to send idx = __PARAM2 ' times to send character TX_BYTE: DO WHILE idx > 0 SEROUT Sio, Baud, theChar ' send the character DEC idx ' update count, exit if 0 LOOP RETURN
This subroutine (SEND_CHAR) expects two parameters: the character to transmit (using SEROUT), and the number of times to send the character.
A subroutine can be constructed to modify any variable that is passed to it (by address using '@'). For example:
INVERT_BITS SUB 1 ' subroutine with 1 parameter ' ------------------------------ Start: TRIS_B = %00000000 ' make RB pins outputs Main: myBits = $A5 ' myBits = %10100101 INVERT_BITS @myBits ' pass address of variable RB = myBits ' RB = %01011010 ($5A) END ' ------------------------------ INVERT_BITS: regAddr = __PARAM1 ' save address GET rtnAddr, regVal ' get value from address regVal = ~regVal ' invert bits PUT rtnAddr, regVal ' update passed variable RETURN
An easier method, however, is to allow the subroutine to pass a value directly back to the caller. This update to the program above performs the same function, yet is easier to understand and prevents possible errors resulting is missing '@' headers.
INVERT_BITS SUB 1 ' subroutine with 1 parameter ' ------------------------------ Start: TRIS_B = %00000000 ' make RB pins outputs Main: myBits = $A5 ' myBits = %10100101 myBits = INVERT_BITS myBits ' pass value, get one back RB = myBits ' RB = %01011010 ($5A) END ' ------------------------------ INVERT_BITS: regVal = __PARAM1 ' get value from caller regVal = ~regVal ' invert bits RETURN regVal ' return value to caller
Notice that this style eliminates the need for a variable that holds the address of the target variable and simplifies the subroutine code. By using a defined function (with FUNC) the subroutine can return a word value.
Passing Strings
SX/B allows the programmer to pass a literal or stored (with DATA) string to
a subroutine. String passing requires at least two parameters to handle the
base and offset address bytes to the string (these values are used by READ).
See READ for an example of string use in SX/B.
related instruction: GOTO