Program Structure and Flow

Two of the most significant characteristics of a computer are its ability to perform computations and its ability to make decisions. If the execution sequence could never be changed within a program, the computer could do little more than plug numbers into a formula. Computers have powerful computational features, but the heart of a computer's intelligence is its ability to make decisions. This chapter discusses the ways that decisions are used in controlling the flow of program execution.

The Program Counter

The program counter is the part of the computer's internal system that tells it which line to execute. Unless otherwise specified, the program counter automatically updates at the end of each line so that it points to the next program line.

This fundamental type of program flow is called "linear flow" and is illustrated in the following example. With this example, visualize the flow of statement execution as being a straight line through the program listing.

 
Program Lines               Value in Program Counter
                                at End of Line

120  R=R+2                          130
130  Area=PI*R^2                    131
131  PRINT R                        140
140  PRINT "Area =";Area            150
150  STOP                        don't care
 

Although linear flow seems very elementary, always remember that this is the computer's normal mode of operation.

There are three general categories of program flow. These are:

In addition to capabilities in all three of these categories, your computer also has a powerful special case of selection, called event-initiated branching. The rest of this chapter shows how to use all of these types of program flow and gives suggestions for choosing the type of flow that is best for your application.

Sequence

This section describes the types of sequences of program execution:

Linear Flow

The simplest form of sequence is linear flow. The preceding section showed an example of this type of flow. Although linear flow is not at all glamorous, it has a very important purpose. Most operations required of the computer are too complex to perform using one line of BASIC. Linear flow allows many program lines to be grouped together to perform a specific task in a predictable manner. Although this form of flow requires little explanation, keep these characteristics in mind:

Halting Program Execution

One of the obvious alternatives to executing the next line in sequence is not to execute anything. There are three statements that can be used to block the execution of the next line and halt program flow. Each of these statements has a specific purpose, as explained in the following paragraphs.

A main program is a list of program lines with an END statement on the last line. Marking the end of the main program is the primary purpose of the END statement. Therefore, a program can contain only one END statement. The secondary purpose of the END statement is stopping program execution. When an END statement is executed, program flow stops and the program moves into the stopped (non-continuable) state.

It is often necessary to stop the program flow at some point other than the end of the main program. This is the purpose of the STOP statement. A program can contain any number of STOP statements in any program context. When a STOP statement is executed, program flow stops and the program moves into the stopped (non-continuable) state. Also, if the STOP statement is executed in a subprogram context, the main program context is restored. (Subprograms and context switching are explained in the "User-Defined Functions and Subprograms" chapter.)

As an example of the use of STOP and END, consider the following program.

 

100  Radius=5
110  Circum=PI*2*Radius
120  PRINT INT(Circum)
130  STOP
140  Area=PI*Radius^2
150  PRINT INT(Area)
160  END
 

When the [RUN] key ([f3] in the System menu of ITF keyboards) is pressed, the computer prints 31 on the CRT and the Run Indicator (lower right corner of CRT) goes off. This first press of the [RUN] key caused linear execution of lines 100 thru 130, with line 130 stopping that execution. If the [RUN] key is pressed again, the same thing will happen; the program does not resume execution from its stopping point in response to a RUN command. However, RUN can specify a starting point. So, execute RUN 140. The computer prints 0 and stops. This command caused linear execution of lines 140 thru 160, with line 160 stopping that execution. However, a RUN command also causes a prerun initialization which zeroed the value of the variable Radius.

You could try pressing [CONTINUE] or [CONT] ([f2] in the System menu of ITF keyboards) in the preceding example, but you will get an error. A stopped program is not continuable. This leads up to the third statement for halting program flow. Replace the STOP statement on line 130 with a PAUSE statement, yielding the following program.

 
100  Radius=5
110  Circum=PI*2*Radius
120  PRINT INT(Circum)
130  PAUSE
140  Area=PI*Radius^2
150  PRINT INT(Area)
160  END
 

Now when the program is run, and the computer prints 31 on the CRT. Then when [CONTINUE] is pressed, the computer prints 78 on the CRT. The purpose of the PAUSE statement is to temporarily halt program execution, leaving the program counter intact and the program in a continuable state. One common use for the PAUSE statement is in program troubleshooting and debugging. This is covered in the "Program Debugging" chapter. Another use for PAUSE is to allow time for the computer user to read messages or follow instructions. Interfacing with a human is covered in greater depth in the "Communicating with the Operator" chapter, but here is one example of using the PAUSE statement in this way.

 

100  PRINT "This program generates a cross-reference"
110  PRINT "printout. The file to be cross-referenced"
120  PRINT "must be an ASCII file containing a BASIC"
130  PRINT "program."
140  PRINT
150  PRINT "Insert the disk with your files on it and"
160  PRINT "press CONTINUE."
170  PAUSE
180  !     Program execution resumes here after CONTINUE
 

Lines 100 thru 160 are instructions to the program user. Since a user will often just load a program and run it, the programmer cannot assume that the user's disk is in place at the start of the program. The instructions on the CRT remind the user of the program's purpose and review the initial actions needed. The PAUSE statement on line 170 gives the user all the time he needs to read the instructions, remove the program disk, and insert the "data disk." It would be ridiculous to use a WAIT statement to try to anticipate the number of seconds required for these actions. The PAUSE statement gives freedom to the user to take as little or as much time as necessary.

