ugBASIC User Manual

Control structures

Any programming language gives to the programmer a set of instructions that allow computer programs to make decisions: they are called "control structures". This section will give you a quick overview on the topic.

goto gosub pop if loop controlled calculated periodic

Unconditional jumps

One way of forcing programs to jump to a specified locations is to use the GOTO instruction, followed by a specific target destination. In ugBASIC destinations can be a literal label or a line number. Label markers can consist of names that use any string of letters or numbers, as well as the underscore character (_), and they must be ended with the colon character :.

Take a look at this example:

label:
   COLOR BORDER YELLOW
   WAIT 100 MS
   COLOR BORDER BLACK
   WAIT 100 MS
   GOTO label

Like any BASIC, also numbers may be used to identify specific lines, and the program can be commanded to GOTO one of these optional markers, like this:

10 COLOR BORDER YELLOW
20 WAIT 100 MS
30 COLOR BORDER BLACK
40 WAIT 100 MS
50 GOTO 10

Note that line numbers could be different from identification numbers, and labels are much easier to remember and to locate.

Returning from jumps



Routines can be packaged to perform a specific task, and such routines could be split into smaller set of instructions as “sub-routines”. GOSUB is another, also if a bit antiquated, command used to perform a jump to a sub-routine. As with GOTO, there are two alternative targets for a GOSUB instruction: labels and line numbers. To make sub-routines easier to spot in your program listings, it is good practice to place them at the end of the main.

start:
   GOSUB yellowBorder
   WAIT 1000 MS
   GOSUB blackBorder
   WAIT 1000 MS
   GOTO start
yellowBorder:
   COLOR BORDER YELLOW
   RETURN
blackBorder:
   COLOR BORDER BLACK
   RETURN

When a program execute a GOSUB instruction, it must be instructed to RETURN to the main program after the subroutine has been executed. A single GOSUB statement can be linked to several RETURN commands, allowing exits from any number of different points in the routine.

   x = 0
start:
   GOSUB incrementX
   COLOR BORDER x
   WAIT 500 MS
   GOTO start
incrementX:
   x = x + 1
   IF x > 16 THEN : x = 0 : RETURN : ENDIF
   RETURN

There is no fixed rule. After the RETURN, a jump is made back to the instruction immediately after the original GOSUB.

Popping returning addresses

Normally, you cannot exit from a GOSUB statement using a standard GOTO. To avoid this limitation you can use the POP command:

   x = 0
   y = 0
start:
   x = x + 1
   COLOR BORDER x + y
   GOSUB incrementX
   GOTO start
nextCycle:
   y = y + 1
   IF y > 7 THEN : y = 0 : ENDIF
   GOTO start
incrementX:
   IF x > 7 THEN : POP : x = 0 : GOTO nextCycle : ENDIF
   RETURN

It removes the return address generated by a GOSUB, allowing you to leave the subroutine not using a RETURN statement. In the above example, when the color index in x is greater than 7, y is incremented but outside the original subroutine.

Decision making

The most used command to implement the decision making process is the IF...THEN...ENDIF structure. It allows simple decisions to be made within a program, so IF a condition is true THEN the computer decides to take a particular course of action, up to reach ENDIF. If the condition is not true, the machine does something else. The list of condition in an IF...THEN...ENDIF structure can be any list of tests, including the use of AND and OR keywords. Another keyword understood is ELSE. This is the "alternative" choice. Remember that an IF...THEN...ELSE...ENDIF statement is not limited to a single line so you can build a "structured test".

   x = 0
   y = 0
start:
   IF x > 16 THEN
      y = 0
   ENDIF
   IF x = 0 THEN
      y = 1
   ENDIF
   IF y = 1 THEN : x = x + 1 : ELSE : x = x - 1 : ENDIF
   COLOR BORDER x + y
   WAIT x * 50 MS
   GOTO start

In a structured test, each test is set up with an IF and a THEN, and ended with a matching ENDIF. The statements in a structured test are separated by colons on any particular line, as usual, but can extend over any number of lines in your listing, as required.

   x = 0
   y = 0
