Some Examples of XPL0 Programs

First, let's get the Hello World program out of the way:
                code Text=12;
                Text(0, "Hello World!")
"Text" is a built-in routine that displays text strings enclosed in quote marks. The zero (0) tells where to send the message. In this case it's sent to the display screen, but it could just as easily be sent to the printer or out the serial port by using a different number.


Guess

Let's write a more extensive program. This is a guessing game where the computer thinks of a number between 1 and 100, and we try to guess it. After each guess, the program tells us whether we are high or low. The program goes through these steps:
  1. Think of a number between 1 and 100.
  2. Get a guess from the keyboard.
  3. Test the guess against the number.
  4. Repeat steps 2 and 3 until the guess is the number.
Here are the same steps translated into XPL0:
                begin
                MakeNumber;
                repeat  InputGuess;
                        TestGuess
                until Guess = Number
                end
Note that the program is almost word for word the same as the description of the task. First we "make a number" then we repeatedly "input a guess" and "test the guess" until it's the number.

There needs to be more to this program since it does not tell how to make a number, input a guess, or test the guess. Each of these operations is a subroutine to the main program. In XPL0 these subroutines are called procedures. Here's how to write each of these procedures.

                procedure MakeNumber;
                begin
                Number:= Ran(100) + 1
                end
This procedure generates a random number between 1 and 100 (inclusive) and puts that number into the variable called "Number".

                procedure InputGuess;
                begin
                Text(0, "Input guess: ");
                Guess:= IntIn(0)
                end
This procedure displays the message: "Input guess: " on the monitor (output device 0) and gets a number (INTeger IN) from the keyboard (input device 0). In XPL0 nine different input and output devices can be called from the program. This allows direct access to the monitor, keyboard, printer, disk files, and so forth.

                procedure TestGuess;
                begin
                if Guess = Number then Text(0, "Correct!")
                else
                        if Guess > Number then Text(0, "Too high")
                        else Text(0, "Too low");
                CrLf(0)
                end
This procedure is more complicated but still easy to understand. If the computer's number is equal to the guess then we execute one statement; if it's not equal then we execute another statement. If the numbers are equal, we tell the user that the guess is correct; if they are not equal, we test if the guess is high or low and tell the user. CrLf(0) starts a new line on the monitor (Carriage Return and Line Feed).

Here is the complete program:

                code Ran=1, CrLf=9, IntIn=10, Text=12;
                integer Guess, Number;

                procedure MakeNumber;
                begin
                Number:= Ran(100) + 1
                end;

                procedure InputGuess;
                begin
                Text(0, "Input guess: ");
                Guess:= IntIn(0)
                end;

                procedure TestGuess;
                begin
                if Guess = Number then Text(0, "Correct!")
                else
                        if Guess > Number then Text(0, "Too high")
                        else Text(0, "Too low");
                CrLf(0)
                end;

                begin
                MakeNumber;
                repeat  InputGuess;
                        TestGuess
                until Guess = Number
                end
Two new items are shown. The command word "code" is used to give names to intrinsics. Intrinsics are built-in subroutines that do common operations. For example, "Ran" is the name of the random-number intrinsic, and "Ran" is used to call this random-number generator as a subroutine. The second item is the command word "integer". This declares a name and allocates memory space for each variable that follows it.

Note that the main procedure is the last block in the program. An XPL0 program is read starting at the bottom to get the main flow and working upward to get the details in the procedures.

Here is an example of what this program does when it runs:

                Input guess: 50
                Too high
                Input guess: 25
                Too high
                Input guess: 9
                Too low
                Input guess: 18
                Correct!
It's generally a good practice to break a program up into subroutines, but something this simple would probably be written more like this:
                include c:\cxpl\codesi;
                integer Guess, Number;

                begin
                Number:= Ran(100)+1;

                repeat  Text(0, "Input guess: ");
                        Guess:= IntIn(0);

                        Text(0, if Guess = Number then "Correct!"
                                else if Guess > Number then "Too high"
                                else "Too low");
                        CrLf(0);
                until Guess = Number;
                end;

Weed

Here's a little ditty for the mathematically inclined. The liberal use of comments (set off with backslashes "\") help explain what's going on.

\Weed.xpl	15-Jan-2006
\Plots the "Weed" function
include	c:\cxpl\codesi;	\intrinsic routine declarations

define	S = 100.,	\size of plotted image (scale factor)
	N = 32000.,	\number of points plotted
	Pi2 = 3.14159265*2.;