When [CONTINUE] ([f2]) is pressed, the program resumes with any necessary input of file names and assignments. Questions such as "Have you inserted the proper disk?" are unnecessary now. The user has already indicated compliance with the instructions by pressing [CONTINUE].

Simple Branching

An alternative to linear flow is branching. Although conditional branching is one of the building blocks for selection structures, the unconditional branch is simply a redirection of sequential flow. The keywords which provide unconditional branching are GOTO, GOSUB, CALL, and FN. The CALL and FN keywords invoke new contexts, in addition to their branching action. This is a complex action that is the topic of the "Subprograms and User-Defined Functions" chapter. This section discusses the use of GOSUB and GOTO.

Using GOTO

First, you should be aware that the structuring capabilities available in BASIC make it possible to avoid the use of the unconditional GOTO in most applications. You should also be aware that this is a highly-desirable goal. (See the section on "Top-Down Design" in the "User-Defined Functions and Subprograms" chapter.)

The only difference between linear flow and a GOTO is that the GOTO loads the program counter with a value that is (usually) different from the next-higher line number. The GOTO statement can specify either the line number or the line label of the destination. The following example shows the program flow and contents of the program counter in a program segment containing a GOTO.

 
Program Lines            Value in Program Counter
                             at End of Line

180  R=R+2                        190      
190  Area=PI*R^2                  200
200  GOTO 240                     240
210  Width=Width+1                220
220  Length=Length+1              230
230  Area=Width*Length            240
240  PRINT "Area =";Area          250
250  GOTO 210                     210 
 

As you can see, the execution is still sequential and no decision-making is involved. The first GOTO (line 200) produces a forward jump, and the second GOTO (line 250) produces a backward jump. A forward jump is used to skip over a section of the program. An unconditional backward jump can produce an infinite loop. This is the endless repetition of a section of the program. In this example, the infinite loop is line 210 thru 250.

An infinite loop by itself is not usually a desirable program structure. However, it does have its place when mixed with conditional branching or event-initiated branching. Examples of these structures are given later in this chapter.

Using GOSUB

The GOSUB statement is used to transfer program execution to a subroutine. Note that a subroutine and a subprogram are very different in HP BASIC. Calling a subprogram invokes a new context. Subprograms can declare formal parameters and local variables. A subroutine is simply a segment of a program that is entered with a GOSUB and exited with a RETURN. Subroutines are always in the same context as the program line that invokes them. There are no parameters passed and no local variables. If you are a newcomer to HP BASIC, be careful to distinguish between these two terms. They have been used differently in some other programming languages.

The GOSUB is very useful in structuring and controlling programs. The similarity it has to a procedure call is that program flow can automatically return to the proper line when the subroutine is finished. The GOSUB statement can specify either the line label or the line number of the desired subroutine entry point. The following example shows the program flow and contents of the program counter in a program segment containing a GOSUB.

 
Program Lines              Value in Program Counter
                              at End of Line

300  R=R+2                         310               
310  Area=PI*R^2                   320
320  GOSUB 1000                   1000 (see below)
330  Width=Width+1                 340
340  Length=Length+1               350
350  !  Program continues
 .
 .
 .
1000  PRINT Area;"square in."     1010
1010  Cent=Area*6.4516            1020
1020  PRINT Cent;"square cm"      1030
1030  PRINT                       1040
1040  RETURN                       330
 

Line Labels and Comments

Before we go further, there are two topics that we should cover: line labels and comments.

Note that a line label precedes the executable statement in a program line, while a comment follows the executable statement (if there is one). The following program illustrates the use of both line labels and comments.

 
10 Start:     ! Program begins here.
20            PRINT "Hello"
30            GOTO Finish
40            DISP "Stop"            ! This line is never executed.
50 Finish:         PRINT "Goodbye"   ! PRINT is the executable statement.
60                 GOTO Start
70                 END
 

Line 10 includes the line label "Start" and a comment, but no executable statement. Line 50 includes the line label "Finish", the executable statement PRINT, and ends with a comment. This program simply prints "Hello" and "Goodbye" repeatedly until the program is paused. Normally you will want to avoid such "endless loops." To do this you can use conditional branching, which is covered in the next section. Note that you cannot have a program line with only a line label. There must be an executable statement or at least a comment. For example:

 
100    Label:
 

is not allowed. However, the following line is legal even thought the "comment" consists of only the comment symbol (!):

 
100    Label:    !
 

Selection

The heart of a computer's decision-making power is the category of program flow called selection, or conditional execution. As the name implies, a certain segment of the program either is or is not executed according to the results of a test or condition. This is the basic action which gives the computer an appearance of possessing intelligence. Actually, it is the intelligence of the programmer which is remembered by the program and reflected in the pattern of conditional execution.

This section presents the conditional-execution statements according to various applications.

  1. Conditional execution of one segment.
  2. Conditionally choosing one of two segments.
  3. Conditionally choosing one of many segments.

Conditional Execution of One Segment

The basic decision to execute or not execute a program segment is made by the IF...THEN statement. This statement includes a numeric expression that is evaluated as being either true or false. If true (non-zero), the conditional segment is executed. If false (zero), the conditional segment is bypassed. Although the expression contained in an IF...THEN is treated as a Boolean expression, note that there is no "BOOLEAN" data type. Any valid numeric expression is allowed.

The conditional segment can be either a single BASIC statement or a program segment containing any number of statements. The first example shows conditional execution of a single BASIC statement.

 
100  IF Ph>7.7 THEN OUTPUT Valve USING "#,B";0
 