start:
   IF x > 16 THEN
      y = 0
   ELSE IF x = 0 THEN
      y = 1
   ELSE
      REM does nothing!
   ENDIF
   IF y = 1 THEN : x = x + 1 : ELSE : x = x - 1 : ENDIF
   COLOR BORDER x + y
   WAIT x * 50 MS
   GOTO start

Moreover, there is the capability to allow multiple tests to be performed. The keyword is ELSE IF, and it must be used within a normal IF...ENDIF statement, and the only rule to remember is that there must be one ELSE just before the ENDIF. This sort of test waits for an expression, and if the expression is true, then what comes after it is executed.

About logical decisions, ugBASIC understands the following character symbols, which are used as a form of short-hand function:

  • = is used as "equal to" (it can also be written as ==, as well);
  • <> is used as “not equal to”;
  • > is used as "greater than";
  • < is used as "less than";
  • >= is used as "greater than or equal to";
  • <= is used as "less than or equal to";
There are also three special values that can be used during the decision making process:
  • TRUE that returns 255;
  • FALSE that returns 0;
  • NOT that returns the logical inverse of the given value.
In all the conditional operations such as IF...THEN and REPEAT...UNTIL, the value of -1 is used to represent TRUE, and the value of 0 is used to represent FALSE. NOT is used to swap over every digit in a binary number from a 0 to a 1, and vice versa. Since 255 (-1, TRUE) can be expressed in binary as %11111111, then NOT TRUE will be equal to FALSE, and a logical NOT operation is achieved.

Loops and repetitions

ugBASIC offers all of the expected programming short-cuts to allow sections of code to be repeated as often as necessary. These repeated parts of programs are known as "loops". In particular, loops can be defined using the pair DO...LOOP, and it will loop a list of statements forever, with DO acting as the marker position for the LOOP to return to.

DO
   COLOR BORDER YELLOW
   COLOR BORDER BLACK
   WAIT 500 MS
LOOP

The instruction EXIT forces the program to leave a loop immediately, and it can be used to escape from all the types of loop, such as FOR...NEXT, REPEAT...UNTIL, WHILE...WEND and DO...LOOP.

DO
   COLOR BORDER YELLOW
   WAIT 500 MS
   COLOR BORDER BLACK
   WAIT 500 MS
   DO
      COLOR BORDER RED
      WAIT 1000 MS
      EXIT
      REM The border will never be green!
      COLOR BORDER GREEN
      WAIT 1000 MS
   LOOP
LOOP

You can nest any number of loops, and when used on its own, EXIT will “short-circuit” the innermost loop only. By including the number after EXIT, that number of nested loops will be taken into account before the EXIT is made.

DO
   COLOR BORDER YELLOW
   WAIT 500 MS
   COLOR BORDER BLACK
   WAIT 500 MS
   DO
      COLOR BORDER RED
      WAIT 1000 MS
      COLOR BORDER GREEN
      WAIT 1000 MS
      DO
         COLOR BORDER RED
         WAIT 1000 MS
         EXIT 2 : REM EXIT 2 = skip 1 more loop, because "EXIT" == "EXIT 1"!
         REM and the second loop will be skipped
         COLOR BORDER GREEN
         WAIT 1000 MS
      LOOP
   LOOP
LOOP

In this way, the program will jump directly to the instruction immediately after the relevant loop. If you need to leave a loop as a result of a specific set of conditions, this can be made by using the EXIT IF instruction. As explained above, in conditional operations, the value -1 (255) represents TRUE, whereas a zero represents FALSE. After using EXIT IF, an expression is given which consists of one or more tests. The EXIT will only be performed IF the result is found to be 255 (-1, TRUE). As with the command EXIT, an optional number can be given to specify the number of loops to be jumped from, otherwise only the current loop will be aborted.

The WHILE...WEND commands provides a convenient way of making the program repeat a group of instructions all the time a particular condition is true.

For example, this program will show a decrement number from 10 to 1 and restart from 10 again:

DO
   x = 10
   WHILE x > 0
      PRINT x
      x = x - 1
   WEND
LOOP