integer	X, Y;		\graphic coordinates (pixels)
real	A, D;		\angle and distance (polar coordinates)

begin
SetVid($12);		\640x480 graphics in 16 colors
A:= 0.;			\for A:= 0 to Pi2 do...
repeat
 D:= (1.+Sin(A)) * (1.+.9*Cos(8.*A)) * (1.+.1*Cos(24.*A)) * (.9+.05*Cos(200.*A));
 X:= Fix(D*Cos(A)*S);	\convert polar to rectangular coordinates
 Y:= Fix(D*Sin(A)*S);
 Point(X+320, 400-Y, 2\green\);
 A:= A + Pi2/N;
until A >= Pi2;
X:= ChIn(1);		\wait for keystroke
SetVid($03);		\restore normal text mode
end;
Screen shot Ignore the hairy math for a moment and notice the intrinsic routine called SetVid. This sets the video mode to hex 12, which is a graphic display mode.

The intrinsics Sin and Cos convert angles to their sine and cosine values. Real numbers (also called floating-point numbers) are used along with integers. The intrinsic Fix converts a real number to its closest integer.

The Point intrinsic plots the resulting coordinates.

SetVid is also used to restore the display to its normal text mode after ChIn receives a character from the keyboard.


Clock

Here's an example of how calls to DOS functions (which also work under Windows) can be used to make a clock.

\Clock.xpl	15-Jan-2006
\Display the current time of day

inc	c:\cxpl\codesi;		\include intrinsic code declarations


proc	NumOut(N);		\Output a 2-digit number, including leading zero
int	N;
begin
if N <= 9 then ChOut(0, ^0);
IntOut(0, N);
end;	\NumOut


proc	ShowTime;		\Display current time (e.g: 12:03:45)
int	Reg;
begin
Reg:= GetReg;			\address of array that has copy of CPU registers
Reg(0):= $2C00;			\call DOS function 2C (hex)
SoftInt($21);			\DOS calls are interrupt 21 (hex)
NumOut(Reg(2) >> 8);		\the high byte of register CX contains the hours
ChOut(0, ^:);
NumOut(Reg(2) & $00FF);		\the low byte of CX contains the minutes
ChOut(0, ^:);
NumOut(Reg(3) >> 8);		\the high byte of DX contains the seconds
end;	\ShowTime


begin	\Main
repeat	ChOut(0, $0D);		\carriage return moves to the start of the line
	ShowTime;		\call routine to show the current time
until ChkKey;			\keep repeating until a key is struck
end;	\Main
The intrinsics GetReg and SoftInt are used to access DOS and BIOS routines. GetReg returns the address of an array where a copy of the processor's hardware registers are stored. Values (such as $2C00 in the example) may be stored into this array. When SoftInt is called, the values in the array are loaded into the processor's registers and the specified interrupt ($21 in the example) is called.

ChOut outputs a character (such as ":") to the display, and IntOut outputs an integer's value to the display. ChkKey checks the keyboard for a keystroke.

The AND operator "&" and the SHIFT operator ">>" manipulate the individual bits in an integer.

Command words such as "integer" and "include" can be abbreviated to their first three letters.


Matrix

Here's a 5-line screen saver:
include	c:\cxpl\codes;			   \standard library 'code' definitions
repeat	Cursor(Ran(80), Ran(25));	   \randomly select location on screen
	Attrib(if Ran(2) then 10 else 2);  \randomly select light or dark green
	ChOut(6, Ran(2)+^0);		   \randomly output an ASCII 0 or 1
until	ChkKey;				   \run until a key is struck
The intrinsic Cursor specifies the column and row on the screen where characters will appear. The upper-left corner is 0,0.

The Attrib intrinsic specifies the attributes (usually the foreground and background colors) used for displaying characters on device 6. The argument "if Ran(2) then 10 else 2" is an example of an 'if' expression, which is different than the more common 'if' statement. It's equivalent to the C expression: "rand()%2 ? 10 : 2". If Ran(2) returns a zero value, it's interpreted as being 'false', while any non-zero value (such as 1) is treated as being 'true'. Thus the argument evaluates to either 2 (for false) or 10 (for true). These are the values of the two shades of green for EGA (and newer) video modes.

Screen shot


These examples give you some idea of what XPL0 can do and how it works. This is all explained in detail in a manual that is included in the 16-bit version of the language.

Back to the XPL0 home page

Last updated: 14-Jun-2007