Notice the test (Ph>7.7) and the conditional statement (OUTPUT Valve..) which appear on either side of the keyword THEN. When the computer executes this program line, it evaluates the expression Ph>7.7. If the value contained in the variable Ph is 7.7 or less, the expression evaluates to 0 (false), and the line is exited. If the value contained in the variable Ph is greater than 7.7, the expression evaluates as 1 (true), and the OUTPUT statement is executed. If you don't already understand logical and relational operators, refer to the "Numeric Computation" and "String Manipulation" chapters. (Although "pH" is the correct chemical expression, it is not valid as a BASIC variable name and thus, "Ph" has been substituted. For further information about naming variables, refer to "Naming Variables" in chapter 3.)

By the way, the image specifier #,B causes the output of a single byte. In the example, the value for that byte is specified as zero (all bits cleared). Presumably, this turns off all devices connected to a GPIO (digital) interface.

Note that the same variable is allowed on both sides of an IF...THEN statement. For example, the following statement could be used to keep a user-supplied value within bounds.

 
IF Number>9 THEN Number=9
 

When the computer executes this statement, it checks the initial value of Number. If the variable contains a value less than or equal to nine, that value is left unchanged, and the statement is exited. If the value of Number is greater than nine, the conditional assignment is performed, replacing the original value in Number with the value nine.

Prohibited Statements

Certain statements are not allowed as the conditional statement in a single-line IF...THEN. The disallowed statements are used for various purposes, but the "common denominator" is that the computer needs to find them during prerun as the first keyword on a line.

Multiple-Line Conditional Segments

If the conditional program segment requires more than one statement, a slightly different structure is used. Let's expand the valve-control example.

 
100  IF Ph>7.7 THEN
110    OUTPUT Valve USING "#,B";0
120    PRINT "Final Ph =";Ph
130    GOSUB Next_tube
140  END IF
150  !  Program continues here
 

Any number of program lines can be placed between a THEN and an END IF statement. In executing this example, the computer evaluates the expression Ph>7.7 in the IF...THEN statement. If the result is false, the program counter is set to 150, and execution resumes with the line following the END IF statement. If the condition is true, the program counter is set to 110, and the three conditional statements (lines 110, 120, 130) are executed. Program flow then picks up at line 150, because the END IF is only used during prerun.

When using multiple-line IF...THEN structures, remember to mark the end of the structure with an END IF statement and don't put any of the statements on the same line as the IF...THEN. If the beginning and end of the structure are not properly marked, the computer reports error 347 during prerun.

The conditional segment can contain any statement except one that is used to set context boundaries (such as END or DEF FN). In the previous example, the GOSUB Next_tube could have been a GOTO Next_tube. In that case, program execution does not pass through 150 when the condition is true. A false condition would cause a branch to line 150, while a true condition would send execution from line 100, to 110, to 120, to 130, and then to the line labeled "Next_tube."

If structuring statements are used within a multiple-line IF...THEN, the entire structure must be contained in one conditional segment. This are nested constructs. The following example shows some properly nested constructs. Notice that the use of indenting improves the readability of the code.

 

1000  IF Flag THEN
1010    IF End_of_page THEN
1020      FOR I=1 TO Skip_length
1030        PRINT
1040        Lines=Lines+1
1050      NEXT I
1060    END IF
1070  END IF
 

Choosing One of Two Segments

Often you want a program flow that passes through only one of two paths depending upon a condition. For example, in the following example program, if Flag = 1, then the program flow at the end of line 420 would pass directly to line 480. However, if Flag = 0, then the program flow at line 400 would pass directly to line 430. If you have ever been forced to program this type of structure using only the conditional GOTO, you know that the result is much more confusing than it needs to be.

 
400  IF Flag THEN
410    R=R+2
420    Area=PI*R^2
430  ELSE
440    Width=Width+1
450    Length=Length+1
460    Area=Width*Length
470  END IF
480  PRINT "Area =";Area
490  !  Program Continues
 

This language has an IF...THEN...ELSE structure which makes the one-of-two choice easy and readable. The following example looks at a device selector which may or may not contain a primary address. The variable Isc is needed later in the program and must be only an interface select code. If the operator-supplied device selector is greater than 31, the interface select code is extracted from it. If it is equal to or less than 31, it already is an interface select code. (This example assumes that no secondary addressing is used.)

 

500  IF Select>31 THEN
510    Isc=Select DIV 100
520  ELSE
530    Isc=Select
540  END IF
 

Notice that this structure is similar to the multiple-line IF...THEN shown previously. The only difference is the addition of the keyword ELSE. Like the previous example, the structure is terminated by END IF, and the proper nesting of other structures is allowed.

Choosing One of Many Segments

Using SELECT

Consider as an example the processing of readings from a voltmeter. In this example, we assume that the reading has already been entered, and it contained a function code. These hypothetical function codes identify the type of reading and are shown in the following table.
Function Codes
Function Code Type of Reading
DV DC Volts
DI DC Current
OM Ohms

The first example shows the use of the SELECT construct. The function code is contained in the variable Funct$. For the sake of simplicity, the example does not show any actual processing. Comments are used to identify the location of the processing segments. The rules about illegal statements and proper nesting are the same as those discussed previously in the IF...THEN section.

 
2000  SELECT Funct$
2010  CASE "DV"
2020    !
2030    ! Processing for DC Volts
2040    !
2050  CASE "DI"
2060    !
2070    ! Processing for DC Amps
2080    !
2090  CASE "OM"
2100    !
2110    ! Processing for Ohms
2120    !
2130  CASE ELSE
2140    BEEP
2150    PRINT "INVALID READING"
2160  END SELECT
2170  ! Program execution continues here
 

