QBasic/Subroutines and Functions

Purpose
Subroutines and functions are ways to break up your code into reusable 'lumps'. They allow the programmer reuse a large set of common instructions just by calling the appropriate procedure or function.

For example, lets say you need to PRINT multiple Tables of values. One way to do this is to just enter all the Table PRINT commands directly into where you need them. However this not only makes the program very large but also makes it harder to debug or change the 'style' of the table. A simpler way is to create a single 'Print Table' procedure and enter all of the PRINT commands there. Then, each time you need to print a Table, you would simply 'call' the 'Print Table' procedure with a list of the values to be printed.

Procedure vs. Function
A procedure does something and does not return anything for the programmer. For example, a procedure might be used to set the screen mode and palette.

A function does something and RETURNS a value. For example, if you need to find the average of two values, you might write a function that takes in two numbers and returns the average.

GOTO and GOSUB
The GOTO and GOSUB statements were the original methods by which subroutines were created. They were most common on older basic implementations and are kept around for compatibility reasons; however, their use is not recommended in other programming languages or in large scale projects, both because GOTO's make it harder to 'follow' the program flow and because GOSUB's do not 'isolate' the changes made to any variables.

These two commands depend on Labels, which come in one of two forms. The first and older form involves writing line numbers at the beginning of each line (usually in increments of 10). The newer method looks similar to other programming languages, which is a single word followed by a colon.

The GOTO statement is simple; it just moves the execution point to a given Label:

The GOSUB statement transfers control to a given Label, however when a RETURN statement is encountered, execution returns to the line following the GOSUB statement. Any changes made within the GOSUB will be to actual variables used within the 'main' code.

ON ERROR
The ON ERROR allows you to define an error handler for your program; when an error occurs, it immediately jumps to the given label. The control returns once the program reaches a RESUME statement, which can either return control to the same point, the next statement, or any other desired label.

Within Qbasic, the error handler cannot be located within any subroutines. As such, any error checking or flags will have to be handled through the use of variables that are shared with the main module.

NOTE: If your error handling routine does not have a "resume" statement in it (IOW you try to do it all with gotos) error handling will only work once - the next "on error" will be ignored and the program ends as if you had no "on error" statement at all. This problem does not seem to be mentioned in any of the documentation. It took me three hours to figure out why two nearly identical program portions acted so differently.

Declaring a subprocedure
A superior method of declaring a subprocedure is using the SUB statement block, because (by default) any new variables used within the subprocedure are discarded on exit.

Under the QBasic IDE, doing so moves the SUB block to its own window to prevent accidental deletion of the module, and allows the easier organization of the program code.

Calling a subprocedure is as simple as writing the name of the subprocedure (passing any required parameters). If you want, you can use the CALL statement to indicate to other programmers that it is a subprocedure.

Whilst the Parameters passed into subprocedures are passed by 'reference' (i.e. they take on a new name within the SUB), any changes that are made to the values are 'reflected back' into the originals. By default, all other variables used within the SUB are discarded when the END SUB is reached (or an EXIT SUB is executed), except as below :-

To 'preserve' the values of variables used within the SUB for re-use on the next CALL, use the STATIC keyword at the end.

If you need access to a variable (that has not been passed as a parameter), use the SHARED keyword to define each at the start of the subprocedure (a SHARED variable retains its name).

Declaring a function
A function is a form of subroutine that returns a value. Everything that applies in defining a subroutine also applies to a function. Within the function, the return value is created by using the function name as a variable - the return value is then passed to the calling expression when a valid exit or end call is reached. There are two ways to return from a function, one way is to reach the END FUNCTION statement, the other is to make a call to EXIT FUNCTION. The difference between END FUNCTION and EXIT FUNCTION is that there can only be a single END FUNCTION and it must appear after all other code for the function as it denotes the end of a code block. EXIT FUNCTION can occur multiple times and can be placed anywhere deemed appropriate.

Functions are declared in the same way as variables - it returns the variable type it's defined to return, in the same way variables are defined to contain their specified type. By default, it is a number, but appending a dollar sign indicates that it is returning a string.

Functions can only be called within an expression; unlike subroutines, they are not a standalone statement.