xpl0.txt 15-Feb-2022 XPL0 on the Raspberry Pi There are two versions of the XPL compiler: XPLR and XPL0. The XPLR compiler is based on XPLN, the IBM-PC's non-optimizing 16-bit compiler. It's extended to use 32-bit integers, fast in-lined floating point instructions, 64 megabytes of heap space, and executable program sizes up to 16 megabytes. It also supports the graphic capabilities of the RPi, which are a superset of the PC's CGA, EGA, VGA and VESA modes. The second compiler, XPL0, is loosely based on XPLPX, the PC's optimizing 32-bit compiler. However, instead of outputting assembly code directly, it outputs an iil file (a variant of the PC's i2l file). The iil file is then run through a code generator and a code scheduler. The xpliil.xpl code generator produces optimized assembly code. This is sped up slightly by running it through the xplsch.xpl code scheduler. The result is assembly code that runs up to four times faster than that produced by XPLR. The XPLR compiler is normally run by a script file called "x" that creates an executable program. The XPL0 compiler is similarly run by a script called "xx". This runs the XPL0 compiler, the xpliil code generator, the xplsch scheduler, and Linux's gcc to create an executable program. COMPATIBILITY The following is a list of the most common compatibility issues when converting XPL0 programs from their PC environment to the RPi: * "include C:\CXPL\CODESI" must be changed to "include codesr" or, better yet, omitted altogether, in which case all intrinsic names are auto- matically declared. * File names, like used with 'include' and the FOpen intrinsic, are now case-sensitive, and the convention is to use lowercase names. (Note that what DOS and Windows call "file handles" Linux calls "file descriptors.") * Arrays set up with the Reserve intrinsic must double the number of bytes if 16-bit integers were used. (This might be a good time to replace Reserved arrays with declared arrays.) * Segment arrays must be converted to regular arrays, as explained below. * The MAlloc intrinsic allocates bytes instead of (16-byte) paragraphs, and it returns the starting address instead of the starting segment address. The input argument for the Release intrinsic uses the starting address instead of a segment address. * The HexOut intrinsic displays eight digits instead of just four. However, the SetHexDigits intrinsic (104) can be called to specify a different number of digits. * Short reals must be converted to regular, 64-bit reals. Since a previous release (version 0.5 on 11-Apr-2014) there have been a couple changes to the way things work. Output device 0 no longer supports color, which makes it more consistent with the PC; and arguments for the GetMouse and GetMouseMove intrinsics are now handled a safer way. Also, printer devices 2 and 5 have been implemented. Device 5 is much faster than 2, which is noticeable when printing graphics. Multiple printers are not supported. A generic printer driver is assumed to have been installed. When using grapic modes, it's no longer necessary to execute SetVid(3) to return to text mode before exiting a program. This is because text mode is now set automatically, if necessary, after waiting for a final keystroke. Unlike on the PC, device 6 characters only work reliably in graphic, not text ($03), modes. Device 6 characters aren't restricted to character cell boundaries, such as set by the Cursor intrinsic, but instead can be positioned to any pixel using the Move intrinsic. Device 6 normally uses an 8x16-pixel serif font, device $106 uses an 8x8 sans-serif font, and device $206 uses an 8x14 sans-serif font. If the video mode is set to a low resolution then device 6 uses a shorter font so that 25 lines fill the screen. For instance, if video mode $13 is set, which is 320x200, then device 6 uses an 8x8 font (200/8 = 25). This is consistent with the way the PC works. If the foreground and background colors set by the Attrib intrinsic (69) are the same then a character's background is not drawn (contrary to what the manual states). Unlike on the PC, input from device 6 returns Esc sequences for non-ASCII keys (such as the arrow or Fn keys) rather than returning a zero. Use ChIn(1) to get the scan code, like on the PC. The Sound intrinsic (39) only emits a tone when the optimizing compiler (xx) is used. However, like always, Sound works as a rough time delay for either version of the compiler. Tones only sound approximately the same as on the PC. Short durations of 1 or 2 (18ths of seconds), and long wavelengths above 30000 microseconds might not emit any sound at all. (Making simple beeps on the RPi is unbelievably complicated!) INTRINSICS These PC intrinsics are currently not implemented (and some may never be): Chain=28, Write=30, Read=31, SoftInt=34, GetReg=35, Blit=36, Peek=37, Poke=38, POut=64, PIn=65, IntRet=66, ExtJmp=67, ExtCal=68, Equip=77, Shrink=78, and Irq=80. Attempting to use an unimplemented intrinsic will cause an error at compile time. Of course, also not available are the PC's DOS and BIOS interrupt calls. To compensate, some new intrinsics have been added. 64. Floor(real): This intrinsic rounds down to a (real) integer value. For example, Floor(3.9) returns 3.0. 65. Ceil(real): This intrinsic rounds up to a (real) integer value. For example, Ceil(3.2) returns 4.0, and Ceil(-3.2) returns -3.0. 66. Pow(realX,realY): This intrinsic returns X raised to the Y power. For example, Pow(2.0, 10.0) returns 1024.0, Pow(-2.0, 3.0) returns -3.0, and Pow(2.0, 0.5) returns 1.414.... 77. ShowMouse(Boolean): This intrinsic turns the display of the mouse pointer on and off. The mouse pointer defaults to being off. 78. MoveMouse: This intrinsic moves the displayed mouse pointer as needed to track the position of the mouse. 80. RGB:= GetPalette(Reg): This intrinsic returns the red, green, and blue intensities for the specified color register. The palette determines the colors displayed for video modes with a depth of eight bits or fewer. There are 256 color registers, thus Reg varies from 0 to 255. The returned RGB value contains the blue intensity in the low byte, the green in the next higher byte, and the red in the byte above that. Thus intensities range from 0 to 255 with this format: $00RRGGBB. (The low two bits of each intensity are not used by the hardware.) 81. Paint(X,Y,W,H,Image,W2): Actually this is not completely new. It was introduced in the PC's 32-bit compiler, XPLPX. It's explained in detail below. 82. Time:= GetTime: This returns the current time in microseconds. The unsigned 32-bit integer rolls over about every hour and 11 minutes. 83. BackUp: This intrinsic was also introduced in XPLPX. It enables the last byte read from any input device to be reread. It's like C's ungetc function. It's handy, for example, when you want to provide the opportunity for a user to either type in a new number or accept the current value by typing a non-numeric character such as the Enter key. BackUp enables the first digit to be reread by the IntIn intrinsic. Here's an example: int Number, Digit; [Number:= 123; \default value IntOut(0, Number); CrLf(0); Digit:= ChIn(0); if Digit>=^0 & Digit<=^9 then \change it [BackUp; Number:= IntIn(0)]; IntOut(0, Number); CrLf(0); \confirm result ] 84: SetFB(width, height, depth): This sets the displayed frame buffer to a specified width, height, and depth. Widths should be multiples of 32 pixels. Heights can be almost anything, but pixels are always square rather than being stretched to fill the screen (like on the PC). Depths can be 4, 8, 16, 24, or 32 bits. A depth of 8 uses a palette for colors (like the PC's VGA mode $13). A depth of 4 is the same as depth 8, but bit 7 specifies that the color selected by the lower seven bits is XORed with the color already present on the screen. Depths of 16 and 24 are like the PC's high color and true color modes. A depth of 32 provides a byte that specifies (alpha) transparency that ranges from 0 being completely transparent (invisible) to $FF being completely opaque. Since calling this intrinsic is regarded as setting a graphic mode, the flashing cursor is turned off. It can be turned on if desired with the ShowCursor intrinsic (88). The SetVid intrinsic (45) configures the standard CGA, EGA, and VGA modes, and these VESA modes: Bits 640x480 800x600 1024x768 1280x1024 ---- ------- ------- -------- --------- 4 $12 $102 $104 $106 8 $101 $103 $105 $107 16 $111 $114 $117 $11A 24 $112 $115 $118 $11B Sometime near the beginning of 2015, Raspbian swapped the red and blue colors used in the 24- and 32-bit graphic modes. Earlier versions of Raspbian can be made consistent with this new standard by adding framebuffer_swap=1 to /boot/config.txt. 85. Success:= OpenMouse: This initializes the mouse and sets it to the center of the screen, although its pointer remains hidden. This intrinsic returns 'false' if unsuccessful. If a mouse is not connected, this does not detect its absence. Since the mouse is now opened automatically, this intrinsic is no longer needed. 86. Address:= GetMouse: This returns the address of an integer array that contains the state of the mouse. The integers are: 0: X position (from left edge of screen, in pixels) 1: Y position (down from top of screen, in pixels) 2: buttons: bit 0 set = left button down bit 1 set = right button down bit 2 set = middle button down For example, if Address(2) = 3, it means that both the left and right buttons are currently being pressed. 87. Address:= GetMouseMove: This returns the address of an integer array that contains the distance that the mouse moved (in pixels) since the previous call to this intrinsic. The integers are: 0: change in X position (leftward is positive, in pixels) 1: change in Y position (downward is positive, in pixels) 88. ShowCursor(Boolean): This turns the flashing cursor off or on. The flashing cursor defaults to being on for text display modes and off for graphic display modes. The flashing cursor is automatically turned back on when a program exits. 89. Char:= GetKey: This intrinsic is obsolete now that its features are included in ChIn(1). This gets the ASCII value of a character struck on the keyboard. It's like the ChIn intrinsic, but it also handles non-ASCII keys in a convenient way. Linux's read(stdin) returns escape sequences for the function keys, arrow keys, and even for the Esc key itself. Since this can complicate things, this intrinsic instead converts these non- ASCII keys to the negative value of their scan codes (which are listed in appendix A.4 of the XPL0 manual). F11 and F12 and Ctrl+Function keys are not available. Only the Alt values for Alt+A through Alt+Z are available. (The Alt+Function keys are used by Linux to switch terminals.) This intrinsic does not echo characters to the screen, and Ctrl+C does not abort the program. If the Pause key is struck, another key must be struck before this will return (with a zero). 90. SetPalette(Register,R,G,B): This changes a color in the 256-color palette that's used with 4- and 8-bit depth graphics. The R,G,B values range from 0 through 255, but only the high 6 bits are actually used (just like VGA on the PC). A 4- or 8-bit graphic mode must already be set up with either intrinsic SetFB (84) or SetVid (45). Unfortunately, SetPalette has a couple bizarre problems (due to the Linux frame buffer driver): color register 15 or 255 must be set to make any register actually change, and (incredibly) the frame buffer memory might be zeroed (erased) as a side effect. This latter problem was fixed in the September 8, 2014 version of Raspbian. (The Linux command "uname -a" displays the version and date.) 91. Address:= GetFont(Set): This is used to return the address of a 256-character font table. If Set=0 then Address points to the 8x16 table, which has 16 bytes per character. If Set=1 then the address of the 8x8 table is returned, and if Set=2 the address of the 8x14 table is returned. One use for accessing these font tables is to make banner programs with giant letters. 92. SetFont(Height,Address): This changes the character font table used by device 6. Height is the number of bytes per character, which is also the height of a character cell in pixels. Character cells are always 8 pixels wide. "Address" is the location of the replacement table. There's no way to change the font used by devices $106 and $206. 93. Bits:= GetShiftKeys: This returns the current state of some keyboard keys. If Bits=1 then the right Shift key is held down. If Bits=4 then a Ctrl key is down, and if Bits=8 then an Alt key is down. If, for example, Bits=$D then all three keys are currently held down. If Bits=$20 then Num Lock is enabled. The following keys are assigned bits: $8000_0000 Esc $4000_0000 Spacebar $8_0000 W $8000 I $0800 Up Arrow $0008 Alt $4_0000 A $4000 J $0400 Left Arrow $0004 Ctrl $2_0000 S $2000 K $0200 Down Arrow $0002 Left Shift $1_0000 D $1000 L $0100 Right Arrow $0001 Right Shift 94. DelayUS(Duration): This delays Duration microseconds. For example, DelayUS(54945) delays about 1/18 of a second, which is the duration of an IBM-PC system clock tick. 95. Address:= GetDateTime: This returns the address of a byte (char) array containing the current system date and time. The bytes are: 0: year since 1900 1: month (1..12) 2: day (1..31) 3: hour (0..23) 4: minute (0..59) 5: second (0..59) 6: hundredths of seconds (0..99) 7: day of week (0=Sun, 1=Mon, ... 6=Sat) 96. InsertKey(Char): This inserts a character into the keyboard's input buffer. The next key read from ChIn(1) will be the inserted character. Several characters can be inserted before being read out in sequence by ChIn(1). Keys can also be read out with ChIn(0), but there may already be other characters ahead of the inserted keys in its buffer. This intrinsic is useful for GUI buttons and menu items that use shortcut keys. 97. Address:= GetFB: This returns the address of an integer array that contains information about the currently displayed frame buffer. The integers are: 0: width (in pixels) 1: height (in pixels) 2: depth (in bits per pixel, e.g: 8, 16, 32) 3: frame buffer's memory address 4: graphic pen horizontal position (penx) 5: graphic pen vertical position (peny) 98. WaitForVSync: This waits for the vertical sync signal that occurs every 1/60th of a second at the beginning of each video frame. It's useful for animations. This signal was only made available in versions of Raspbian on and after September 8, 2014. 99. ShowPage(Page): This sets the page of the video memory that gets displayed. There are two pages: 0 and 1. Page 1 immediately follows page 0 in memory. Images can be built up out of sight on page 1, while page 0 is being displayed, by adding an offset to the Y coordinate equal to the screen height. For example: Point(X, Y+480, $0F). This intrinsic works in versions of Raspbian on and after September 8, 2014. This intrinsic was previously named "DisplayPage." 100. CopyMem(Dst, Src, Size): This copies an array of bytes from the address in Src to the address in Dst. It's equivalent to, but many times faster than: for I:= 0 to Size-1 do Dst(I):= Src(I); It also works if the source and destination areas overlap. 101. FillMem(Addr, Value, Size): This fills an array of bytes. It's equivalent to, but many times faster than: for I:= 0 to Size-1 do Addr(I):= Value; 102. NewAddress:= ReallocMem(OldAddress, Bytes): This resizes (usually enlarges) a block of memory that was previously set up by MAlloc (73) or modified by ReallocMem. OldAddress is the location of the block previously allocated. Bytes is the new size of the block, in bytes. Reallocated memory remains available until the Release intrinsic (74) is called. If insufficient memory is available (or Bytes is 0) then RUN-TIME ERROR 2: OUT OF MEMORY is trapped. (If untrapped, NewAddress is set to OldAddress.) When enlarging a block of memory, the contents of the original block is copied to a newly allocated block if necessary. 103. PlaySoundFile("filename.ext"): This starts playing a sound file, which can be one of these types: wav, mp3, ogg, and many others. The xpl program continues to run while the sound is playing. (Appending "-o hdmi" changes output from the phone jack to hdmi.) 104. SetHexDigits(digits): This sets the number of digits output by the HexOut intrinsic (27). For example, if you set the argument to 3 and output the number $12, HexOut will output "012". If the number being output is larger than the number of specified digits, all the digits are output. For the example, $1234 would output "1234". The number of digits output defaults to eight for compatibility with old programs. - - - The 32-bit version of the HexIn intrinsic (26) works slightly differently than the 16-bit version. The 32-bit version returns after reading 8 hex digits whether or not the digits are terminated with a non-hex character (such as a space or carriage return). The 16-bit PC version only returns when the digits are terminated. Thus the value returned for 16-bit XPL0 is the last 4 digits if more than 4 digits are input, and the value returned for 32-bit XPL0 is the first 8 digits if more than 8 digits are input. There are new command words such as 'fix', 'float', 'sqrt', 'sq', and 'abs'. These perform their functions faster than their corresponding intrinsics by using in-lined code instead of calling a subroutine. Also 'abs', 'sqrt' and 'sq' work for both real and integer arguments. The commands 'rem' and 'swap' are not new. They're just faster versions of the intrinsics Rem and Swap. PAINT INTRINSIC A relatively new intrinsic has been added called Paint (81). It's used to quickly copy an image to video memory, which is useful for animations. It has six integer arguments: Paint(X, Y, W, H, Image, W2); X,Y are the coordinates where the upper-left corner of the Image data will be displayed on the screen. W,H are the width and height (in pixels) of the Image data. "Image" is the address of the Image data array. W2 is the actual width (in pixels) of the Image data array. (This argument provides some neat features, but it's currently not used.) For 4- and 8-bit color modes ($0D, $12, $102, $104, $106, $13, $101, $103, $105, $107) the Image array is reserved as a byte array, for example: char Image(640*480). For the 16-bit color modes ($111, $114, $117, $11A) the size of the Image array must be doubled, for example: char Image(640*480*2). You (the programmer) must combine a pair of bytes for each pixel, and deal with the way the hardware packs the colors into the resulting 16-bit half- word, which is like this: bit: F E D C B A 9 8 7 6 5 4 3 2 1 0 color: r r r r r g g g g g g b b b b b For the 24-bit color modes ($112, $115, $118, $11B) Image is reserved as an integer array, for example: int Image(640*480). The order of the colors in the 4-byte integer is: $IIRRGGBB, where II is the intensity, or brightness, ranging from 0 (black) to $FF (full brightness). The colors are the same if SetFB (84) is used to set the depth to 32 bits. WARNING: The brightness feature does not work in some distributions of the operating system. To enable it, remove "framebuffer_ignore_alpha=1" in /boot/config.txt. SEGMENTS Segment addressing is supported by the 16-bit versions of XPL0. However, since 32-bit XPL0 can directly access up to 4GB of memory, and since segment addressing is a confusing kludge, there's little incentive to continue supporting it, and it's not done on the Raspberry Pi. Fortunately it's easy to modify existing 16-bit .xpl code to eliminate segment arrays, and the resulting code is simpler. For example: seg char Buffer(1); begin Buffer(0):= MAlloc(1024); \1024 paragraphs of 16 bytes each for I:= 0, 16383 do Buffer(0, I):= 0; . . . can be simplified using a normal one-dimensional array: char Buffer(16384); begin for I:= 0, 16383 do Buffer(I):= 0; . . . IN-LINE ASSEMBLY CODE Assembly code can be inserted directly into an xpl file. The command word "asm" is used to insert assembly code. Characters that follow are sent to the output file (.s). For example: asm mov r0, #102 @ comment asm ldr r1, Frog @ Comment asm add r0, r1 asm str r0, Frog Assembly code must be written in lowercase characters except where an xpl variable or constant name is used. Those are written the usual way with at least the first letter capitalized. This enables the compiler to distinguish them from the rest of the assembly code so that it can substitute them with their assembly code representations. For instance, in the above example, "Frog" might be replaced with something like [r11,#4]. Capital letters may be used in comments preceded with an at-sign (@) because they are ignored by the compiler (as well as the assembler). A practical application might be to replace xpl code with more efficient assembly code to speed up a critical loop. The following example shows an efficient way to reverse the order of the bytes in an integer. Note that several lines of assembly code can be enclosed in braces: asm {ldr r0, Frog @ 0x12345678 rev r0, r0 str r0, Frog @ 0x78563412 } Another application is to call Linux system routines. For example, this deletes any existing file in the current directory whose name is pointed to by the variable OutBack (which must be a zero-terminated string). It then renames the file whose name is pointed to by OutFile to the name in OutBack. asm {ldr r0, OutBack @ delete file if it exists bl remove ldr r0, OutFile @ rename, for example, outfile.ext ldr r1, OutBack @ to outfile.bak bl rename } Besides variable names, defined constants can also be used. Note that there is no # preceding MinusOne since it's generated automatically. Also note that hex numbers in assembly code are represented by 0x instead of $. def MinusOne = -1; asm mov r0, #-1 asm mov r1, MinusOne asm mov r2, MinusOne + 0x41 Registers r4 to r15 should not be altered. If you must use them, use push and pop to preserve them. Line labels can be used if they are in lowercase and if they don't conflict with names already used. Labels such as "l" (ell) followed by a number don't conflict with labels generated by the compilers. If there is a conflict, the assembler will display an error message. Variables should be either local or at global level 0. Intermediate level variables aren't supported.