Your Second GFA-BASIC 3.0 Manual Second Edition (c) Han Kempen Coevorden, 1990 Everything you always wanted to know about GFA-Basic 3.0, but were afraid to ask CONTENTS INTRODUCTION .......................... 7 1. GENERAL Start-up .............................. 9 Application ........................... 9 Monitor ............................... 10 Break ................................. 10 Operating System ...................... 10 2. THE EDITOR Abbreviated Commands .................. 12 Syntax ................................ 12 Folded Procedures ..................... 13 Tab ................................... 13 Cut and Paste ......................... 13 Load .................................. 13 Save .................................. 14 Llist ................................. 14 Insert-mode ........................... 15 Direct mode ........................... 15 DEFLIST ............................... 15 Special Characters .................... 16 3. VARIABLES Variable Type ......................... 17 DEFWRD ................................ 17 Boolean ............................... 17 Integer ............................... 18 Floating Point ........................ 18 VAR ................................... 18 FUNCTION .............................. 19 CLEAR ................................. 19 ERASE ................................. 19 DUMP .................................. 19 TYPE .................................. 20 READ .................................. 20 SWAP .................................. 20 TIME$ ................................. 21 TIMER ................................. 21 DATE$ ................................. 22 4. MEMORY RAM ................................... 23 INT{} ................................. 23 RESERVE ............................... 23 INLINE ................................ 24 MALLOC ................................ 25 5. SORT QSORT v. SSORT ........................ 26 QSORT of number-arrays ................ 26 QSORT of string-arrays ................ 26 6. OPERATORS and NUMERICAL FUNCTIONS \ ..................................... 29 PRED and SUCC ......................... 29 MOD ................................... 29 BCLR .................................. 29 BSET .................................. 30 BCHG .................................. 30 LOG ................................... 30 SINQ and COSQ ......................... 30 EQV ................................... 31 CARD and SWAP ......................... 31 MAX ................................... 31 Correlation ........................... 31 7. STRINGS INSTR ................................. 32 LSET and RSET ......................... 32 Parser ................................ 32 8. KEYBOARD INPUT INKEY$ ................................ 33 INPUT ................................. 34 INPUT$ ................................ 35 LINE INPUT ............................ 35 KEYTEST ............................... 35 KEYGET ................................ 35 KEYLOOK ............................... 36 KEYPRESS .............................. 37 KEYDEF ................................ 37 Keyboard .............................. 37 Keyclick, Keyrepeat and CapsLock ...... 38 9. SCREEN OUTPUT PRINT ................................. 40 LOCATE ................................ 41 PRINT TAB ............................. 42 Setscreen (XBIOS 5) ................... 42 Font .................................. 43 10. PRINTER Printer ready ......................... 45 HARDCOPY .............................. 45 Printer-commands ...................... 47 11. FILES Floppy Write Test ..................... 50 Step Rate ............................. 50 RAM-disk .............................. 50 DIR$() ................................ 50 DIR and FILES ......................... 51 FSFIRST and FSNEXT .................... 52 EXIST ................................. 54 LOF ................................... 54 TOUCH ................................. 54 NAME .................................. 54 KILL .................................. 54 File Copy ............................. 55 Disk Format ........................... 55 File Allocation Table (FAT) ........... 58 Sectors ............................... 59 Bootsector ............................ 61 BLOAD ................................. 61 INP and OUT ........................... 62 INPUT and LINE INPUT .................. 62 STORE and RECALL ...................... 62 FILESELECT ............................ 63 12. MIDI INPMID$ ............................... 67 INP ................................... 67 Midi-commands ......................... 67 13. MODEM INPAUX$ ............................... 70 INP ................................... 70 Rsconf (XBIOS 15)...................... 70 14. MOUSE Editor ................................ 71 Fileselector .......................... 71 MOUSE ................................. 71 SETMOUSE .............................. 71 DEFMOUSE .............................. 72 15. JOYSTICK STRIG and STICK ....................... 74 16. SOUND SOUND and WAVE ........................ 75 Dosound (XBIOS 32) .................... 75 Samples ............................... 76 Speech ................................ 77 Soundmachine .......................... 77 17. PROGRAM DECISIONS IF ... ENDIF .......................... 78 SELECT ................................ 78 18. PROGRAM LOOPS Calculations .......................... 79 FOR ... NEXT .......................... 79 Loops ................................. 80 19. PROGRAM CONTROL GOSUB ................................. 82 ON BREAK GOSUB ........................ 82 ERROR ................................. 82 EVERY and AFTER ....................... 82 GOTO .................................. 82 DELAY ................................. 83 CHAIN ................................. 83 EXEC .................................. 83 20. GRAPHICS SETCOLOR and VSETCOLOR ................ 84 Palette ............................... 84 DEFMARK ............................... 87 DEFFILL ............................... 87 DEFLINE ............................... 89 DEFTEXT ............................... 89 GRAPHMODE ............................. 90 PLOT and DRAW ......................... 91 PCIRCLE ............................... 92 CURVE ................................. 92 TEXT .................................. 92 SPRITE ................................ 93 VQT_EXTENT ............................ 95 Line-A ................................ 95 HLINE ................................. 96 ACHAR and ATEXT ....................... 96 GET and PUT ........................... 96 Degas-Pictures ........................ 97 Neochrome-Pictures .................... 98 VSYNC ................................. 98 Scroll ................................ 98 ACLIP ................................. 99 Blitter ............................... 99 21. EVENTS MENU() ................................ 101 ON MENU BUTTON ........................ 101 ON MENU IBOX .......................... 102 22. PULLDOWN MENU OPENW 0 ............................... 103 Desk-submenu .......................... 103 File-submenu .......................... 103 23. WINDOWS GFA-windows ........................... 104 CLOSEW ................................ 104 TITLEW ................................ 105 CLEARW ................................ 105 24. AES-LIBRARY ALERT ................................. 106 SHEL_GET and SHEL_PUT ................. 106 25. GFAXPERT-FILES GFAXPERT.DOC .......................... 108 GFAXPERT.LIB .......................... 108 INLINE ................................ 108 STANxxxx.LST .......................... 108 START ................................. 112 small print ........................... 113 EPILOGUE .............................. 114 INDEX ................................. 115 INTRODUCTION Right now you are reading the second edition of the text GFAXPERT.DOC. This text is not meant to be a replacement of your GFA-manual. On the contrary, I assume you are already familiar with the GFA-manual and now want to know everything about GFA-Basic 3.0 that is not described properly in the manual. This text is about the (hidden) power of GFA-Basic 3.0. You will find nothing about version 3.5, apart from this sentence. And this sentence, because I would like to mention that I don't understand why GFA launched version 3.5. GFA-Basic is becoming far too expensive. All remarks in this text, especially about bugs, are based on GFA-Basic 3.07. I still hope version 3.08 will become available (how naive...), otherwise I'll have to wait for version 4. Not version 4.00 of course, because I know there will be at least 200 bugs in the first release. I wrote this text because I was disappointed by the contents of most books about GFA-Basic 3.0. Some authors use page after page to describe a boring program. Others delve deeply into menu's, windows and RSC, but ignore the "regular" Basic-commands. Instead of complaining, I decided to write the book that I would really like to see myself. Here it is. Forget all other books about GFA-Basic, you need only two things: your GFA-manual and this text. And to be honest, perhaps a good book about the AES-library, because you won't find much about that subject in this text. In this text you will find quite a few Procedures. Most Procedures that are listed (or mentioned) in this text can be found in one of the LST- files in the folder GFAXPERT.LIB. Downloaders should look for the file GFAXPRT2.ARC. I strongly advise you to read the chapter 'GFAXPERT-FILES' thoroughly, before you try to use the Procedure-library GFAXPERT.LIB. Especially note the following : (1) word-variables are the default (2) a couple of variables are declared as Standard Globals (3) some Standard Functions are defined (4) a few Standard Procedures are present (5) the Standard Array color.index() is declared A Procedure could use any of the Standard Globals, Functions, Procedures or the Standard Array. I usually mention this in comment-lines in the Procedure. If you Merge such a Procedure into an existing program, you will have to adapt your program or the Procedure. I still believe in GFA-Basic 3.0. And I also believe in sharing ideas, Procedures and programs with other users, starting with you. If you feel guilty about receiving all these goodies for (almost) nothing, you will find a free consult in the chapter 'EPILOGUE'. I could only write this text because many GFA-users shared their experience with others. I dedicate this text to them. Special thanks to everyone who pointed out bugs, mistakes and omissions in the first edition of GFAXPERT.DOC. All mistakes in the second edition are made by Han Kempen 1. GENERAL Start-up Programs in an AUTO-folder are executed automatically after a reset. The interpreter GFABASIC.PRG is a GEM-program, and cannot be started in this way. With TOS 1.4 you can install a GEM-program as auto-booting. With older TOS-versions you could use a program like HEADSTRT.PRG in your AUTO- folder to start GFABASIC.PRG automatically. You can't start a GFA-program this way (I think). If you have written a (compiled) program that can be run either from the AUTO-folder or from the desktop, you can determine which is the case : IF PEEK(&H2C+4)=0 ! 4th byte of Line-F vector (...) ! AUTO ELSE (...) ! desktop ENDIF There are several ways to (re)start your computer. The obvious one is to switch the ST off, wait a few seconds (15 seconds with a 1040 ST!), and switch on again. This is called a "cold" or "hard" reset. Your computer suffers a little, and it takes some time. If you use the reset-button on your ST, you perform a "warm" or "soft" reset. The operating system automatically performs a warm reset if you switch between Low and Medium resolution on the desktop. If you suspect a program of changing system variables, you should always use a cold reset. After a warm reset the system variables in low memory are not initialised again. Garbage will stay there and will undoubtedly lead to interesting effects. You can perform both a warm and a cold reset from GFA-Basic with the following Procedures : PROCEDURE coldstart SLPOKE &H420,0 SLPOKE &H426,0 ! probably not necessary SLPOKE &H43A,0 ~XBIOS(38,L:LPEEK(4)) RETURN ' PROCEDURE warmstart ~XBIOS(38,L:LPEEK(4)) RETURN If you would like to boot from your second (external) drive B, try : SLPOKE &H446,1 ! boot from drive B after next reset @warmstart Application It is convenient to install the extension GFA as an application for GFABASIC.PRG. Click once on GFABASIC.PRG and choose Install Application from the Options-menu. Type GFA as Document Type, click on OK and save the desktop. If you double-click a GFA-program (extension .GFA) from the desktop, GFABASIC.PRG is automatically loaded first. Choosing Install Application you will only see the most recently installed application. Use a disk-editor to examine the file DESKTOP.INF and you will find all installed applications (look for #G). You could also use the method described in the paragraph 'SHEL_GET and SHEL_PUT', or the RECALL-method from the paragraph 'STORE and RECALL'. Monitor The Atari colour monitor SC1224 works with a vertical frequency of either 50 Hz or 60 Hz : SPOKE &HFF820A,254 ! 50 Hz SPOKE &HFF820A,252 ! 60 Hz For 60 Hz, bit 1 of the Sync Mode Register is cleared. Don't change bit 0, or the video controller chip will not use the so-called sync pulses. After a reset, the operating system defaults to 50 Hz. The screen is a little larger than at 60 Hz, but the screen flickers slightly. If you connect your ST to a TV through a modulator, you should use 50 Hz. Otherwise you are advised to use 60 Hz. Break It's not easy to find in the GFA-manual: you can stop a running program by pressing simultaneously. But if you're reading this text, you know this already. It's impossible to interrupt a program during DELAY! Study the Standard Procedure Break in one of the STANxxxx.LST-files to see how I react after a 'Break'. Operating System If you program in a language like GFA-Basic, you won't notice much of the actual workhorse inside your ST-computer: The Operating System (TOS). But even GFA-Basic does not have a Basic equivalent for all TOS-functions, although you can use almost all functions from GFA-Basic. TOS can be divided in two main parts: (GEM)DOS and GEM. The first is a collection of "lower level" routines for communication with keyboard, screen, printer, etc. In GFA-Basic you can call these routines with the commands BIOS, XBIOS and GEMDOS. The Graphics Environment Manager (GEM) consists of two collections of routines: the VDI (Virtual Device Interface) and the AES (Application Environment Services). The VDI takes care of regular graphics and should have included GDOS. Atari didn't include GDOS in the VDI, so you have to load it if you need it. Most VDI- functions have a Basic equivalent in GFA. The AES takes care of the communication with the user through menu, Alert-box, window, etc. Most AES-functions can be accessed through the AES-library in GFA-Basic 3.0. With GEMDOS-function 48 (Sversion) you can find the version of your GEMDOS. For both the old TOS and the Blitter-TOS &H1300 (version 0.19) is returned. The French Turbo-DOS has version 0.20 and the new Rainbow TOS of 1988 has version 0.21. Another way to find out the version of TOS uses the system header of TOS (not necessarily located in ROM!) : adr%=LPEEK(&H4F2) version$=HEX$(DPEEK(adr%+2)) The good old ROM-TOS (1986, actually not so good) has version &H0100 (1.0), the Mega-ST Blitter-TOS (1987) version &H0102 (1.2). And of course the new TOS ('Rainbow TOS') has version 1.4. You could also examine the date of your TOS-version : date$=HEX$(LPEEK(adr%+24)) My ancient TOS 1.0 has '11201985' as the date. 2. THE EDITOR Abbreviated Commands The editor recognizes the following abbreviations (not a complete list) : ALINE - ALI FILESELECT- FILE POLYMARK - POLYM ARECT - AR FILL - FI PRINT - P or ? ARRAYFILL - ARR FUNCTION - FU PROCEDURE - PRO ATEXT - AT GOSUB - G or @ PSET - PS BMOVE - B GRAPHMODE - GRA QUIT - Q BOUNDARY - BOU HIDEM - HI REPEAT - REP CASE - CA HLINE - HL RESTORE - RES CIRCLE - CI IF - I RETURN - RET CLOSE - CL INPUT - INP RSET - RS COLOR - C LINE - LI SELECT - S DATA - D LINE INPUT- LI SETCOLOR - SET DEFFILL - DEFF LOCAL - LOC SETMOUSE - SETM DEFLINE - DE LOOP - L SGET - SG DEFMARK - DEFMA LPRINT - LPR SHOWM - SH DEFMOUSE - DEFM LSET - LS SWAP - SW DEFTEXT - DEFT MID$(..)= - MI ..)= TEXT - T DELETE - DEL MOUSE - MOU UNTIL - U DRAW - DR NEXT - N VOID - V EDIT - ED OPEN - O VSYNC - VS ELLIPSE - ELL PAUSE - PA WAVE - WA ELSE - E PBOX - PB WEND - WE ENDFUNC - ENDF PCIRCLE - PC WHILE - W ENDIF - EN PELLIPSE - PE ENDSELECT - ENDS PLOT - PL ERASE - ERA POLYFILL - POLYF EXIT IF - EX POLYLINE - POL If the abbreviated command is followed by anything else, you have to insert a space (e.g. 'C 1'), except with '@' and '?' : @proc1 G proc1 ' ?"hello" P "hello" Syntax The parser checks for correct syntax after you press . Many typo- bugs are prevented this way. The only disadvantage is that the parser recognizes some variables as commands. It's impossible to use the follo- wing names as the first word on a line: data_byte|, dirty$, double, printer$, file%, quit!. The last one is nasty, because the parser changes the line 'quit!=FALSE' into 'QUIT!=FALSE' without warning for a syntax- error. If you now run the program you will return to the desktop when QUIT is encountered. Of course you have not lost your valuable program, because you always Save before you Run. Do you really? If the parser refuses the name of a variable, you can use LET (e.g. 'LET quit!=FALSE'). But you will have to change the name if it is a label (e.g. the label 'data1:' could be changed into '1data:' or 'd.ata1:'). Folded Procedures If you press on a Procedure-line, all Procedures follo- wing and including the current one are folded/unfolded. You can unfold all Procedures at once by putting the cursor on the first Procedure-line and pressing . I suggest you leave all Procedures in the Procedure-Library GFAXPERT.LIB folded, unless you want to examine a Procedure. The editor-commands 'Find' and 'Replace' will skip folded Procedures. Never change the Procedure-line of a folded Procedure, always unfold it first. Tab If you press , the cursor jumps to the next tab-position, without altering the current line. If you use , the line is filled with spaces from the current cursor-position to the next tab- position. Pressing erases all consecutive spaces to the left of the current cursor-postition. If the current cursor-position happens to be a space, this space and all spaces to the right are erased as well. Cut and Paste If you press

, the current line from the cursor to the end of the line is cut, and saved in an internal buffer. inserts the saved line at the current cursor-position. You can use this method to "cut and paste" a part of a line. Press

, then to restore the original line. Move the cursor to the desired position and press . Block-operations are only possible with complete lines. Load You can use Load only with *.GFA-files, not with ASCII-files (such as the *.LST-files). ST-Basic files are ASCII-files, so you can Merge them. More recent programs in GFA-Basic (from 3.04) cannot be loaded by earlier interpreters (up to 3.02). Of course you should have the most current version (at least version 3.07), but the following method should always work. Lengthen the program until you can load it successfully : OPEN "A",#1,file$ PRINT #1,STRING$(1000,0) CLOSE #1 Save After 'Save' (*.GFA-file) or 'Save,A' (*.LST-file) with an existing file, the old file is renamed with a BAK-extension. Nice precaution, but you should delete those *.BAK-files every now and then. If you kill and save files regularly, new files will be stored in a fragmented way. Loading a fragmented file takes more time than loading a file that occupies consecutive sectors on the disk. You can correct this as follows. First make a backup-disk (yes, you should already have one). Copy all files to a RAM-disk by clicking on the drive-icon and dragging it to the window of the destination drive. You can't use disk-copy (dragging drive-icon to drive-icon) because the destination-drive is a RAM-disk. Format the source-disk and copy all files back (this must be a file-copy, not a disk-copy). Now, all files are saved on consecutive sectors. You could speed up things a little bit more by copying the most-used files first. The drive-head now needs less time to reach these files. Don't try to 'Save,A' an existing file to a full disk. I have tried it once and lost both the original file (should have become a *.BAK-file) and of course the saved file. Also, the editor (or TOS) had erased the program from memory... Thank you. Llist There has been some confusion about the following point-commands for the printer : .p- - point-commands are not printed .p+ - point-commands are printed again .llxx - line-width .plxx - page-length .pa - form feed Sometimes the commands '.cp' and '.nu' are mentioned, but I don't know how to use these. You can only use one point-command in one line. I use the following lines for my printer (96 characters/line in Elite-mode) : .p- .n4 .lr3 .ll88 Save this as LLIST.LST and Merge it after the last line of a program before choosing 'Llist'. The actual listing is then printed with 80 characters/line, preceded by the line-numbers (4 characters + space). If your program is longer than 10000 lines, you should use '.n5', but in that case you probably don't have time to read this. A nice touch is the automatic execution of a form feed after printing the listing. If the listing contains special characters (ASCII-code < 32 or > 126), some of those characters might be interpreted as printer-commands. My printer switches to condensed printing after receiving the Atari-symbol. Installing the proper printer-driver (e.g. PTEPSON.PRG) will prevent that. But I never install a printer-driver because there are some serious disadvantages. Read more about it in the paragraph 'HARDCOPY'. You can stop the printing process by pressing the 'Break'-combination , unless Break has been disabled with 'ON BREAK CONT'. Your printer will continue printing though, until its input-buffer is empty, or until you turn the printer off. Insert-mode If you return to the editor, you'll always be in Insert-mode, even if you left the editor in Overwrite-mode. By the way, the editor restores the original colour-palette, so you are not bothered by palette-changes in a program. In the old days I was sometimes caught by the dreaded phenomenon of black characters on a black background, but now you can always read the listing on your screen. By the second way, there is only one thing I don't like about the GFA- editor. It's not easy to merge a Procedure from a LST-file with the program you're working on. Save the program, clear memory with 'New', 'Merge' the LST-file, mark the Procedure as a block, save the block, 'Load' the original program, find the correct line and 'Merge' the saved block. A new command NMerge (New+Merge) would make the life of a GFA- programmer a little easier. A better idea would be the command BMerge (Block-Merge). Then I could choose this command, clip a Procedure-block, and the block would be inserted in the listing automatically. Are you reading this, Frank? If you are, how about a function for changing upper case into lower case. In the GFA-EDITOR I SOMETIMES (oops) forget I pressed CapsLock. And how about a function to clear a line from the cursor to the next space-character. And... Direct Mode Enter the Direct Mode by pressing or in the editor. In Direct Mode you can restore the previously used line with . With and you can even recall up to 8 of the last used lines. Press to switch between Insert-mode and Overwrite-mode. As usual clears the line, but if you press on an empty line, you will return to the editor immediately. You can also return to the editor with , even without first clearing the command-line. You can call Procedures in your program from the Direct Mode (e.g. '@show'). If you (temporarily) merge some special Procedures with the program you are developing, you could use the Direct Mode as a Command Line Interpreter. DEFLIST The command 'DEFLIST n' only works in Direct Mode, not in a program. You can also choose 'Deflist' from the drop-down menu, after clicking on the Atari-symbol. Special Characters You can enter characters with ASCII-code 32-126 directly from the key- board. These characters are usually called ASCII-characters. The codes 0-31 and 127 are used as control-codes, but additionally special charac- ters have been assigned to these codes. The characters with code 128-255 are sometimes called "extended ASCII-characters" and are partly identical to the IBM-characters with the same codes. I loosely use the expression "ASCII-code" for all codes 0-255. You can enter characters with code 0-31 or 127-255 by pressing and then entering the character code. Release the -key and the character appears on your screen. E.g., you could enter the Escape- character by holding down and pressing <2> and <7>. This is much faster than entering CHR$(27), but Llisting a file with Escape- characters is probably not a good idea (less important, 1st Word Plus could get confused too). Llisting CHR$(27) doesn't bother your printer at all. It's not possible to enter CHR$(10) or CHR$(13) with the 'Alternate'- way. You can enter the first by pressing and then <1> <0> . For code 13 you have to use CHR$(13). Never use code 4 (EOT, visible as left arrow) in a LST-file. If you Merge a LST-file, the GFA-editor thinks the end of the file is reached at that point (&H04) and refuses to load anything following this code! 3. VARIABLES Variable-type In GFA-Basic we use the expressions 'Byte', 'Word' and 'Integer' for the three integer-variables. Because all three are integers, the expression 'Integer' for the 4-byte integer-variable is sligthly confusing. In other languages this variable is called 'Longword' or simply 'Long'. An address in RAM should always be a 4-byte integer. DEFWRD I prefer to declare word-variables as the default (for variables without postfix) with: DEFWRD "a-z" I recommend the use of word-variables (2 bytes) for two reasons. In calculations, the use of the special integer-operators (ADD, SUB, INC, etc.) speeds the program up considerably. If you insist on using the regular operators (+, -, etc.) you should use floating point variables instead. Using the regular operators, the interpreter has to convert integer-variables to floating point, does the calculation and converts the result back to integer again. That's why a#*b# is calculated faster than a&*b&. But MUL(a&,b&) is much faster than a#*b#. It takes some time to recognize what an expression like DIV(a,MUL(ADD(a,b),SUB(b,c))) means. I suggest you use the regular operators if the calculation-time is not critical. In loops, the gain in calculation time really counts, so you should use the integer-operators. That way you will learn Polish too. The second reason for using word-variables is, that in a compiled program calculations with word-integers are usually the fastest. IMPORTANT: if a number-variable has no postfix in this text, you should assume it's a word-variable. I'll use the postfix |, % or # where appropriate. Please note that the interpreter assumes a number-variable without postfix is a floating point variable, unless you use DEFWRD "a-z". Boolean The following five lines : IF number>0 test!=TRUE ELSE test!=FALSE ENDIF can be shortened to just one line : test!=(number>0) This works, because the '>'-operator returns TRUE or FALSE (actually -1 or 0). Another little trick : IF i=1 n=n*2 ELSE IF i=2 n=n*5 ELSE n=0 ENDIF This could be shortened to : n = n*-2*(i=1) + n*-5*(i=2) The example is ridiculous, but the principle involved could be useful. The expressions 'i=1' and 'i=2' are either 0 (FALSE) or -1 (TRUE). It is not necessary to use something like : IF flag!=TRUE (...) ENDIF You can simply use : IF flag! (...) ENDIF Integer You can't assign 2^31 to a 4-byte integer-variable. Although an integer contains 32 bits, you can't use bit 31. This bit is a flag for a negative integer. The largest positive number you can assign to an integer-variable is therefore 2^31-1 (2147483647). I could have written an analogue paragraph about the 2-byte word-variables, but I didn't. Floating Point The range of floating point variables (postfix #) is : -1.0E1000 < x# < 1.0E1000 Larger or smaller numbers can be used in calculations, but not printed, because the exponent may contain not more than 3 digits (1.0E1000 is displayed as 1.0E;00). VAR If you call a Procedure and use VAR (call by reference), all variables and/or arrays after VAR are called by reference. An example to clarify this : @test(10,5,number%,array%()) (...) PROCEDURE test(a,b,VAR x%,y%()) ' now a=10 and b=5 (call by value) ' number% and array%() can now be used as x% and y%() x%=a+b ! global variable number% is now 15 ARRAYFILL y%(),1 ! all elements of array%() are now 1 RETURN In prehistoric days you could have used SWAP, but VAR makes life easier : @test(*a%()) (...) PROCEDURE test(ptr%) SWAP *ptr%,x%() ! array a%() temporarily renamed as x%() (...) ! do something with the array SWAP *ptr%,x%() ! restore pointer before leaving Procedure RETURN FUNCTION You can only leave a FUNCTION by RETURNing a value or a string. This value/string is usually assigned to a variable. If the FUNCTION returns a string, the function-name has to end with '$' : FUNCTION test RETURN 126 ENDFUNC ' FUNCTION test$ RETURN "this is a string" ENDFUNC CLEAR Because CLEAR is automatically executed when you run a program, it's not necessary to start you program with this command. ERASE It's impossible to reDIMension an existing array. You first have to ERASE the existing array and then you can DIMension a new array. It is not necessary to test for the existence of an array with DIM?() before you use ERASE. In other words, you can use ERASE even if the array doesn't exist : ERASE array$() ! just in case this array already exists DIM array$(200) After ERASEing an array, GFA rearranges the remaining arrays. All arrays that have been DIMensioned after the deleted array are moved in order to fill the gap of the deleted array. This is important if you use V:array(0) in your program (read the paragraph 'RESERVE'). DUMP Examine all variables in your program by typing 'DUMP' in Direct Mode. Press to slow down the scrolling-speed, or press the right -key to temporarily stop the scrolling. The Procedure Debug enables you to use DUMP during debugging. This is the best way to discover those nasty typo-bugs in a variable-name. You'll probably be surprised to see the names of deleted variables as well. Also, any variable-name you used in Direct Mode appears. All these names are SAVEd with the program! Delete all unwanted names as follows (a RAM-disk would be convenient) : - Load the file - Save,A (press in Fileselector) - New - Merge (press again) - Save (press once more) The file could be much shorter after this operation. TYPE The command TYPE does not seem to work properly if you use local variables. I don't know what's wrong. READ As a rule, I always RESTORE the appropriate label before READing DATA- lines. That way I can use DATA-lines in Procedures : PROCEDURE read.data RESTORE these.data (...) ! READ the DATA here these.data: DATA 1,2,3,4 RETURN SWAP The 52 cards in bridge (or another card-game) can be represented by a byte-array. Fill the elements 1-52 of the array with the value 1-52. The values 1-13 would represent the Club-cards (2,3,4,...,Q,K,A), values 14-26 the Diamonds, values 27-39 the Hearts and values 40-52 the Spades. Shuffling the cards can now be simulated with the Procedure Shuffle : DIM deck|(52) FOR i=1 TO 52 ! ignore index 0 deck|(i)=i NEXT i @shuffle(deck|()) (...) PROCEDURE shuffle(VAR proc|()) LOCAL i,j FOR i=DIM?(proc|())-1 DOWNTO 2 j=RAND(i)+1 SWAP proc|(j),proc|(i) NEXT i RETURN TIME$ You can use TIME$ to print the current time on the screen. In the Procedure Time you'll find a way to print the time every second, although TIME$ is updated every two seconds : PROCEDURE time ' *** activate with : EVERY 200 GOSUB time ' *** global : TIMER$ LOCAL t$ t$=TIME$ IF t$=timer$ MID$(timer$,8)=SUCC(RIGHT$(timer$)) ELSE timer$=t$ ENDIF PRINT AT(1,1);timer$ RETURN TIMER If you need a stopwatch, you can use the following Procedures : PROCEDURE stopwatch ' *** global : STOP.SECONDS# STOP.H STOP.M STOP.S WATCH.ON! LOCAL s# IF watch.on! stop.watch#=TIMER stop.seconds#=(stop.watch#-start.watch#)/200 stop.h=stop.seconds#/3600 s#=stop.seconds#-stop.h*3600 stop.m=s#/60 stop.s=s#-stop.m*60 watch.on!=FALSE ELSE watch.on!=TRUE start.watch#=TIMER ENDIF RETURN ' PROCEDURE print.stopwatch IF stop.h>0 PRINT stop.h;" h ";stop.m;" m"; ELSE IF stop.m>0 PRINT stop.m;" m ";stop.s;" s"; ELSE IF stop.seconds#>=10 PRINT USING "##.# s",stop.seconds#; ELSE PRINT USING "#.## s",stop.seconds#; ENDIF ENDIF ENDIF RETURN You start the stopwatch by calling the Procedure Stopwatch. Calling this Procedure again will stop the stopwatch. Then you can PRINT the elapsed time at the current cursor-position with the Procedure Print.stopwatch, or you could use one of the Global Variables from the Procedure Stopwatch to do something else. DATE$ Find the day of the week with the Procedure Day.of.week : PROCEDURE day.of.week(day.date$,VAR day$) LOCAL day,mp,month,year,m,h,w,week$,n day=VAL(LEFT$(day.date$,2)) mp=INSTR(day.date$,".") month=VAL(MID$(day.date$,mp+1,2)) year=VAL(RIGHT$(day.date$,4)) IF month<=2 m=10+month year=year-1 ELSE m=month-2 ENDIF h=year/100 y=year-100*h w=(TRUNC(2.6*m-0.2)+day+y+TRUNC(y/4)+TRUNC(h/4)-2*h) MOD 7 RESTORE weekdays FOR n=0 TO w READ day$ NEXT n ' weekdays: DATA Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday RETURN This Procedure uses Zeller's Congruence to determine the day of the week. 4. MEMORY RAM An overview of the memory of my 1040 ST (start at the bottom) : address &H FFFFF top of memory of 1040 ST (1024 K) &H FFD00 767 unused (?) bytes XBIOS(2) = &H F8000 screen memory (32000 bytes) HIMEM = &H F4000 16384 unused bytes (MALLOC(-1)) &H ..... free memory (length FRE(0) bytes) &H 388EA program + variables (length varies) &H 10C2E GFA-Basic 3.07 interpreter BASEPAGE = &H 10B2E Basepage GFA-Basic (256 bytes) &H A100 start of available RAM &H 6100 global AES-variables &H 29B4 global BIOS- and GEMDOS-variables L~A = &H 293A Line A variables &H 93A local BIOS-variables + BIOS-stack &H 400 BIOS system-variables &H 0 exception vectors The BIOS system-variables (&H400 - &H4FF) are "cast in concrete" by Atari. Other (undocumented) variables in RAM should be avoided. INT{} You can use either INT{adr%} or WORD{adr%}. As you know, w=WORD{adr%} is faster than w=DPEEK(adr%), but you can't use WORD{} and the other related commands in supervisor mode. This means you can't access memory below address &H800. Of course you can PEEK/DPEEK/LPEEK everywhere (DPEEK and LPEEK on even addresses only), and you can use SPOKE/SDPOKE/SLPOKE to write in supervisor mode. RESERVE The command RESERVE can be used in three different ways : RESERVE n% : reserve n% bytes for GFA-Basic, release RAM up to HIMEM RESERVE -n% : reserve last n% bytes of RAM up to HIMEM RESERVE : restore to original The second command is my favourite. You must use a multiple of 256 with RESERVE. After 'RESERVE -400', only 256 bytes are released to GEMDOS. In this case you would have to use 'RESERVE -512', although you need only 400 bytes! Use the Function Multiple if arithmetic is not your strongest point : RESERVE -@multiple(n%,256) Use 'RESERVE -n%' only once in your program. If you RESERVE memory a second time with 'RESERVE -m%', GFA releases m% bytes, not n%+m%. The command RESERVE (restore) does not always function properly. Especial- ly after EXEC 3 it's often impossible to restore the memory. I suspect this has something to do with the use of the malloc-function by the operating system. You could try the following : RESERVE -n% ! reserve as much as needed (multiple of 256) base%=EXEC(3,...) ! load, but don't start (...) ~MFREE(HIMEM) ! memory above GFA-Basic ~MFREE(base%) ! memory from Basepage of loaded program RESERVE ! hope it works now Don't be surprised by hang-ups or bombs after this operation... There are several ways to reserve a part of memory for special purposes such as music, pictures or other programs. E.g., you could use a string- variable to store a complete screen or a GET-picture. Don't use strings if garbage collection is a serious risk. If a large array is declared, the interpreter sometimes moves the strings in memory to create space for the new array. If you assign a new string to an existing string-variable, the old string is not erased. During garbage collection all unused strings are deleted and the active strings are rearranged. This means the address of a string (accessed through VARPTR or V:) is not fixed. That's not important for a GET-picture, because 'PUT x,y,pic$' still works all right. But if you swap screens with ~XBIOS(5,L:adr%,-1,-1), or call a routine with ~C:adr%(...), garbage collection will be fatal. One solution is not to use a variable for the address, but to use VARPTR or V:. A different approach would be the use of a byte-array : OPEN "I",#1,file$ LET bytes%=LOF(#1) ! how much space is needed? CLOSE #1 DIM assembler|(bytes%-1) ! create space for assembler-routine adr%=V:assembler|(0) BLOAD file$,adr% ! load the routine You can use the variable adr% safely, because a garbage collection has no influence on arrays. However, after ERASEing an array, all arrays that have been DIMensioned after the deleted array are moved in memory. So the array-method is not reliable either, unless you are certain that ERASE will not be used after you have determined the address of the byte-array. Another solution would be to declare the byte-array early in your program, so it will not be moved after ERASE. The only complete safe way is the use of INLINE or MALLOC. INLINE Use D (=DUMP) to dump an Inline-file in Hex-code to the printer. Don't use 'Save,A', because you will lose the Inline-code. You can't use INLINE in LST-files. If you would like to use INLINE in combination with a LST-file you could proceed as follows. First, create an INLINE-folder in the main directory and SAVE the Inline-code as a file (extension INL) in this folder. Merge the LST-file in your program and load the Inline-code in the INLINE-line. The program should be saved with 'Save'. The only limitation with INLINE is the maximum length of 32746 bytes. If you need more space, you have to use MALLOC. If you change the length of an existing INLINE-line, the editor sometimes erases a few lines from your program. It's safer to completely delete the old INLINE-line and then enter the new INLINE-line. MALLOC This is how you could use MALLOC : unused%=MALLOC(-1) ! "unused" memory above HIMEM (used by GEM) RESERVE -n% ! release last n% bytes to GEMDOS max%=MALLOC(-1) IF max%<>0 adr%=MALLOC(max%-unused%) ! n% bytes now available to GFA-Basic ELSE ' error ENDIF (...) ! do something interesting ~MFREE(adr%) ! give back to GEMDOS... RESERVE ! and back to GFA-Basic Atari recommends you leave at least 8K to GEM, so you can't go wrong if you leave 16384 bytes to GEM (see paragraph 'RAM'). Do not use MALLOC to allocate a lot of small memory-blocks, as GEMDOS will get confused. This problem is related to the "40-folder limit", because MALLOC uses the same buffer that is used to store information about a folder (see paragraph 'FILESELECT'). Allocate one large area and split it up in as many parts as you need. Also consider the use of INLINE, strings or byte-arrays instead of using MALLOC (read the paragraph 'RESERVE'). Do not use MALLOC in an accessory, or the allocated memory may be lost if the user changes the resolution. MALLOC(-1) returns the size of the largest available memory-block. Expect problems if another program has allocated a couple of separate memory- blocks to GEMDOS. In that case you would have to use MALLOC(-1) again, until a returned value of '0' indicates there are no more memory-blocks available. 5. SORT QSORT v. SSORT QSORT is faster than SSORT, unless the array is almost sorted already. I understand QSORT uses the recursive Quick-sort method, so you need more memory than with SSORT (iterative Shell-sort). Sort-freaks can study the Procedures Quick.sort.int and Shell.sort.int to see how the Quick-sort and Shell-sort can be implemented with ordinary GFA-Basic commands (like that dance: Quick, Shell, slow). QSORT of number-arrays Number-arrays can be QSORTed in three different ways : QSORT x%() - sorts array ; also with x|(), x&() or x#() QSORT x%(),n - sorts elements 0 through n-1 QSORT x%(),n,y%() - elements of y%() are swapped too In all cases x%(-1) results in sorting in decreasing order. The third method is interesting if you need a sorted output, but don't want to change the original array. You can accomplish this as follows : - copy x%() to temporary array temp%() - determine index of last element : last=DIM?(temp%())-1 - create index array : DIM index%(last) - fill index-array with index 0 through last - sort array : QSORT temp%(),last+1,index%() - ERASE temp%() The index-array must be an integer-array (postfix %). Also, the number of elements is not optional if you sort with an index-array. You can now print the numbers in array x%() in increasing order : FOR i=0 TO last PRINT x%(index%(i)) NEXT i I have never seen the correct syntax for QSORT in print, so here it is : QSORT x([s])[,n[,y%()]] QSORT of string-arrays A string-array can be sorted in the same three ways as described for number-arrays. If you have created an index-array with last_name$(), you could print an alphabetical list : FOR i=0 TO last PRINT first_name$(index%(i))'last_name$(index%(i)) NEXT i There are two extra possibilities with string-arrays. For the first possibility, create a byte-array and fill with appropriate ASCII-values : DIM ascii|(255) ARRAYFILL ascii|(),32 ! CHR$(32) is the space-character FOR i=48 TO 57 ascii|(i)=i ! 0 - 9 NEXT i FOR i=65 TO 90 ascii|(i)=i ! A - Z NEXT i FOR i=97 TO 122 ascii|(i)=SUB(i,32) ! a - z converted to A - Z NEXT i All characters that are not numbers or letters will be treated as a space (ASCII-value 32). Use the array ascii|() for sorting a string-array : QSORT x$() WITH ascii|() Now 'Atari' and 'ATARI' are treated exactly the same, because the interpreter uses the array ascii|() to convert lower case letters tempora- rily to upper case before sorting. You can even treat the international characters (ASCII-values > 127) correctly, e.g. ascii|(129)=85. Use a few DATA-lines to add the correct ASCII-value, like this : DATA 128,67,129,85,130,69 You can combine this method with an index-array as well. The correct syntax is : QSORT x$([s]) WITH b|() [,n[y%()]] If you have done your homework, you should now be able to write a clever alphabetical sorting-program yourself. You wanted to become an expert, didn't you? Compare your program with the Procedures Ascii.qsort and String.index.qsort : PROCEDURE ascii.qsort(VAR txt$()) IF DIM?(ascii|())=0 @initio.ascii.array ENDIF QSORT txt$() WITH ascii|() RETURN ' PROCEDURE initio.ascii.array LOCAL i,code1,code2 DIM ascii|(255) ARRAYFILL ascii(),32 ! fill with space-character FOR i=48 TO 57 ascii|(i)=i ! 0 - 9 NEXT i FOR i=65 TO 90 ascii|(i)=i ! A - Z NEXT i FOR i=97 TO 122 ascii|(i)=SUB(i,32) ! a - z, converted to A - Z NEXT i RESTORE ascii.data REPEAT READ code1,code2 ascii|(code1)=code2 UNTIL code1=0 ' ascii.data: ' *** format : ASCII-code,replacement DATA 128,67,129,85,130,69,131,65,132,65,133,65,134,65,135,67 DATA 136,69,137,69,138,69,139,73,140,73,141,73,142,65,143,65 DATA 144,69,145,65,146,65,147,79,148,79,149,79,150,85,151,85 DATA 152,121,153,79,154,85,155,67,158,83,160,65,161,73,162,79 DATA 163,85,164,78,165,78,166,65,167,79,176,65,177,79,178,79 DATA 179,79,180,79,181,79,182,65,183,65,184,79,192,121,193,121 DATA 225,83,0,0 RETURN ' PROCEDURE string.index.qsort(switch!,VAR txt$(),index%()) ' *** the index-array has to exist already ' *** if switch!=TRUE, array ascii|() is used LOCAL last,i last=DIM?(txt$())-1 ! index of last element DIM temp$(last) FOR i=0 TO last temp$(i)=txt$(i) NEXT i FOR i=0 TO last index%(i)=i NEXT i IF switch! IF DIM?(ascii|())=0 @initio.ascii.array ENDIF QSORT temp$() WITH ascii|(),last+1,index%() ELSE QSORT temp$(),last+1,index%() ENDIF ERASE temp$() RETURN The second extra possibility with string-arrays is the use of 'OFFSET o' to ignore the first 'o' characters during sorting. Suppose you have created an array files$() with FILES and would like to sort the files by length : QSORT files$() OFFSET 13,n The first character (either a space or '*') and the filename (the next 12 characters) are now ignored during sorting. 6. OPERATORS and NUMERICAL FUNCTIONS \ The operator '\' (backslash) is identical to DIV : a=b\c Integer-division is not as useful as the other integer-operations, because the result is an integer. Of course it is, but this can easily result in large rounding errors. PRED and SUCC PRED(i) is faster than i-1 and SUCC(i) is faster than i+1. Both functions can also be used with strings. Note the clever use (ahem) of SUCC in the following line from the Procedure Time : MID$(timer$,8)=SUCC(RIGHT$(timer$)) The rightmost digit of timer$ (always even) is increased with one. MOD If you use a counter in a loop in order to do something every time the counter reaches a multiple of 10, you could do this as follows : IF counter MOD 10=0 ! multiple of 10? (...) ! yes, do something ENDIF You could also use : IF MOD(counter,10)=0 (...) ENDIF BCLR In GFA-Basic version 2.x you used AND to clear a bit : x|=x| AND &X11111011 ! clear bit 2 of this byte Remember, the first (rightmost) bit is bit 0, so a byte contains bit 0-7 from right to left. In GFA-Basic 3.0 you clear a bit simply with : x|=BCLR(x|,2) ! clear bit 2 of this byte But if you want to clear more than one bit, you should use the AND-method: x|=x| AND &X11110000 ! clear bit 0-3 of this byte In this case you use a so-called mask to clear certain bits. You can use AND also as a function : y|=AND(x|,&X11110000) ! even faster than previous example This way you can test if a bit-mask "fits" a variable : IF AND(x|,mask|) (...) ENDIF An example of this method : IF AND(BIOS(11,-1),&X1001) (...) ! user pressed + right ENDIF BSET In GFA-Basic 2.x you needed OR to set a bit, but now you use BSET : x|=BSET(x|,1) You still need the OR-method if you want to set more than one bit fast : x|=x| OR &X1010 ! set bit 1 and 3 The mask &X1010 is used to set bits 1 and 3. Compare this with the AND- method, where 0 is used to clear a bit, while here 1 is used to set a bit. You can use OR not only as an operator, but also as a function : y|=OR(x|,&X1010) BCHG Instead of BCHG you should use the XOR-method if you want to change more than one bit (with a mask) : x|=x| XOR &X1110 ! change bit 1-3 XOR can be used as a function as well. LOG Logarithms with a base other than 10 or e are computed as follows : LOG(x)/LOG(base) SINQ and COSQ If you are going to convert SIN (or COS) into SINQ (COSQ), you'll have to change from radials to degrees. For example, you would convert SIN(x) to SINQ(DEG(x)). Although SINQ is about five times faster than SIN, this extra computation makes the difference a little less spectacular. If you are lucky, the variable already is in degrees, so you can use SINQ immediately. SINQ and COSQ should not be used if you need very accurate results, only if you plot the result and are not going to use it for further computations. Examine the difference between the use of SIN/COS and SINQ/COSQ to see if the less accurate result is acceptable. In High resolution try something like this : fac#2*PI/639 DRAW 0,200 FOR x=0 TO 639 y#=SIN(x*fac#) DRAW TO x,200+y#*200 NEXT x ~INP(2) CLS ' DRAW 0,200 FOR x=0 TO 639 y#=SINQ(DEG(x*fac#)) DRAW TO x,200+y#*200 NEXT x ~INP(2) CLS ' fac#=360/639 DRAW 0,200 FOR x=0 TO 639 y#=SINQ(x*fac#) DRAW TO x,200+y#*200 NEXT x ~INP(2) EQV EQV doesn't work properly. EQV(0,-1) should be 0, but equals -65536. The bits 16-31 are always set: EQV seems to look at the bits 0-15 only. CARD and SWAP If a 4-byte integer (postfix %) consists of two words, you can extract these with : low.word=CARD(x%) high.word=CARD(SWAP(x%)) Both words are interpreted as positive numbers. Unfortunately, there is no analogue function to swap the low and high byte of a word. That would be useful if you want to convert a word into/from Intel-format (MS-DOS). Rotating 8 bits should do the trick : FUNCTION intel.word(x%) RETURN CARD(ROR&(x%,8)) ! swap low/high byte ENDFUNC Now you should be able to extract the low and high byte of a word : low.byte|=BYTE(x) high.byte|=BYTE(@intel.word(x)) MAX This is how you could find the highest value in a word-array : PROCEDURE max.array(VAR proc(),high) ' *** return highest value of array proc() LOCAL last,n last=DIM?(proc())-1 high=proc(1) ! proc(0) ignored FOR n=2 TO last high=MAX(high,proc(n)) NEXT n RETURN I often store special information in the element with index 0. Correlation Use the Procedure Correlate to determine the correlation between two arrays. 7. STRINGS INSTR The command INSTR(a$,b$,i) always returns '1' if a$=b$. For i=1 that's fine, but not for i>1. Don't blame me, I'm just the messenger. LSET and RSET If you use LSET or RSET, the string is padded with spaces : a$=" 345" LSET a$="12" PRINT a$ ! the interpreter prints "12 ", not "12345" You can use RSET for right justification of columns : f$=SPACE$(10) FOR i=1 TO 10 RSET f$=text$(i) PRINT f$ NEXT i Parser Parsing a string, you will frequently have to split the string into the target$, the string before and the string after that target$. The following functions could be helpful : FUNCTION before$(source$,target$) ' *** returns part of source$ before target$ ' *** returns complete source$ if no target$ found LOCAL p p=INSTR(source$,target$) IF p=0 RETURN source$ ELSE RETURN LEFT$(source$,p-1) ENDIF ENDFUNC ' FUNCTION after$(source$,target$) ' *** returns part of source$ after target$ ' *** returns nullstring if no target$ found LOCAL p p=INSTR(source$,target$) IF p=0 RETURN "" ELSE RETURN MID$(source$,p+LEN(target$)) ENDIF ENDFUNC 8. KEYBOARD INPUT INKEY$ All keypresses are saved in the keyboard-buffer. If you don't want INKEY$ to read an "old" key, you should first clear the buffer : REPEAT UNTIL INKEY$="" ! clear keyboard-buffer key$="" REPEAT key$=INKEY$ ! wait for new key UNTIL key$<>"" In the following table you'll find a few useful decimal ASCII-codes you can use after 'key$=INKEY$'. In the third column the hexadecimal scan-code of the key is also mentioned (see paragraph 'KEYGET'). key ASC(key$) scancode 8 &H0E 9 &H0F and 13 &H1C and &H72 27 &H01 127 &H53 key ASC(RIGHT$(key$)) 59 &H3B 68 &H44 84 &H54 93 &H5D 98 &H62 97 &H61 82 &H52 71 &H47 75 &H4B 77 &H4D 72 &H48 80 &H50 Keys in the second part of this table return a 2-byte value after INKEY$. You only need the low byte, the high byte is &H00. That's why a Standard Global like help$ is defined as 'CHR$(0)+CHR$(98)'. Then it's easy to check if was pressed : IF key$=help$ (...) ! ENDIF Otherwise, you have to test as follows : IF ASC(RIGHT$(key$))=98 (...) ! ENDIF However, read the paragraph 'KEYGET' for the ultimate keypress-processor. If you are just waiting for any keypress, you could use either of the following methods (clear the keyboard-buffer first) : ~INP(2) ! my favourite ' KEYGET code% ! perhaps this is clearer in a listing ' REPEAT ! a loop is also possible UNTIL LEN(INKEY$) The latter method is needed if you are waiting for any keypress or any mouse-click : REPEAT UNTIL LEN(INKEY$) OR MOUSEK INPUT If you don't want the question mark to appear after INPUT, use : LOCATE col,lin INPUT "",txt$ The nullstring and the comma are essential. Most of the time you'll need something like : LOCATE col,lin INPUT "Enter your name : ",name$ But if the instruction-line is not the same as the INPUT-line, use : PRINT AT(col1,lin1);"Enter your name :" LOCATE col2,lin2 INPUT "",name$ After INPUT, the user can press to switch between Insert-mode and Overwrite-mode. I have not been able to discover how to input character- codes 0-31 after INPUT. It's not important anyway, but I read somewhere you could use for this purpose. The Alternate-method can be used for character-codes 128-255 (not 127). In the following table you'll find some important characters with the decimal ASCII-code : character ASCII-code character ASCII-code character ASCII-code - 160 - 130 - 161 - 133 - 138 - 141 - 132 - 137 - 139 - 131 - 136 - 140 - 162 - 163 - 152 - 149 - 151 - 158 - 148 - 129 - 147 - 150 - 224 - 240 - 171 - 225 - 241 - 172 - 235 - 242 - 253 - 230 - 243 - 227 - 246 - 231 - 247 - 155 - 189 - 156 - 221 - 159 If you want to use one of these characters after INPUT, you should hold down, enter the code, and release . I hope your printer-driver could digest this table... You can use , , and on an INPUT-line, but also : - cursor to start of input-line - cursor to end of line - erase line The first two feel slightly unnatural, I would prefer it the other way: up to end, down to start. Both INPUT and LINE INPUT use a special cursor, so it doesn't make much sense to use XBIOS 21 (Curscon) to do something interesting with the TOS- cursor. INPUT$ For the input of a secret password, you could use something like : PRINT "type password (5 characters) : "; code$=INPUT$(5) The password does not appear on the screen. LINE INPUT LINE INPUT uses the underscore (_) as the cursor in a window. After you press , the underscore is not completely erased. The rightmost pixel remains visible. I think this is a GEM-bug. KEYTEST The KEYTEST-function does not respond to keys such as , , etc. KEYGET KEYGET waits for a keypress, just like INP(2). But KEYGET is far more flexible, because it returns the ASCII-code and the scan-code of any key and also the state of the special keys , , and . Consult your manual for tables of ASCII-codes and scan-codes (in the paragraph 'INKEY$' you already encountered some important codes). Study the following example to get an impression of the easy way you can examine all keypresses with KEYGET : @initio_keyget ! assign variables to the three codes ' DO KEYGET get_code% @keyget ! process keypress there (not included) LOOP ' PROCEDURE initio_keyget ABSOLUTE ascii|,V:get_code%+3 ABSOLUTE scan|,V:get_code%+1 ABSOLUTE status|,V:get_code% RETURN You will have to write your own Keyget-Procedure. You can check if any of the special keys has been pressed, by using BTST(status|,bit) : bit 0 = Right bit 1 = Left bit 2 = bit 3 = bit 4 = You could discover if the user had pressed with : IF scan|=&H50 AND BTST(status|,2) (...) ENDIF If you are only interested in monitoring the five special keys, you could use BIOS 11 (Kbshift) as well : status|=BIOS(11,-1) Use the same table as above to test if bit 0-4 is set. You are advised to clear the keyboard-buffer before leaving the Procedure Keyget. In most cases the scan-code of a key is the same, whether you pressed a special key simultaneously or not. But watch out for the following exceptions! For to the scan-codes &H54 to &H5D are returned (not &H3B to &H44). On an MS-DOS computer these codes are used for the keys F11 to F20. The combinations (&H73) and (&H74) also have special codes. Blame MS-DOS. The combinations <1> to <=> have the special codes &H78 to &H83. That's ALT1 to ALT= for MS-DOS. KEYLOOK According to an unconfirmed report, KEYLOOK does not function properly with the pre-Blitter TOS. KEYPRESS KEYPRESS uses the same 4-byte format as KEYGET: &Hccss00aa. In it you will recognize the ASCII-code (&Haa), the scan-code (&Hss) and the code for the special keys (&Hcc). If you want to simulate the pressing of a key in an Alert box, you will have to send both the ASCII-code and the scan- code. Use &H1C000D to simulate the pressing of . Or &H04620062 for , although that certainly won't help in an Alert box. If you don't need the scan-code (e.g. with INPUT), you can use just KEYPRESS &Haa. KEYDEF The editor always uses KEYPAD &X101110, so you will have to set bit 4 yourself (e.g. KEYPAD &X10000) before you will be able to use KEYDEF in your program. Keyboard As far as I know, there are four different keyboards available : USA (QWERTY), English (QWERTY), German (QWERTZ) and French (AZERTY). The key with scan-code &H2B (to the right of ) has a different ASCII-code in each version : version ASCII-code character USA &H5C (92) \ English &H23 (35) # German &H7E (126) ~ French &H40 (64) @ You could use XBIOS 16 (Keytbl) to determine the keyboard-version : PROCEDURE keyboard.version SELECT PEEK(LPEEK(XBIOS(16,L:-1,L:-1,L:-1))+&H2B) CASE &H5C usa.keybrd!=TRUE CASE &H23 english.keybrd!=TRUE CASE &H7E german.keybrd!=TRUE CASE &H40 french.keybrd!=TRUE ENDSELECT RETURN You should take into account the differences between the keyboard-versions if you are writing a program that should run smoothly in any country. In the following table I have gathered all keys that have not the same meaning on the four keyboard-versions : scancode USA English German French &H0C - - ) &H0D = = - ' &H10 Q Q Q A &H11 W W W Z &H15 Y Y Z Y &H1A [ [ [ &H1B ] ] + ] &H1E A A A Q &H27 ; ; M &H28 ' ' \ &H29 ` ` # ` &H2B \ # ~ @ &H2C Z Z Y W &H32 M M M , &H33 , , , ; &H34 . . . : &H35 / / - = &H60 none \ < < If you are going to use any scan-code from this table, you should be very careful. It's not nice to ask a German user to press , but test for scan-code &H15 in your program... If you insist on doing things the hard way, you can find the ASCII-value that is assigned to a key with XBIOS 16. Actually there are three tables: one for a normal keypress, one for a shifted key and one for a keypress with CapsLock on : keytbl%=LPEEK(XBIOS(16,L:-1,L:-1,L:-1)) shift%=keytbl%+&H80 capslock%=shift%+&H80 Now you can find the ASCII-code for any scan-code (< &H80) : ascii=PEEK(keytbl%+scancode) ! normal key ascii=PEEK(shift%+scancode) ! shifted key ascii=PEEK(capslock%+scancode) ! CapsLock on Keyclick, Keyrepeat and CapsLock Normally, you need the Keyclick as an audible feedback. Sometimes you have to switch the Keyclick off, e.g. while an XBIOS 32 song is playing : PROCEDURE key.click(switch!) IF switch! SPOKE &H484,BSET(PEEK(&H484),0) ! keyclick on ELSE SPOKE &H484,BCLR(PEEK(&H484),0) ! keyclick off ENDIF RETURN If your program reacts slowly after a keypress, you probably have to switch the Keyrepeat temporarily off : PROCEDURE key.repeat(switch!) IF switch! SPOKE &H484,BSET(PEEK(&H484),1) ! key-repeat on ELSE SPOKE &H484,BCLR(PEEK(&H484),1) ! key-repeat off ENDIF RETURN You can switch CapsLock on or off with : PROCEDURE caps(switch!) IF switch! ~BIOS(11,BSET(BIOS(11,-1),4)) ! CapsLock on ELSE ~BIOS(11,BCLR(BIOS(11,-1),4)) ! CapsLock off ENDIF RETURN 9. SCREEN OUTPUT PRINT It is very important to know if PRINT will be used on the so-called TOS- screen (no windows opened), or in a window. TOS emulates the VT52-terminal of Digital Equipment, so if you PRINT on the TOS-screen, the VT52-codes will be interpreted as commands. But in a window these codes are printed as characters! In both High and Medium resolution you can PRINT 25 lines of 80 characters, but in Low resolution it's 25 lines of 40 characters only. Normally you can't PRINT a character at position (80,25) in High or Medium resolution, or at (40,25) in Low resolution. Try the following : PRINT AT(80,25);"X"; and you will see that a linefeed is executed automatically, in spite of the semicolon after "X". On the TOS-screen you can put a character at this position by using the VT52-command 'wrap off' : PRINT AT(80,25);"*wX"; ! use the Escape-character instead of * After 'Esc w' the linefeed is suppressed. The easiest way to enter the Escape-character in the GFA-editor is to hold down and then to enter <2> <7>. If you PRINT a string that doesn't fit on the current line, the remaining characters are either printed on the next line ('Esc v', the default in the interpreter, not in a compiled program), or discarded ('Esc w'). Actually, after 'Esc w' all characters up to the first CHR$(10) or CHR$(13) are discarded. It's impossible to PRINT characters with ASCII-code 0-31 on the TOS- screen. However, you can print any character with : OUT 5,code If necessary, use LOCATE first. After opening a window (OPENW x) the command DEFTEXT will change size and colour of PRINTed text as well! One advantage is that you can now PRINT in different colours on the screen. On the TOS-screen, all PRINTed text has the same colour. This colour is determined by colour-index 1 and is also used for the Alert-box and the Fileselector. The background on the screen is determined by colour-index 0. This colour is used after CLS. It is possible to PRINT in different colours on the TOS-screen, by using the VT52-code 'Esc b'. The background of PRINTed text can be changed with VT52-code 'Esc c'. Use the following functions to experiment : DEFFN ink$(color)=CHR$(27)+"b"+CHR$(color) DEFFN paper$(color)=CHR$(27)+"c"+CHR$(color) Use the colour-table from the paragraph 'SETCOLOR' or be prepared to become very frustrated. I use the Standard Array color.index() as the colour-table in my programs, so I change the colour of the PRINTed text or the back-ground with the Standard Functions : DEFFN ink$(color)=CHR$(27)+"b"+CHR$(color.index(color)) DEFFN paper$(color)=CHR$(27)+"c"+CHR$(color.index(color)) Remember, the VT52-codes have to be PRINTed to become effective, like this : PRINT @ink$(red);@paper$(green);" this text is eye-catching " Note the spaces at beginning and end of the string to emphasize the text- colour against the background-colour. Of course you have to declare the variables red and green first (usually colour-index 2 and 3; or if you don't use the VDI colour-index, try the numbers 1 and 2). I define the default colours as Standard Globals in my programs. Before I forget, it won't work in High resolution. Sorry, couldn't resist that one. In order to catch the eye of the user in High resolution you can PRINT reverse on the TOS-screen : PRINT "this is *p IMPORTANT *q" ! enter Esc instead of * Enter the Escape-character in the usual way. Note again the extra space both before and after the word that should stand out. Of course you could also use CHR$(27) : PRINT "this is ";CHR$(27);"p IMPORTANT ";CHR$(27);"q" More difficult to read on the screen, but easier to Llist. I use the Standard Function Rev$ in all programs : DEFFN rev$(txt$)=CHR$(27)+"p"+txt$+CHR$(27)+"q" If you use a comma with PRINT, the cursor will jump to the next tabulator- stop. Tab-stops are at position 1, 17, 33, 49 and 65. Try the following to see what I mean (in High or Medium resolution) : PRINT "1","17","33","49","65","1","17" If you use the comma after the last tab-stop, a linefeed is executed and the cursor jumps to the first tab-stop on the next line. The easiest way to use double quotes is by using double double quotes (read this twice to make sure you understand it) : PRINT "double quotes printed the ""easy"" way" In a DATA-line a single double quote suffices : DATA "look Ma, "double quotes" again" Or, simply : DATA look Ma, "double quotes" again Try the Procedure Fastprint in High resolution if you find PRINT too slow. This Procedure prints about four times faster! VT52-commands are ignored, because characters are copied straight from the font-table to the screen. LOCATE The syntax of PRINT AT and LOCATE is now less confusing : PRINT AT(column,line) LOCATE column,line In older versions of GFA-Basic it was LOCATE line,column. Check this if you run an old program and text is PRINTed on the wrong place. PRINT TAB PRINT TAB behaves strangely if the position is greater than 80. Try the following in High or Medium resolution : FOR i=0 TO 30 PRINT TAB(i*20);i; NEXT i One way to solve this problem is : PRINT TAB(MOD(i*20,80));i; ! do use semicolons But I think this is a GFA-bug that probably will be corrected in future versions. You can combine TAB with PRINT AT and with PRINT USING : PRINT AT(1,1);"1";TAB(40);"40" PRINT TAB(40);USING "##",40 Setscreen (XBIOS 5) With XBIOS 5 (Setscreen) it is possible to change the resolution from Low to Medium and from Medium to Low. Unfortunately, GEM ignores the switch, so GEM-commands (e.g. ALERT, TEXT, MOUSE) do not work properly! But you could change from Low to Medium resolution to show text on the TOS-screen with PRINT (and VT52-commands). Most users will be grateful for the improved readability of the text : ~XBIOS(5,L:-1,L:-1,1) ! switch from Low to Medium (...) ! print text in Medium resolution ~XBIOS(5,L:-1,L:-1,0) ! and go back to Low If you change the resolution, the VT52-emulator is automatically initialised. You'll probably have to adjust the palette before you can read the text without sun-glasses. Don't forget to save and restore the old palette. XBIOS 5 is very useful if you would like to draw on a screen before showing it to the user. Drawing on an "invisible" screen is indeed possible, because the operating system uses two screens: the physical screen (visible on your monitor) and the logical screen (usually, but not necessarily, the same as the physical screen). All graphical (GEM-)com- mands, including the TEXT-command, are always sent to the logical screen. But TOS will send PRINT-commands to the physical screen, unless you've opened a window! If the address of logical and physical screen is not the same, you have your invisible screen (except for PRINTing on the TOS- screen). The address of the logical screen must be a multiple of 256. Study the Procedures Initio.logical.screen, Swap.screen and Restore.physical.screen to see how you could use the described method for animation : PROCEDURE initio.logical.screen ' *** global : SCREEN.1% SCREEN.2% DIM screen.2|(32256) screen.2%=VARPTR(screen.2|(0)) screen.2%=screen.2%+256-(screen.2% MOD 256) screen.1%=physbase% ~XBIOS(5,L:screen.2%,L:-1,-1) ! invisible screen.2 is now active RETURN ' PROCEDURE swap.screen ' *** physical and logical screen are swapped SWAP screen.1%,screen.2% VSYNC ! avoid flash ~XBIOS(5,L:screen.2%,L:screen.1%,-1) ! swap the screens RETURN ' PROCEDURE restore.physical.screen ~XBIOS(5,L:physbase%,L:physbase%,-1) RETURN On some ST-computers XBIOS 5 does not function properly after installation of a RAM-disk. In that case you could change the address of the logical screen with : VSYNC SLPOKE &H45E,adr% Font TOS has three built-in system-fonts. The default PRINT-font for High resolution is the 8x16 font (equals DEFTEXT ,,,13 for TEXT), while the 8x8 font (equals DEFTEXT ,,,6) is used in Medium and Low resolution. You can switch between these two fonts with the following Procedures : PROCEDURE font.8x16 LOCAL a$,adr% a$=MKI$(&HA000)+MKI$(&H2009)+MKI$(&H4E75) adr%=VARPTR(a$) adr%=C:adr%() ! address of font-table {INTIN}={adr%+8} ! pointer to 8x16 system-font VDISYS 5,2,0,102 RETURN ' PROCEDURE font.8x8 LOCAL a$,adr% a$=MKI$(&HA000)+MKI$(&H2009)+MKI$(&H4E75) adr%=VARPTR(a$) adr%=C:adr%() ! address of font-table {INTIN}={adr%+4} ! pointer to 8x8 system-font VDISYS 5,2,0,102 RETURN Both Procedures seem to have problems with the accessory QUICKST, but I never use it with GFA-Basic because there are other problems (INPUT) as well. If I don't use it, how did I discover this? Good question. The third font is used for icons, but for some reason can not become the current system-font. The VDI-function seems to work only with fonts containing characters of width 8 pixels. The function is not officially documented by Atari (?). You can replace the system-font by a font that has been created with FONTKIT by Jeremy Hughes (4114 byte A1_xxxxx.FON file for High resolution). Use the Procedure Change.font for this purpose and call the Procedure Normal.font to restore the original system-font : PROCEDURE change.font ' *** global : NEW.FONT! NORMAL.FONT% LOCAL adr%,new.font% ' ' *** load A1_xxxxx.FON file (4114 bytes) here INLINE new.font%,4114 ' adr%=L~A-22 normal.font%={adr%} SLPOKE adr%,new.font% new.font!=TRUE RETURN ' PROCEDURE normal.font IF new.font! SLPOKE L~A-22,normal.font% new.font!=FALSE ENDIF RETURN I have not yet discovered how to use a FONTKIT-font with TEXT. Yes, I could load a new font after installing GDOS, but that's not what I'm looking for. Is there a quick and not-dirty way to convince GEM that a new font has been installed? A font-table for the 8x16 font occupies 4096 bytes (16 bytes/character, 256 characters). A FONTKIT-font usually has a name attached at the end, that's why I reserve 4114 bytes. TOS ignores the name completely, it's only used by the accessory FONSEL.ACC. You can load any 4096-byte font in the Procedure Change.font, you don't even have to change 4114 into 4096. Although you lose 18 bytes if you don't. 10. PRINTER Printer ready If you send data to your printer (HARDCOPY, LPRINT, etc.), your ST will wait 30 looooooong seconds if the printer happens to be not ready. Always check if the printer is ready before sending data to the printer, e.g. by calling the following Procedure : PROCEDURE printer.ready LOCAL k DO EXIT IF GEMDOS(17) ALERT 3," printer| | not ready !!",1," OK ",k LOOP RETURN HARDCOPY You can send a screendump to the printer by using the command HARDCOPY or by pressing . In both cases you can abort the screendump by pressing . You can use CONTROL.ACC to change the printer-parameters. Don't forget to save the desktop, in order to store these parameters in the file DESKTOP.INF. Look for '#b' with your disk-editor if you're curious. The parameters will only be read from DESKTOP.INF if CONTROL.ACC is installed after a reset! I suggest the use of XBIOS 33 (Setprt) in a program instead of using CONTROL.ACC. For a screendump from the High resolution screen to an Epson-compatible printer, clear bit 1 and set bit 2 : PROCEDURE screendump ~XBIOS(33,&X100) ! screendump to Epson(-compatible) printer HARDCOPY RETURN If you set bit 2, TOS assumes you connected an Epson-printer (960 pixels/line). Clear bit 2 and TOS assumes you have an Atari-printer (1280 pixels/line). If you use HARDCOPY, the width/height ratio of the printout does not correspond with that of the screen. For a reasonably fast 1:1 screendump, study the Procedure High.screendump.epson (Epson FX-80, or other 9-pin Epson-compatible printer) or the Procedure High.screendump.star24 (24-pin Star LC24-10) : PROCEDURE high.screendump.star24 LOCAL m$,b$,k,scrn.start%,col,b%,x,d%,p|,b1|,b2|,b3|,n lf$=CHR$(10) ff$=CHR$(12) DEFFN bit.image$(m,d)=CHR$(27)+"*"+CHR$(m)+CHR$(MOD(d,256))+ CHR$(d/256) DEFFN line.space$(n)=CHR$(27)+"3"+CHR$(n) initialize$=CHR$(27)+"@" scrn.start%=XBIOS(2) ' LPRINT initialize$; FOR col=0 TO 79 b%=scrn.start%+col LPRINT SPC(8); LPRINT @line.space$(24); LPRINT @bit.image$(33,800); FOR x=399 TO 0 STEP -1 d%=ADD(b%,MUL(x,80)) p|=BYTE{d%} IF p|=0 OUT 0,0,0,0,0,0,0 ELSE CLR b1|,b2|,b3| IF BTST(p|,0) b1|=7 ENDIF IF BTST(p|,1) ADD b1|,56 ENDIF IF BTST(p|,2) ADD b1|,192 b2|=1 ENDIF IF BTST(p|,3) ADD b2|,14 ENDIF IF BTST(p|,4) ADD b2|,112 ENDIF IF BTST(p|,5) ADD b2|,128 b3|=3 ENDIF IF BTST(p|,6) ADD b3|,28 ENDIF IF BTST(p|,7) ADD b3|,224 ENDIF OUT 0,b3|,b2|,b1|,b3|,b2|,b1| ENDIF NEXT x LPRINT EXIT IF INKEY$=esc$ NEXT col LPRINT ff$; LPRINT initialize$; RETURN For a more flexible approach, study the Procedure Degas.screendump (Star LC24-10, possible formats : 27x17, 18x11, 13.5x8.5 or 9x5.5 cm). It should be possible to send only a (GET-)rectangle to your printer with XBIOS 36 (Prtblk) or V_OUTPUT_WINDOW (VDI 5,Escape 21). Anybody out there who knows how? Never, I repeat, never swap disks during a screendump, as this could be fatal for the new disk. TOS ignores the write-protect state during a screendump, so it will miss the disk-swap completely. TOS will use the old FAT for the new disk, and that usually is fatal (no pun intended). If you have installed a GFA-Basic printer-driver (e.g. PTEPSON.PRG), a screendump seems to be impossible. The bit-image mode of the printer can't be used after the driver has been installed. Printer-commands Most matrix-printers recognize either IBM- or Epson-commands (or both). I use the following Procedure to define the most important printer-commands for my Star LC24-10. Adapt the definitions to your own printer, but stick to the names for the global variables and functions. Other users could then use your programs easily after replacing the Procedure Initio.printer with their own : PROCEDURE initio.printer ' *** initializes global printer-variables for Star LC24-10 ' *** DIP-switch settings : ' 1-1 OFF 2-1 ON ' 1-2 ON 2-2 ON ' 1-3 OFF 2-3 ON ' 1-4 ON 2-4 ON ' 1-5 ON 2-5 OFF ' 1-6 ON 2-6 OFF ' 1-7 ON 2-7 ON ' 1-8 ON 2-8 ON ' LOCAL c$,f$ c$=CHR$(27) f$=CHR$(28) ' draft.char$=c$+"x0" lq.char$=c$+"x1" ' courier.style$=c$+"k0"+lq.char$ prestige.style$=c$+"k2"+lq.char$ orator.style$=c$+"k3"+lq.char$ script.style$=c$+"k4"+lq.char$ ' normal.char$=c$+"q0" outlined.char$=c$+"q1" shadow.char$=c$+"q2" outlined.shadow.char$=c$+"q3" ' italic.on$=c$+"4" italic.off$=c$+"5" ' emphasized.on$=c$+"E" emphasized.off$=c$+"F" ' underline.on$=c$+"-1" underline.off$=c$+"-0" ' bold.draft$=draft.char$+emphasized.on$+double.on$ bold.lq$=lq$+double.on$ bold.off$=emphasized.off$+double.off$ ' superscript.on$=c$+"S0" superscript.off$=c$+"T" subscript.on$=c$+"S1" subscript.off$=c$+"T" ' epson.set$=c$+"t0" ibm.set$=c$+"t1"+c$+"6" DEFFN special.on$(n)=c$+"\"+CHR$(MOD(n,256))+CHR$(DIV(n,256)) DEFFN ibm.special$(n)=c$+"^"+CHR$(n) DEFFN epson.special$(n)=ibm.set$+@ibm.special$(n)+epson.set$ ' pica$=c$+"P" ' elite$=c$+"M" ' condensed.on$=CHR$(15) condensed.off$=CHR$(18) ' large.on$=c$+"W1" large.off$=c$+"W0" large.line$=CHR$(14) ' courier.proportional$=courier.style$+c$+"p1" prestige.proportional$=prestige.style$+c$+"p1" proportional.off$=c$+"p0" ' DEFFN master.mode$(n)=c$+"!"+CHR$(n) underline=128 italic=64 expanded=32 LET double.strike=16 emphasized=8 condensed=4 proportional=2 elite=1 ' normal.size$=c$+"h"+CHR$(0) LET double.size$=c$+"h"+CHR$(1) quad.size$=c$+"h"+CHR$(2) LET double.height$=c$+"w1" normal.height$=c$+"w0" normal.width$=f$+"E"+CHR$(0) LET double.width$=f$+"E"+CHR$(1) triple.width$=f$+"E"+CHR$(2) ' lf$=CHR$(10) DEFFN lf$(n)=c$+"f1"+CHR$(n) rev.lf$=c$+CHR$(10) ' ff$=CHR$(12) rev.ff$=c$+CHR$(12) ' justify.left$=c$+"a0" justify.right$=c$+"a2" justify.full$=c$+"a3" center$=c$+"a1" ' reset$=c$+"@" ' RETURN Of course you could delete all variables you don't need in your program. And it really would be nice if everyone uses the proposed names. One of the strong points of Public Domain GFA-programs is that it's easy to adapt a program. Or rather, it should be easy. Using the above Procedure is one step in the right direction. 11. FILES Floppy Write Test You are advised to switch the Write Verify test off : SPOKE &H444,0 ! test off SPOKE &H444,1 ! test on (default) According to experts like Dave Small and Bill Wilkinson the Verify test is a complete waste of valuable time if you write to a disk. Step Rate You will find the current step-rate of your drive with : PRINT DPEEK(&H440) The following values are possible : 0 - 6 ms 1 - 12 ms 2 - 2 ms 3 - 3 ms (default) The operating system only looks at this value after a reset (?). For an external 5.25"-drive you probably have to use 12 ms. RAM-disk Drive D is often reserved for a RAM-disk. GFA will recognize a RAM-disk with DFREE(4) only if it was already present at the time the interpreter was loaded. After switching off your 1040 ST you should wait at least 15 seconds before switching on again. Otherwise an old RAM-disk (or something else in RAM, e.g. a virus...) may still be present when you switch your computer on again. If a RAM-disk is not installed properly after a reset, the reason could be hidden in the drive bit-table at &H4C2. The old TOS does not clear this table and a RAM-disk can only be installed as drive D if bit 3 is cleared. By the way, use drive C (bit 2) only for a harddisk. DIR$() Use GEMDOS 25 (Dgetdrv) to find the current drive and combine this with DIR$(drive) to find the current path : drive=GEMDOS(25) ! drive 0 - 15 drive$=CHR$(65+drive) ! drive A - P p$=DIR$(drive+1) IF p$="" path$=drive$+":\" ! main directory ELSE path$=drive$+":"+p$+"\" ENDIF With DIR$(0) you'll find the path of the current drive, not necessarily drive A. DIR$(1) returns the path of drive A. GEMDOS remembers the last used path for all available drives. See also the Standard Procedure Get.path. If you run GFA-Basic from the main directory and load a GFA-program from a folder, DIR$(0) will return the nullstring (""), not the folder. After using CHDIR with the folder-name, the correct path will be returned. I use CHDIR in the Shell-programs, so the Standard Global default.path$ will contain the path of the GFA-program. This makes life easier if you want to load data-files from the same folder, but don't know the precise path when you write the program. It would be nice if you could determine the path of the running GFA-program in the program itself. You can make an array-table of available drives with the aid of BIOS 10 (Drvmap) : DIM drive!(15) SELECT DPEEK(&H4A6) CASE 1 drive!(0)=TRUE CASE 2 drive!(0)=TRUE drive!(1)=TRUE ENDSELECT table%=BIOS(10) FOR n=2 TO 15 IF BTST(table%,n) drive!(n)=TRUE ENDIF NEXT n You can check if a harddisk is connected with : IF PEEK(&H472)<>0 harddisk!=TRUE ENDIF DIR and FILES After DIR, FILES, TRON or DUMP you can slow down the scrolling with . You can temporarily stop the scrolling by holding down the right -key. The output after DIR fits on any screen, but the FILES- output is too wide for Low resolution. DIR will show only files in the current directory. FILES will also show folders (marked with *). With DIR (and FILESELECT) you will not be able to see "hidden" files or "system" files, but FILES will show all files. You can also search for hidden and/or system files with FSFIRST and FSNEXT by setting bit 1 and/or bit 2 of the attribute-byte. Perhaps you have noticed that after the command FILES the first two lines are peculiar if you happen to be in a folder. The first "name" in a folder is always '.' (one dot) and the second always '..' (two dots). Time and date are incorrect, because the authors of (the old) TOS forgot to convert these to MS-DOS format. In case of nested folders, the operating system finds the preceding folder through a pointer of the '..'-file. That's why you can use 'CHDIR ..' to return to the preceding folder. Don't try this in the main directory, or you'll get an error. FSFIRST and FSNEXT The DTA-buffer is usually found at BASEPAGE+128 (it's always there after start-up), but you should not count on it. Use FGETDTA() to find the current address, before FSFIRST : dta.adr%=FGETDTA() e%=FSFIRST(format$,attr) FSFIRST returns -33 if no file has been found. FSNEXT returns -49 if no more files are found. In an accessory it's safer to create a new DTA-buffer : old.dta%=FGETDTA() ! old buffer dta$=STRING$(44,0) dta.adr%=V:dta$ ~FSETDTA(dta.adr%) ! new buffer (...) ! FSFIRST/FSNEXT ~FSETDTA(old.dta%) ! restore old buffer The Desktop will appreciate all this extra work. The 44 byte DTA-buffer (Data Transfer Address) contains the following data after a succesful FSFIRST or FSNEXT : offset 0-20 - reserved 21 - attribute-byte 22-23 - time 24-25 - date 26-29 - file-length 30-43 - filename (including the extension, terminated by &H00) The attributes and the filename can be read from the buffer with : attr=BYTE{dta.adr%+21} file$=CHAR{dta.adr%+30} The DTA-buffer is not an exact copy of the relevant information in the directory of the disk. With a disk-editor you would find a slot of 32 bytes for each file or folder : offset 0- 7 - file- or folder-name (without extension) 8-10 - extension 11 - attribute-byte 12-21 - reserved 22-23 - time 24-25 - date 26-27 - FAT-pointer 28-31 - file-length The first byte of the filename has a special meaning in the following cases : &H00 - free slot, never used before &HE5 - erased file, now free slot &H2E - subdirectory Both time and date are stored in MS-DOS format. Consult your GFA-manual for more information. The FAT-pointer, also in an MS-DOS format (Intel- format : first low byte, then high byte), points to the first cluster of the file. If you are looking at a folder, the FAT-pointer points to the cluster where you will find the directory of this folder (subdirectory). If you are looking at a subdirectory (i.e. you are in a folder), the first two slots are reserved for the files '.' and '..' (&H2E and &H2E2E). This has already been mentioned in the paragraph 'DIR and FILES'. Finally, the file-length is stored in, you guessed it, MS-DOS format. You might wonder what MS-DOS has to do with Atari-disks. Read the paragraphs 'Disk Format' and 'File Allocation Table' for the explanation. If you use the attribute-byte &X10000 you will find both folders and files! If the folders in a directory don't have an extension and all files do have an extension, you could find all folders in the main directory as follows : e%=FSFIRST("\*",&X10000) ! and e%=FSNEXT() for next folders If you can't use this simple method, you'll have to check after each successful FSFIRST/FSNEXT if it's a folder or a file : IF BTST(BYTE{dta.adr%+21},4) (...) ! yes, it's a folder ENDIF Use the attribute-byte 0 (i.e. no bits set) to find ordinary files only. Use attribute-byte &X1000 to find the disk-name : dta.adr%=FGETDTA() e%=FSFIRST("\*.*",&X1000) ! finds disk-name only, not files disk.name$=CHAR{dta.adr%+30} You can read the attributes of a file or folder with GEMDOS 67 (Fattrib) : attr%=GEMDOS(67,L:V:filename$,0,0) If the file (or folder) is not found, attr% is -33 (or -34), otherwise attr% contains the attributes in the usual format. You can even change the attributes of files with : r%=GEMDOS(67,L:V:filename$,1,attribute%) It's not possible to change the attributes of folders or the disk-name with GEMDOS 67. One way to do that is to change the directory-sector with the aid of BIOS 4 (Rwabs). If you really feel the urge to experiment, you should realize that one tiny mistake could ruin the disk. EXIST You can use EXIST to test if a folder exists, but only if the folder contains at least one file : IF EXIST("\FOLDER\*.*") (...) ! folder found ELSE (...) ! folder not found or empty folder ENDIF LOF The length of a file is easily determined with LOF : OPEN "I",#1,file$ length%=LOF(#1) CLOSE #1 To determine the number of records in a random file you could divide the file-length by the total FIELD-length. TOUCH Use this method with TOUCH : OPEN "U",#1,file$ TOUCH #1 CLOSE #1 NAME With the old TOS you can only change the name of files, not of folders. Even from the desktop you can't change the name of a folder, so choose it carefully. KILL KILLing a file does not erase it from the disk. The first byte of the filename is changed to &HE5. Unfortunately you can't restore a killed file by simply changing this byte with a disk-editor. The operating system will be able to find the first cluster of the restored file, because the first FAT-pointer is located in the directory. The next clusters can only be found through the FAT (File Allocation Table), but after KILL all pointers to this file are irreversibly erased. If you have not killed any file on the disk before your fatal mistake, you are extremely lucky and will find all clusters have been stored consecutively. But after some killing and saving on the disk, the file could be dispersed over the entire disk. Some programs are able to help you, but you will have to recognize clusters as belonging to the killed file. That's easy with ASCII-files, but almost impossible with other files. So, don't KILL unless you have to. File Copy You can copy a file source$ to dest$ (use complete pathnames!) with : PROCEDURE file.copy(source$,dest$) LOCAL block% OPEN "I",#90,source$ OPEN "O",#91,dest$ block%=LOF(#90) WHILE block%>32000 PRINT #91,INPUT$(32000,#90); SUB block%,32000 WEND PRINT #91,INPUT$(block%,#90); CLOSE #90 CLOSE #91 RETURN Do not copy a file "to itself" on a harddisk. Thanks to yet another bug in TOS, this action could completely wipe out the harddisk. Or perhaps this should be called a feature of TOS, put there to punish the crazy user who tries to copy a file to itself. Disk Format A disk contains 80 concentric tracks (numbered 0 - 79) or more. Sometimes the expression "cylinder" is used instead of "track". Each track is divided into 9, 10 or even 11 sectors. One sector can contain 512 data- bytes. In order to be compatible with MS-DOS, TOS formats a disk with 80 tracks and 9 sectors/track. Actually it's easy to fit 10 sectors in one track. With a little more effort you can create room for 11 sectors, but some drives run slightly too fast and are not able to read the 11th sector! With a disk-editor you can examine the 512 data-bytes of a sector, but you can't examine the sector-layout without accessing the Floppy Disk Controller (FDC) directly. In that case you would find the following layout for each sector : data-separator (GAP) - 15 bytes ID-Address mark - 1 byte sector-header - 4 bytes (track, side, sector, size) CRC of sector header - 2 bytes data separator - 37 bytes Data-Address Mark - 1 byte data bytes - 512 bytes CRC of data bytes - 2 bytes data separator - 40 bytes The data separator bytes are there to synchronize the FDC properly. The FDC recognizes the sector-header by the preceding ID-Address mark. The sector-header itself contains information about the current track, side and sector and also about the size of the data-field (usually 512 bytes). The FDC checks both the sector-header and the data-field for corrupted bytes by comparing a computed "checksum" with the stored CRC-value. The operating system cannot read/write one byte from/to a sector, only complete sectors are read or written. GFA-Basic takes care of all the dirty work. First some bad news. A CRC-error is not always recognized by the ROM of 520 ST's and 1040 ST's (bug in XBIOS 8, Floprd). If your palms are now getting sweatty, you could check your most precious disks with XBIOS 19 (Flopver). This function checks for 'lost data, RNF- or CRC-errors'. Create a buffer of 1024 bytes and call XBIOS 19. A sector (512 bytes) is loaded from the disk in drive A or B (0 or 1) into the second part of the buffer and checked. If a bad sector is found, the sector-number is stored as a word in the first part of the buffer. After checking all sectors in one track you have to examine the word-list in the buffer. Hope you will find only &H0000 there. I leave the writing of this program as an exercise to the reader. Never thought I would use that phrase myself. Allright, here's something to get started : buffer$=STRING$(1024,0) adr%=V:buffer$ r%=XBIOS(19,L:adr%,L:0,drive,1,track,side,9) ! if 9 sectors/track You should now be able to find out if the track on this side (0 or 1) is OK. Good luck. You can use BIOS 7 (Getbpb) to examine the disk-format in the so-called BIOS-Parameter-Block (BPB) of the disk : bpb.adr%=BIOS(7,drive) ! address of BPB, or 0 (= error) In 9 words you'll find the following information in the BPB : offset 0 - bytes/sector (usually 512) 2 - sectors/cluster (usually 2) 4 - bytes/cluster 6 - number of directory-sectors 8 - length of FAT 10 - first sector of second FAT 12 - first data-sector 14 - total clusters 16 - flag (not used) Use GEMDOS 54 (Dfree) to find out how many free clusters are available, or simply use DFREE if you want to know how many free bytes are available on a drive. Due to a bug, GEMDOS misses the last two clusters on a disk. You can't write to these clusters (2048 bytes down the drain...), but you can read these clusters if they do contain data. That would be a miracle, or an MS-DOS disk. You'll probably use XBIOS 10 (Flopfmt) to format a disk from GFA-Basic. If you do, use &H0000 as the virgin-value for the first 18 sectors, and then &HE5E5 for all data-sectors. You can use either 9 or 10 sectors/track, not 11. If you use 10 sectors/track, you should fill the first two tracks with &H0000 and fill sectors 19 and 20 with &HE5E5 afterwards (read the paragraph 'Sectors'). I don't recommend more than 80 tracks, certainly not more than 82 tracks. If XBIOS 10 returns a value other than 0, you'll find a list of bad sectors in the buffer you used (terminated with &H00). If one of the first 18 sectors is bad, you can throw the disk away. Using XBIOS 10, the interleave-factor should be 1. This means the sectors on a track are numbered consecutively: 1,2,3,4,etc. I don't understand why some programmers use another value and call it 'Fast Format'. The FDC needs more time to read a complete track if the interleave-factor is not 1. Perhaps they try to create the so-called Twisted (or Skewed) format. TOS loses time if the head moves to the next track. Because the Seek with Verify flag is set, the FDC first verifies the track-number and then reads the sector-number. While checking the track-number, sector 1 was passing, so we have to wait for one complete spin of the disk (200 ms, yawn) until sector 1 can be read. One solution is to clear the Seek with Verify flag, but that could lead to nasty problems if the head still rattles slightly at the time sector 1 is read. The best solution is the Twisted format (adopted by Atari from the Mega ST onwards). For a single-sided disk with 9 sectors on one track this means : track 0 : sector 1,2,3,4,5,6,7,8,9 track 1 : sector 8,9,1,2,3,4,5,6,7 track 2 : sector 6,7,8,9,1,2,3,4,5 etc. Now, sector 1 is encountered almost immediately after the track-number is verified. A 1-sector offset is not possible, but a 2-sector offset is enough to settle the rattling head. It is impossible to read data faster from a disk! But I'm afraid you can't format a Twisted disk with XBIOS 10 if you have an old TOS. You have to use a special format-program. If you format a disk from the desktop, bad clusters are flagged with a special value in the FAT. However, if TOS 1.4 encounters a bad sector, something goes wrong and the FAT is corrupted. One more reason to quit smoking, because smoke-particles definitely constitute a serious hazard to the health of your disks. You can use XBIOS 18 (Protobt) to create a bootsector on the formatted disk. Don't worry about the media-byte (disk-type), because TOS doesn't use it. Do use &H1000000 to generate a random serial number, because it is very important that different disks have different serial numbers! Write the bootsector to the disk with XBIOS 9 (Flopwr), not with BIOS 4 (Rwabs): r%=XBIOS(9,L:buffer%,L:0,drive%,1,0,0,1) Why are different serial numbers so important? If TOS suspects a disk- swap, the serial number is read from the disk. A disk-swap can only be recognized if the number on the new disk is different from the old number. If the new disk contains the same serial number, TOS uses the FAT of the previous disk. Writing to a swapped disk will probably zap it, if you follow me. Disk-copiers copy everything, including serial numbers. Be careful! Although it is possible to format a disk from GFA-Basic, I don't recommend it (now he tells us...). My favourite format is : 80 tracks 10 sectors/track Twisted format I use TWISTER.PRG (not Public Domain, as far as I know), but you could use a Public Domain program like DCOPY (actually Shareware). The Desktop can't copy a Twisted disk, but file-copy is always possible. DCOPY copies any format, including Twisted format. File Allocation Table (FAT) The first sector on a disk is the boot-sector. The next five sectors are reserved for the FAT. And the next five for a copy of the FAT (actually it's the other way around). Finally, the main (or root) directory occupies the next 7 sectors (32 bytes for each slot). This means that the first 18 sectors (No. 0-17) are reserved for the operating system. All other sectors are available for storing files. The storage-unit for files is a cluster. A cluster consists of two consecutive sectors (1024 bytes). A file that contains only 1 byte will therefore still occupy 1024 bytes (1 K) on the disk. When a file is saved, the operating system looks for the first empty cluster, then the next, etc. Information about available clusters is stored in the FAT as a collection of pointers. Due to inefficient programming, the search for free clusters takes a long time. Try DFREE on a harddisk and you'll agree. Install a program like FATSPEED.PRG (Public Domain, by Ulrich Kuebler) to speed this up! The first three bytes of the FAT are not used by TOS, but are there to enable MS-DOS to read an ST-formatted disk. TOS writes &HF7FFFF, where the first byte (&HF7) is supposed to be the media-byte. Unfortunately MS-DOS doesn't understand this and refuses to read the directory properly. I'm not quite sure why, but changing the first three bytes to &H000000 seems to work. You could try &HF8FFFF (80 tracks, 9 sectors/track, single sided disk) or &HF9FFFF (double sided disk) instead. Or you could use the media- byte at offset 21 from the bootsector. To make your MS-DOS friends completely happy, you should change the first three bytes of the bootsec- tor to: &HEB3890. Sometimes (DOS 4.0 ?) you have to change the next three bytes as well: &H49424D (that's IBM in ASCII; please watch your language). Better still, let them use their own disk editor on their MS-DOS computer (e.g. Norton Utilities). Why should you do all the work? By the way, your ST should be able to use an MS-DOS disk without any modifications. Each FAT-pointer consists of one and a halve byte (3 nibbles, i.e. 12 bits). In hexadecimal notation this means three digits for one pointer. The first pointer (No. 0) points to cluster No. 0, the second to No. 1, etc. . Because the first two pointers don't count, you have to subtract two to find the actual cluster (pointer 2 points to cluster No. 0, etc.). The first cluster starts at sector No. 18 (remember, sectors 0-17 are reserved for bookkeeping), so you could find the first sector of a cluster with : (pointer - 2) * 2 + 18 The second sector of this cluster is of course the next one. TOS reads the pointers in a peculiar (MS-DOS) way. Suppose the FAT-sector starts with : F7 FF FF 03 40 00 FF 0F 00 Without further explanation, this translates to : FAT-pointer 0 and 1 are ignored FAT-pointer 2 = &H003 (next cluster on sector 20 + 21) FAT-pointer 3 = &H004 (next cluster on sector 22 + 23) FAT-pointer 4 = &HFFF (last cluster of this file) FAT-pointer 5 = &H000 (free cluster) In order to understand this, you have to consult the following table : &H000 : cluster still available &HFF1 - &HFF7 : bad cluster, never available &HFF8 - &HFFF : last cluster of this file (End Of File) &H002 - &HFF0 : pointer to next cluster Assuming the FAT-pointer &H002 at offset 26 in the directory (you'll find &H0200 with a disk editor, that's Intel format with low byte first again), you should be able to figure out that this file will be found in sectors 18 through 23. Sectors 24/25 are empty, so this cluster is available for a new file. Any questions? The one big advantage of all this is compatibi- lity with MS-DOS disks. With TOS 1.4 your ST-disks can be made completely compatible, so you don't even have to change a few bytes. Right, you have just read the Fatman-book. The movie will be released shortly, but you can NOW buy those fabulous Fatman-shirts and fantastic Fatman-buttons. Call Atari for further details. Sectors There are two different methods to assign a number to a sector. The first one is to number the "physical" sectors in each track from 1 to 9 (assuming 9 sectors/track). This way you could say the bootsector is sector 1 on track 0 (on side 0 if the disk is double sided). But GEMDOS doesn't care about tracks or sides, it counts "logical" sectors from 0 to 719 (80 tracks, 9 sectors/track, one sided disk) or from 0 to 1439 (double sided disk). According to GEMDOS, the bootsector is on sector 0. And on a double sided disk, physical sector 1 on track 0 of side 1 (the other side) would be sector 9. With BIOS 4 (Rwabs) you can read (and write) complete logical sectors : buffer$=SPACE$(512) ! 512 bytes for 1 sector buffer%=V:buffer$ ! address of buffer r%=BIOS(4,0,L:buffer%,1,sector%,drive%) ! load the sucker You would load the bootsector from the disk in drive A with : r%=BIOS(4,0,L:buffer%,1,0,0) You can use BIOS 4 not only with floppy disks, but also with a harddisk or a RAM-disk. After loading a sector, you can read one byte with : b|=BYTE{buffer%+n} ! n from 0 to 511 You can read a word with : w=WORD{buffer%+n} But only if the word starts at an even address. Otherwise you have to use: w=BYTE{buffer%+n+1}+256*BYTE{buffer%+n} If necessary, you can speed this up by using the special integer commands for addition and multiplication. How about this Polish monster : DEFFN word(adr%)=ADD(BYTE{SUCC(adr%)},MUL(256,BYTE{adr%})) Then you would use : w=@word(ADD(buffer%,n)) If you use BIOS 4 to write a sector after formatting a disk, you should use '3' as a flag (not '1') : r%=BIOS(4,3,L:buffer%,1,sector%,drive%) You can also read and write physical sectors with XBIOS 8 (Floprd) and XBIOS 9 (Flopwr). With these commands you could even read/write all sectors on one track. This is the only way to read a track, because reading a complete track is impossible due to a bug in the FDC. If you swap a disk after loading/writing a sector you should be careful. Testing with BIOS 9 (Mediach) you could miss the disk-swap. This could be fatal, because TOS uses the FAT of the other disk! I think you could use BIOS 7 (Getbpb) in most cases, but use the Procedure Force.mediach if in doubt : PROCEDURE force.mediach LOCAL x$,old.vector%,a% x$=SPACE$(12) old.vector%=LPEEK(&H47E) a%=V:x$ DPOKE a%,&H2B7C LPOKE a%+2,old.vector% DPOKE a%+6,&H47E DPOKE a%+8,&H7002 DPOKE a%+10,&H4E75 SLPOKE &H47E,a% ~DFREE(0) ! current drive RETURN To be more precise, BIOS 7 can't be used in the following situation : - TOS reads a sector - you swap disks - you use BIOS 7 in your program - TOS reads a sector Now TOS will assume there has been no disk-swap, because there has been no disk-swap after the last BIOS 7 call! You definitely need the Procedure Force.mediach in this case. By the track, TOS also ignores disk-swaps during a screendump (HARDCOPY), or while using DMA (harddisk, laser printer). TOS detects a disk-swap by monitoring the write-protect state. To see this, you should turn all lights off and then watch the drive-light closely. Closer. You can turn the lights on again. If the drive is empty, TOS gets a write-protect signal and assumes the user might have swapped disks. BIOS 9 should return '1' at this point. TOS checks if you really did swap disks by reading the serial number from the bootsector and comparing it with the current number. Only if these numbers are different, a disk-swap is recognized by TOS (BIOS 9 should return '2' now). You probably deduced that TOS will read the bootsector also if you use a write-protected disk. Continuously reading the bootsector is a waste of time, so TOS waits 1.5 seconds before looking again. Never swap disks within 1.5 seconds after a read/write- operation. The drive keeps spinning for 2 seconds, so you can't go wrong if you wait until the drive-light is off before swapping disks. Bootsector In the following table you'll find the lay-out of a bootsector. All words are in Intel-format, except CHKSUM. offset length name 0 2 &H6038 = branch to bootroutine 2 6 FILLER fill-bytes 8 3 SERIAL serial-number of disk 11 2 BPS bytes/sector (512) 13 1 SPC sectors/cluster (2) 14 2 RES reserved sectors (1, Bootsector) 16 1 NFATS number of FAT's (2) 17 2 NDIRS max. entries in main directory 19 2 NSECTS total sectors 21 1 MEDIA media-byte (not used by TOS) 22 2 SPF sectors/FAT (5) 24 2 SPT sectors/track 26 2 NSIDES sides (1 or 2; no joke this time) 28 2 NHID hidden sectors (ignored by TOS) 30 2 EXECFLAG start of bootcode : flag 32 2 LDMODE 0=load FNAME; <>0=load sectors 34 2 SSECT first sector (LDMODE<>0) 36 2 SECTCNT number of sectors 38 4 LDADDR load at this RAM-address 42 4 FATBUF address of FAT-buffer 46 11 FNAME filename (nnnnnnnneee) (LDMODE=0) 57 1 DUMMY fill-byte 58 boot-routine (could be a boot-virus) 510 2 CHKSUM TOS determines if the bootsector is executable by adding all bytes. If this sum (AND &HFFFF) equals &H1234, the bootsector is executable. If you use GFA-Basic this probably means you have an ancient ST with TOS in RAM, or a boot-virus. A normal GFA-disk has only &H00- or &HE5-bytes from the offset 58. BLOAD BLOAD needs an address, unless you have used BSAVE in the same program before. In that case the BSAVE-address is used automatically by BLOAD if you don't specify a new address. BLOAD (BSAVE) is easier to use than BGET (BPUT), because you don't have to open the file. But with BLOAD you can only load the entire file, while BGET allows you to load any part of the file. INP and OUT Both INP and OUT can also be used with 2-byte and 4-byte integers : a|=INP(#n) a&=INP&(#n) a%=INP%(#n) OUT #n,a| OUT& #n,a& OUT% #n,a% INPUT and LINE INPUT Because GFA now uses a 4K-buffer for each opened file (version 3.07), reading data from a file with (LINE) INPUT goes much faster. STORE and RECALL For very fast loading and saving of string-arrays, you should use RECALL and STORE. You can also store (or recall) a part of an array as follows : STORE #1,txt$(),10 ! store elements 0 through 9 STORE #1,txt$(),5 TO 10 ! store elements 5 through 10 The correct syntax is : STORE #i,x$()[,n[ TO m]] RECALL #i,x$(),n[ TO m],x% (use n=-1 for complete array) Of course you have to open the file first. If you STORE a text-array, GFA puts &H0D0A (CHR$(13);CHR$(10)) after each element. This is the same format as used by 1st Word Plus (WP Mode off, i.e. ASCII-mode), Tempus, etc. If you're going to show more than a few text-lines, you could enter the text as DATA-lines, e.g. with the Procedures Initio.text.array and Initio.text.array.low (for Low resolution) : PROCEDURE initio.text.array ' *** global : TEXT$() LOCAL lines,line$,n lines=0 RESTORE txt.data READ line$ REPEAT INC lines READ line$ UNTIL line$="***" ERASE text$() DIM text$(lines) RESTORE txt.data FOR n=1 TO lines READ text$ text$(n)=SPACE$(5)+text$ ! left margin of 5 spaces !! NEXT n txt.data: DATA text DATA *** RETURN If you are going to show more text, I suggest you use 1st Word Plus, or any other wordprocessor or text-editor that can save your text as an ASCII-file. With 1st Word Plus, I use a Ruler length of 70 and the following Page Layout Form : Paper length 66 TOF margin 19 Head margin 4 Foot margin 4 BOF margin 19 Lines/page 20 Enter the text and save as an ASCII-file (turn WP Mode off before saving). In your GFA-Basic program you would first load the text in a string-array: DIM text$(lines) OPEN "I",#1,file$ RECALL #1,text$(),-1,lines% ! lines% lines+1 CLOSE #1 Then you could use the Procedure Show.text.page (High or Medium resolu- tion) to show the text with 20 lines/screen. The Procedure uses a left and right margin of 5 characters, so that's why you have to use 70 charac- ters/line in your wordprocessor. If the text-array is full, you won't get an error if the file contains more text-lines! You could use a 2-dimensional string-array to store first and last names : name$(i,0)=first_name$ name$(i,1)=last_name$ Again, STORE and RECALL are very fast. But it is now necessary to use exactly the same dimensions with RECALL that you used with STORE. If the dimensions don't match, the array will be scrambled after RECALL. FILESELECT Don't use the underscore '_' in the path-line of the Fileselector, because a bug in the old TOS will then cause a few bombs. Owners of a Mega ST or TOS-version 1.4 can use as many underscores as they like. Changing drives in the Fileselector (old TOS) is not easy. Click on the path-line and press to clear the line. Enter the drive, e.g. 'D:\', and click on the bar under the path-line in order to read the new directory. Also click on this bar after changing disks (in this case you would press on the desktop). Selecting drive A is easier: just clear the path-line and click on the bar. The TOS-code that takes care of all this work is also known as the bartender. If you have changed the extension in the path-line, you should click just below the bar. If you click on the bar, the path-line is overwritten with '*.*' ! That same bartender strikes again. The correct syntax for calling the Fileselector is : FILESELECT [#title$,]path$,default$,file$ The title is only used in TOS-version 1.4 and ignored in older TOS- versions. The default$ usually is the null-string (""), but don't use the null-string for path$ or the Fileselector will freeze. Use "\*.*" as path$ for all files in the main directory. Do use the backslash in the pathname (e.g. "A:\*.*"). Due to a bug in GEM, the wrong drive is sometimes used if you forget the backslash ("A:*.*"). If the user has selected a file, file$ will contain the path and filename. The file$ will be the null-string if the user selected . A third possibility is easily overlooked: the user could have selected without choosing a file. In that case file$ contains the current path, ending with a backslash. Do not despair if you need a title, but don't have TOS 1.4. Try the Procedure Fileselect (High or Medium resolution) or Fileselect.low : PROCEDURE fileselect(path$,default$,txt$,left$,right$,VAR file$) ' *** print optional title (light text) to left and right LOCAL screen$,y.fac SGET screen$ CLS IF high.res! y.fac=1 ELSE y.fac=2 ENDIF DEFTEXT black,2,900,32 TEXT 100,350/y.fac,300/y.fac,left$ DEFTEXT ,,2700 TEXT 540,50/y.fac,300/y.fac,right$ DEFTEXT ,0,0,13 PRINT AT(1,3);@center$(txt$) GRAPHMODE 3 DEFFILL black,1 BOUNDARY 0 IF high.res! BOX 157,25,482,54 PLOT 157,25 PBOX 159,27,480,52 ELSE BOX 157,12,482,27 PLOT 157,12 PBOX 160,14,479,24 ENDIF BOUNDARY 1 GRAPHMODE 1 FILESELECT path$,default$,file$ SPUT screen$ RETURN As a programmer, you should take into account the possibility that a user might start your program from drive A, a harddisk or a RAM-disk. I use the Standard Global default.path$ to remember where the program was started. If the user changes the (default) path in the Fileselector, you should note the change and use the new path if the Fileselector is called again. Use the Procedure Parse.filename for this purpose : PROCEDURE parse.filename(parse.name$,VAR drive$,path$,file$,ext$) LOCAL pos,first,last,last!,search,parse.file$ ' parse.name$=UPPER$(parse.name$) IF MID$(parse.name$,2,1)=":" drive$=LEFT$(parse.name$,1) ELSE drive$=CHR$(65+GEMDOS(&H19)) ! current drive ENDIF ' pos=1 last!=FALSE last=0 first=INSTR(1,parse.name$,"\") REPEAT search=INSTR(pos,parse.name$,"\") IF search>0 pos=search+1 last=search ELSE last!=TRUE ENDIF UNTIL last! IF last>0 ! backslash discovered path$=MID$(parse.name$,first,last-first+1) parse.file$=MID$(parse.name$,last+1) ELSE ! no '\' path$="" pos=INSTR(1,parse.name$,":") IF pos>0 parse.file$=MID$(parse.name$,pos+1) ELSE parse.file$=parse.name$ ENDIF ENDIF pos=INSTR(parse.file$,".") IF pos>0 ! name with extension ext$=MID$(parse.file$,pos+1) file$=LEFT$(parse.file$,pos-1) ELSE ! name without extension ext$="" file$=parse.file$ ENDIF RETURN If you have a joystick with Auto-Fire on, you should switch it off. The Fileselector doesn't like Auto-Fire. Neither do I. The Fileselector will warn you with a modest 'ping' if it counts more than 100 files/folders. It will show only the first 100 files/folders. I think 45 files in one folder is really the limit for impatient users like myself. More than 100 is a crime that should be punished with more than a 'ping'. The main directory of drive A can't contain more than 112 files/folders, because the 7 directory-sectors contain 112 32-byte slots. Every time you open a folder (in the Fileselector, or otherwise), TOS stores information about the folder in a table. After opening/accessing 40 folders, TOS will delete clusters, cross-link clusters, and do other nasty things. Your disk could be completely destroyed, thanks to this bug. Atari enlarged the buffer in TOS 1.2 and fixed the bug in TOS 1.4. Atari also distributes the program FOLDRxxx.PRG to extend the 40-folder limit with 100 (FOLDR100.PRG) or more. Be careful with SHOW INFO, it's easy to exceed the 40-folder limit! You could recognize a disaster by one of the following symptoms : - unexpected '0 bytes in 0 items' message in directory - folder-names trashed (usually lots of Greek letters) - Show Info crashes or shows weird information Don't be afraid of a new virus. It's only a TOS-bug. Immediately reset your ST, try to salvage as many files as possible and reformat the disk. If all files are lost, you will have to use your back-up files. If you don't have back-up files, you have nothing left but my sympathy. 12. MIDI INPMID$ With the command INPMID$ the internal Midi-buffer is read and at the same time cleared. You can find the buffer with XBIOS 14 (Iorec) : adr%=LPEEK(XBIOS(14,2)) The default size of this buffer is only 128 bytes, but you can use the Procedure Change.midi.buffer to change the size of the buffer : PROCEDURE change.midi.buffer(size%) LOCAL ptr% ptr%=XBIOS(14,2) ERASE buffer|() DIM buffer|(size%-1) LPOKE ptr%,VARPTR(buffer|(0)) ! start-address of new buffer DPOKE ptr%+4,size% ! size DPOKE ptr%+6,0 ! buffer-head DPOKE ptr%+8,0 ! buffer-tail DPOKE ptr%+10,0 ! low mark (not used) DPOKE ptr%+12,size%-1 ! high mark (not used) RETURN Because handshake is impossible with Midi, you need a large buffer if Midi-bytes are coming in fast. INP If you use INP(3) to read Midi-bytes, you should first check with INP?(3) if the Midi-buffer contains data. If you use INP(3) and no bytes are available, your ST will freeze. Until you reset the computer. Midi-commands Study the Procedures in the file MIDI.LST to see how you can use Midi- commands in GFA-Basic. With the Procedures Record.midi and Play.midi you could write a simple Midi-recorder. And the Procedure Midi.monitor can be used to examine incoming Midi-messages. PROCEDURE record.midi(VAR midi.byte|(),midi.time%()) ' *** uses Procedure All.midi.off ' *** global : LAST.MIDI.BYTE% LOCAL last%,buffer$,i%,t%,time%,byte|,delay%,j%,m$,k ARRAYFILL midi.byte|(),0 ARRAYFILL midi.time%(),0 last%=DIM?(midi.byte|())-1 m$=STR$(last%)+" bytes available ;| |stop recording|" m$=m$+"by pressing space" ALERT 3,m$,1,"RECORD",k REPEAT UNTIL INKEY$="" buffer$=INPMID$ ! clear MIDI-buffer i%=1 t%=TIMER REPEAT IF INP?(3) byte|=INP(3) IF byte|<>254 time%=SUB(TIMER,t%) midi.byte|(i%)=byte| midi.time%(i%)=time% INC i% ENDIF ENDIF UNTIL i%=last% OR INKEY$=" " last.midi.byte%=i%-1 @all.midi.off delay%=midi.time%(1) ! subtract time for first note FOR j%=1 TO i% SUB midi.time%(j%),delay% NEXT j% m$="|"+STR$(i%)+" bytes recorded" ALERT 3,m$,1," OK ",k RETURN ' PROCEDURE play.midi(VAR midi.byte|(),midi.time%()) ' *** uses Procedure All.midi.off LOCAL m$,k,i%,t%,time% m$="record of "+STR$(INT(midi.time%(last.midi.byte%)/200))+ m$=m$+" seconds| |(stop by pressing space)" ALERT 3,m$,1,"PLAY",k REPEAT UNTIL INKEY$="" i%=1 t%=TIMER REPEAT time%=SUB(TIMER,t%) IF midi.time%(i%)<=time% OUT 3,midi.byte|(i%) INC i% ENDIF UNTIL i%=last.midi.byte% OR INKEY$=" " @all.midi.off RETURN ' PROCEDURE midi.monitor LOCAL m$,k,byte|,byte$,hex$,bin$,buffer$,key$ byte$=SPACE$(3) hex$=SPACE$(2) bin$=SPACE$(8) m$="all incoming bytes|(except 254) are|printed on screen ;|" m$=m$+"stop by pressing space" ALERT 1,m$,1,"START",k m$="press||for CLS" ALERT 1,m$,1," OK ",k REPEAT UNTIL INKEY$="" buffer$=INPMID$ ! clear MIDI-buffer CLS PRINT TAB(10);"dec";TAB(20);"hex";TAB(30);"binary" REPEAT REPEAT key$=INKEY$ IF INP?(3) byte|=INP(3) IF byte|<>254 IF TIMER-t%>200 PRINT ENDIF t%=TIMER RSET byte$=STR$(byte|) RSET hex$=HEX$(byte|) RSET bin$=BIN$(byte|) PRINT TAB(10);byte$;TAB(20);hex$;TAB(30);bin$ ENDIF ENDIF UNTIL key$=" " OR key$=CHR$(13) IF key$=CHR$(13) CLS ENDIF UNTIL key$=" " @all.midi.off REPEAT UNTIL INKEY$="" PRINT " (press any key)" ~INP(2) RETURN 13. MODEM INPAUX$ With the command INPAUX$ the internal RS232-buffer is read and at the same time cleared. You can find the address of the input-buffer with : adr.in%=LPEEK(XBIOS(14,0)) The output-buffer can be located with : adr.out%=LPEEK(XBIOS(14,0)+14) INP If you use INP(1) to read incoming bytes, you should always check with INP?(1) if the RS232-buffer contains data. Rsconf (XBIOS 15) With XBIOS 15 (Rsconf) you can change the RS232-parameters. A few baudrates : 0 - 19200 baud 1 - 9600 baud 4 - 2400 baud 7 - 1200 baud 9 - 300 baud 14 - 75 baud Use -1 for parameters you don't want to change. Due to a TOS-bug, you can't use 75 baud, because '14' results in 120 baud. Also, the old TOS (pre-Blitter age) can't handle hardware handshake with RTS/CTS-signals. Atari has released a bug-fix that should enable any TOS to use RTS/CTS. Software handshaking (XON/XOFF) functions properly. The default after power-up is no handshake protocol. 14. MOUSE Editor Sometimes the editor seems to freeze while the cursor is blinking rapidly. Just move the mouse and the editor comes alive again. Fileselector Sometimes the same blinking mouse appears after calling the Fileselector or an Alert-box. I've read somewhere this could be due to the combination of a VDI-function (for the mouse) and an AES-function (for Fileselector or Alert-box). GFA takes care of the first and GEM of the second, and sometimes this seems to result in a conflict. MOUSE If you repeatedly call a Procedure in which you test for a mouse-click, you should incorporate a short pause in the Procedure (e.g. PAUSE 5). Otherwise the Procedure might be called again while the user is still holding the button down. You could also wait until the user releases the button : REPEAT UNTIL MOUSEK=0 ! wait until button is released If you find it difficult to move the mouse accurately, you could use to move the mouse-cursor one pixel in the desired direction. If you press as well, you can "drag" something accurately as if the left mouse-button was pressed. You can find the maximal x- and y-coordinates of the mouse with : x=DPEEK(&H9862) y=DPEEK(&H9864) But there's no guaranty you will find the coordinates there. Experiment with XBIOS 0 for a more reliable method. Don't ask me how. MOUSE returns negative coordinates if the mouse is to the left of or above the current window (or the origin that has been selected with CLIP OFFSET). SETMOUSE A mouseclick can be simulated with : SETMOUSE mx,my,mk If you try this with an Alert-button you have to move the mouse after- wards, or the button won't be selected. I don't know why, so I can't tell if this is a bug. DEFMOUSE You can use one of the many Public Domain mouse-editors to design your own mouse-mutant. But it's also easy to create a new mouse-cursor with the Procedure Initio.mouse1 : PROCEDURE initio.mouse1 ' *** global : MOUSE1$ RESTORE pattern.mouse1 @make.mouse(mouse1$) pattern.mouse1: ' *** x,y,mode(0=normal;1=XOR),mask-colour,mouse-colour DATA 0,0,0,0,1 ' *** mask-pattern (1 = pixel on , 0 = pixel off) DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 ' *** mouse-pattern DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 RETURN ' PROCEDURE make.mouse(VAR m$) LOCAL x,y,mode,msk.color,mouse.color,n,msk%,mouse%,msk.pat$ LOCAL mouse.pat$,msk$,mouse$,pat$ CLR msk.pat$,mouse.pat$,pat$ READ x,y,mode,msk.color,mouse.color FOR n=1 TO 16 READ msk$ msk%=VAL("&X"+msk$) msk.pat$=msk.pat$+MKI$(msk%) NEXT n FOR n=1 TO 16 READ mouse$ LET mouse%=VAL("&X"+mouse$) LET mouse.pat$=mouse.pat$+MKI$(mouse%) NEXT n m$=MKI$(x)+MKI$(y)+MKI$(mode)+MKI$(color.index(msk.color)) m$=m$+MKI$(color.index(mouse.color))+msk.pat$+mouse.pat$ RETURN The mask should be an exact copy of the mouse-pattern if you need a transparant mouse. Leave the mask empty (all '0') and the mouse will disappear behind objects on the screen. Fill the mask with '1' and the 16x16 mouse will always remain visible. Switch mask- and mouse-colour in the first DATA-line to create a "reverse" mouse. Or use any VDI colour- index that is available in the current resolution. 15. JOYSTICK STRIG and STICK Here is an example of the use of STRIG and STICK if your joystick is connected to Port 1 (your mouse is connected to Port 0) : STICK 1 ! joystick-mode (your mouse is now dead) REPEAT IF STRIG(1) joy=3 ELSE joy=STICK(1) ENDIF ON joy GOSUB n,s,fire,w,nw,sw,dummy,e,ne,se PAUSE 5 (...) UNTIL condition! STICK 0 ! back to mouse-mode You need eight Procedures for the eight possible directions (see below) and one Procedure for the Fire-button. The value 7 ('dummy') never occurs. If you don't touch the joystick, the value of 'joy' will be 0, and this means no Procedure will be called. A short pause is advisable, because GFA-Basic is too fast. 5 1 9 \|/ directions : 4-0-8 /|\ 6 2 10 If you desperately need an active mouse while using the joystick, you could try the following "dirty" method : REPEAT IF MOUSEK=2 joy=3 ELSE joy=BYTE{&HE09} ! for TOS-version 1.0 !! ENDIF ON joy GOSUB n,s,fire,w,nw,sw,dummy,e,ne,se PAUSE 5 UNTIL condition! 16. SOUND SOUND and WAVE After a SOUND-command, the sound sometimes continues in spite of the elapsed time. If the command is followed by another SOUND- or a WAVE- command, the time is handled correctly. This certainly sounds like a GFA- bug. The easiest way to stop all sound is : WAVE 0,0 ! turn all sound off Sound of a certain frequency can be produced with : SOUND ch,vol,#ROUND(125000/freq%),pause Although our ST is not famous for its brilliant sound, you can produce nice sound-effects with the simple commands SOUND and WAVE. Check out the Procedures Siren.sound, Tideli.sound, Bounce1.sound and Bounce2.sound to hear what I mean. Dosound (XBIOS 32) XBIOS 32 (Dosound) can be used to play music in a special format. I propose to use the extension 'X32' for song-files in this format. The operating system takes care of playing the music during interrupts (every 1/50th second). Take a look at the Procedures Play.song and Stop.song to see how you could use XBIOS 32 in your programs. You can even play a song continuously with the Procedure Play.cont.song. Temporarily stopping a song is possible with the Procedure Interrupt.song. If you use XBIOS 32 to play music, you are advised to switch the key-click off. Otherwise the music will stop as soon as the user presses a key. Once I discovered XBIOS 32 did not work during the initialization (mainly the filling of arrays) of a large program. I had converted the program from GFA-Basic 2.0 to 3.0, but I did not have the patience to find out what caused this problem. XBIOS 32 can also be used for sound-effects. I have developed the Procedure Initio.sound for building sound-strings from a few DATA-lines. I hereby declare this as the standard method for creating sound-strings. After @do.sound(sound$) you can hear the sound-effect. In the following example the sound-string bounce3$ is created : PROCEDURE initio.sound ' *** commands in DATA-lines : ' *** REG = 14 parameters for registers 0-13 ' *** END = end of sound-string ' *** PAUSE = pause (followed by time in 1/50 seconds) ' *** VAR = decrease/increase tone : channel,start,+/-step, ' *** end-value ' bounce3.sound: DATA REG,0,0,0,0,0,0,27,248,16,16,16,35,95,0 DATA VAR,3,255,-1,116 DATA PAUSE,255,END RESTORE bounce3.sound @sound.string(bounce3$) RETURN ' PROCEDURE sound.string(VAR s$) LOCAL n,snd$,snd,channel,begin,step,end s$="" DO READ snd$ snd$=UPPER$(snd$) EXIT IF snd$="END" IF snd$="REG" FOR n=0 TO 13 READ snd s$=s$+CHR$(n)+CHR$(snd) NEXT n ENDIF IF snd$="PAUSE" READ snd s$=s$+CHR$(130)+CHR$(snd) ENDIF IF snd$="VAR" READ channel,begin,step,end s$=s$+CHR$(128)+CHR$(begin)+CHR$(129)+CHR$(channel)+CHR$(step) s$=s$+CHR$(end) ENDIF LOOP s$=s$+CHR$(255)+CHR$(0) ! terminator RETURN ' PROCEDURE do.sound(sound$) VOID XBIOS(32,L:VARPTR(sound$)) RETURN Samples From GFA-Basic you can surprise the user with a sampled sound. Examine the Procedures Sample, Load.sample and Play.sample to see how. You'll have to find suitable samples first. Look out for sound-effects and speech- samples. Personally, I just love the famous Perfect-sample. Speech Your ST can talk to you with a little help (STSPEECH.TOS). My current GFA- version (3.07) refuses to cooperate with the Procedures Initio.speech and Talk, but I have included these anyway. Perhaps you can discover the bug. In earlier versions both Procedures did work. A reset is necessary because EXEC 3 is used. Soundmachine You can play songs, created with Soundmachine (TommySoftware), from GFA- Basic. You'll need the Procedures Initio.soundmachine and Soundmachine, and a song-file. You'll probably wonder if it's really your ST that's playing the song. Soundmachine II is out, and looks even better. In this new version you can choose between playing songs with samples, or without samples (or samples for one of the three channels). You'll need the Procedure Sm.initio or Msm.initio and several others. Try to find the Public Domain demo-programs SAMSOUND.GFA, CHIPSND.GFA and SND_PLAY.GFA. End of commercial, continue with text. 17. PROGRAM DECISIONS IF ... ENDIF If the value of a certain variable must fall in the range min%-max%, you could program that as follows : IF n%>max% n%=max% ENDIF IF n% keys fast enough : ON BREAK GOSUB break (...) PROCEDURE break ON BREAK CONT (...) ON BREAK GOSUB break RETURN In this case the Break-Procedure is activated again before leaving the Procedure. Study the Standard Procedure Break (in one of the STANxxxx.LST- files) for an example of this method. ERROR You can simulate ERRORs with values from -127 to 127. For GFA-errors use values from 0 to 93, for bomb-errors 102 (2 bombs) to 109 (9 bombs) and for TOS-errors -1 to -67. EVERY and AFTER It's not possible to use EVERY and AFTER at the same time. Both commands don't work during a long PAUSE or DELAY (or any other command that takes a lot of time). You can only call Procedures without parameters. Don't make the Procedure too long, or it may be called while being processed! In a compiled program you have to incorporate '$I+ U+', or EVERY and AFTER can't be used. GOTO You can't use GOTO in a Procedure, a Function or a FOR ... NEXT loop. DELAY The DELAY-command does not operate correctly in version 3.07 of GFA-Basic. During DELAY a Break is impossible. A nastier bug is the appearance of the mouse-cursor during DELAY, even after HIDEM. You are advised to use PAUSE instead. CHAIN In GFA-Basic all variables and arrays are lost after CHAINing the next program. However, you could use the 160-byte buffer of the scrap-library to pass a short message to the next program : buffer$=SPACE$(160) ! 160 bytes maximum ?? message$="this message was sent by the previous program" message$=message$+CHR$(0) LSET buffer$=message$ r%=SCRP_WRITE(buffer$) ! r%=0 if error CHAIN file$ Read the message with : buffer$=SPACE$(160) r%=SCRP_READ(buffer$) message$=CHAR{V:buffer$} The use of this buffer is completely illegal, but who cares if you don't use a scrap-library? Unfortunately the GFA-editor seems to think so too, so you should experiment a little before trusting this method. EXEC If you are going to run another program more than once, you'll have to use EXEC 3 : base%=EXEC(3,file$,"","") ! load, but don't start yet base$=STR$(base%) cmdl$=CHR$(LEN(base$)+1)+base$ ! create command line (...) r%=EXEC(4,"",cmdl$,"") ! now run it The variable r% contains a value returned by the program (or -39 if not enough memory was available). Repeat the last line if you want to run the loaded program again. Of course you should use EXEC 0 if you're going to run the program one time only. Read the paragraph 'RESERVE' if you are going to use EXEC 3. If you call a '*.PRG'-program with EXEC 0, you pass the null-string ("") as the command-line. You need the command-line only if you call a '*.TTP'- program. The command-line is converted to upper case and can't exceed 125 bytes. The first byte of the command-line (usually) determines the length of the line, so the command-line can't contain more than 124 characters. You can use this in a TTP-program (compiled GFA-Basic program, extension changed to TTP), but it's easier to read the command-line with : cmdl$=CHAR{BASEPAGE+&H81} 20. GRAPHICS SETCOLOR and VSETCOLOR With commands like COLOR, DEFTEXT, DEFFILL, etc., you use a VDI colour- index. Unfortunately SETCOLOR uses a different table : VDI colour-index : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SETCOLOR (Low rez) : 0 15 1 2 4 6 3 5 7 8 9 10 12 14 11 13 SETCOLOR (Med rez) : 0 3 1 2 From this table it follows you would have to use 'SETCOLOR 2,r,g,b' to change colour-index 3. By the way, all Line-A commands use the SETCOLOR- table as well! This is not a "bug", but a consequence of two different colour-tables that are used by GEMDOS and GEM. You are advised to use : VSETCOLOR index,r,g,b This index is exactly the same as the colour-index, so you would use 'VSETCOLOR 2,r,g,b' in order to change colour-index 2. If you like, you could use the hexadecimal 3-byte mix-value : VSETCOLOR index,&Hrgb But you won't like it at all, because this doesn't work properly. GFA- Basic swaps the r- and g-byte, so you would have to use : VSETCOLOR index,&Hbgr Better avoid this method, or you'll experience colour-changes if GFA corrects this bug in a future version. In High resolution you can invert the screen-colours with : VSETCOLOR 0,0 ! reverse High-screen (black background) VSETCOLOR 1,0 ! normal High-screen (black letters) An inverted screen is perhaps slightly less suitable for text, but graphics look superb. Colour-index 0 determines the colour of the background in Low and Medium resolution. This index also determines the colour of the border on your screen. You can change this colour, although you can't draw or PRINT on the screen-border of your colour monitor. The colour of all PRINTed text is determined by colour-index 1, unless you use the 'Esc b' command (read the paragraph 'PRINT' again if your long-term memory is too short) : VSETCOLOR 0,r,g,b ! change colour of background VSETCOLOR 1,r,g,b ! change colour of all PRINTed text Palette Before changing colours, you should always save the current palette. And do restore the old palette before the user exits the program. I hate programs that return to a pink or yellow desktop. You can store the palette either in an integer array or in a string, using XBIOS 7 (Setcolor) : PROCEDURE save.palette LOCAL i ERASE old.palette%() DIM old.palette%(15) FOR i=0 TO 15 old.palette%(i)=XBIOS(7,i,-1) NEXT i RETURN ' PROCEDURE make.palette.string(VAR pal$) LOCAL n pal$="" FOR n=0 TO 15 pal$=pal$+MKI$(XBIOS(7,n,-1)) NEXT n RETURN The string-method is compatible with Degas. Restore the old palette with the corresponding Procedures : PROCEDURE restore.palette LOCAL i FOR i=0 TO 15 VOID XBIOS(7,i,old.palette%(i)) NEXT i RETURN ' PROCEDURE change.palette(pal.string$) VOID XBIOS(6,L:VARPTR(pal.string$)) RETURN You could also use the Standard Procedure Standard.low.colors or Standard.med.colors to restore the default palette. You can find these Procedures in the STANxxxx.LST-files. You can examine the rgb-value of a certain VDI colour-index with : PROCEDURE rgb.value(index,VAR rgb$) LOCAL col% col%=XBIOS(7,color.index(index),-1) rgb$=RIGHT$(HEX$(col%),3) RETURN The Standard Array color.index() is used to convert the VDI colour-index. A completely new palette for Medium resolution can be installed with : PROCEDURE new.med.colors LOCAL n,r,g,b,col$ RESTORE med.new.col.data FOR n=0 TO 3 READ col$ r=VAL(LEFT$(col$)) g=VAL(MID$(col$,2,1)) b=VAL(RIGHT$(col$)) VSETCOLOR n,r,g,b NEXT n med.new.col.data DATA 000,000,000,000 RETURN You can show the current palette on the screen with the Procedure Palette.box : PROCEDURE palette.box(x,y,h,w) ' *** left upper corner of rectangle at x,y ' *** rectangle-height h; width of one colour-box w LOCAL arect.fill,fill.adr%,i,x1,x2 x2=x+16*w+2 COLOR black BOX x,y,x2,y+h arect.fill=-1 f%=V:arect.fill IF low.res! FOR i=0 TO 15 x1=ADD(SUCC(x),MUL(i,w)) ARECT x1,SUCC(y),ADD(x1,w),PRED(ADD(y,h)),color.index(i),0,f%,0 NEXT i ELSE IF med.res! FOR i=0 TO 3 x1=ADD(SUCC(x),MUL(i,w)) ARECT x1,SUCC(y),ADD(x1,w),PRED(ADD(y,h)),color.index(i),0,f%,0 NEXT i ENDIF RETURN You can darken the screen by dimming all colours simultaneously with the Procedure Dim.colors : PROCEDURE dim.colors(reg1,reg2,val) ' *** dim colours from VDI colour-index reg1 to reg2 with val ' *** for val=1 colour 254 (rgb) will become 143 LOCAL i,r,g,b FOR i=reg1 TO reg2 @rgb.value(i,rgb$) r=MAX(PRED(VAL(LEFT$(rgb$))),0) g=MAX(PRED(VAL(MID$(rgb$,2,1))),0) b=MAX(PRED(VAL(RIGHT$(rgb$))),0) VSETCOLOR i,r,g,b NEXT i RETURN Nothing is impossible in GFA-Basic, even colour-cycling is easy with EVERY in the Procedure Color.cycle : PROCEDURE color.cycle(reg1,reg2,time) ' *** cycles colours from VDI colour-index reg1 to reg2 ' *** global : COLOR.CYCLE! COL.REG1 COL.REG2 IF NOT color.cycle! col.reg1=reg1 col.reg2=reg2 color.cycle!=TRUE EVERY time GOSUB cycle.once ELSE color.cycle!=FALSE EVERY STOP ENDIF RETURN ' PROCEDURE cycle.once LOCAL col1%,col2% col1%=XBIOS(7,color.index(col.reg2),-1) FOR reg=col.reg1 TO PRED(col.reg2) col2%=XBIOS(7,color.index(reg),-1) ~XBIOS(7,color.index(reg),col1%) SWAP col1%,col2% NEXT reg ~XBIOS(7,color.index(col.reg2),col1%) RETURN Call the Procedure Color.cycle again to stop the colour-cycling. DEFMARK The point as mark-symbol (No. 1) cannot be enlarged. Other mark-symbols can be enlarged, but all lines in the symbol retain a width of one pixel. The size of a symbol must be a multiple of 11 plus 6: 0, 17, 28, 39, 50. For a value in between, the prior allowed size is chosen. Perhaps there is some magic hidden in the allowed sizes, or am I missing something? DEFFILL If you haven't defined your own Fill-pattern, the Atari-symbol will be used after DEFFILL 1,4,x. The desktop Fill-pattern is DEFFILL 1,2,4. The Fill-pattern string can consist of 16, 32 or 64 words (MKI$-format). Word 1 to 16 is needed for bitplane 0 (High resolution), word 17 to 32 for bitplane 1 (Medium resolution) and word 33 to 64 for the bitplanes 2 and 3 (Low resolution). In all resolutions, the pattern occupies a rectangle of 16x16 pixels on the screen. The same 16x16 rectangle is also used for the mouse-cursor and sprites. You can always use a Fill-pattern in a lower resolution, e.g. a High-pattern in Medium or Low resolution, but not the other way around. Examine the three Procedures Initio.high.fill1, Initio.med.fill1 and Initio.low.fill1 to see how easy you can design your own Fill-patterns. There's gold in them thar Fills. You could surprise the user with a bomb-pattern : PROCEDURE initio.bomb.fill ' *** global : BOMB.FILL$ RESTORE bomb.fill @make.high.fill(bomb.fill$) bomb.fill: DATA 0000011000000000 DATA 0010100100000000 DATA 0000000010000000 DATA 0100100001000000 DATA 0001000111110000 DATA 0000000111110000 DATA 0000011111111100 DATA 0000111111111110 DATA 0000110111111110 DATA 0001111111111111 DATA 0001111111101111 DATA 0000111111101110 DATA 0000111111011110 DATA 0000011111111100 DATA 0000001111111000 DATA 0000000011100000 RETURN ' PROCEDURE make.high.fill(VAR fill$) LOCAL i,pat$,pat% CLR fill$ FOR i=1 TO 16 READ pat$ pat%=VAL("&X"+pat$) fill$=fill$+MKI$(pat%) NEXT i RETURN Be careful, a screen filled with this pattern might provoke a heart- attack. If it doesn't, you could try the following dirty trick (High resolution only) with the Procedures Busy and Achtung : @busy ! looks like the computer is very busy ~INP(2) ! but nothing happens, until the user presses a key... @achtung PAUSE 500 CLS PRINT " Thank you for your patience, I'm not busy anymore ..." PAUSE 150 Many Fill-patterns are available as files. You can use these with something like Procedure Initio.fill1 : PROCEDURE initio.fill1(VAR pattern$) LOCAL bytes bytes=32 ! 32 bytes for High resolution ' *** load Fill-pattern (32 bytes for High resolution) here INLINE fill1%,32 pattern$=STRING$(bytes,0) BMOVE fill1%,V:pattern$,bytes DEFFILL ,pattern$ RETURN FILLing a screen with a pattern takes some time, especially in High resolution. Use the following method for almost immediate filling of the entire High resolution screen : PROCEDURE full.fill(fill%) ACLIP 1,0,0,639,399 ARECT 0,0,639,399,1,0,fill%,15 ACLIP 0,0,0,639,399 RETURN Fill% is the address of a FILL-pattern (32 bytes). This very fast alternative Fill-method works in High resolution only. DEFLINE After 'DEFLINE ,n' all horizontal lines have a width of n pixels (n should be odd), except in Medium resolution. But if n is larger than 1, all vertical lines are n+2 pixels wide! I have trouble counting the pixels in Medium resolution. I think the width of horizontal lines is : n : 1 3 5 7 9 width : 1 1 3 3 5 etc. Is this documented anywhere? You define your own Line-patterns by using a negative 16-bit value (from -&X1 to -&X1111111111111111). Each set bit corresponds with a pixel in the Line-pattern (High resolution). The highest bit (15) corresponds with the leftmost pixel of the pattern. Don't get confused if the editor changes the negative binary number you entered as a pattern. The editor uses a special notation for negative binary numbers. GFA-Basic represents integer numbers as binary strings of 32 bits. The most significant bit (31) determines the sign of the integer. If this bit is 0, the remaining 31 bits represent an "ordinary" positive number. But if the most significant bit is 1, the remainder is a negative number in 'two's complement' notation. I won't try to explain that. In any case, the next time you type -&X111 and a friend is watching you, don't blink your eyes, but casually remark "of course the editor converts this into two's complement notation". That's also the reason GFA-Basic can work with integers from -(2^31) to +(2^31)-1. DEFTEXT In some publications you can read about 'shadowed text' (style = 32), but unfortunately our GEM doesn't know this style. You can even find the mask for shadowed text with WORD{L~A+92}, but it's 0. I never succeeded in finding the current text-style with VDISYS 38 (vqt_attributes). Either a bug in GEM or my mistake. I don't want to know, because the easy way is : txt.style=WORD{L~A+90} This could be important if you intend to use DEFTEXT in a Procedure, but would like to restore the original DEFTEXT-settings before leaving the Procedure. With DEFTEXT you can set the height of TEXT-letters. A letter occupies more space though, determined by the letter-box : letter-height letter-box system-font 4 6 x 6 icon 6 8 x 8 Medium & Low rez 13 8 x 16 High resolution The letter-height is the distance from Descent-Line to Ascent-Line, but the Bottom-Line and Top-Line lie at least one pixel lower/higher. The height of the letter-box is the distance from Bottom-Line to Top-Line. Most letters rest on the Base-Line, but letters with a descender (g,j,p, etc.) rest on the Descent-Line. If you are still with me, from top to bottom we have the following lines : Top-Line Ascent-Line Base-Line Descent-Line Bottom-Line Drop me a line if you don't understand this. GRAPHMODE If you draw a rectangle with BOX in GRAPHMODE 3 (Xor-mode), the pixel in the left upper corner is not drawn. Actually this pixel is drawn twice, and in GRAPHMODE 3 this means the pixel disappears. Use PLOT to draw this pixel : GRAPHMODE 3 BOX x1,y1,x2,y2 PLOT x1,y1 ! and fill the gap With PBOX in GRAPHMODE 3 you'll also get trouble in the same corner. Avoid this by using the command 'BOUNDARY 0' first : GRAPHMODE 3 BOUNDARY 0 PBOX 50,50,100,100 GRAPHMODE 3 is especially useful if you make a temporary drawing. Draw the same picture a second time to restore the original screen. Examine Proce- dures like Rubber.line, Draw.box and Drag.box for examples of this method. The Procedure Rubber.line is used to draw a line from (x,y) to the mouse- cursor : PROCEDURE rubber.line(x,y,VAR x2,y2) LOCAL x1,y1,x2,y2,k GRAPHMODE 3 DEFMOUSE 3 SHOWM MOUSE x1,y1,k REPEAT ! main loop LINE x,y,x1,y1 ! draw line REPEAT MOUSE x2,y2,k UNTIL x2<>x1 OR y2<>y1 OR k>0 ! mouse moved LINE x,y,x1,y1 ! undraw line x1=x2 y1=y2 UNTIL k>0 ! mouse-click : ready (x2 and y2 returned) GRAPHMODE 1 LINE x,y,x2,y2 ! this is it HIDEM DEFMOUSE 0 PAUSE 10 RETURN You could also use the 'GRAPHMODE 3'-method for animation, but the XBIOS 5 (Setscreen) method is more suitable. Don't try to draw in GRAPHMODE 3 with a linewidth greater than 1 pixel. GEM will surprise you with some modern art if you can't resist the temptation. If you want to confirm a particular choice of the user, you can invert the relevant part of the screen with the Procedure Invert.block. Call this Procedure again with the same parameters to restore the original screen : PROCEDURE invert.block(x1,y1,x2,y2,color) GRAPHMODE 3 DEFFILL color,1 BOUNDARY 0 PBOX x1,y1,x2,y2 BOUNDARY 1 GRAPHMODE 1 RETURN You can 'grey out' an unavailable option on the screen with : PROCEDURE block.dimmer(x1,y1,x2,y2,color) GRAPHMODE 3 DEFFILL color,2,2 BOUNDARY 0 PBOX x1,y1,x2,y2 BOUNDARY 1 GRAPHMODE 1 RETURN Call this Procedure again to restore the screen. The entire (High resolution) screen can be dimmed with : PROCEDURE screen.dimmer ' *** global : DIMMER.SCREEN$ DIMMER.SWITCH! IF dimmer.switch! SPUT dimmer.screen$ dimmer.switch!=FALSE ELSE SGET dimmer.screen$ GRAPHMODE 4 DEFFILL 1,2,4 PBOX 0,0,639,399 dimmer.switch!=TRUE ENDIF RETURN The screen will be restored if you call the Procedure again. PLOT and DRAW You can use both 'PLOT x,y' and 'DRAW x,y' to set a point on the screen. The size of the point can be changed : DEFLINE ,size,2,2 ! change size of points But the shapes you'll see don't look like points anymore, due to the same problem as described in the paragraph 'DEFLINE'. Use PCIRCLE for proper fat points. PCIRCLE With CLIP on, a PCIRCLE touching the upper screen-border is not filled properly in High resolution : CLIP 0,0,640,400 PCIRCLE 0,0,50 I don't know if we should blame GFA or GEM for not filling the two top- lines in the circle. CURVE With the command CURVE you can draw a Bezier-curve : CURVE x1,y1,x2,y2,x3,y3,x4,y4 The Bezier-curve starts at (x1,y1) and ends at (x4,y4). The other two points act like little magnets. You can also use this command to draw a 'normal' curve between two points by letting the points (x3,y3) and (x4,y4) coincide. Try the following to see what I mean : GRAPHMODE 3 MOUSE x2,y2,k DO CURVE 10,100,x2,y2,110,100,110,100 ! draw curve REPEAT MOUSE x,y,k UNTIL x<>x2 OR y<>y2 CURVE 10,100,x2,y2,110,100,110,100 ! erase curve x2=x y2=y LOOP This method could be used to draw large letters. TEXT The coordinates used with TEXT determine the start of the Base-Line of the text. The descenders of letters like 'g', 'j', and 'p' lie below the Base- Line. This is especially important, not to say frustrating, if you use TEXT with an angle of 90, 180 or 270 degrees. The text rotates anticlock- wise around the TEXT-coordinates! If you intend to combine PRINTed text with TEXT, you probably will like the Procedure Text.at as the analogue of 'PRINT AT' : PROCEDURE text.at(c,l,t$) TEXT (c-1)*char.width,l*char.height+3*high.res!+2*(NOT high.res!),t$ RETURN You can use TEXT to print "digital" numbers (ASCII-code 16-25) : FUNCTION digital$(number$) LOCAL dig$,i CLR dig$ FOR i=1 TO LEN(number$) dig$=dig$+CHR$(BCLR(ASC(MID$(number$,i,1)),5)) NEXT i RETURN dig$ ENDFUNC Use this Function as follows : TEXT x,y,@digital$("1237") SPRITE You could design and save sprites in vitro with one of the many Sprite- editors that are available. Or you can use something like the Procedure Initio.sprite1 to create a sprite in vivo. Compare this Procedure also with the Procedure Initio.mouse1 : PROCEDURE initio.sprite1 ' *** global : SPRITE1$ RESTORE pattern.sprite1 @make.sprite(sprite1$) pattern.sprite1: ' *** x,y,mode(0=normal;1=XOR),mask-colour,sprite-colour DATA 0,0,0,0,1 ' *** mask-pattern (1 = pixel on , 0 = pixel off) DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 ' *** sprite-pattern DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 DATA 0000000000000000 RETURN ' PROCEDURE make.sprite(VAR s$) LOCAL x,y,mode,msk.color,spr.color,n,msk%,spr%,msk.pat$,spr.pat$ LOCAL msk$,spr$,pat$ CLR msk.pat$,spr.pat$,pat$ READ x,y,mode,msk.color,spr.color FOR n=1 TO 16 READ msk$ msk%=VAL("&X"+msk$) msk.pat$=msk.pat$+MKI$(msk%) NEXT n FOR n=1 TO 16 READ spr$ spr%=VAL("&X"+spr$) spr.pat$=spr.pat$+MKI$(spr%) NEXT n FOR n=1 TO 16 pat$=pat$+MID$(msk.pat$,n*2-1,2)+MID$(spr.pat$,n*2-1,2) NEXT n s$=MKI$(x)+MKI$(y)+MKI$(mode)+MKI$(color.index(msk.color)) s$=s$+MKI$(color.index(spr.color))+pat$ RETURN The mask should be an exact copy of the sprite-pattern if you need a transparant sprite. Leave the mask empty (all '0') and the sprite will disappear behind objects on the screen. Fill the mask with '1' and the 16x16 sprite will always remain visible. Switch mask- and sprite-colour in the first DATA-line to create a "reverse" sprite. Or use any VDI colour- index that is available in the current resolution. If you put a sprite on the screen, the background (16x16 pixels) is temporarily saved. Using more than one sprite simultaneously, it is essential you remove sprites in reverse order. This is necessary because a sprite could overlap another sprite. Removing the top sprite first ensures that the original background will reappear after removing the first sprite, e.g. : DO (...) SPRITE s2$ ! remove sprites... SPRITE s1$ VSYNC ! prevents blinking, but slows down SPRITE s1$,x1,y1 ! draw sprites... SPRITE s2$,x2,y2 (...) LOOP VQT_EXTENT The function VQT_EXTENT can be used if you would like to draw a rectangle around a text-string : ~VQT_EXTENT(txt$,x1,y1,x2,y2,x3,y3,x4,y4) The coordinates of the four corners depend on the angle of the text (0, 90, 180 or 270 degrees) and of course on the size of the text. The point (x1,y1) is the lower left corner of the imaginary rectangle around the text and the other points are arranged anticlockwise around the text- string. Because the text rotates around the TEXT-coordinates (start of the Base-Line), it will take some trial and error to determine the correct position of the rectangle if the angle is not 0 degrees. In the following table you'll find the "true" origin of the rectangle, the width and the height of the rectangle, and also the actual position of the lower left corner of the text-block (x1,y1) : angle position (x1,y1) origin width height 0 lower left x1,y1 x2 y4 900 lower right x4,y4 x1 y3 1800 upper right x3,y3 x4 y2 2700 upper left x2,y2 x3 x4 The coordinates of the origin are (0,0), so with an angle of 0 degrees both x1 and y1 are 0. The rectangle rests on the x-axis, while the left side coincides with the y-axis. This is a "mathematical" y-axis, not a "screen" y-axis. This means you go upwards for positive y-values. For an angle of 0 degrees the coordinates will be : (x4,y4). .(x3,y3) . TEXTBLOCK . (x1,y1) (x2,y2) If you understand the table, you should be surprised by the height 'x4' instead of 'y1' at an angle of 270 degrees. I think I discovered a bug in GEM here. Correct GEM by changing the following variables if an angle of 270 degrees is used : y1=x4 SWAP x4,y4 Now, the height is 'y1' as you suspected. Clever, aren't we? I have not been able to confirm my discovery. None of my reference-books mention the bug. Line-A The Line-A commands are faster than the corresponding VDI-commands in GFA- Basic. The difference should be even greater after loading GDOS (?). In the following table you can find how many times faster the Line-A command is : PSET / PLOT = 3 PTST / POINT = 2 HLINE / LINE = 2 ARECT / PBOX = 1.5 The syntax of Line-A commands is more complicated, but that's no problem for us GFA-experts. Line-A commands use the 'SETCOLOR-index', so you'll probably need the Standard Array color.index(). HLINE An additional advantage of the commands HLINE, ARECT and APOLY is that you don't have to change the DEFFILL-parameters in the main program. For solid horizontal lines, use : pattern=-1 adr%=V:pattern HLINE x1,y,x2,color,mode,adr%,0 You can't use &X1111111111111111 (16 bits) for the pattern, because bit 15 of a word-variable is a flag for a negative number. Yes, that's why the largest positive word is 2^15 - 1 (32767). The solution to this little problem is to assign -1 to the word-variable. You have my word, now all 16 bits are 1. Use BIN$ if you don't believe me. For very complicated patterns you could use a word-array : DIM pattern(i) adr%=V:pattern(0) (...) ! put fill-pattern in pattern(0) to pattern(i) HLINE x1,y,x2,color,mode,adr%,i ACHAR and ATEXT It's difficult to combine the Line-A commands ACHAR and ATEXT with TEXT. The coordinates used with ACHAR and ATEXT determine the position of the left upper corner of the (first) letter-box. That's the Top-Line, not the Base-Line. You can't use the text-style underlined (8) with ACHAR and ATEXT. Probably a GFA-bug. GET and PUT You could save a rectangular part of the screen as follows : GET x1,y1,x2,y2,pic$ BSAVE file$,V:pic$,LEN(pic$) ! use the extension PUT in the filename Later, you could put the saved picture back on the screen with : OPEN "I",#1,file$ LET bytes%=LOF(#1) ! how many bytes needed? CLOSE #1 picture$=SPACE$(bytes%) ! reserve some space, BLOAD file$,V:picture$ ! load the picture PUT x,y,picture$ ! and look at it The Procedures Message, Warning, Message.on and Message.off use GET and PUT to save and later restore the part of the screen that is used for text. A GET-string starts with three words for width, height and number of bitplanes. The number of bitplanes is determined by the resolution: 1 for High, 2 for Medium and 4 for Low. After these three words follows the actual picture as a list of words. As the picture-width is not necessarily a multiple of 16, any bits beyond the right border will be ignored by the PUT-command. Here is the connection between GRAPHMODE and PUT-mode : GRAPHMODE PUT-mode 1 3 (default) 2 7 3 6 4 13 I have encountered a few programs in GFA-Basic 2.0 where PUT was used just outside the screen. In GFA-Basic 3.0 the program didn't work. The same occurred in a program where a picture was BLOADed a few bytes before the screen-RAM. I don't understand why GFA-Basic 3.0 doesn't accept this, as there is some unused space there (read the paragraph 'RAM'), but I've learned to correct this when I convert a program from GFA-Basic 2.0 to version 3.0. Degas-Pictures A Degas picture-file contains not only the actual picture (same format as SGET-picture), but also the colour-palette. Use the Procedure Show.degas to load and show a Degas-picture : PROCEDURE show.degas(degas$) LOCAL degas.picture$,degas.picture%,degas.palette$ LOCAL degas.palette% degas.picture$=SPACE$(32000) degas.picture%=VARPTR(degas.picture$) degas.palette$=SPACE$(32) degas.palette%=VARPTR(degas.palette$) OPEN "I",#90,degas$ SEEK #90,2 ! skip resolution BGET #90,degas.palette%,32 ! load palette of picture SEEK #90,34 BGET #90,degas.picture%,32000 ! load actual picture CLOSE #90 ~XBIOS(6,L:degas.palette%) ! change palette SPUT degas.picture$ ! show the picture RETURN The original Degas-files have a length of 32034 bytes : 1 word - resolution 16 words - palette 16000 words - picture data You can use BTST to check the resolution (bit 0 = Low, bit 1 = Medium, bit 2 = High), but you could also look at the extension of the filename (PI1/PI2/PI3 for Low/Medium/High). In the second Degas-version (Degas Elite), 16 words for "colour-animation" can be added after the picture data. A Degas Elite picture can (and really should!) be saved in a compressed format. The extensions PC1/PC2/PC3 are used for Low/Medium/High resolution. Also, the highest bit of the resolution-word is set as a flag for a compressed picture. With the Procedure Show.comp.degas you can load and show a compressed Degas-picture. Neochrome-Pictures Sometimes the "Neochrome"-format is used for pictures (32128 bytes) : 1 integer - resolution (0/1/2 for Low/Medium/High) 16 words - palette 12 bytes - filename (nnnnnnnn.eee) 4 bytes + 1 word - colour animation data 18 integers - reserved 16000 words - picture data VSYNC The VSYNC-command is useful if you want to prevent irritating blinking during animation. Always VSYNC before drawing a new picture in an animation sequence (including SPRITE-animation). The program is slowed down of course, because it waits for a vertical blank interrupt before starting to draw. But it looks much nicer. Scroll With BMOVE and RC_COPY you can accomplish pretty smooth scrolling. GET/PUT is too slow, and only works on the logical screen. BITBLT is more flexible, but also more complicated. And not faster (?). Study the Procedures Scroll.up and Scroll.down to see how you could scroll the entire screen up or down : PROCEDURE scroll.up(scroll.lines,scroll.color) LOCAL n,bytes,move.bytes,source% IF high.res! LET bytes=80*scroll.lines ELSE LET bytes=160*scroll.lines ENDIF move.bytes=32000-bytes source%=physbase%+bytes VSYNC BMOVE source%,physbase%,move.bytes DEFFILL scroll.color PBOX 0,scrn.y.max-scroll.lines+1,scrn.x.max,scrn.y.max FOR n=1 TO DIV(scrn.y.max,scroll.lines) VSYNC BMOVE source%,physbase%,move.bytes NEXT n RETURN Can anybody explain the interference lines that sometimes appear during the scrolling? Several fade-over effects can be found in the Procedures Up.scroll, Slider.scroll, etc. Most Procedures can only be used in High resolution. For scrolling of one or more text-lines you could use the Procedures Scroll.text.up and Scroll.text.down : PROCEDURE scroll.text.up(begin,end) LOCAL screen%,sx,sy,w,h,dx,dy IF begin>1 AND end>=begin screen%=XBIOS(3) ! logical screen sx=0 sy=(begin-1)*char.height w=scrn.x.max h=(end-begin+1)*char.height dx=0 dy=sy-char.height RC_COPY screen%,sx,sy,w,h TO screen%,dx,dy ELSE PRINT bel$; ENDIF RETURN This method is also used in the Procedure Debug. ACLIP Line-A commands are 'CLIP-sensitive', so you should always use an appro- priate ACLIP-command before a Line-A command. The following Line-A commands are not influenced by ACLIP : ALINE, HLINE, PSET, PTST and BITBLT. Blitter The Blitter-TOS (1987) allows you to switch the Blitter on/off. I couldn't test the following Procedure, because I don't have a Blitter. But if I understand the function XBIOS 64 (Blitmode) correctly, you could switch the Blitter on and off from GFA-Basic : PROCEDURE blitter(switch!) LOCAL status status=XBIOS(64,-1) IF BTST(status,1) ! Blitter available? IF switch! status=BSET(status,0) ! Blitter on ELSE status=BCLR(status,0) ! Blitter off ENDIF ~XBIOS(64,status) ! do it ENDIF RETURN By the way, Line A commands do not use the Blitter. The Blitter-chip contains a hardware-routine that replaces the Line A BitBlt-function (BITBLT in GFA-Basic). All Mega ST's have a Blitter installed. 21. EVENTS MENU() There is no easy way to test in a program if an accessory has been selected by the user. MENU(1) looks promising, but the value 40 (select accessory) or 41 (close accessory) is only sent to the accessory! It would have been nice to be able to determine if an accessory has been closed, because GEM will clear the accessory-window and will fill the space with the desktop-pattern. No easy solution for this one, sorry. The best idea probably is to test frequently with 'IF MENU(1)=20' if a redraw is necessary, e.g. : ON MENU MESSAGE GOSUB redraw (...) PROCEDURE redraw IF MENU(1)=20 (...) ! redraw screen ENDIF RETURN This method only works if you have opened a window. ON MENU BUTTON The syntax for ON MENU BUTTON is : ON MENU BUTTON clicks,button,event GOSUB proc For both 'button' and 'event' you can use the numbers 0-3. The variable 'clicks' stands for the maximal number of clicks you want to register. If you choose '2', the Procedure will be called if the user clicks once or twice : ON MENU BUTTON 2,1,1 GOSUB proc It's not possible to wait for a double-click, unless the progam is really waiting, and not doing anything else : SELECT EVNT_BUTTON(2,1,1) CASE 1 ' clicked once CASE 2 ' clicked twice ENDSELECT But you can't combine this with an ON MENU loop. If you use the described method : ON MENU BUTTON 2,1,1 GOSUB proc you could use MENU(15) in the called Procedure to check if the user clicked twice. The same Procedure would be called after a single click, but you could ignore that. Fasten your seatbelts now. If you run such a program the first time, a double click is not registered in MENU(15). If you suspect a bug and run the program again, MENU(15) works all right. Nasty. Another bug, but this time a GEM-bug (I think), after : ON MENU BUTTON 1,2,2 GOSUB proc The Procedure is called immediately, whether the right mouse-button was pressed or not. If you want to switch ON MENU BUTTON temporarily off, use : ON MENU BUTTON clicks,button,event GOSUB dummy The Procedure Dummy should be empty. Use the same method to switch other ON MENU commands temporarily off. ON MENU IBOX You can define two independent rectangles with ON MENU IBOX and/or ON MENU OBOX, either : - one IBOX and one OBOX - two IBOXes - two OBOXes 22. PULLDOWN MENU OPENW 0 Even if you don't need a window, you could 'OPENW 0' if you use a pulldown menu. The top line (y-coordinates 0 to 18 in High resolution, that's where your menu is) is now protected against accidental drawing. After 'OPENW 0' 19 is always added to the y-coordinate, so with 'PLOT 0,0' the point is actually drawn at (0,19). Desk-submenu The first submenu in a pulldown menu should be the Desk-submenu ('Desk'), usually with the following lay-out : Info ------------ Accessory 1 Accessory 2 (...) If the user chooses the Info-item, you should show some information about the program. The AES takes care of the accessory-items, you simply use '1,2,3,4,5,6' in the corresponding DATA-line. If you use '-,-,-,-,-,-' the user won't be able to choose an accessory from your program, but all loaded accessories still occupy memory. File-submenu Most pulldown-menu's contain a File-submenu ('File') as the second submenu. The following lay-out is more or less standard : New file ^N Open file ... ^O ----------------- Close ^C Save ^S Save as ... ^M Abort ^A ----------------- Quit ^Q With '...' you announce that further input from the user will be requested. If at all possible, you should offer optional keyboard- alternatives for the experienced user. With '^N' you remind the user of the alternative. The character with ASCII-code 7 is used as the symbol for . By general agreement, the Quit-choice always is the last item of the File-submenu. By the way, never leave the last (rightmost) submenu without options, e.g. during development of a program. There must be at least one option in the last submenu, or it's reset-time again. 23. WINDOWS GFA-windows You'll notice that this chapter is rather short. This reflects my reservations about the use of windows. I know very few programs that use windows sensibly. For these programs GEM-windows indeed are a blessing. But most programs are better off without windows. GEM just slows the screen-output down. Cover your TOS-screen with windows only if you really need them. GFA-Basic has four "easy" windows for not too complicated jobs. Draw an imaginary cross on the screen. The intersection of the two lines is determined by the coordinates in 'OPENW n,x,y'. If you show more than one window on the screen, you should realize that resizing one window auto- matically changes the size of the other three windows as well (remember the cross?). Use the AES window-commands if you need independent windows. The four GFA-windows reserve the top line for a pulldown menu. If you use your own AES-windows, you should protect the top line yourself, e.g. with 'OPENW 0'. A standard-method for the use of GFA-windows looks like this : TITLEW #1," title " ! automatically centered INFOW #1,"information" ! left justified OPENW 1 ! or use FULLW #1 CLEARW 1 (...) CLOSEW 1 CLOSEW 0 I don't know how to use '#' properly. The editor does not accept '#' in 'OPEN #1', you have to use 'OPENW 1'. If you use FULLW instead of OPENW, you have to use 'FULLW #1'. In this case the '#' has to be used, or the command doesn't work at all! The GFA-editor always inserts a '#' after TITLEW and INFOW, even if you didn't type one. What the # is going on? CLOSEW If you have opened a window in your program, always use 'CLOSEW 0' before returning to the editor. If you don't, you can't work normally in Direct Mode. If you did forget, you can type 'CLOSEW 0' in Direct Mode and everything works fine again. If you use the AES window-commands, always call WIND_CLOSE(handle) before WIND_DELETE(handle). TITLEW You can clear a title with : TITLEW #n," " Don't use "" instead of " ", or you won't be able to move the window afterwards! CLEARW After 'CLEARW #n' all visible areas of the window are cleared, without activating the window. GFA uses WIND_UPDATE and WIND_GET for this command. 'CLEARW n' both clears and activates the window. 24. AES-LIBRARY ALERT If you need an empty line in an Alert-box, use : ALERT,3,"...| |...",1,"...",k Note the space between the two vertical rules. GFA-Basic allows 4 lines of 30 characters (High/Medium rez) if you use ALERT, but with FORM_ALERT you can use 5 lines. In both cases you can use not more than 3 buttons, each at most 8 characters (High/Medium rez) wide: m$=" line 1 | line 2 | line 3 | line 4 " ALERT 3,m$,1," OK ",k ' m$="[3][ line 1 | line 2 | line 3 | line 4 | line 5 ][ OK ]" k=FORM_ALERT(1,m$) In Low resolution you can use only half the number of characters you use in High/Medium resolution. If some characters are not visible in a button, try broadening the Alert- box by adding spaces to the widest text-line. If you have pressed the left mouse-button on the very spot where an Alert- button is about to appear, something goes wrong. GEM appears to remember your last mouse-click, and erroneously assumes you pressed the button after the Alert-box appeared. GEM only selects the button if the mouse- click occurred in the exact area of the Alert-button. To avoid this problem you should make sure the user has released the mouse-button before you call the Alert-box : REPEAT UNTIL MOUSEK=0 ! wait until mouse released By the way, the AES uses an 8K buffer to save the part of the screen where the Alert-box appears. Ditto for a menu. SHEL_GET and SHEL_PUT GFA-Basic is run from a 'Shell-Program', usually the Desktop. In that case the file DESKTOP.INF can be found in the Environment-Buffer. You can examine this file by transferring it to a string : d$=STRING$(1024,0) r%=SHEL_GET(1024,d$) PRINTing this string is much easier than using a disk-editor to examine DESKTOP.INF. After changing the string, you could put it back in the buffer with : r%=SHEL_PUT(1024,d$) This way you could change the lay-out of the desktop. If you change the string, you should know that the Desktop recognizes the end of DESKTOP.INF by the byte &H1A. So don't forget to put CHR$(26) at the end. If you want to save the new buffer, try : OPEN "O",#1,"\DESKTOP.INF" PRINT #1,LEFT$(d$,INSTR(d$,CHR$(26))) CLOSE #1 Is that all there is to tell about the AES-library? Certainly not, so at this point you will probably be bitterly disappointed. I avoid the AES- Library whenever I can. Anyway, this is a subject that is thoroughly covered in most books about GFA-Basic 3.0. Everyone is entitled to his own opinion about the usefulness of RSC-files and other exotic subjects. Better still, write this chapter yourself. And the same to you too! 25. GFAXPERT-FILES Take your time to read this chapter carefully, before using the GFAXPERT- Procedures or programs. The paragraph 'STANxxxx.LST' is essential reading. Don't forget to read the 'small print' in the last paragraph. GFAXPERT.DOC You're reading it. GFAXPERT.LIB This folder (in GFAXPRT2.ARC for downloaders) contains a large collection of Procedures in several LST-files. Each LST-file contains one or more folded Procedures. Most Procedures are also listed in this text, or are at least mentioned. I suggest you Llist all the LST-files with unfolded Procedures for easy reference. You could also Llist all LST-files again with folded Procedures, so you will be able to locate a Procedure quickly. Always read the comment-lines (' ***) in a Procedure before using it. You will find there if other Procedures are used, or Standard Globals (see paragraph 'STANxxxx.LST'). Sometimes a Procedure returns a global variable. Most variables are declared as LOCAL. And do read the relevant chapter of this text before using a Procedure. GFA has released a library of graphics- and sound-routines. I am curious about the quality of these routines, but I certainly do not intend to pay DM 148.- for the library. My advice would be to use the library GFAXPERT.LIB and pay DM 148.- to me. INLINE The INLINE-folder (in GFAXPRT2.ARC for downloaders) contains INLINE-files that you will have to load after Merging certain Procedures. This is clearly indicated in such Procedures. STANxxxx.LST Before writing a program, I decide in which resolution the program will run. Then I Merge the corresponding STANxxx.LST-file (in the folder GFAXPERT.LIB) and use that as the standard framework for my program : STANHIGH.LST - High resolution STANMED.LST - Medium resolution STANLOW.LST - Low resolution STANHIME.LST - High or Medium resolution STANALL.LST - all resolutions After deleting the first two lines and entering the name of the program (e.g. TEST.GFA), I Save the program as AAPROG.GFA in the Procedure- library. In this folder I have a few LST-files ready for easy Merging of Procedures into the program (AABLOCK1.LST, AABLOCK2.LST, etc.). Before you continue, you should Llist the five STANxxxx.LST-files. Merge STANHIGH.LST, unfold all Procedures, then Llist. In the other files you could restrict unfolding to the Procedure Initio (and perhaps the Procedures xxx.mode and Standard.xxx.colors). I'll wait until you have the listings of the STAN-files in front of you... Right, first I'll discuss STANHIGH.LST thoroughly. Later I'll point out some important features in the other STAN-files. You will not become an expert in structured programming immediately, but using the STAN-files should help. (1) The first line ('STANHIGH.LST') is there to remind you how to 'Save,A' this file after you have changed it. Delete it if you are going to develop a new program. (2) Enter the name of your new program (e.g. TEST.GFA) and immediately save the program as AAPROG.GFA in the Procedure-library. (3) Word-variables are defined as the default. (4) The Procedure Initio is called. In this Procedure the current resolution is checked, and after that, a few important global variables are defined (Standard Globals). The following Standard Globals are defined (read the comments in the listing for more information) : high.res! scrn.col.max return$ scrn.lin.max esc$ default.path$ help$ white undo$ physbase% black logbase% interpreter$ on! run.only$ scrn.x.max off! start.gfa$ scrn.y.max start.prg$ bel$ char.width char.height Also, the Standard Array color.index() is created, although you'll only need it for a few Procedures (e.g. Initio.sprite1). (5) The Procedure Title.screen is not activated yet. You'll probably want to change that Procedure. (6) A normal should be possible while developing a program. In the finished program you could activate the Procedure Break, or delete it. Be very careful with if the program uses RESERVE or has changed the address of the physical/logical screen. (7) The main program usually is not much more than a list of Procedure- calls, e.g. : @start.game @play.game @score (8) Try to leave the program by calling the Procedure Exit. During development it's more convenient to leave with 'EDIT'. (9) Two Standard Functions are defined: Center$ for centering text and Rev$ for PRINTing reverse. The last function uses VT52-codes, so use it on the TOS-screen only. (10) The Standard Procedure High.mode (called from Initio) checks the current resolution and aborts with an appropriate message if the program is run in the wrong resolution. (11) The Standard Procedure Get.path (called from Initio) returns the current path. Using the interpreter, GEMDOS returns the path of the interpreter (e.g. 'A:\'), not of the running program! That's why I use CHDRIVE + CHDIR in the shell-programs. In that case the Standard Global default.path$ does contain the path of the running GFA-program. If you develop the program, you should use CHDRIVE/CHDIR in Direct Mode if necessary. I am not happy with this method, but I know no other solution for GFA-programs. If I don't use this method, I always define the path in the Initiation-part, e.g. : path$="A:\GAMES\" ! where are the data-files? Now another GFA-user only has to change the path and the program can find the necessary files. For owners of the Run-Only interpreter you should describe the proper configuration in a READ.ME-file. With compiled GFA- programs there is no problem, because GEMDOS will return the path of the running program. (12) I like to begin every program with a title-screen. Change the Standard Procedure Title.screen, or delete it if you don't like title- screens in your programs. (13) The Standard Procedure Return is used by Title.screen. (14) The Standard Procedure Break can be deleted if you don't need it. Don't use 'ON BREAK CONT' in your program, unless you have a very good reason to do so. Note that I have made it possible to use the normal way if the user chooses while pressing the - combination. (15) The Standard Procedure Exit should always be used. This Procedure tries to go back to one of the shell-programs. If that's impossible, the Procedure decides to end with 'EDIT' or 'SYSTEM'. Is it possible to test in a program if it is run by the interpreter or if it is a compiled program? If the answer is yes, this Procedure could be improved. (16) Other Procedures can be inserted after '*** Procedures ***'. Merge Procedures from the library GFAXPERT.LIB (or your own library) here. I use the following method : - New, so memory is clear - Merge relevant LST-file from library - choose (folded) Procedure and Write as block to AABLOCKx.LST - repeat previous steps for other Procedure(s) - Load AAPROG.GFA - Merge AABLOCKx.LST at appropriate position(s) - Save program (press in Fileselector) Of course, you'll have to write a few Procedures from scratch. Before you know it, you'll have reached The End. It's so easy to write a program in GFA-Basic 3.0, that I actually enjoy programming. (17) Debugging is perhaps less fun than programming. Merge the Procedure Debug temporarily in your program if you need some help. And do save your program regularly! Fall back to the program AAPROG.BAK if your latest improvements were fatal. If you are completely satisfied, save the program under its proper name (TEST.GFA). Also save a back-up on another disk, just in case something terrible should happen to your original program. (18) In STANALL.LST you'll notice in the Procedure Initio that I always start with the default palette (Procedures Standard.med.colors and Standard.low.colors). New Standard Globals in STANALL.LST are : red med.res! green low.res! The Standard Array color.index() is very important in Medium or Low resolution. (19) In STANHIME.LST (my favourite) the only important difference is the Procedure High.med.mode, where the current resolution is checked. (20) In STANLOW.LST all 16 default-colours are now defined as Standard Globals : white blue grey l.purple black d.blue l.black d.purple red brown l.blue d.yellow green d.green bluegreen l.yellow With two new Standard Functions you can change the colour of PRINTed text (Ink$) or the colour of the background (Paper$). Both functions work on the TOS-screen only. The current resolution is checked in the Procedure Low.mode. (21) Nothing new in STANMED.LST, except the Procedure Med.mode to check the resolution. Perhaps you find all this talk about a "standard" program-structure very boring. If you write a program, why care about structure? Because it's much easier to change a structured program, especially if you didn't write the program yourself. Change an unstructured program, and you're going to be surprised by some (hidden) unwanted side-effects. Make other users happy, spread your beautiful programs only if they are structured and well commented. Do spread the (listable) source of your program, so other users can learn from you. And don't be insulted if others improve your program. I certainly will not be insulted if you send me your own thoughts about structured programming in GFA-Basic 3.0. START In the START-folder (in GFAXPRT2.ARC for downloaders) you will find the following programs : START.GFA - High or Medium resolution STARTLOW.GFA - Low resolution GFASTART.GFA - all resolutions I use two different disks for my GFA-programs. One for programs that run in High and/or Medium resolution (save the desktop in Medium resolution!). And one for programs that run in Low resolution. START.GFA and STARTLOW.GFA are used as a kind of shell to run the GFA- programs on these disks. Not a true shell, but it "feels" like all GFA- programs are run from START.GFA and STARTLOW.GFA. I start a GFA-session by double-clicking the shell-program on the desktop ('GFA' has been installed as application for GFABASIC.PRG). All my programs try to CHAIN one of the shell-programs when the user exits the program. Two important remarks if you decide to use these shell- programs : - vertical frequency is switched to 60 Hz in Medium or Low resolu- tion; not a good idea if you use a TV through a modulator! - Write Verify Test is switched off If you like, you can activate the check for a boot-virus in the shell- programs. The shell-program and GFABASIC.PRG (or GFABASRO.PRG) should be in the main directory! GFASTART.PRG is used in the same way with compiled GFA-programs. All my compiled GFA-programs try to CHAIN this shell-program when exiting. The source for this program is GFASTART.GFA. Compile this as GFASTART.PRG and put it in the main directory. All three START-programs contain plenty comments, so you should be able to understand how the programs work by studying the listings. The shell- programs certainly can be improved, I'm not yet satisfied with the current programs. small print Read this paragraph carefully, before using the GFAXPERT-files. The files are not Public Domain! You are free to use and change all GFAXPERT-files, but only for personal use. Certainly not for commercial use. You are invited to copy the original GFAXPERT-disk or the GFAXPRTx.ARC- files and give these to your friends. But you are not allowed to change anything on the disk or in the files. If you feel the urge to change something, don't do it, but write to me (read the chapter 'EPILOGUE'). You are not allowed to sell (files from) the GFAXPERT-disk or one of the GFAXPRTx.ARC-files. The disk GFAXPERT may only be sold by so-called Public Domain Clubs if they have my written permission to do so. BBS's are free to make the original GFAXPRTx.ARC-files available for downloading. Any part from the text GFAXPERT.DOC or the Procedure-library GFAXPERT.LIB may be quoted in newsletters or magazines, if accompanied by a reference like : from GFAXPERT (2nd ed.) by Han Kempen I cannot be held responsible for any damage that may result from running a GFAXPERT-program, using a Procedure from the library GFAXPERT.LIB, or using information from this text. (c) Han Kempen, 3 July 1990 EPILOGUE Well, that's all folks. I'm quite certain there are still a few bugs to be found in this text and in the Procedure-Library GFAXPERT.LIB. By now you should have become an expert in GFA-Basic 3.0, so you will be able to spot them bugs immediately. Please let me know if you find one. Perhaps you still have some unanswered questions about GFA-Basic. Or an answer to one of my own questions in this text. Or some neat Procedures. Or a brilliant program. I would appreciate it very much if you would send your letter and/or disk to : Han Kempen Rubensstraat 12 7741 AR Coevorden the Netherlands Do share your ideas, Procedures and programs with others, starting with me. Consider it a small payment for the GFAXPERT-files. Thanks. Wishing you many happy hours with GFA-Basic 3.0, Han Kempen INDEX (#) = Procedure or Function \ ..................................... 29 " ..................................... 41 1st Word Plus ......................... 62 40-folder limit ....................... 66 abbreviated commands .................. 12 accessory ............................. 25,101 ACHAR ................................. 96 ACLIP ................................. 99 After$ (#) ............................ 32 AFTER ................................. 82 ALERT ................................. 106 application ........................... 9 ARECT ................................. 88 ASCII-code ............................ 33,34,36,37 Ascii.qsort (#) ....................... 27 ATEXT ................................. 96 attribute ............................. 53 AUTO .................................. 9 BCHG .................................. 30 BCLR .................................. 29 Before$ (#) ........................... 32 Bezier-curve .......................... 92 BGET .................................. 62 BIOS 4 (Rwabs) ....................... 59 BIOS 7 (Getbpb) ...................... 56,60 BIOS 9 (Mediach) ..................... 60 BIOS 10 (Drvmap) ...................... 51 BIOS 11 (Kbshift) ..................... 36 bit-mask .............................. 29 Blitter (#) ........................... 99 BLOAD ................................. 61 Block.dimmer (#) ...................... 91 Boolean ............................... 17 boot .................................. 9,61 BPB ................................... 56 BPUT .................................. 62 break ................................. 10 Break (#) ............................. 110 BSAVE ................................. 61 BSET .................................. 30,79 bug (GFA) ............................. 12,20,24,25,31,32,36,42,80,83, 84,92,96,97,101 bug (TOS) ............................. 25,35,55,56,63,64,66,70,95,102 calculations .......................... 79 Caps (#) .............................. 39 CapsLock .............................. 38 CARD .................................. 31 CHAIN ................................. 83 Change.font (#) ....................... 44 Change.midi.buffer (#) ................ 67 Change.palette (#) .................... 85 characters ............................ 16,34 CHDIR ................................. 51,110 CLEAR ................................. 19 CLEARW ................................ 105 CLIP .................................. 92 CLOSEW ................................ 104 cluster ............................... 58 Coldstart (#) ......................... 9 color ................................. 40 Color.cycle (#) ....................... 86 correlation ........................... 31 COSQ .................................. 30 CURVE ................................. 92 Cut and Paste ......................... 13 Cycle.once (#) ........................ 87 DATE$ ................................. 22 Day.of.week (#) ....................... 22 Debug (#) ............................. 19,111 DEFFILL ............................... 87 DEFLINE ............................... 89 DEFLIST ............................... 15 DEFMARK ............................... 87 DEFMOUSE .............................. 72 DEFTEXT ............................... 89 DEFWRD ................................ 17 Degas-Pictures ........................ 97 Degas.screendump (#) .................. 46 DELAY ................................. 83 Desk-submenu .......................... 103 DESKTOP.INF ........................... 106 DFREE ................................. 56,58 Digital$ (#) .......................... 93 DIM ................................... 19 Dim.colors (#) ........................ 86 DIR$() ................................ 50 DIR ................................... 51 Direct mode ........................... 15 disk format ........................... 55 disk-swap ............................. 60 Do.sound (#) .......................... 76 DRAW .................................. 91 DTA-buffer ............................ 52 DUMP .................................. 19 Editor ................................ 71 EQV ................................... 31 ERASE ................................. 19,24 ERROR ................................. 82 EVERY ................................. 82 EXEC .................................. 24,83 EXIST ................................. 54 Exit (#) .............................. 110 fade-over ............................. 99 Fastprint (#) ......................... 41 FAT ................................... 58 FDC ................................... 55 FGETDTA ............................... 52 File Allocation Table ................. 58 File.copy (#) ......................... 55 File-submenu .......................... 103 FILES ................................. 51 FILESELECT ............................ 63 Fileselect (#) ........................ 64 Fileselector .......................... 63,71 floating point ........................ 18 Floppy Write Test ..................... 50 Folded Procedures ..................... 13 font .................................. 43,89 Font.8x16 (#) ......................... 43 Font.8x8 (#) .......................... 43 FONTKIT ............................... 44 FOR ... NEXT .......................... 79 Force.mediach (#) ..................... 60 FORM_ALERT ............................ 106 FSETDTA ............................... 52 FSFIRST ............................... 52 FSNEXT ................................ 52 Full.fill (#) ......................... 88 FULLW ................................. 104 FUNCTION .............................. 19 GEMDOS 17 (Cprnos) .................... 45 GEMDOS 25 (Dgetdrv) ................... 50 GEMDOS 48 (Sversion) .................. 10 GEMDOS 54 (Dfree) ..................... 56 GEMDOS 67 (Fattrb) .................... 53 GET ................................... 96 Get.path (#) .......................... 110 GFA-windows ........................... 104 GFAXPERT.DOC .......................... 7,108 GFAXPERT.LIB .......................... 7,108 GOSUB ................................. 82 GOTO .................................. 82 GRAPHMODE ............................. 90 HARDCOPY .............................. 45 harddisk .............................. 51,55,58,59 High.screendump.epson (#) ............. 45 High.screendump.star24 (#) ............ 45 HLINE ................................. 96 IBOX .................................. 102 IF ... ENDIF .......................... 78 INFOW ................................. 194 Initio (#) ............................ 109 Initio.fill1 (#) ...................... 88 Initio.high.fill1 (#) ................. 87 Initio.keyget (#) ..................... 36 Initio.logical.screen (#) ............. 42 Initio.mouse1 (#) ..................... 72 Initio.printer (#) .................... 47 Initio.sound (#) ...................... 75 Initio.sprite1 (#) .................... 93 Initio.text.array (#) ................. 62 Ink$ (#) .............................. 40 INKEY$ ................................ 33 INLINE ................................ 24,108 INP ................................... 62,67,70 INPAUX$ ............................... 70 INPMID$ ............................... 67 INPUT ................................. 34,62 INPUT$ ................................ 35 Insert-mode ........................... 15 INSTR ................................. 32 INT{} ................................. 23 integer ............................... 18 Intel.word (#) ........................ 31 interleave ............................ 57 Invert.block (#) ...................... 91 joystick .............................. 74 Key.click (#) ......................... 38 Key.repeat (#) ........................ 39 keyboard .............................. 37 keyboard-buffer ....................... 33 Keyboard.version (#) .................. 37 KEYDEF ................................ 37 KEYGET ................................ 35 KEYLOOK ............................... 36 KEYPAD ................................ 37 KEYPRESS .............................. 37 KEYTEST ............................... 35 KILL .................................. 54 Line-A ................................ 95 LINE INPUT ............................ 35,62 Llist ................................. 14 Load .................................. 13 LOCATE ................................ 41 LOF ................................... 54 LOG ................................... 30 logical screen ........................ 42 loops ................................. 80,119 LSET .................................. 32 Make.high.fill (#) .................... 88 Make.mouse (#) ........................ 73 Make.palette.string (#) ............... 85 Make.sprite (#) ....................... 94 MALLOC ................................ 25 MAX ................................... 31,78 Max.array (#) ......................... 31 memory ................................ 23,24,25 MENU() ................................ 101 Midi-buffer ........................... 67 Midi-commands ......................... 67 Midi.monitor (#) ...................... 68 MIN ................................... 78 MOD ................................... 29 monitor ............................... 10 MOUSE ................................. 71 MS-DOS disk ........................... 58 NAME .................................. 54 Neochrome-Pictures .................... 98 New.med.colors (#) .................... 85 Normal.font (#) ....................... 44 NOT ................................... 81 OBOX .................................. 102 ON BREAK GOSUB ........................ 82 ON MENU BUTTON ........................ 101 ON MENU IBOX .......................... 102 OPENW ................................. 104 OPENW 0 ............................... 103 Operating System ...................... 10 OUT ................................... 40,62 Overwrite-mode ........................ 15 palette ............................... 84,111 Palette.box (#) ....................... 86 Paper$ (#) ............................ 40 Parse.filename (#) .................... 65 parser ................................ 32 PCIRCLE ............................... 92 physical screen ....................... 42 Play.midi (#) ......................... 68 PLOT .................................. 91 point-commands ........................ 14 PRED .................................. 29 PRINT ................................. 40 Print.stopwatch (#) ................... 21 PRINT TAB ............................. 42 printer-commands ...................... 47 printer-driver ........................ 14 printer-parameters .................... 45 Printer.ready (#) ..................... 45 PUT ................................... 96 QSORT ................................. 26 RAM ................................... 23 RAM-disk .............................. 50 READ .................................. 20 RECALL ................................ 62 Record.midi (#) ....................... 67 RESERVE ............................... 23 reset ................................. 9,50 RESTORE ............................... 20 Restore.palette (#) ................... 85 Restore.physical.screen (#) ........... 43 Return (#) ............................ 110 reverse ............................... 41,84 Rgb.value (#) ......................... 85 right justification ................... 32 RS232-buffer .......................... 70 RSET .................................. 32 Rubber.line (#) ....................... 90 samples ............................... 76 Save .................................. 14 Save,A ................................ 14 Save.palette (#) ...................... 85 scan-code ............................. 33,36,37,38 scrap-library ......................... 83 Screen.dimmer (#) ..................... 91 Screendump (#) ........................ 45 Scroll.text.up (#) .................... 99 Scroll.up (#) ......................... 98 sectors ............................... 59 SELECT ................................ 78 serial number ......................... 57 SETCOLOR .............................. 84 SETMOUSE .............................. 71 SHEL_GET .............................. 106 SHEL_PUT .............................. 106 shell-program ......................... 112 SHL ................................... 79 Show.degas (#) ........................ 97 Show.text.page (#) .................... 63 Shuffle (#) ........................... 20 SINQ .................................. 30 SOUND ................................. 75 Soundmachine .......................... 77 special characters .................... 16 speech ................................ 77 SPRITE ................................ 93 SSORT ................................. 26 Standard .............................. 7 Standard Array ........................ 109,111 Standard Functions .................... 110,111 Standard Globals ...................... 109,111 STANxxxx.LST .......................... 108 START ................................. 112 start-up .............................. 9 Step Rate ............................. 50 STICK ................................. 74 Stopwatch (#) ......................... 21 STORE ................................. 62 STRIG ................................. 74 String.index.qsort (#) ................ 28 SUCC .................................. 29 supervisor mode ....................... 23 SWAP .................................. 20,31 Swap.screen (#) ....................... 43 syntax ................................ 12 system-font ........................... 43,89 Tab ................................... 13,41 TAB ................................... 42 TEXT .................................. 92 text-array ............................ 62 Text.at (#) ........................... 92 TIME$ ................................. 21 Time (#) .............................. 21 TIMER ................................. 21 Title.screen (#) ...................... 110 TITLEW ................................ 104,105 TOS ................................... 10 TOS-screen ............................ 40 TOUCH ................................. 54 twisted format ........................ 57 TYPE .................................. 20 VAR ................................... 18 variable type ......................... 17 VDISYS 38 (vqt_attributes) ............ 89 VQT_EXTENT ............................ 95 VSETCOLOR ............................. 84 VSYNC ................................. 98 VT52 .................................. 40 Warmstart (#) ......................... 9 WAVE .................................. 75 WIND_CLOSE ............................ 104 word .................................. 17,31 WORD .................................. 23,59 write-protect ......................... 60 XBIOS 0 (Initmous) ................... 71 XBIOS 5 (Setscreen) .................. 42 XBIOS 6 (Setpalette) ................. 85,97 XBIOS 7 (Setcolor) ................... 84 XBIOS 8 (Floprd) ..................... 60 XBIOS 10 (Flopfmt) .................... 56 XBIOS 15 (Rsconf) ..................... 70 XBIOS 16 (Keytbl) ..................... 37,38 XBIOS 18 (Protobt) .................... 57 XBIOS 19 (Flopver) .................... 56 XBIOS 32 (Dosound) .................... 75 XBIOS 33 (Setprt) ..................... 45 XBIOS 36 (Prtblk) ..................... 47 XBIOS 64 (Blitmode) ................... 99