Notice that the SELECT construct starts with a SELECT statement specifying the variable to be tested and ends with an END SELECT statement. The anticipated values are placed in CASE statements. Although this example shows a string tested against simple literals, the SELECT statement works for numeric or string variables or expressions. The CASE statements can contain constants, variables, expressions, comparison operators, or a range specification. The anticipated values, or match items, must be of the same type (numeric or string) as the tested variable.

The CASE ELSE statement is optional. It defines a program segment that is executed if the tested variable does not match any of the cases. If CASE ELSE is not included and no match is found, program execution simply continues with the line following END SELECT.

 
CASE 1 TO 31 Processing for interface select code
  
CASE -1,1,3 TO 7,>15 Specifying multiple matches
 
CASE CHR$(27)& ")@"& Eol$ Using a string expression
 

You should be aware that if an error occurs when the computer tries to evaluate an expression in a CASE statement, the error is reported for the line containing the SELECT statement. An error message pointing to a SELECT statement actually means that there was an error in that line or in one of the CASE statements.

Repetition

Humans usually prefer tasks with variety that avoid tedious repetition. A computer does not have this shortcoming. You have four structures available for creating repetition. The FOR...NEXT structure is used for repeating a program segment a predetermined number of times. Two other structures (REPEAT...UNTIL and WHILE...END WHILE) are used for repeating a program segment indefinitely, waiting for a specified condition to occur. The LOOP...EXIT IF structure is used to create an iterative structure that allows multiple exit points at arbitrary locations.

Fixed Number of Iterations

The general concept of repetitive program flow can be shown with the FOR...NEXT structure. With this structure, a program segment is executed a predetermined number of times. The FOR statement marks the beginning of the repeated segment and establishes the number of repetitions. The NEXT statement marks the end of the repeated segment. This structure uses a numeric variable as a loop counter. This variable is available for use within the loop, if desired.

The number of loop iterations is determined by the FOR statement. This statement identifies the loop counter, assigns a starting value to it, specifies the desired final value, and determines the step size that will be used to take the loop counter from the starting value to the final value. For example, in the following FOR...NEXT loop, the starting value is 10, the final value is 0, and the step size is -1. The repeated segment is lines 210 through 240.

 
200  FOR Count=10 TO 0 STEP -1
210    BEEP
220    PRINT Count
230    WAIT 1
240  NEXT Count
 

When the loop counter is an INTEGER, the number of iterations can be predicted using the following formula: INT ([Step Size + Final Value - Starting Value] / Step Size )

Note that the formula applies to the values in the variables, not necessarily the numbers in the program source. For example, if you use an INTEGER loop counter and specify a step size of 0.7, the value will be rounded to one. Therefore, 1 should be used in the formula, not 0.7.

The loop counter can be a REAL number, with REAL quantities for the step size, starting, or final values. In some cases, using REAL numbers will cause the number of iterations to be off by one from the preceding formula. This is because the NEXT statement performs an "increment and compare," and there is a slight inaccuracy in the comparison of REAL numbers. If you are interested, this is discussed in the next chapter. However, there is no clean way around it with FOR...NEXT loops. Here is an example:

 
200  Count=0
210  FOR X=10 TO 20
220    Count=Count+1
230    PRINT Count
240  NEXT X
 

According to the formula, this loop should execute 11 times: INT((1+20-10)/1)=11. The result on the CRT confirms this when the loop is executed. If line 210 is changed to:

 
210 FOR X=1 TO 2 STEP .1
 

the formula still yields 11 as the number of iterations. However, executing the loop produces only 10 repetitions. This is because of a very, very small accumulated error that results from the successive addition of one-tenth. The error is less significant than the 15th digit, but is discernible by the computer. In this case, rounding cannot be performed at a time that would help. When you find yourself in this situation, one way out is to add a slight adjustment factor to the final value. The following line does give the 11 iterations predicted by the formula.

 
210 FOR X=1 TO 2.05 STEP .1
 

Remembering the "increment and compare" operation at the bottom of the loop is helpful. After the loop counter is updated, it is compared to the final value established by the FOR statement. If the loop counter has passed the specified final value, the loop is exited. If it has not passed the specified final value, the loop is repeated. The loop counter retains its exit value after the loop is finished. This is not necessarily one full step past the final value. For example: FOR I=1 TO 9.9.

Conditional Number of Iterations

The FOR...NEXT loop produces a fixed number of iterations, established by the FOR statement before the loop is executed. Some applications need a loop that is executed until a certain condition is true, without specifically stating the number of iterations involved. Consider a very simple example. The following segment asks the operator to input a positive number. Presumably, negative numbers are not acceptable. A looping structure is used to repeat the entry operation if an improper value is given. Notice that it is not important how many times the loop is executed. If it only takes once, that is just fine. If the poor operator takes ten tries before he realizes what the computer is asking for, so be it. What is important is that a specific condition is met. In this example, the condition is that a value be non-negative. As soon as that condition has been satisfied, the loop is exited.

 
800  REPEAT
810    INPUT "Enter a positive number",Number
820  UNTIL Number>=0
 

A typical use of this is an iterative problem involving non-linear increments. One example is musical notes. Performing the same operation on all the notes in a 3-octave band is a repetitive process, but not a linear one. Musical notes are related geometrically by the 12th root of two. The following example simply prints the frequencies involved, but your application could involve any number of operations.

 

