JES: Just Educational Services

Jonathan E. Sisk's
Pick/BASIC: A Programmer's Guide

Chapter 17 - External Subroutines

Click to enlarge

Another type of subroutine is available in the Pick/BASIC language. This is called an external subroutine. A subroutine is a program that contains the statements to perform an operation. In Chapter 13, you examined local subroutines; a local subroutine is found in the same item as the program that uses it. An external subroutine, on the other hand is a separate item which contains program statements. Consequently, an external subroutine may be "shared" by multiple programs. This principle assists in making programs more modular.

Enter the programs in Fig. 17-1 and Fig. 17-2. Then compile and catalog them both.

ABOUT PROGRAM EXAMPLE 15

For Program Example 15, think of EX.015 as the master program. It "calls" the external subroutine STRIP.CONTROL. Examine the logic of EX.015, as illustrated in Fig. 17-1.

Line 6 starts a loop. On line 7, the screen is cleared and the cursor is positioned to column position 3 on row 3. The operator is prompted to enter a string that contains control characters. Note: When testing this program, be careful with the control characters you use; some of them do strange things to keyboards and terminals. A <Control-G> (the "bell' ') usually is a safe choice. Also, it is normal to not see a control character on the screen when it is entered. Enter several non-control characters along with the control characters, so that you will be better able to see the effect of the routine.

EX.015
001 * EX.015
002 * External subroutines
003 * mm/dd/yy: date last modified
004 * JES: author's initials
005 *
006 LOOP
007 PRINT @(-1): @(3,3):
008 PRINT "ENTER A STRING THAT CONTAINS CONTROL CHARACTERS"
009 INPUT STRING
010 UNTIL STRING = "" OR STRING = "QUIT" DO
011 CALL STRIP.CONTROL(STRING)
012 REPEAT
013 PRINT "EX.015 TERMINATED"
014 END

Fig. 17-1. Program Example 15.

STRIP.CONTROL
001 SUBROUTINE STRIP.CONTROL(STRING)
002 * STRIPS CONTROL CHARACTERS FROM STRING
003 * mm/dd/yy: date last modified
004 * JES: author's initials
005 *
006 STRING = OCONV(STRING,"MCP")
007 PRINT "BEFORE STRIPPING, HERE'S HOW THE STRING LOOKS :"
008 PRINT STRING
009 PRINT
010 *
011 10 * LOOP TO STRIP CHARACTERS OUT
012 *
013 NUMBER.OF.DOTS = COUNT(STRING,".") ;* HOW MANY ARE THERE?
014 *
015 FOR I = 1 TO NUMBER.OF.DOTS
016 FOUND = INDEX (STRING,".",I)
017 IF FOUND THEN
018 STRING = STRING[1,FOUND-1]: STRING[FOUND+l,33000]
019 PRINT STRING
020 END ELSE
021 PRINT "AFTER STRIPPING, HERE'S HOW IT LOOKS :"
022 PRINT STRING
023 END
024 NEXT I
025 RETURN

Fig. 17-2. The STRIP.CONTROL external subroutine.

The string containing the control characters is stored in the variable STRING on line 9. Line 10 checks to see if a null or the word "QUIT" was entered, in which case the program falls out of the loop, displays the message "EX .0 15 TERMINATED," and then stops.

If a non-null string is received, however, line 11 is executed. This "calls" the external subroutine:

011 CALL STRIP.CONTROL(STRING)

The CALL statement is used to locate and activate an external subroutine. It has the ability to "pass" arguments and/or expressions into the external subroutine, which may then act upon them, change them, and return them to the "master" program upon termination of the external subroutine.

The CALL statement has the general format:

CALL program.name

or

CALL program.name(argument{,argument...})

Any optional arguments passed from the master program must be captured by the external subroutine. The names of the variables do not necessarily have to be the same, but they must be passed in the same order that they are received. Any number of arguments may be passed, and each must be delimited by a comma. Note that the list of arguments must be enclosed in parentheses.

In EX.015, only one argument was passed into the STRIP.CONTROL external subroutine. This was the variable STRING, which is the string that contains the control characters.

CREATING AN EXTERNAL SUBROUTINE

Every external subroutine must have the SUBROUTINE statement on the first line of the program. The SUBROUTINE statement has the general format:

SUBROUTINE {program.name}

or

SUBROUTINE {program.name} (argument{,argument...})

In line 1 of the subroutine, STRIP.CONTROL, the following statement appears:

001 SUBROUTINE STRIP.CONTROL(STRING)

This defines the program as an external subroutine and further indicates the argument to be received.

Line 6 performs an output conversion on the STRING variable, using the "MCP" conversion code:

006  STRING = OCONV(STRING,"MCP")

The "MCP" conversion cede "masks" all the control characters and turns them into periods. To a PICK/BASIC program, control characters are those characters in the ASCII ceding scheme which have a decimal value from 1 through 31, and all of the characters above decimal 127. Unfortunately, this happens to include the special reserved delimiters (attribute, value, and subvalue marks), so special attention must be paid when using this function.

Line 7 displays the message:

BEFORE STRIPPING, HERE'S HOW THE STRING LOOKS :

Line 8 outputs the contents of the STRING variable. All of the control characters will now appear as periods in the string of characters that you entered.

STRIPPING CONTROL CHARACTERS FROM A STRING

The heart of subroutine STRIP.CONTROL is in lines 13-24:

013 NUMBER.OF.DOTS = COUNT(STRING,".") ;* HOW MANY ARE THERE?
014 *
015 FOR I = 1 TO NUMBER.OF.DOTS
016 FOUND = INDEX (STRING,".",I)
017 IF FOUND THEN
018 STRING = STRING[1,FOUND-1]: STRING[FOUND+l,33000]
019 PRINT STRING
020 END ELSE
021 PRINT "AFTER STRIPPING, HERE'S HOW IT LOOKS :"
022 PRINT STRING
023 END
024 NEXT I
025 RETURN

On line 13 of the subroutine, the COUNT function is used to determine the number of periods (control characters) present in the string. The numeric value that the COUNT function returns is stored in the variable NUMBER.OF.DOTS, which is used as the upper boundary of the FOR- NEXT statement on line 15.

On line 16, the INDEX function is called upon to search for and report the character position of the first period in the STRING variable. If the INDEX function detects a period, the corresponding character position at which it was found is stored in the variable FOUND.

Line 17 checks the FOUND variable to determine if it is true (non-zero and numeric), which indicates that a period was found. If FOUND evaluates True, then the statements on lines 18 and 19 are executed, to remove the period from the string. Line 18 is:

STRING = STRING[1,FOUND-1] :STRING[FOUND+l,33000]

The first portion of this line, which is:

STRING = STRING[1,FOUND-1]

tells the program to perform a "text extraction" (substring) function on the variable STRING, extracting all of the characters from the first character in the string to the position indicated by the value of the calculation "FOUND-1" (found minus one).

Suppose, for instance, that the string appeared as:

XXX...XXX

When the statement STRING = STRING[1 ,FOUND-1] is applied against this value, the number 4 is stored in the variable FOUND, since the first period appears in the fourth character position of the string. Consequently, in this example, the statement:

STRING = STRING[1,FOUND-1]

would be exactly the same as executing the statement:

STRING = STRING[I,3]

The result of this operation is temporarily held while the second half of the statement is executed. This second portion appears as:

STRING [FOUND+I,LEN(STRING)]

A calculated text extraction again is performed. This time, the beginning character position is calculated by taking the current value of FOUND and adding 1 to it. This means that you are starting the extraction one character past the control character (period). The number of characters to extract from this beginning point is specified by the number 33000 (since no string can be larger than 32K anyway, this assures that the entire string is affected). Using the same sample data as before:

XXX...XXX

The second portion would extract "..XXX" as the remaining characters in the string. Now the operation can be completed. Again, the statement appeared as:

STRING = STRING[1,FOUND-1]:STRING[FOUND+l,33000]

The ":" (concatenation) symbol appears between the two expressions. This takes the result from the first portion of the statement, concatenates the result of the second portion of the statement, and then stores the result back in the STRING variable. Line 19 displays the result of the operation. There will be one less period in the resulting string.

This loop is repeated until all of the control characters have been stripped from the string. Upon removing the last period, the resulting string is displayed and the external subroutine executes the RETURN statement on line 25. This returns execution to the next executable statement after the CALL statement in the master program. Just like internal subroutines, each external subroutine must contain at least one RETURN statement to return execution to the program that activated it.

Note that if the input string that was stripped of control characters had contained any "real" periods, they too would have been stripped. This could have been prevented by storing the "original" string in a variable and adding the following logic:

ORIGINAL = STRING
STRING = OCONV(STRING,"MCP")
*
*
IF FOUND AND ORIGINAL[FOUND,1] # "." THEN ...

SOME NOTES ABOUT MODULAR CODE AND SUBROUTINES

Making programming more modular has some distinct advantages. The program and external subroutine in Example 15 illustrated a means of stripping control character input from the keyboard. This external subroutine could be connected to every program that receives input to perform its single task. There are many other operations that are capable of being made modular, such as verifying a date to ensure that it is a valid format and within an acceptable range.

The single largest advantage to separating a self-standing operation as a module or external subroutine is that it only has to be coded once. This way, when you want to change the program or add a feature, it only has to be changed in one place. Another advantage occurs as a side effect of the first advantage. Programs start to become smaller as sections of redundant code are removed and replaced with calls to external subroutines.

Here's an example of a modular program:

PROGRAM: MAINLINE
001 CALL INITIALIZE
002 CALL GET.DATA
003 CALL POST
004 CALL WRAPUP

This is modular code to the extreme, although the special-purpose routines are not shared.

Some experts argue that "in-line" code runs faster than using external subroutines. This is true, but the tradeoffs are enormous. Using "in-line" code means that every time an operation is needed, the code for that operation is duplicated where it is needed. True "in-line" code avoids GOTO statements like the plague.

The basis of the defense of not using external subroutines is the overhead that is involved in fetching the executable object code from disk and loading it into main memory, where it may be used. It is no secret that the more trips that you have to make to the disk, the slower things go. In the old days, when RAM was expensive, this argument had some merit. These days, RAM is cheap, cheap, cheap! Many implementations of Pick allow multiple megabytes of RAM.

The nature of the Pick virtual memory manager is very friendly to external subroutines. Suppose a program called an external subroutine. If the executable object code for that particular subroutine is not found in main memory, then the code is located on disk and moved to an available buffer in real memory. This is known as paging or frame_faulting. If another program requests the same object code, it is detected in main memory and made immediately available to the requesting program. This is known as program re-entrancy.

As long as the code is resident in main memory, it is there for anyone who wants to use it. This makes for a very strong argument in favor of using subroutines. Not only does breaking programs into subroutines make applications more maintainable, but with large-memory systems, it is likely that the most often used subroutines will stay in main memory, since multiple processes may be requesting them.

Previous chapter Next chapter Top

 

Creative Commons License
Jonathan E. Sisk's "Pick/BASIC: A Programmer's Guide" by Jonathan E. Sisk is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at jes.com.