Chapter 14 - Subroutines

Sometimes different parts of your program will have rather similar jobs to do, & you will find yourself typing the same lines in twice or more; however this is not necessary. You can type the lines in once, in the form known as a subroutine, & then use, or call, them anywhere else in the program without having to type them in again.

    To do this, you use the statements GOSUB (GO to SUBroutine) & RETURN.

       GOSUB n

where n is the line number of the first line in the subroutine, is just like GOTO n except that the computer remembers the line number of the GOSUB statement so that it can come back again after doing the subroutine. It does this remembering by putting the line number (the return address) on top of a pie of them (the GOSUB stack).

       RETURN

takes the top line number off the GOSUB stack, & goes to the line after it.

    As a first example,

        10 PRINT "THIS IS THE MAIN PROGRAM",

        20 GOSUB 1000

        30 PRINT "AND AGAIN";

        40 GOSUB 1000

        50 PRINT "AND THAT IS ALL."

        60 STOP

        1000 REM SUBROUTINE STARTS HERE

        1010 PRINT "THIS IS THE SUBROUTINE,"

        1020 RETURN

    The STOP statement in line 60 is very important because otherwise the program will run on into the subroutine & cause error 7 when the RETURN statement is reached.

    For a less trivial example, suppose you want to write a computer program to handle pounds, shillings and pence. Those with long memories will remember that before 1971 a pound was divided into twenty shillings - so a shilling is 5p - & a shilling was subdivided into twelve old pence; d was the abbreviation for an old penny.) You will have three variables L, S & D (any maybe others - L1, S1, D1 & so on), and arithmetic is dead easy. First you do it separately on the pounds, shillings and pence - for instance, to add two sums of money, you add the pence, add the shillings and add the pounds; to double a sum of money you double the pence, double the shillings and double the pounds; and so on. When all that is done, adjust it to the correct form so that the pence are between 0 & 11, and the shillings between 0 & 19. This last stage is common to all the operations, so we can make it into a subroutine.

    Laying aside the notion of subroutines for a moment, it is worth your while trying to write the program yourself. Give the arbitrary numbers L, S & D, how do you convert them into proper pounds, shillings & pence? Part of the problem is that you will start thinking of odder & odder cases.

    What first springs to mind will probably be something like 1..25s..17d, which you want to convert to 2..6s..5d. Not so difficult. But suppose you have negative numbers? A dept of 1..25s..17d, or -1..-25s..-17d, might well turn out as -3..13s..7d, which is rather an odd way of expressing it (as though people only ever lend each other whole pounds). And what about fractions? If you divide 1..25s..17d by two, you get 5..12.5s..8.5d, & although this has the pence, 8.5, between 0 & 11; the shillings, 12.5, between 0 & 19, it is certainly not as good as 1..3s..2.5d. Try & work out your own answers to all this - & use them in a computer program - before you read any further.

Here is one solution.

        1000 REM SUBROUTINE TO ADJUST L.S.D. TO THE NORMAL FORM FOR POUNDS, SHILLINGS AND PENCE

        1010 LET D=240*L+12*S+D

        1020 REM NOW EVERYTHING IS IN PENCE

        1030 LET E=SGN D

        1040 LET D=ABS D

        1050 REM WE WORK WITH D POSITIVE, HOLDING ITS SIGN IN E

        1060 LET S=INT (D/12)

        1070 LET D=(D-12*S)*E

        1080 LET L=INT (S/20)*E

        1090 LET S=S*E-20*L

        1100 RETURN

    On its own, this is not much use because there is no program to set up L, S & d beforehand, nor to do anything with them afterwards. Type in the main program, & also another subroutine to print out L, S & D.

        10 INPUT L

        20 INPUT S

        30 INPUT D

        40 GOSUB 2000

        45 REM PRINT THE VALUES

        50 PRINT

        60 PRINT " = ";

        70 GOSUB 1000

        75 REM THE ADJUSTMENT

        80 GOSUB 2000

        85 REM PRINT THE VALUES

        90 PRINT

        100 GOTO 10

        2000 REM SUBROUTINE TO PRINT L,S AND D

        2010 PRINT "£";L;"..";S;"S..";D;"D";

        2020 RETURN

    (Recall from chapter 9 that the empty PRINT statement in line 50 prints a black line.)

    Clearly we have saved on program by using the printing subroutine at 2000, & this in itself is a very common use for subroutines: to shorten programs. However, the adjustment subroutine in fact makes the program longer - by a GOSUB & a RETURN; so program length is not the only consideration. Used with skill, subroutines can make programs easier to understand for the ones that matter, humans.

    The main program is simplified by its using more powerful statements: each GOSUB represents some complicated BASIC, but you can forget that - only the net result matters. Because of this, it is much easier to grasp the main structure of the program.

    The subroutines, on the other hand, are simplified for a very different reason, namely that they are shorter. They still use the same old plodding LET & PRINT statements, but they only have to do a part of the whole job & so are easier to write.

    The skill lies in choosing the level - or levels - at which to write the subroutines. They must be big enough to have a significant impact on the main program, yet small enough to be significantly easier to write than a complete program without subroutines. These examples (not recommended) illustrate this.

    First,

        10 GOSUB 1000

        20 GOTO 10

        1000 INPUT L

        1010 INPUT S

        1020 INPUT D

        1030 PRINT " ";L;"..";S;"S..";D;"D";TAB 8;"=";

        1040 LET D=240*L+12*S+D

           :    :

           :    :

        2000 RETURN