1200  Note=110      ! Start at low A
1210  REPEAT
1220    PRINT Note;
1230    Note=Note*2^(1/12)
1240  UNTIL Note>880  ! End at high A
 

For this example, a FOR...NEXT loop might have been used, with the loop counter appearing in an exponent. That would work because it is relatively easy to know how many notes there are in three octaves of the musical scale. However, the REPEAT...UNTIL structure is more flexible than FOR...NEXT when working with exponential data in general. Examples often occur in the area of graphics. The following segment could be used to plot audio frequency data, where the x-axis is logarithmic.

 

1500  Freq=20
1510  MOVE LOG(Freq),FNFunction(Freq)
1520  REPEAT
1530    DRAW LOG(Freq),FNFunction(Freq)
1540    Freq=Freq*1.2
1550  UNTIL Freq>20000
 

The WHILE loop is used for the same purpose as the REPEAT loop. The only difference between the two is the location of the test for exiting the loop. The REPEAT loop has its test at the bottom. This means that the loop is always executed at least once, regardless of the value of the condition. The WHILE loop has its test at the top. Therefore, it is possible for the loop to be skipped entirely (if the conditions so dictate). The following segment shows the same plotting example using a WHILE loop.

 

1500  Freq=20
1510  MOVE LOG(Freq),FNFunction(Freq)
1520  WHILE Freq<=20000
1530    DRAW LOG(Freq),FNFunction(Freq)
1540    Freq=Freq*1.2
1550  END WHILE
 

The REPEAT...UNTIL and WHILE structures are especially useful for tasks that are impossible with a FOR...NEXT loop. One such situation is a loop where both the loop counter and the final value are changing. Consider the example of stripping all control characters from a string. This can't be done in a loop that starts FOR I=1 TO LEN(A$), because the length of A$ changes each time a character is deleted. Therefore, the loop counter used as a subscript will eventually exceed the length of the string by more than one, generating an error. The WHILE loop does not have this problem. Note that the test at the top of the loop prevents the subscripting from being attempted on a null string. This is necessary to avoid an error.

 

600  I=1
610  WHILE I<=LEN(A$)
620    IF A$[I;1]<CHR$(32) THEN
630      A$[I]=A$[I+1]
640    ELSE
650      I=I+1
660    END IF
670  END WHILE
 

Arbitrary Exit Points

A pass through any of the loop structures discussed so far included the entire program segment between the top and the bottom of the loop. There are times when this is not the desired program flow. The LOOP structure defines the repeated program segment and allows any number of conditional exits points in that segment.

For the first example, consider a search-and-replace operation on string data. In this example, the "shift out" control character is being used to initiate underlining on a printer that understands standard escape sequences. The "shift in" control character is used to turn off the underline mode. (There is nothing significant about this choice of characters. any combination of characters could serve the same purpose.)

One approach is to use a loop to search every character in every string to see if it is one of the special characters. There are two problems with this method. First, it is a little cumbersome when the replacement string is a different length than the target string. Second, it is slow. Admittedly, speed it not a significant consideration when driving common mechanical printers. But the destination might eventually be a laser printer or mass storage file, making the program's speed more visible.

A better approach is to use the POS function to locate the target string. Since this function locates only the first occurrence of a pattern, it must be placed in a loop to insure that multiple occurrences will be found. The LOOP structure is well suited to this task, as shown in the following example.

2000  LOOP
2010    Position=POS(A$,CHR$(14))
2020  EXIT IF NOT Position
2030    A$[Position]=CHR$(27)& "& dD"& A$[Position+1]
2040  END LOOP
2050  !
2060  LOOP
2070    Position=POS(A$,CHR$(15))
2080  EXIT IF NOT Position
2090    A$[Position]=CHR$(27)& "& d@"& A$[Position+1]
2100  END LOOP
2110  ! Last EXIT goes to here
 

In this segment, all occurrences of "shift out" are replaced by "escape & dD" to enable underline mode. All occurrences of "shift in" are replaced by "escape & d@" to disable underlining. Notice that there is no problem replacing one character with four (assuming that A$ is large enough). Lines containing no special characters are processed by only two POS functions, which is much faster and cleaner than performing two comparisons for every character in every line.

Another common use for this structure is the processing of operator input. Recall the REPEAT...UNTIL example that tested for the input of a positive number. Although this structure kept the computer happy, it left the operator in the dark. The LOOP structure provides for the additional processing needed, as shown in the following example.

 
200  LOOP
210    INPUT "Enter a positive number.",Number
220  EXIT IF Number>=0
230    BEEP
240    PRINT
250    PRINT "Negative numbers are not allowed."
260    PRINT "Repeat entry with a positive number.
270  END LOOP
 

Another point to remember is that the LOOP structure permits more than one exit point. This allows loops that are exited on a "whichever comes first" basis. Also, the EXIT IF statement can be at the top or bottom of the loop. This means that the LOOP structure can serve the same purposes as REPEAT...UNTIL and WHILE...END WHILE, if that suits your programming style.

The EXIT IF statement must appear at the same nesting level as the LOOP statement for a given loop. This requirement is best shown with an example. In the "WRONG" example, the EXIT IF statement has been nested one level deeper than the LOOP statement because it was placed in an IF...THEN structure.

WRONG:

600  LOOP
610    Test=RND-.5
620    IF Test<0 THEN
630      GOSUB Negative
640    ELSE
650      EXIT IF Test>.4
660      GOSUB Positive
670    END IF
680  END LOOP
 

