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 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.
This section describes the types of sequences of program execution:
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:
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].
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.
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.
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
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: !
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.
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.
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.
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
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.
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.
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.
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
.
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
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.
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
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
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
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.") |
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.
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".
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.
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.
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.
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.
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:
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.
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.
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.)