& second

        10 GOSUB 1010

        20 GOSUB 1020

        30 GOSUB 1030

        40 GOSUB 1040

        50 GOSUB 1050

           :    :

           :    :

        30 GOTO 10

        1010 INPUT L

        1015 RETURN

        1020 INPUT S

        1025 RETURN

        1030 INPUT D

        1035 RETURN

        1040 PRINT " ";L;"..";S;"S..";D;"D";TAB 8; "=";

        1045 RETURN

        1050 LET D=240*L+12*S+D

        1055 RETURN

           :    :

           :    :

    The first, with its single powerful subroutine, & the second, with its many trivial ones, demonstrate quite opposite extremes, but with equal futility.

    A subroutine can happily call another, or even itself (a subroutine that calls itself is recursive), so don't be afraid to having several layers.
 
 

Summary

    Statements: GOSUB, RETURN
 
 

Exercises

1. The example program is virtually a universal LSD calculator. How would you use it.

    (i) to convert pounds & new pence into pounds, shillings & pence?

    (ii) to convert guineas into pounds & shillings? (1 guinea = 1..1s)

    (iii) to find fractions of a pound? (e.g. a third of a pound, or a mark, is 6s..8d.

    Put in a line to round the pence off to the nearest farthing (1/4d).
 
 

2. Add two statements to the program:

        4 LET ADJUST=1000

        7 LET LSDPRINT=2000

& change

    GOSUB 1000 to GOSUB ADJUST

    GOSUB 2000 to GOSUB LSDPRINT

    This works exactly as you'd hope; in fact the line number in a GOSUB (or GOTO or RUN) statement can be any numerical expression. (Don't expect this to work on computers other than the ZX81, because it is not standard BASIC.)

    This sort of stuff can work wonders for the clarity of your programs.
 
 

3. Rewrite the main program in the example to do something quite different, but still using the same subroutines.
 
 

4.      ... GOSUB n

         ... RETURN

in consecutive lines can be replaced by

        ... GOTO n

    Why?
 
 

5. A subroutine can have several entry points. For instance, because of the way our main program uses them, with GOSUB 1000 followed immediately by GOSUB 2000, we can replace our two subroutines by one big one that adjusts L, S & D & then prints them. It has two entry points: one at the beginning for the whole subroutine, & another further on for the printing part only.

    Make the necessary rearrangements.
 
 

6. Run the program:

        10 GOSUB 20

        20 GOSUB 10

    The return addresses are pushed on to the GOSUB stack in droves, but they never get taken off again & eventually there is no room for any more in the computer. The program then stops with error 4 (see appendix B).

    You might have difficulty in clearing them out again without losing everything, but this will work.

    (i) Delete the two GOSUB statements.

    (ii) Insert two new lines

        11 RETURN

        21 RETURN

    (iii) Type

       RETURN

    The return addresses will be stripped off until you get error 7.

    (iv) Change your program so you don't get the same thing happening again.

How does this work?


Previous: Chapter 13    Next: Chapter 15