RIGHT:

600  LOOP
610    Test=RND-.5
620  EXIT IF Test>.4
630    IF Test<0 THEN
640      GOSUB Negative
650    ELSE
660      GOSUB Positive
670    END IF
680  END LOOP
 

Event-Initiated Branching

Your computer has a special kind of program flow that provides some very powerful tools. This tool, called event-initiated branching, uses interrupts to redirect program flow. The process can be visualized as a special case of selection. Every time program flow leaves a line, the computer executes an "event-checking" routine. This is set of machine-language "if...then" statements concerning interrupts. If an event is:

Then this "event-checking" routine causes the program to branch (as specified in the ON-event statement).

The process of "event checking" is represented in the following line. Notice that it is possible for event-initiated branching to occur at the end of any program line, which includes the lines of a subprogram. This can give the appearance of "middle-of-line" branching when it occurs during a user-defined function.

 
100  X=Radius*FNMy_function/COS(Angle)^^Exponent
 

In the above example, the branch may occur while FNMy_function is being executed.

In the following example, these potential branching points are marked by the words gosub event_check. This does not refer to a BASIC subroutine, but is just a symbolic reminder of where event-initiated branching can occur. If the operating system finds an event has occurred (and the corresponding branch is currently enabled), then a branch is initiated. If not, program execution resumes with the "normal" program flow.

 

10  PRINT X  ! gosub event_check 
20  X=X+1    ! gosub event_check 
30  GOTO 10  ! gosub event_check
 

Types of Events