WHILE marks the start of this loop, and the condition is checked for a value of 255 (-1, TRUE) from this starting position through to the end position, which is marked by a WEND. The condition is then checked again at every turn of the loop, until it is no longer true. You are, obviously, free to use AND, OR and NOT to qualify the conditions to be checked.

DO
   x = 10
   REPEAT
      PRINT x
      x = x - 1
   UNTIL x == 0
LOOP

Instead of checking if a condition is true or false at the start of a loop, REPEAT...UNTIL makes its check at the end of a loop. REPEAT marks the start and UNTIL the end of the loop to be checked. This means that if a condition is false at the beginning of a WHILE...WEND structure, that loop will never be performed at all, but if it is true at the beginning of a REPEAT...UNTIL structure, the loop will be performed at least once.

Controlled loops

Control can be made much more definite than relying on whether conditions are true or false. In particular, when deciding how many times a loop is to be repeated, you can use the FOR...NEXT control structure. For example, this sample program will print numbers from 1 to 100:

i = 0
FOR i = 1 TO 100
   PRINT i
NEXT

Each FOR statement must be matched by a single NEXT, and pairs of FOR...NEXT loops can be nested inside one another. Each loop repeats a list of instructions for a specific number of times, governed by an index which counts the number of times the loop is repeated. Once inside the loop, this index can be read by the program as if it is a normal variable.


i = 0 : j = 0
FOR i = 1 TO 100
   FOR j = 1 TO 5
      PRINT i * j
   NEXT
NEXT

Normally, the index counter is increased by 1 unit at every turn of a FOR...NEXT loop. When the current value exceeds that of the last number specified, the loop is terminated. STEP is used to change the size of increase in the index value.

Forced jumps by expression

Jumps can be made whenever a particular variable is recognised, in other words, regardless of any other conditions. GOTO and GOSUB are examples of a unconditional jump. ON can be used to force the program to jump to a pre-defined position when it recognises a specified variable and against a choice of several positions, depending on what value is held by the variable at the time it is spotted.

For example, this program will print repeatly the sequence "1, 2, 3":

   times = 1
start:
   ON times GOSUB first, second, third
   times = times + 1
   IF times > 3 THEN
      times = 1
   ENDIF
   GOTO start
first:
   PRINT 1
second:
   PRINT 2
third:
   PRINT 3

The ON instruction can force a unconditional jump (GOTO) or with returning (GOSUB). To work properly, the expression must have a value from 1 up to the number of the highest possible destination. If the expression has a value of 0 or greater than the highest possibile destination, no jump will be performed.

Take this example:

IF level=1 THEN GOTO level1
IF level=2 THEN GOTO level2
IF level=2 THEN GOTO level3

Can be rewritten as:

ON level GOTO level1, level2, level3

The use of an ON GOSUB structure is identical to ON...GOTO, except that it must employ a RETURN (or a POP!) to jump back to the instruction immediately after the ON...GOSUB statement. Destinations may be given as the name of a label, or the identification number of a line between 1 and the maximum number of possible destinations.

Periodic calls

The EVERY statement is used to call up a sub-routine or a procedure at regular intervals, without interfering with the main program. By specifying the length of time between every call, measured in TICKS, you can call a sub-routine.

   colorIndex = 0
   EVERY 50 TICKS GOSUB changeBorderColor
   EVERY ON
   HALT
changeBorderColor:
   COLOR BORDER colorIndex
   colorIndex = colorIndex + 1
   IF colorIndex > 16 THEN
      colorIndex = 0
   ENDIF
   EVERY ON
   RETURN

Note that the sub execution time should be less than the interval time, or the main program timings will be affected. After a sub-routine has been entered, the EVERY system is automatically disabled. This means that, in order to call this feature continuously, an EVERY ON command must be inserted into a sub-routine before the final RETURN statement.

So, EVERY ON should be used before the relevant sub-routine has finished executing, while EVERY OFF is the default condition, and is used to disable the automatic calling process altogether.


Any problem?

If you have found a problem, if you think there is a bug or, more simply, you would like something to be improved, write a topic on the official forum, or open an issue on GitHub.

Thank you!