WHITE LIGHTNING - SECTION 3 IDEAL by John Gross IDEAL has been designed to facilitate the manipulation of sprites and screen data and, with its 100 or so instructions, provides a powerful and comprehensive animation sub-language. Time should be taken to gain familiarity with the available commands before undertaking the first big project. Remember that, by using the colon definitions of Forth, new words can very easily be added to the language, built from the existing Forth and graphics words. Mastering this technique effectively will save a great deal of space and, in many cases, execution time. SPRITE A sprite is a software controllable graphics character. White Lightning supports up to 255 sprites with user-selectable dimensions. SCREEN WINDOWS A screen window is a section of the screen defined by the four variables COL, ROW, HGT and LEN. Columns are in the range 0 to 31, Rows are in the range 0 to 23, Heights are in the range 1 to 24, and Lengths are in the range 1 to 32. The unit for each is the character. COL and ROW specify the position of the top left-hand corner of the window, with ROW 0 at the top of the screen and COL 0 on the left-hand side of the screen. HGT and LEN define the size of the window. To see an example type: 5 ROW ! 6 COL ! 4 HGT ! 3 LEN ! INVV [CR] The window has been inverted to mark it out. SPRITE WINDOWS A sprite window is a section of the sprite defined by the Forth variables SCOL, SROW, HGT and LEN. This time SCOL and SROW specify the position of the top left-hand corner of the sprite window. HGT and LEN are again used to specify the dimensions of the window. SPRITE SPACE Sprite space is the area of memory containing the previously defined sprites. The variable SPST holds the address of the start of the sprite space, so SPST should never be loaded with a new value unless a COLD# command is being executed, or you're quite sure you know what you're doing!! SPND points to the first free byte after sprite space. SPND should never be higher than FFF0 Hex if routines are being executed in Background. PIXEL DATA For those not acquainted with the workings of the Spectrum screen display, each character on the screen is produced as follows: each character cell is an array of 64 (8x8) pixels. A pixel is a 'dot' which can be INK colour or PAPER colour. The bytes which define a particular character or block of characters are referred to as pixel data. ATTRIBUTE DATA The colour of the INK and PAPER in each particular cell, together with the BRIGHT and FLASH attributes of the character, are controlled by a separate byte. The bytes which define the attributes of the block of characters are referred to as Attribute data. Pixel data and Attribute data are frequently treated as separate entities. SCREEN OPERATIONS Often, it is required to carry out an operation, such as a scroll or a reflection, on one particular section of the screen. Four variables are used to define a screen window; these are COL, ROW, HGT and LEN. The co-ordinates of the top left-hand corner are held in COL and ROW, where COL is measured from the left and ROW from the top. Both values are in characters. HGT and LEN are the dimensions of the window. COL + LEN must be in the range 1 to 32, and ROW + HGT must be in the range 1 to 24. Commands in this group are postfixed with a "V"; eg. WRL1V, INVV, MIRV. SCREEN/SPRITE OPERATIONS These are operations between the screen and a sprite. The dimensions of the sprite are used as the dimensions of the screen window, and COL and ROW are used to give the co-ordinates of the top left of the window. If the window overlaps the edge of the screen the command will not execute. Typical commands in this group are the PUTs and GETs, which move sprites between the screen and memory. Commands in this group are postfixed with an "S"; eg. PUTBLS, GETXRS. SPRITE OPERATIONS These cover more or less the same commands as the screen operations, but this time a complete sprite is used instead of a screen window. The only parameter required is the sprite number stored in SPN. Commands in this group are postfixed with a "M"; eg. WRR4M, ATTUPM. SCREEN/SPRITE WINDOW OPERATIONS These are operations between a screen window and a sprite window. As before, ROW, COL, HGT and LEN define the screen window; but this time, SCOL and SROW are used to define the position of the window within the sprite. SCOL and SROW are measured in characters, SROW from the top and SCOL from the left. If SROW + HGT is greater than 24 or the sprite height, or SCOL + LEN is greater than 32 or the sprite width, the commands will not execute. These commands are postfixed with an "S"; eg. GWATTS, PWORS. SPRITE/SPRITE WINDOW OPERATIONS These are operations between a whole sprite and a sprite window. The two sprite numbers are held in SP1 (the whole sprite) and SP2 (the sprite which contains the window). The dimensions of the window are the dimensions of the sprite whose number is held in SP1. The position of the window in the sprite whose number is held in SP2 is specified by SCOL and SROW. Commands in this group are postfixed with a "M"; eg. GWATTM, PWNDM. SPRITE/SPRITE OPERATIONS These are operations between sprites which usually have the same dimensions, or, as in the case of the SPIN command, transposed dimensions. SP1 and SP2 hold the sprite numbers. Commands in this group are postfixed with a "M"; eg. COPORM, SPINM. DUMMY SPRITE A dummy sprite is a sprite which does not contain data for display. It may be used, for instance, to store a machine code subroutine, an array, or maybe a collision detection sprite. IDEAL VARIABLES The IDEAL sub-language uses 27 variables in all; these are: VARIABLE USE ROW Holds the row (Y co-ord) in characters, measured from the top of the screen (0-23). LEN Holds the width of the current screen window (1-32), or the width of the sprite being defined (1-255). Units are characters. COL Holds the column (X co-ord) in characters, measured from the left of the screen (0-31). HGT Holds the height of the current screen window (1-24), or the height of the sprite being defined (1-255). Units are characters. SROW Holds the row (Y co-ord) within the sprite whose number is held in SP2, measured from the top (0-(HGT-1)). Units are characters. SCOL Holds the column (X co-ord) within the sprite whose number is held in SP2, measured from the left (0-(LEN-1)). Units are characters. NPX Holds the size and direction of the vertical scrolls. Positive scrolls are upward and negative downward. Units are pixels and not characters. SPN Used to hold the sprite number for those words which operate on only one sprite (1-255). SP1 Where operations involve a sprite and a sprite window, SP1 holds the number of the sprite which does not contain the window (1-255). Where a sprite is to be spun into a second sprite, SP1 holds the number of the first sprite (1-255). SP2 Where operations involve a sprite and a sprite window, SP2 holds the number of the sprite which does contain the window (1-255). Where a sprite is to be spun into a second sprite, SP2 holds the number of the second sprite (1-255). SPST Holds the start address of the sprite space. SPND Holds the end address of the sprite space; ie. the first free byte after the last sprite. This is the address of the foreground scrolling buffer. SLEN Holds the length of the sprite space to be cleared by the COLD# command. MLEN Holds the size and direction of the relocation. A positive value relocates sprites to higher memory and a negative value to lower memory. SPTR On return from the TEST command, SPTR points to the start of the sprite. DPTR On return from the TEST command, DPTR points to the start of the pixel data. Alternate Variables Eleven of the previously listed variables are replicated for use by the background program. These are ROW', COL', LEN', HGT', NPX', SPN', SP1', SP2', SROW', SCOL' and SPND'. When a word is executed in background, the eleven alternate variables are automatically switched with the eleven background variables; when execution is complete, the variables are switched again to restore them to their former state. Suppose, for example, that the background program is to scroll left 1 pixel with wrap (WRL1V), with an area of screen 6 characters wide and 4 characters high, with top left co-ordinates row = 5, column = 7. Now type the following: CLS 6 LEN' ! 4 HGT' ! 5 ROW' ! 7 COL' ! ' WRL1V INT-ON [CR] The window is now scrolling but you can't see it, because there is no data in the window. Type VLIST [CR] and watch the data as it scrolls through the window. The data in the window will be slanting to the left, because the foreground program was scrolling up at the same time as the background program scrolled left. Leaving the background program running, type: 10 LEN' ! [CR] and the window will widen. Type: INT-OFF ' WRR8V INT-ON [CR] and the screen will scroll to the right, this time much more rapidly. Now type: INT-OFF [CR] to halt the background program. In the above example we set background variables from foreground. If we were to set the background variables in the background program, then foreground and background variables would already have been switched before execution. To set up the same windows, we would now have to use ROW, COL, HGT and LEN, and not ROW', COL', HGT' and LEN'. To define a word to do this, type: : FRED 6 LEN ! 4 HGT ! 1 ROW ! 2 COL ! WRL1V ; [CR] To run "FRED" in background, type: ' FRED INT-ON [CR] Since the variables were this time being assigned values in the background program itself, the alternate variables set was being accessed with the normal names. Now type: INT-OFF FORGET FRED [CR] to halt the background program and clear the definition. Operating in this way, a word will work in foreground or background without any need to change the variable names. The alternate variables are only used directly by a foreground program that is required to change background variables, or a background program that is required to change foreground variables. If the previous example is a little confusing at first, carry out your own experimentation until it becomes clear. ERRORS The graphics commands do not in many cases provide the user with error messages, but instead, if an attempt is made to execute a command which is not possible, for instance scrolling a screen window which lies partly off the screen, the command will simply not execute. This does have the advantage that the user is freed from testing edge conditions, but does mean that a little extra care needs to be exercised. See the words ADJM and ADJV. Errors are generated if an attempt is made to access a non-existent sprite, or to insert an already existing sprite using ISPRITE. SPRITE AND BUFFER ORGANISATION Before discussing the sprite manipulation commands in detail, it is worth describing the organisation of sprites in some detail. The user does not need this information, but it is made available for interest and an overall appreciation of the language structure. Sprites are stored as one contiguous block of data whose start address is held in the variable SPST. The first free byte after the final sprite contains a zero and this address is held in the variable SPND. The format of each sprite is as follows: 1st byte The sprite number, which must be in the range 1 to 255. 2nd & 3rd bytes The address of the start of the next sprite. 4th byte The width of the sprite in characters (1 to 255). 5th byte The height of the sprite in characters (1 to 255). 8*height*length bytes Pixel data. Height*length bytes Attribute data. This means that the total space allocated to each sprite is 9*height*length+5 bytes. Sprite numbers do not need to run sequentially, but the earlier a sprite is defined the more rapid its access. LOADING SPRITES FROM TAPE Sprites saved to tape using the development software can be loaded into the main program at the start of the session when the "LOAD SPRITES Y/N" prompt appears. If sprites are loaded in this manner, the sprite data, together with the necessary pointers, will be loaded. SPST and SPND are automatically set and the sprites will be ready for use. If sprites are saved and later loaded from White Lightning, SPST and SPND will need to be set by hand. THE BUFFER When vertical scrolling takes place, be it for pixel data or attributes, with or without wrap, data has to be temporarily stored for later retrieval. If a vertical scroll is executed by the foreground program then the buffer is pointed to by SPND, so the space immediately above sprites is used. When the sprite development software is used, a prompt is issued at the start of the session, which asks the user whether or not buffer size should be changed. If the buffer size is not changed then it remains 256 bytes long. The user can enter a larger or smaller value if preferred, though the default value of 256 will cover most eventualities. Scrolling attributes uses one byte for each column of the width; scrolling pixel data uses one byte for each column of the width multiplied by the number of pixels being scrolled (the value held in NPX). The buffer space need only be large enough to accommodate the largest scroll, as foreground scrolls will not take place simultaneously. Suppose a sprite or screen window 8 characters high by 4 characters wide is to be scrolled by 10 pixels. (The direction, ie. the sign of NPX, does not matter.) The space required is 4*10 = 40 bytes. If you find at some later stage that you have not allowed enough buffer space, you can always relocate sprite space downward and likewise, if you have more than you need, you can relocate upward. BACKGROUND SCROLLING When programs are executed in background it is risky to share a common scrolling buffer, since the background program could execute while the foreground program is using the buffer. For this reason, a second buffer pointer is used for background scrolling. The variable holding the address of the background buffer is SPND'. When White Lightning is first entered, SPND' points to the 256 free bytes in the printer buffer at decimal 23296. The user can move this buffer by changing the value held in SPND'. It is not a bad idea to allocate enough buffer space for both foreground and background scrolling above the sprite space and assign SPND' to point to the space after the foreground buffer. Suppose, for example, the foreground program requires 200 bytes and the background 300 bytes, with the buffer currently set to 256 bytes. 500 bytes are needed in all, so sprites need to be relocated down by 500-256 = 244 bytes. Type: -244 MLEN ! RELOCATE [CR] Note that MLEN is now negative since relocation is downward. SPND' should be set 200 bytes into the buffer to leave space for the foreground data. To do this type: SPND @ 200 + SPND' ! [CR] If memory is really tight and the buffer has to be shared, then the background program can be temporarily disabled using DI, but as soon as the vertical scroll is executed and EI must be executed to re-enable the background program. If, for example, a screen window 12 characters wide and 4 characters high is to be scrolled vertically by 8 pixels with wrap, and the background program is to be inhibited, type: 0 0 AT 8 NPX ! 12 LEN ! 4 HGT ! 4 COL ! 4 ROW ! DI WCRV EI [CR] It is best to re-enable the background program as soon as possible, preferably, as above, the next word. Until you get used to the package leave the buffers as they are on entry to White Lightning. Use ISPRITE and DSPRITE, and not SPRITE and WIPE, to define new sprites. The only time you really need to worry about changing buffer sizes or positions is when you have a dire need to save a few extra bytes. IDEAL MNEMONICS To help yourself become acquainted with the language, it is worth noting the following: 1. Words which involve only the screen are postfixed "V" for "Video Operations". 2. Words which involve only operations on or between sprites are postfixed with "M" for "Memory Operations". 3. Words which involve operations between the screen and sprites are postfixed with "S" for "Screen/Sprite Operations". 4. BLS indicates that data is being "block shifted" to a destination and will replace whatever was there. 5. ORS indicates that data is being "Shifted and ORed" so the destination data will be ORed with the source data. 6. NDS indicates that data is to be "Shifted and ANDed". 7. XRS indicates that data is to be "Shifted and XORed". 8. ATT indicates that the operation is on attribute data. 9. WR indicates that data will be scrolled with wrap. 10. SC indicates that data will be scrolled without wrap. 11. GW "Get Window" indicates that data is being moved from a window into a sprite. 12. PW "Put Window" indicates that data is being put into a window from a sprite. 13. COP indicates an operation between two sprites with the same dimensions. THE BASIC INTERFACE The BASIC interface was provided to increase the flexibility of the language and allow the newcomer to Forth a gradual transition. Some applications are actually more suited to BASIC, but for games writing in general Forth is much more appropriate, and we hope that this facility will not discourage people from experimenting with Forth. There are four words to master at the Forth end and three USR calls to master at the BASIC end. Do not use CLEAR or NEW whilst in BASIC. The Command Level When White Lightning is first entered from a COLD start, BASIC is located beneath Forth and there is approximately 1k of program space if microdrives are not in use. This is ample space if BASIC is only to be used as command level, to LOAD and SAVE for instance, but if programs are to be written you will need to execute the RESERVE command. For the time being, however, let's just consider operation at the command level. To enter BASIC from Forth type: PROG [CR] To re-enter Forth from BASIC just use: PRINT USR 24836 [CR] This is the normal WARM start entry. Note that PRINT USR must be used and not RANDOMIZE USR, or an OUT OF SCREEN error may occur. BASIC AS A SUBROUTINE At the next level, lines of BASIC can be executed as if they were subroutines and then return made to your Forth program. The word at the Forth end is GOTO. To return to Forth and continue execution use PRINT USR 30006. To begin with, space needs to be made in the dictionary for the basic program. The word used to do this is RESERVE. What RESERVE actually does is to make space in the dictionary and reset BASIC's system variables to point to this new area. This does mean, however, that if a second reserve is done, without FORGETting the old space, then the old space is lost and can never be re-accessed. Do not execute a Forth COLD start while BASIC is reserved or a RAMTOP error may occur if insufficient memory is reserved. Always execute PROG as the next command after RESERVE. As an example, try the following: DECIMAL 2000 RESERVE PROG [CR] This will set up the BASIC space and then enter it at command mode. The following lines of BASIC can now be entered: 1000 PRINT "LINE 1000 OF BASIC": PRINT USR 30006 2000 PRINT "LINE 2000 OF BASIC" 2010 FOR i=1 TO 8: PRINT i: NEXT i 2020 PRINT USR 30006 After entering these lines type: PRINT USR 24836 to re-enter Forth at command level. Let's now define a word which executes some Forth, some BASIC, some more Forth, some more BASIC, and finally some more Forth. To begin executing BASIC at a particular line, all that we need to do is put the line number on the stack and then execute GOTO. Try the following: : FBDEM ." IN FORTH " CR 1000 GOTO ." BACK IN FORTH " [CR] CR 2000 GOTO ." FORTH AGAIN " ; [CR] Now type FBDEM [CR] A more useful application would be to define words to handle cassette loading and saving. BASIC source is saved and loaded in the normal way from the reserved BASIC area. FORTH AS A SUBROUTINE If you're an "out and out BASIC person" you're probably more likely to want to execute Forth as a subroutine. To return to BASIC from Forth use the word RETUSR. To call Forth from BASIC use RANDOMIZE USR 30000. Note that on this occasion it is a RANDOMIZE USR and not a PRINT USR. Using the previously reserved space we can try another example. First type: PROG [CR] to enter BASIC, then add the following lines: 3000 PRINT " CALLING FORTH ": RANDOMIZE USR 30000 3010 PRINT " BACK IN BASIC ": PRINT USR 30006 Now type PRINT USR 24836 to re-enter Forth. Now type: : BFDEM ." GOTO BASIC " CR 30000 GOTO ." FORTH CALLED " [CR] RETUSR ." ENDING IN FORTH " CR ; [CR] Now type BFDEM [CR] to see the result. Now type FORGET FBDEM [CR] Passing Parameters Forth variables can easily be PEEKed and POKEd from BASIC and used not only to pass data, but also to control the execution of Forth. As an example, suppose we wished to select one of four Forth words at any one time with a call from BASIC. Let the Forth words simply be ." WORD1 ", ." WORD2 ", ." WORD3 ", ." WORD4 ". First we'll need a variable to pass the parameter, so type: 0 VARIABLE CONTROL CONTROL U. [CR] This will set up a variable called CONTROL, set it to zero and then print the address of the least significant byte, which we'll use to pass the information. For the sake of this example suppose the address was 50000. We'll now use the CASE construct to select the word to execute. Use the following definition: : SELECT CASE 1 OF ." WORD1 " ENDOF 2 OF ." WORD2 " [CR] ENDOF 2 OF ." WORD3 " ENDOF 4 OF ." WORD4 " ENDOF [CR] ENDCASE CR ; [CR] If the value in CONTROL is 1 to 4, the appropriate word will be executed. : RUN 4000 GOTO BEGIN CONTROL @ [CR] DUP SELECT DUP IF RETUSR ENDIF 0= UNTIL ; [CR] The BASIC program is initially entered at 4000 and could take the following form: 4000 REM REPLACE ADDRESS 50000 WITH THE ADDRESS OF CONTROL 4010 PRINT " EXECUTE WORD1 ": GOSUB 5000 4020 PRINT " EXECUTE WORD2 ": GOSUB 5010 4030 PRINT " EXECUTE WORD3 ": GOSUB 5020 4040 PRINT " EXECUTE WORD4 ": GOSUB 5030 4050 PRINT " FINISH ": POKE 50000,0: PRINT USR 30006 5000 POKE 50000,1: RANDOMIZE USR 30000: RETURN 5010 POKE 50000,2: RANDOMIZE USR 30000: RETURN 5020 POKE 50000,3: RANDOMIZE USR 30000: RETURN 5030 POKE 50000,4: RANDOMIZE USR 30000: RETURN Note that when the final return is made to Forth a PRINT USR 30006 is used. If a RANDOMIZE USR 30006 call is made to Forth a RETUSR must be executed of the BASIC stack will be left corrupted. To reset the stack if it has been corrupted, use PROG to enter BASIC and then re-enter Forth with the WARM start, PRINT USR 24836. PROGRAM DEVELOPMENT At any one time, there are up to five areas of development: Forth source code, BASIC source code, sprites, the Forth language itself, and finally the compiled and completed program. Forth Source As previously discussed under the section on editing, Forth source is divided into screens, each of 512 bytes in length. Each screen can be individually loaded, saved and compiled in any order required. Screens can even be saved and then loaded back into different screens. The real advantage of this comes when you're writing really large programs. As sprite space becomes large, it will work down over the higher screens, and this can be clearly seen when an attempt is made to List them. Don't CLEAR these screens or the sprite data will be lost! If really large programs are required and sprites have over-run the top screens, then programs can be compiled a few screens at a time, loading each time into the available screens, compiling and then loading the next section. Of course, you don't need to load the sprites until compilation is complete, but it's useful to have the facility just in case. To save Forth source you'll need to consult Table 1, the table of screen addresses. If, for instance, you wanted to save screens 6 to 11, then the start address would be 52224 decimal, and the length just 6 times 512. Type 6 512 * . [CR] to find this figure, which is 3072. To save the source, type PROG to enter BASIC and then type: SAVE "filename" CODE 52224,3072 To re-enter White Lightning, type PRINT USR 24836 to do a WARM start. To load the source, either type Y in response to the LOAD SOURCE Y/N prompt at the beginning of the session, or exit to BASIC using PROG, then type: LOAD "filename" CODE where filename is optional. If you want to load the code into a different screen area from that in which it was saved, type: LOAD "filename" CODE start,length where 'start' is the address of the screen to be loaded, and 'length' is the number of screens to be loaded multiplied by 512. Again, White Lightning should be re-entered with a PRINT USR 24836. Do not use RANDOMIZE USR 24836 or an OUT OF SCREEN error may occur. BASIC Source Before BASIC source can be used in White Lightning programs, the user must execute a RESERVE to make space for the BASIC program. To reserve, for example, 1k, type: DECIMAL 1024 RESERVE. This will allocate 1024 bytes for BASIC source code within the dictionary. If at some later stage you execute a second RESERVE the previous 1024 bytes are not reclaimed, so if you find you have not allocated enough space, save the BASIC source, FORGET all previous definitions, execute a COLD START, and start the compilation from scratch. You can now do a second RESERVE. To save BASIC source, type PROG [CR] to enter BASIC if you're not already there, then just type SAVE "filename" as normal and re-enter White Lightning with PRINT USR 24836. Likewise, source can be reloaded by entering BASIC with a PROG, using LOAD "filename" and then re-entering Forth with a PRINT USR 24836. Sprites Sprites can be saved from White Lightning and then re-loaded into White Lightning, but sprites saved by White Lightning cannot be loaded into the sprite development software, which requires the additional array information preceding sprites which is not saved by White Lightning. Sprite development should always be done using the development software, but if you do wish to save the sprites for later merging then do the following: 1. Find the start of sprites by typing SPST @ U. 2. Find the length to save by typing SPND @ SPST @ - 1+ U. 3. Note the start and length, then return to BASIC using PROG. 4. Save using SAVE "filename" CODE start,length. 5. Re-enter White Lightning using PRINT USR 24836. Merging Sprites Two blocks of sprites can be merged together in the main program using the following procedure: 1. Make a note of the SPST and SPND values of the second block to be merged. These are displayed by the sprite development software. 2. Load the main White Lightning package and then load the first block of sprites in response to the "LOAD SPRITES Y/N" prompt. 3. Load source as required, and once in the main program relocate the first block of sprites downwards by the size of the second block. Suppose the decimal values for SPST and SPND of the second block were 60000 and 65280 respectively, then type: DECIMAL 60000 65280 - SPST @ + U. [CR] (The DECIMAL is not required if you are already in DECIMAL mode.) This will calculate the new start after relocation. It is well worth checking that this will not run over your source code, so here is a quick calculation that will tell you if you have enough space. You need to know the highest screen number that you intend to use, for example screen 18. Type: 18 512 * 49664 + U. [CR] This will print the first free byte after screen 18. So long as this result is lower than the new sprite start after relocation you can proceed. Again, using the previous example where the block to be merged has SPST and SPND of 60000 and 65280 respectively, the line to type is: DECIMAL 60000 65280 - MLEN ! RELOCATE [CR] The RELOCATE command uses the value held in MLEN as the relocation length, a negative value, as above, relocates downward and a positive value upward. 4. Before loading the second block of sprites, the values of the new SPST and SPND should be calculated and noted. Type: SPST @ U. SPND @ 65280 60000 - + DUP SPND ! U. [CR] Take a note of these two values. If the previous steps have been carried out correctly the second number (the new SPND) should be the same value as the old SPND before relocation. 5. Type PROG [CR] to exit back to BASIC then type LOAD "" CODE. The array of pointers will be ignored but the sprites will be loaded. This assumes that this second block of sprites was also saved using the sprite development software. 6. Type PRINT USR 24836 to re-enter Forth and your sprites should be merged. Note that if a sprite number used in the second block has also been used in the first block, that only the first occurrence will be found. If the first occurrence is destroyed using WIPE or DSPRITE, then the second occurrence will be found. Extending the Forth Itself One of the beauties of the Forth language is that it is extendible, so if you've added a few of your own commands which you would like to become a permanent feature of your customised version, you will need to make a copy. To save the Forth use the following procedure: 1. Type: WARM->COLD [CR] to embed the commands. 2. Type: HERE 24832 - 1+ U. [CR] to print the length to be saved. 3. Type: PROG [CR] to enter BASIC. 4. Save using SAVE "FORTH" CODE 24832,length. 5. Re-enter using PRINT USR 24836. To use the amended version, load White Lightning as normal, exit using PROG, load the new Forth over the old Forth and execute a COLD start using 24832. Compiled and Completed Programs Once the program is fully debugged and running, a final run-time version can be produced. This is the only form in which programs generated from White Lightning can be marketed. If the program makes use of the foreground/background facility, ZAPINT should be typed; if not, then ZAP should be typed. The length of the compiled program is then displayed until a key is pressed and control returned to BASIC to make a copy. The final program should be saved using: SAVE "filename" CODE 24832,length and executed using PRINT USR 24832. Do not use RANDOMIZE USR 24832. Remember that a lot of run-time software is saved with your final code, so even if your program is only two lines long, the resulting program will be pretty large. TABLE 1 Table of Screen Numbers and Addresses Each screen used for editing into consists of 8 lines x 64 characters = 512 bytes. Therefore, if you have only edited into screens 6-9, then there is no need to save all of the screens 1-22, since you only need save from 52224 to 54271 (end of screen 9); ie. 2k bytes. Screen Number Start Address 1 49664 2 50176 3 50688 4 51200 5 51712 6 52224 7 52736 8 53248 9 53760 10 54272 11 54784 12 55296 13 55808 14 56320 15 56832 16 57344 17 57856 18 58368 19 58880 20 59392 21 59904 22 60416 IDEAL GLOSSARY WORD PARAMETERS ACTION WCRV HGT, LEN, COL, ROW, NPX Scroll the window vertically with wrap by NPX pixels SCRV HGT, LEN, COL, ROW, NPX Scroll the window vertically without wrap by NPX pixels WRR1V HGT, LEN, COL, ROW Scroll the window 1 pixel right with wrap WRL1V HGT, LEN, COL, ROW Scroll the window 1 pixel left with wrap WRR4V HGT, LEN, COL, ROW Scroll the window 4 pixels right with wrap WRL4V HGT, LEN, COL, ROW Scroll the window 4 pixels left with wrap WRR8V HGT, LEN, COL, ROW Scroll the window 8 pixels right with wrap WRL8V HGT, LEN, COL, ROW Scroll the window 8 pixels left with wrap SCR1V HGT, LEN, COL, ROW Scroll the window 1 pixel right without wrap SCL1V HGT, LEN, COL, ROW Scroll the window 1 pixel left without wrap SCR4V HGT, LEN, COL, ROW Scroll the window 4 pixels right without wrap SCL4V HGT, LEN, COL, ROW Scroll the window 4 pixels left without wrap SCR8V HGT, LEN, COL, ROW Scroll the window 8 pixels right without wrap SCL8V HGT, LEN, COL, ROW Scroll the window 8 pixels left without wrap ATTRV HGT, LEN, COL, ROW Scroll the window attributes 1 character right with wrap ATTLV HGT, LEN, COL, ROW Scroll the window attributes 1 character left with wrap ATTUPV HGT, LEN, COL, ROW Scroll the window attributes 1 character up with wrap ATTDNV HGT, LEN, COL, ROW Scroll the window attributes 1 character down with wrap WCRM SPN Scroll the sprite vertically with wrap by NPX pixels SCRM SPN Scroll the sprite vertically without wrap by NPX pixels WRR1M SPN Scroll the sprite 1 pixel right with wrap WRL1M SPN Scroll the sprite 1 pixel left with wrap WRR4M SPN Scroll the sprite 4 pixels right with wrap WRL4M SPN Scroll the sprite 4 pixels left with wrap WRR8M SPN Scroll the sprite 8 pixels right with wrap WRL8M SPN Scroll the sprite 8 pixels left with wrap SCR1M SPN Scroll the sprite 1 pixel right without wrap SCL1M SPN Scroll the sprite 1 pixel left without wrap SCR4M SPN Scroll the sprite 4 pixels right without wrap SCL4M SPN Scroll the sprite 4 pixels left without wrap SCR8M SPN Scroll the sprite 8 pixels right without wrap SCL8M SPN Scroll the sprite 8 pixels left without wrap ATTRM SPN Scroll the sprite attributes 1 character right with wrap ATTLM SPN Scroll the sprite attributes 1 character left with wrap ATTUPM SPN Scroll the sprite attributes 1 character up with wrap ATTDNM SPN Scroll the sprite attributes 1 character down with wrap GETBLS SPN, COL, ROW Block move screen data from screen to sprite GETXRS SPN, COL, ROW Logically XOR screen data into sprite data GETORS SPN, COL, ROW Logically OR screen data into sprite data GETNDS SPN, COL, ROW Logically AND screen data into sprite data PUTBLS SPN, COL, ROW Block move sprite data from sprite to screen PUTXRS SPN, COL, ROW Logically XOR sprite data into screen data PUTORS SPN, COL, ROW Logically OR sprite data into screen data PUTNDS SPN, COL, ROW Logically AND sprite data into screen data GWBLS SPN, COL, ROW, SCOL, SROW, HGT, LEN Block move screen data from screen window into sprite window GWXRS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically XOR screen data from screen window into sprite window GWORS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically OR screen data from screen window into sprite window GWNDS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically AND screen data from screen window into sprite window GWATTS SPN, COL, ROW, SCOL, SROW, HGT, LEN Block move attributes from screen window into sprite window PWBLS SPN, COL, ROW, SCOL, SROW, HGT, LEN Block move sprite data from sprite window into screen window PWXRS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically XOR sprite window data into screen window PWORS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically OR sprite window data into screen window PWNDS SPN, COL, ROW, SCOL, SROW, HGT, LEN Logically AND sprite window data into screen window PWATTS SPN, COL, ROW, SCOL, SROW, HGT, LEN Block move sprite window attributes into screen window GWBLM SP1, SP2, SCOL, SROW Block move sprite SP1 into sprite SP2 at SCOL,SROW GWXRM SP1, SP2, SCOL, SROW Logically XOR sprite SP1 into sprite SP2 at SCOL,SROW GWORM SP1, SP2, SCOL, SROW Logically OR sprite SP1 into sprite SP2 at SCOL,SROW GWNDM SP1, SP2, SCOL, SROW Logically AND sprite SP1 into sprite SP2 at SCOL,SROW GWATTM SP1, SP2, SCOL, SROW Block move attributes of sprite SP1 into sprite SP2 at SCOL,SROW PWBLM SP1, SP2, SCOL, SROW Block move window at SCOL,SROW of sprite SP2 into sprite SP1 PWXRM SP1, SP2, SCOL, SROW Logically XOR window at SCOL,SROW of sprite SP2 into sprite SP1 PWORM SP1, SP2, SCOL, SROW Logically OR window at SCOL,SROW of sprite SP2 into sprite SP1 PWNDM SP1, SP2, SCOL, SROW Logically AND window at SCOL,SROW of sprite SP2 into sprite SP1 PWATTM SP1, SP2, SCOL, SROW Block move attributes of window at SCOL,SROW of sprite SP2 into sprite SP1 COPYM SP1, SP2 As GWBLM but SCOL,SROW assumed zero COPXRM SP1, SP2 As GWXRM but SCOL,SROW assumed zero COPORM SP1, SP2 As GWORM but SCOL,SROW assumed zero COPNDM SP1, SP2 As GWNDM but SCOL,SROW assumed zero COPATTM SP1, SP2 As GWATTM but SCOL,SROW assumed zero INVV HGT, LEN, COL, ROW Invert screen window MIRV HGT, LEN, COL, ROW Mirror screen window about its centre MARV HGT, LEN, COL, ROW Mirror screen window attributes about centre INVM SPN Invert sprite data MIRM SPN Mirror sprite about its centre MARM SPN Mirror sprite attributes about centre SPINM SP1, SP2 Rotate sprite SP2 90 degrees clockwise into sprite SP1 DSPM SP1, SP2 Enlarge sprite SP2 into sprite SP1 HALT Suspend CPU operation until next interrupt EI Enable interrupt DI Disable interrupt EXX Exchange IDEAL variables with the alternate IDEAL variables INT-ON FORTH WORD Execute specified Forth word under interrupt INT-OFF Terminate execution of interrupt driven word PROG Enter BASIC RESERVE N1 Reserve N1 bytes in the dictionary for BASIC source GOTO N1 Begin execution of BASIC at line N1 RETUSR Return to BASIC from RANDOMIZE USR 30000 call DSPRITE SPN Delete sprite and recover bytes from below ISPRITE SPN, HGT, LEN Create sprite and move current sprites down to accommodate WIPE SPN Delete sprite and recover bytes from above SPRITE SPN, HGT, LEN Create sprite at free space after last sprite RELOCATE MLEN Relocate sprite space by signed 16-bit length MLEN COLD# SPST, SLEN Reset sprite space to begin at SPST with SLEN bytes cleared to zeros SETAV HGT, LEN, COL, ROW Fill the screen window with the current attributes SETAM SPN Fill the sprite with the current attributes CLSV HGT, LEN, COL, ROW Clear the screen window and fill with the current attributes CLSM SPN Clear the sprite ADJV HGT, LEN, COL, ROW Adjust the screen window to lie on the screen ADJM SPN, COL, ROW Adjust COL, ROW, HGT, LEN, SCOL, SROW such that GETs and PUTs lie on the screen RND N1 Leave a random number between 0 and N1 on the stack OUT# N1, N2 Output LSB of N1 to 16-bit port address N2 IN# N1 Leave on the stack the byte from 16-bit port address N1 ZAPINT Create run-time program with interrupt facility ZAP Create run-time program without interrupt facility CALL N1 Execute machine code subroutine at address N1 KB N1, N2 Test for key press at row N1, column N2 and stack true or false flag SCANV COL, ROW The character position is scanned for screen data and a true or false flag stacked SCANM SPN The sprite is scanned for data and a true or false flag stacked BLEEP N1, N2 Sinclair BEEP; N1 is duration, N2 is pitch ATTON Enable attribute switch ATTOFF Disable attribute switch -- end of Section 3