Event-initiated branching is established by the ON..event statements. Here is a list of the statements that fall in this category, along with the corresponding event that causes a branch:
ON CDIAL an interrupt generated by turning a knob--a rotary pulse generator--on a Control Dial box (see the "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual).
ON CYCLE cyclical (periodic, repetitive) interrupts from the clock (see the "Clock and Timers" chapter of this manual)
ON DELAY a one-time interrupt from the clock (see the "Clock and Timers" chapter of this manual)
ON END an interrupt upon reaching an end-of-file (EOF) condition (see the "Data Storage and Retrieval" chapter of this manual)
ON ERROR an interrupt when a run-time error is encountered (see the "Handling Errors" chapter of this manual)
ON EOR an interrupt when an end-of-record is encountered during a TRANSFER statement (see the "Data Storage and Retrieval" chapter of this manual)
ON EOT an interrupt when an end-of-TRANSFER condition is encountered (see chapter 20, "Transfers and Buffered I/O.")
ON EXT SIGNAL an interrupt made by an HP-UX system generated signal (BASIC/UX only; see chapter 18, "Interrupts and Timeouts.")
ON HIL EXT an interrupt generated by an HP HIL (Human Interface Link) device (see the HP BASIC Interface Reference manual).
ON INTR an interrupt generated by an an interface (see chapter 18, "Interrupts and Timeouts.")
ON KBD an interrupt generated by pressing a key (see the "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual).
ON KEY an interrupt generated by pressing a softkey: [f1] thru [f8] on ITF keyboards, or [k0] through [k9] on 98203 keyboards (see the "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual).
ON KNOB an interrupt generated by turning a knob--a rotary pulse generator: the built-in knob of a 98203 keyboard, an HIL knob, or one of the knobs on a Control Dial box (see the "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual)
ON SIGNAL an interrupt generated by a SIGNAL statement (see the HP BASIC Language Reference)
ON TIME an interrupt from the clock when the clock reaches a specified time (see the "Clock and Timers" chapter of this manual)
ON TIMEOUT an interrupt generated when an interface or device has taken longer than a specified time to respond to a data-transfer handshake (see chapter 18, "Interrupts and Timeouts.")

Example of Event-Initiated Branching

The best way to understand how event-initiated branches operate in a program is to sit down at the computer and try a few examples. Start by entering the following short program, which sets up and enables branches when one of the softkeys is pressed.

 
110  ON KEY 1 LABEL "Inc" GOSUB Plus
120  ON KEY 5 LABEL "Dec" GOSUB Minus
130  !
140 Spin:  DISP X
150  GOTO Spin
160  !
170 Plus:  X=X+1
180  RETURN
190  !
200 Minus:  X=X-1
210  RETURN
220  END
 

Notice the various structures in this sample program. The ON KEY statements are executed only once at the start of the program. Once defined, these event-initiated branches remain in effect for the rest of the program. (Disabling and deactivating are discussed later.)

The program segment labeled Spin is an infinite loop. If it weren't for interrupts, this program couldn't do anything except display a zero. However, there is an implied "if...then" at the end of each HP BASIC program line due to the ON KEY action. This allows a selection process to occur. Either the Plus or the Minus subroutine can be selected as a result of softkey presses. These are normal subroutines terminated with a RETURN statement. (In the context of interrupt programming, these subroutines are called service routines.) The following section of "pseudo-code" shows what the program flow of the Spin segment actually looks like to the computer. Spin: display X if Key0 then gosub Plus if Key5 then gosub Minus goto Spin

This pseudo-code is an over-simplification of what is actually happening, but it shows that the Spin segment is not really an infinite loop with no decision-making structure. Actually, most programs that use event-initiated branching to control program flow will contain what appears to be an infinite loop. That is the easiest way to "keep the computer waiting" while it is waiting for an interrupt.

Now run the sample program you just entered. Notice that the bottom two lines of the screen display an inverse-video label area.

The labels are arranged to correspond to the layout of the softkeys. The labels are displayed when the softkeys are active and are not displayed when the softkeys are not active (See the "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual for additional examples.) Any label which your program has not defined is blank unless the system defines it. The label areas are defined in the ON KEY statement by using the keyword "LABEL" followed by a string.

The starting value in the display line is zero, since numeric variables are initialized to zero at prerun. Each time you press [k1] or [f1], the displayed value of X is incremented. Each time you press [k5] or [f5], the displayed value of X is decremented. This simple demonstration should acquaint you with the basic action of the softkeys.

It is possible to make structures that are much more elaborate, with assignable priorities for each key, and keys that interrupt the service routines of other keys. There are many applications where priorities are not of any real significance, such as the example program running now. However, priorities will sometimes cause unexpected flow problems.

A full discussion on interrupt priority can be found in chapter 18, "Interrupts and Timeouts." If you think you have an application that is "priority sensitive," read that section carefully.

Example of Using the Knob

One characteristic of interrupt-driven program flow is that the computer's decisions can be more easily synchronized with the actions of devices connected to it. This type of application is often called real-time programming. An important example of real-time programming is machine control. A computer running an automatic packing machine must turn off the flow immediately when the jar is full. It is not acceptable for the computer to wait until the inventory printout is done and peanut butter is dumped all over the conveyor belt. Although machine control applications are very important, their extensive interfacing makes them inconvenient or impossible to use as demonstration programs in a manual such as this.

Another common example of real-time programming is computer games. The computer is expected to respond "instantly" to button presses, lever movement, etc. The operator expects immediate correlation between their input and the computer's output or display.

The following program is a very short example that demonstrates a real-time interaction between the knob (either the built-in knob on the HP 98203 keyboard or an HP-HIL knob) and the CRT. If you run this example program and turn the knob, you will see the kind of interaction that might be used for cursor control in a text editor. Obviously, a real cursor-control routine would be much more sophisticated, but this demonstrates the basic idea. (The "Communicating with the Operator" chapter of the HP BASIC Advanced Programming Techniques manual also describes using the knob.)

 

10   ON KNOB .1 GOSUB Move_blip
20 Spin:  GOTO Spin
30   !
40 Move_blip:  !
50   PRINT TABXY(Spotx,Spoty);" ";
60   Spotx=Spotx+KNOBX/5
70   Spoty=Spoty+KNOBY/5
80   IF Spoty<1 THEN Spoty=1
90   IF Spoty>18 THEN Spoty=18
100  IF Spotx<1 THEN Spotx=1
110  IF Spotx>50 THEN Spotx=50
120  PRINT TABXY(Spotx,Spoty);CHR$(127);
130  RETURN
140  END
 

This example uses a short infinite loop to wait for pulses from the knob (line 20). Interrupts from the knob are enabled by the ON KNOB statement in line 10. The service routine erases the old "blip", performs some scaling and range checking on the knob input, and prints the new "blip".

Deactivating Events

Knowing how to "turn off" the interrupt mechanism is just as important as knowing how to enable it. Often, an event is a desired input during one part of the program, but not during another. You might use softkeys to set certain process parameters at the start of a program, but you don't want interrupts from those keys once the process starts. For example, a report generating program could use a softkey to select single or double spacing. This key should be disabled once the printout starts so that an accidental key press does not cause the computer to abort the printout and return to the questions at the beginning of the program. On the other hand, you might want an "Abort" key that does precisely that. The important thing is that you decide on the desired action and make the computer obey your wishes.

Before going any further, let's explain some important terminology. There are two general methods for "turning off" an interrupt. If an interrupt source is deactivated, it no longer has any influence on program flow. You can press a deactivated key all day long and nothing will happen. However, if an event is disabled, its action has only been temporarily postponed. The computer remembers that a key was pressed while it was disabled, and the action for that key will occur at the earliest opportunity once the disabled state is removed. There are examples in this section to demonstrate the difference between these two conditions.

All the "ON-event" statements have a corresponding "OFF-event" statement. This is one way to deactivate an interrupt source.

The following example shows one use of OFF KEY to disable the softkeys. (Note that [k1] is used in the description. If you have an ITF keyboard, just substitute [f1].) A softkey is used to select a parameter for a small printing routine. Each press of [k1] increments and displays the step size that will be used as an interval between the printed numbers. When the desired step size has been selected, [k4] is pressed to start the printout. Enter and run this example. Notice that with line 240 and 250 commented out, the softkey menu, or label area, never changes.

 
100 Begin:   !
110   ON KEY 1 LABEL " DELTA" GOSUB Step_size
120   ON KEY 4 LABEL " START" GOTO Process
130   Inc=1
140   DISP "Step Size = 1"
150   !
160 Spin: GOTO Spin             ! Wait for key press
170   !
180 Step_size:  !
190   Inc=Inc+1                 ! Change increment
200   DISP "Step Size =";Inc
210   RETURN
220   !
230 Process:  !
240 ! OFF KEY
250 ! ON KEY 8 LABEL " ABORT" GOTO Leave
260   Number=0
270   FOR I=1 TO 10
280     Number=Number+Inc
290     PRINT Number;
300     WAIT .6
310   NEXT I
320 Leave:  !
330   OFF KEY 8                 ! Deactivate ABORT
340   PRINT                     ! End line
350   GOTO Begin                ! Start over
360   END
 

Now run the example again and press [k1] or [k4] while the printout is in progress. Notice that the keys are still active and produce undesired effects on the printing process. To "fix this bug," remove the exclamation point from line 240. This disables all the softkeys when the printing process starts. Notice that the softkey menu goes away when no softkeys are active. This is a very handy feature while you are experimenting with interrupts. It provides immediate feedback to indicate when interrupts are active and when they are not.

Finally, remove the exclamation point from line 250. Now, the softkey menu appears during the printing process. However, the choices are different than at the start of the program. The keys used to select the parameter and start the process are not active, because they are not needed at this point in the program. Instead, [k8] can be used to "gracefully" abort the process and return to the start of the program.

The OFF KEY statement can include a key number to deactivate a selected key. This was done in line 330.

Disabling Events

All the previous examples have shown complete deactivation of the softkeys. It is also possible to temporarily disable an event-initiated branch. This is done when an active event is desired in a process, but there is a special section of the program that you don't want to be interrupted. Since it is impossible to predict when an external event will occur, the special section of code can be "protected" with a DISABLE statement. This is sometimes necessary to prevent a certain variable from being changed in the middle of a calculation or to insure that an interface polling sequence runs to completion. It is difficult in a short, simple example to show why you would need to do this. But it is not difficult to show how to do it.

 
100   ON KEY 9 LABEL " ABORT" GOTO Leave
110   !
120 Print_line:  !
130   DISABLE
140   FOR I=1 TO 10
150     PRINT I;
160     WAIT .3
170   NEXT I
180   PRINT
190   ENABLE
200   GOTO Print_line
210   !
220 Leave: END
 

This example shows a DISABLE and ENABLE statement used to "frame" the Print_line segment of the program. The "ABORT" key is active during the entire program, but the branch to exit the routine will not be taken until an entire line is printed. The operator can press the "ABORT" key at any time. The key press will be logged, or remembered, by the computer. Then when the ENABLE statement is executed, the event-initiated branch is taken. Enter and run the example to observe this method of delaying interrupt servicing.

Chaining Programs

With this HP BASIC system, it is also possible to "chain" programs together; that is, one program may be executed, which in turn loads and runs another, which in turn loads and runs yet another, and so forth. This section describes the available methods for chaining programs.

Using LOAD

The LOAD statement clears the current program, brings in a program from a PROG file, with the option of beginning program execution at a specified line. This type of LOAD is performed by adding a line identifier. For example, the following command tells the computer to load the program in file "STONE" and begin execution at line 10:

 
100  LOAD "STONE",10
 

The line identifier may be a label or a line number, but it must identify a line in the main program segment (not in a subprogram or user-defined function).

If you want to communicate any information to the program that is being loaded, you have the following two methods:

The LOAD command cannot be used to bring in arbitrary program segments or append to a main program like GET can.

Using GET

The GET command is used to bring in programs or program segments from an ASCII file, with the options of appending them to an existing program and/or beginning program execution at a specified line.

The following statement:

 
GET "George",100
 

first deletes all program lines from 100 to the end of the program, and then and appends the lines in the file named "George" to the lines that remained at the beginning of the program. The program lines in file "George" would be renumbered to start with line 100.

GET can also specify that program execution is to begin. This is done by specifying two line identifiers. For example:

 
100  GET "RATES",Append_line,Run_line
 

specifies that:

  1. Existing program lines from the line label "Append_line" to the end of the program are to be deleted.
  2. Program lines in the file named "RATES" are to be appended to the current program, beginning at the line labeled "Append_line"; lines of "RATES" are renumbered if necessary.
  3. Program execution is to resume at the line labeled "Run_line".

Although any combination of line identifiers is allowed, the line specified as the start of execution must be in the main program segment (not in a SUB or user-defined function). Execution will not begin if there was an error during the GET operation.

Example of Chaining with GET

A large program can be divided into smaller segments that are run separately by using GET (or LOAD). The following example shows a technique for implementing this method.

First Program Segment:

 
10  COM Ohms,Amps,Volts
20  Ohms=120
30  Volts=240
40  Amps=Volts/Ohms
50  GET "Wattage"
60  END
 

Program Segment in File Named "Wattage":

 
10  COM Ohms,Amps,Volts
20  Watts=Amps*Volts
30  PRINT "Resistance (in ohms)   = ";Ohms
40  PRINT "Power usage (in watts) = ";Watts
50  END
 

Lines 10 through 40 of the first program are executed in normal, serial fashion. Upon reaching line 50, the system deletes all program lines of the program and then GETs the lines of the "Wattage" program. Note that since they have similar COM declarations, the COM variables are preserved (and used by the second program). This feature is very handy to have while chaining programs.

Program-to-Program Communications

As shown in the preceding example, if chained programs are to communicate with one another, you can place values to be communicated in COM variables. The only restriction is that these COM declarations must match exactly, or the existing COM will be cleared when the chained program is loaded. For a description of using COM declarations, see the "Subprograms" chapter of this manual.

One important point to note is the use of the COM statement. The COM statement places variables in a section of memory that is preserved during the GET operation. Since the program saved in the file named "Wattage" also has a COM statement that contains three scalar REAL variables, the COM is preserved (it matches the COM declaration of the "Wattage" program being appended with GET).

If the program segments did not contain matching COM declarations, all variables in the mis-matched COM statements would be destroyed by the "pre-run" that the system performs after appending the new lines but before running the first program line. (For a definition of pre-run, see the "Subprograms and User-Defined Functions" chapter of this manual.)