Safe Zones in IBM BASIC (COMPUTE! Magazine July 1986) There is a way to store a few characters or flags in the PC's memory that will survive the BASIC RUN command. BASIC's CLEAR command gives you the ability to create a safe area of RAM of almost any size. Besides deleting all variables, CLEAR controls the amount of memory available to BASIC. By adding a command and a parameter to the CLEAR command, you can make the BASIC workspace smaller than usual, reserving the extra memory for yourself. The workspace is initially 65,536 bytes but it's easy to reserve some memory at the top of that space. Use this format: CLEAR ,workspace where workspace is a number less than 65536. To calculate the correct value, subtract from 65536 the number of bytes you want to protect. For instance, the command, CLEAR ,65280 reserves the last 256 bytes (65536 - 256 = 65280) of BASIC workspace for your use. When you type RUN after a CLEAR statement like this, the size of the workspace is reset to its default but the data in the reserved area is not affected. As long as the next program begins with a similar CLEAR statement, it can PEEK into the reserved area and find the values that the previous program POKEd there. Here's a simple program that stores some values in a 256-byte reserved area: 10 CLEAR ,65280 20 FOR A=0 TO 255 30 POKE A+65280,A 40 NEXT After you run the program, enter and run this program to read the stored values back: 10 CLEAR ,65280 20 FOR A=0 TO 255 30 PRINT PEEK(A+65280) 40 NEXT ----------------------------------------------------------------- Selective BSAVE/BLOAD (PC Magazine Vol 5 No 13 July 1986 User-to-User) Most users who BSAVE and BLOAD BASICA screens do so a full screen at a time, but it can be very handy to do it in smaller pieces. The LITLBSAV.BAS program BSAVEs a little piece of the screen (three lines) and then lets the user BLOAD it back in various places up and down the screen. This technique works very well off a RAMdisk or hard disk but is slow when run from a floppy. Editor's Note: The program was modified so it BSAVEd three lines (a line of text with a blank line above and below) and let users move the BSAVEd block up and down the screen. (Including blank lines above and below the printed line effectively erases existing printed lines on-screen.) In line 120 the program determines whether the system is monochrome (where PEEK(1097)=7) or not, and in line 130 sets the segment to &HB800 for color or &HB000 for mono. Color systems offer four screen pages, 0 through 3. If you have a color system, you can increase the upper limit in line 200 to 3680 and make the text temporarily "disappear." The nice thing about this technique is that it lets you "pop" up text onto existing screens. However, it won't restore the existing part of the screen it covers over. Still, by BSAVEing portions of screens and BLOADing them at the appropriate locations, you save disk space adn make the BLOAD operation a little faster. Since color screen memory is not dual-ported (as mono memory is), you'll see "chatter" or "snow" while the BLOAD is occurring. Most snow is white, so by setting a white (7) background most of it is covered up. This will run slower on a disk system unless you hit the Up and Down Arrow keys rapidly enough to keep the floppy spinning constantly. These days you can buy slow hard disks for a lot less than $500. Or stock up on system RAM and try this in a RAMdisk. BSAVCALC.BAS calculates the offset and length for BSAVEs (and the offset for BLOADs) less than a full screen in size. The PC text screen memory is arranged so that each character takes up 2 bytes -- one representing the actual character, and one for the character's attribute. The default text screen is 80 characters wide, so each 80-wide lien in memory takes up 160 bytes. Color memory begins at segment &HB800, mono memory at &HB000. You tell BASICA which segment you want with the statement DEF SEG=&HB800 or DEF SEG=&HB000. BSAVE needs to know three things -- the filename it will use to store the screen on your disk, the "offset" (distance in bytes up from the segment specified by DEF SEG), and the length of the file to save. Line 1 goes from offset 0 to offset 159, line 2 from 160 to 319, etc. All BLOAD needs to know is the filename and offset, not the length. 100 'LITLBSAV.BAS 110 DEFINT A-Z:CLEAR:S$=STRING$(4,205) 120 DEF SEG=0:IF PEEK(1097)=7 THEN SG=&HB000 ELSE SG=&HB800 130 DEF SEG=SG:KEY OFF:SCREEN 0,0:COLOR 4,7,6:CLS:LOCATE 2,1,0 140 PRINT S$;" This BLOADs a line at a time (hit ";CHR$(24):" and "; 150 PRINT CHR$(25);" keys to move, or ESC to end) ";S$ 160 BSAVE "TEST.SCR",0,480:CLS:S=3680:GOTO 210 170 I$=INKEY$:IF I$="" THEN 170 ELSE IF I$=CHR$(27) THEN LOCATE 1,1:END 180 I=INSTR("HP",RIGHT$(I$,1)):IF I=0 OR LEN(I$)=1 THEN BEEP:GOTO 210 190 IF I=1 THEN IF S<0 THEN BEEP:GOTO 170 ELSE S=S-160:GOTO 210 200 IF S>3520 THEN BEEP:GOTO 170 ELSE S=S+160 210 BLOAD "TEST.SCR",S:GOTO 170 - - - - - 100 'BSAVCALC.BAS 110 DEF FNST$(Z)=MID$(STR$(Z),2+(Z<0)):Q$=CHR$(34):CLS 120 PRINT "Pick one: 1 - BSAVE 2 - BLOAD (1 or 2)" 130 I$=INKEY$:IF I$="" THEN 130 ELSE IF I$=CHR$(13) THEN END 140 IF INSTR("12",I$)=0 THEN 130 ELSE I=VAL(I$) 150 IF I=2 THEN B$="BLOAD " ELSE B$="BSAVE " 160 PRINT CHR$(30);B$;"information:";STRING$(20,32) 170 INPUT "Enter a start line from 1-25: ",S1 180 IF S1<1 OR S1>25 THEN BEEP:GOTO 170 190 IF I=2 THEN PRINT:PRINT "For line";S1:GOTO 230 200 INPUT "Enter a stop line from 1-25: ",S2 210 IF S2<1 OR S2>25 OR S252 THEN 1180 1310 ' 1320 IF A$="1" THEN SHELL "DATE" 1330 IF A$="2" THEN SHELL "TIME" 1340 IF A$="3" THEN SHELL "PROG2" 1350 ' 1360 CLS 1370 GOTO 1410 1380 ' 1390 PRINT "* * * * * * * ILLEGAL RESPONSE ^ REDO" 1400 ' 1410 PRINT:PRINT "MENU B:" 1420 PRINT:PRINT "1. Run Checkdisk" 1430 PRINT:PRINT "2. Show Disk Directory" 1440 PRINT:PRINT "3. None of the above" 1450 PRINT:PRINT:INPUT "Enter your choice: ",A$ 1460 PRINT 1470 ' 1480 IF A$="" THEN 1390 1490 IF ASC(A$)<49 THEN 1390 1500 IF ASC(A$)>51 THEN 1390 1510 ' 1520 IF A$="3" THEN 1630 1530 IF A$="1" THEN B$="CHKDSK" 1540 IF A$="2" THEN B$="DIR" 1550 INPUT "Enter drive letter: ",C$ 1560 IF C$="" THEN 1550 1565 X=ASC(C$+CHR$(0)):IF X<65 OR X>66 THEN 1550 1570 D$=B$+LEFT$(C$,1)+":" 1580 LOCATE 24,1 1590 IF A$="1" THEN GOSUB 1710 1600 SHELL D$ 1610 PRINT:PRINT 1620 ' 1630 PRINT "End of BASICA program, returning to SYSTEM" 1640 PRINT 1650 PRINT TAB(20) "Normally would return to SYSTEM here," 1660 PRINT TAB(20) "but for debug and demo purposes the" 1670 PRINT TAB(20) "program will restart after a delay" 1680 FOR I=1 TO 5000:NEXT I:RUN 1690 ' 1700 ' 1710 PRINT 1720 PRINT "WARNING: You will get error message `Bad command ...'" 1730 PRINT " if the called program is not on " 1740 PRINT " the disk in the default drive" 1750 RETURN Batch File for Demo: ECHO OFF ECHO . REM Display the system date DATE ECHO . REM Display the system time TIME :ENDPROG2 ----------------------------------------------------------------- BASIC Debugging (PC Magazine August 1986 User-to-User) BASIC is great for interactive programming, but its debugging tools are quite primitive. One of the most irritating limitations is that when you make a simple correction to a single line (even a REMark) BASIC flushes all your variables, closes all your files, and forces you to start the program over to test the correction. When the interpreter finds an error, it prints an error message and halts the program. In many cases, you could continue running the program if only you could re-enter the offending line and re-execute it. To make such on-the-fly corrections, examine the erroneous line with the LIST command. (The problem line will already be displayed if it contains a syntax error.) Then type the following command in direct mode: CHAIN MERGE "CON",ERL,ALL The cursor will drop down to the next line and wait for keyboard input. Next, rekey the bad line, and press Enter and then Ctrl-Z and then Enter again. The program will continue where it left off. The next time you list or save the program, it will contain the repaired program line. Another tip on smart BASIC debugging is to avoid error traps (ON ERROR GOTO) unless they are absolutely necessary. They tend to hide errors that need to be caught, and you will find yourself unable to get to the root of a malfunction. Programs can trap errors right at the source, and then turn error trapping off as soon as possible. For example: 100 'Print report 110 ON ERROR GOTO 130 120 GOTO 150 130 PRINT "Printer ERROR. Hit a key." 140 WHILE INKEY$="":WEND:RESUME 150 LPRINT "ABC Company Report" 160 ON ERROR GOTO 0 170 '...print rest of report .... This example assumes that any printer errors will surface during the printing of the first line of a report. The error trap itself resides directly above the possible error, and error trapping is on only during the printing of that line. Use a similar layout when opening files. Printer and disk I/O are about the only places that an error trap is really needed. You can write program code to test for any other potential problems without resorting to BASIC's error handling. Editor's Note: Using CHAIN MERGE "CON" to fix a BASIC problem without shutting down the active program is a handy trick. Your cursor may temporarily disappear while you're entering the new line, and you may have to hit the Esc key to erase a message on the screen before you type in the CHAIN MERGE... command, but the technique can save a tremendous amount ot time. Note that while this will correct the problem line in memory, be sure to save the file to disk to make the correction permanent. In the report-printing routine, you might want to test the error that BASIC catches with an IF ERR=25 THEN... or IF ERL=150 THEN... to make sure that the actual problem isn't falsely reported as a printer error when it's caused by something else. However, it is right to trap all the usual errors by writing thorough code, not by skimping on traps and waiting for a mistake to fall through to an ON ERROR statement. ----------------------------------------------------------------- Refreshing PAUSE (PC Magazine August 1986 User-to-User) To pause for a keystroke in BASICA, most programmers use either 10 A$=INEKY$:IF A$="" THEN 10 or 10 WHILE INKEY$:WEND A more natural way is to create a machine language subroutine at the beginning of your program and then call it whenever required. Simply insert the following code at the start of any program: 0 CLEAR,5888 1 DEF SEG=5888 2 FOR X.=0 TO 6 3 READ Y. 4 POKE X.,Y. 5 NEXT 6 PAUSE=0 7 DATA 80,180,7,205,33,88,203 and then adda a 100 CALL PAUSE (substituting the appropriate line number for the 100) whenever you want to wait for a keystroke. The example here takes up seven lines, but you can use colons to concatenate it all to a single line starting with 0 and save the line as an ASCII file, then merge this file into any of your existing programs. The X. and Y. variables and line number 0 are uncommon so that they don't interfere with ones already in your programs. The key you hit to break the pause is not stored in the keyboard buffer after the program execution continues. If you need to read the key, change the "7" to a "1" in the DATA statement in line 7. Editor's Note: This may be more natural than the usual methods, but it doesn't really work any better and won't compile as is. If you try it and do want the keystroke to print, after changing the 7 to 1 in the DATA statement, follow the CALL PAUSE with a 200 PRINT INKEY$ (substituting the appropriate line number for the 200). ----------------------------------------------------------------- Compaq Unprotection (PC Magazine August 1986 User-to-User) It's simple to modify the technique for unprotecting BASIC programs described in PC Mag Vol 4 No 25 User-to-User and PC Mag Vol 5 No 10 PC Tutor to work on non-IBM versions of BASIC. Instead of IBM's location 1124, use 1228 for Compaq BASIC 2.11, and 1433 for Compaq BASIC 3.11. If the location is included in the BSAVE ooperation, you may omit it from the BLOAD. Other non-IBM, non-Compaq versions of MS-BASIC may use entirely different locations. Editor's Note: "Protecting" a BASIC file by saving it with the ",p" option doesn't really do much other than prevent naive users from listing the source code. The technique to remove the flimsy protection is to get into BASIC and typing: BSAVE "UN.P",1124,1 (substituting location 1228 or 1433 if you're using Compaq BASIC 2.11 or 3.11, respectively). If you every try to list a BASIC program and see an "Illegal function call" message, just type: BLOAD "UN.P" At this point you should be able to list the source code. If not, make sure you tried the proper offset (1228 or 1433) for the BASIC version you're using. Then resave the formerly protected BASIC program to remove the effects of the ",p" permanently. ----------------------------------------------------------------- STRING$ and CHR$ (COMPUTE! Magazine September 1986) The statement PRINT STRING$(15,32) has exactly the same effect as PRINT " " (15 spaces). STRING$ can be used where any long series of identical characters is needed. For instance, PRINT STRING$(40,46) prints a line consisting of 40 dots. You can also do the same thing through concatenation. To create a string consisting of 30 spaces, for instance, use: SP$=" " FOR J=1 TO 30 SP$=SP$+CHR$(32) NEXT This construction is easy to type and requires only a few more characters than printing the string in literal form. ----------------------------------------------------------------- BASIC Configuration (PC World September 1986 Star-Dot-Star) A short BASIC program can define BASIC's function keys and then erase itself from memory. A short batch file can start BASIC and run this program automatically so that your custom function key assignments are in place. Defining the function key settings in this manner also lets you use the following time-saving trick. Whenever you write a BASIC program, always put a comment on line 1 beginning with the program's name enclosed in quotes. Combining this step with the F10 definition show in BSTART.BAS enables you to save a program anytime with one quick keystroke. The first part of F10's definition, "LIST 1"+CHR$(13), simply lists the first line of the current program -- the line containing the program's name. The two CHR$(30) characters move the cursor back to the beginning of the listed line. SAVE overwrites the line number and the apostrophe, leaving the file name in quotation marks intact. The last CHR$(13) simulates pressing Enter, executing the SAVE command using the supplied file name. This technique should be a boon to anyone who issues SAVE commands frequently, since it reduces that task to a single keystroke. If you prefer to save your programs in ASCII format you can place the ,A after whatever file name you specify. ----------------------------------------------------------------- IBM Custom Characters (COMPUTE! Magazine September 1986) You can redefine the character set on the IBM PC; however, there are a couple of restrictions. Redefined characters must be printed on one of the graphics screens to be seen, and only the upper half of the character set (characters 128-255) can be changed. The following program shows how to redefine CHR$(128) as an alien shape. It displays the custom character on SCREEN 1. 10 DEF SEG=0 20 POINTER=&H7C 'POINTER &H110 for characters 0-127 on the PCjr only 30 FOR VECTOR=0 TO 3:OLDVEC(VECTOR)=PEEK(POINTER+VECTOR):NEXT 40 DEF SEG=&H1700 50 FOR DOTPOS=0 TO 7:READ DOT DATA:POKE DOTPOS,DOTDATA:NEXT 60 DEF SEG=0: 70 SCREEN 1:CLS 80 FOR VECTOR=0 TO 2:POKE(POINTER+VECTOR),0:NEXT:POKE POINTER+3,&H17 90 PRINT CHR$(128) 100 FOR VECTOR=0 TO 3:POKE(POINTER+VECTOR),OLDVEC(VECTOR):NEXT 110 DATA 60,126,90,126,60,36,66,129 You make the computer look to RAM rather than ROM for its character data. If you have at least 128K of RAM in your PC, memory above 96K is unused by BASIC and is thus a safe place to store the custom character data. Line 40 of the program accesses this area with the statement DEF SEG=&H1700. In line 50, the program puts the alien shape data in the area beginning at &H1700. Line 110 contains the data. To make the PC fetch its character data from the segment at &H1700, we must change certain pointers at the bottom of memory. These pointers are four bytes long. The first two bytes represent an offset to the segment address contained in the third and fourth bytes. The pointer to the data for the built-in graphics and foreign language characters (numbered 128-255) is at locatino &H7C. Since the program redefines a character in this range -- CHR$(128) -- we've used this pointer value in line 20. On the PCjr, you can redefine characters in the range 0-127 using the pointer at location &H110. In order to access either character data pointer, you must set DEF SEG to zero since the pointers are at the bottom of memory. The program does this in lines 10 and 60. Before the program ends, the character data pointers must be restored to their default values. If you end the program with the character pointers still modified, the computer can't recognize the custom characters and will fail to respond to any commands. Before modifying the characters, save the default character set pointers (line 30). When you're done printing the custom characters, restore the pointers to their original values (line 100). ----------------------------------------------------------------- Identifying Displays (PC Magazine Vol 5 No 16 Sept 30, 1986 PC Tutor) A user develops applications with the BASIC interpreter and compiles them with Microsoft QuickBASIC. He uses a technique to blank the screen while text is being written to it. This results in a nice, professional-looking video screen that flashes on all at once, rather than visibly being drawn one line at a time. To turn the display off, use the statement: OUT 984,1 and to turn it back on: OUT 984,41 This works perfectly with an IBM or compatible color graphics adapter. The problem is that the programs must also run on some Hercules Graphics Cards (and Hercules clones), and the technique doesn't work with these boards. Different video pages are also used in the programs, and these, too, are ignored by the Hercules card. Editor's Note: This technique won't work on regular IBM Mono Adapters, either. The port being manipulated is the "Mode Control Port Register," which has a different address on color adapters and monochrome adapters, including the Hercules Graphics Card. The Control Port for monochrome displays is at address 952 rather than 984. (In hex, these addresses are 3B8 and 3D8.) You can blank an IBM Monochrome Adapter or Hercules Card with: OUT 952,1 and unblank it with: OUT 952,41 but that's not the best way to do it. The control port address is always 4 higher than the I/O address of the 6845 video chip. That port address is a word (2 bytes) stored at the hex address 0040:0063 in the BIOS data area. So, you can define a variable for the Control Port thus: DEG SEG=&H40 CTRLPORT=4+256*PEEK(&H64)+PEEK(&h63) Now you can simply use the variable CTRLPORT instead of 984 or 952. You won't be able to use separate video pages on an IBM Monochrome Adapter or the Hercules Graphics Card, however; so unless you want to write alternative code for the monochrome boards, it's easier to avoid using video pages altogether. (If you must use video pages, you can still use the value of CTRLPORT to also identify the type of adapter.) In general, however, the best way for your programs to identify the video display is to ask the user on-screen. This is nothing to be ashamed of. You'll find that most software that does anything beyond plain vanilla text output requires some sort of installation program. Some really big-league stuff like Microsoft Word can adapt to different displays without being told, but then we're talking about a 44K WORD.COM program (version 3.0) that identifies displays and provides drivers for them. We're also talking about problems by experts in the field. Further, we're about to see a real explosion in video adapters, and it's going to get tougher and tougher to write programs that work with all of them. The EGA is just the beginning. (The above technique of blanking the screen won't work on an EGA, but it won't do any harm, either.) This is one reason why the graphics device interface of Microsoft Windows is so important to the future of the PC. Windows takes on the responsibility of dealing with the hardware so that you can concentrate on the program. ----------------------------------------------------------------- BSAVE Image Arrays to Disk (PC World October 1986 The Help Screen) Recording a BASIC image array in a disk file and reading that file to set an image variable is simpler than using GET and PUT. To save an image array to disk, issue the DEF SEG statement to set BASIC's data segment as the current memory segment, and execute the BSAVE (binary save) command. The BSAVE command, which saves any portion of memory to disk, requires three parameters: the filespec of the file being saved, the offset (number of bytes) into the current memory segment where the first byte of data representing the image begins, and the number of bytes to be saved. The filespec requires a file name, but it can include a drive specifier and, with BASIC 2.0 and later versions, a path. The offset should point to the beginning of the image array variable. You accomplish this by specifying the array's first element, (0), with the VARPTR function. You already know the size of the variable array because you had to calculate the number of bytes that the image array required to properly DIMension the array. In this example, the image is 34 bytes. Because a single-precision array dedicates 4 bytes per element, a nine-element array will reserve sufficient space. BSAVE.BAS also demonstrates how to load a saved image array. Issue a DEF SEG statement to set BASIC's data segment as the current memory segment, then execute the BLOAD (binary load) command to put the image data into memory. BLOAD requires just two parameters: the filespec and the offset of the current memory segment where the image file should be placed. Point to the beginning of the image array variable with the aid of the VARPTR function. BSAVE.BAS: 10 SCREEN 1:CLS 20 LINE (0,0)-(10,10),3,B 30 CIRCLE (5,5),5,1 40 PAINT (5,5),2,1 50 DIM IMAGE(22) 60 GET (0,0)-(10,10),IMAGE 70 DEF SEG 80 BSAVE "IMAGE.FIL",VARPTR(IMAGE(0)),88 90 ERASE IMAGE 100 DIM IMAGE(22) 110 DEF SEG 120 BLOAD "IMAGE.FIL",VARPTR(IMAGE(0)) 130 PUT (100,100),IMAGE 140 LOCATE 13 ----------------------------------------------------------------- Personalized DATA (PC Magazine Vol 5 No 20 Nov 25, 1986 User-to-User) STAMP.BAS (PC Mag Vol 5 No 8 April 29, 1986) demonstrated how to modify a comment permanently from within a BASIC program. It's also possible to modify a quoted string in an assigned statement or DATA statement, since the BASIC interpreter will set up the string variable to point into the program itself when the string is assigned to, or read to, a string variable. UPDATER.BAS asks for the user's name, displaying the name that is already loaded in the DATA statement as the default. If the user then simply hits the Return key, the program accepts the default; otherwise the program stores the replacement name in the DATA statement and then saves itself. CVI is used to for the string, since this technique avoids possible overflow errors that can result from multiplication. Editor's Note: This kind of trick can come in handy when adding new information on the fly, but be careful with it since it can also write bad data over good data. You can get around this by adding a line to the program that saves a backup "UPDATER.BAK" file to disk before it makes any of the changes in memory and then writes the permanently changed file. 100 'UPDATER.BAS 110 READ DUMMY$ 120 PRINT "Enter your name: [";DUMMY$;"]" 130 LINE INPUT I$ 140 IF I$="" THEN END ELSE I$=I$+SPACE$(19) 150 P%=VARPTR(DUMMY$) 160 L%=PEEK(P%) 170 A%=CVI(CHR$(PEEK(P%+1))+CHR$(PEEK(P%+2))) 180 FOR I%=1 TO L% 190 POKE A%+I%-1,ASC(MID$(I$,I%)) 200 NEXT 210 SAVE "UPDATER 220 DATA "User name goes here" ----------------------------------------------------------------- BASIC and the Environment (PC Magazine Vol 5 No 20 Nov 25, 1986 PC Tutor) You can use the undocumented feature of batch files that allows treating environment strings as replaceable parameters. With this you can let batch files know which device drivers are currently loaded and to prevent running incompatible resident programs. For instance, to load a mouse driver, your AUTOEXEC.BAT file can execute: SET MOUSE=1 Then you can do things in batch files like: IF "%MOUSE%"="1" some command IF NOT "%MOUSE%"="1" some other command Getting at the environment in BASIC is different. Editor's Note: This is a good use of the environment, except it doesn't work right under DOS 3.0. DOS 2.x, 3.1 and 3.2 handle it fine. The segment address of the environment is always stored at offset &H002C of the Program Segment Prefix (PSP) of any program. So, if you could find the segment address of BASICA.COM's PSP (which will be different depending on which resident programs and device drivers were loaded), you could directly access the environment with PEEK. This would be messy and probably involve some assembly language routines. There's a better way, and that is to upgrade to DOS 3.1 or later. The new BASICA 3.0 ENVIRON$ function retrieves strings from the environment based either on the parameter name (such as "PATH") or on a number. The small program below shows both approaches. The WHILE loop in the first part displays the same information as the DOS SET command executed without parameters. 10 I=1 20 WHILE ("" <> ENVIRON$(I)) 30 PRINT ENVIRON$(I) 40 I=I+1 50 WEND 60 PRINT "The COMSPEC is ";ENVIRON$("COMSPEC") ----------------------------------------------------------------- Doing a DIR in BASIC (PC Magazine Vol 5 No 20 Nov 25, 1986 PC Tutor) How do you read a diskette directory information into an array from a BASIC program, and how do you invoke a Print-Screen from within a program automatically? Editor's Note: A very common method used by BASIC programmers for reading the directory is no more elegant than: 1) Clear the screen with CLS 2) Do a FILES command to display a directory on the screen 3) Read the information off the screen character by character with a combination of the LOCATE statement and teh SCREEN function. (The SCREEN function reads characters from the screen; there is also a SCREEN statement used for setting video modes.) If you'd rather not have the users of your program know what you're doing, you can use the COLOR statement to set the same foreground and background colors so nobody can see the FILES display. (The information is still there, of course.) You might also consider another method using the SHELL command, which is documented under BASIC 3.0 and works most of the time under BASIC 2.0. The SHELL command executes another program or a DOS command from BASIC. Your line would read: SHELL ("DIR > DIRLIST") This puts a normal DIR listing into a file called DIRLST. You can then read the DIRLIST file normally and extract the information you want. Other than these approaches, you'd probably have to use an assembly language subroutine. This would definitely be more complex than either of the two methods described above. BASIC is not the only high-level language that has no built-in facility for obtaining directory information: Turbo Pascal and Microsoft C 3.0 don't either. Doing a Print-Screen from a BASIC program is much easier. In fact, the BASIC 3.0 manual shows two methods for doing it in Appendix B on assembly language subroutines. The BIOS Print Screen routine is invoked by an Interrupt 5, which in assembly language takes just two bytes: &HCD and &H05. An assembly language routine in BASIC needs only one more byte, an &HCB, which is a far return back to the main BASIC program. You can determine the bytes needed for an assembly language routine by going into DEBUG assemble mode with the A command and typing: INT 5 RETF and then listing the bytes with the D command. To get these bytes into a BASIC listing, first DIMension an integer array and store the assembly language code in it: 10 DIM APRTSC%(2) 20 APRTSC%(1)=&H05CD 30 APRTSC%(2)=&H90CB Each integer can store 2 bytes. (Note that the integers are stored in reverse order, with the most significant byte higher in memory.) The extra &H90 at the end is a NOP instruction, which does nothing. After the array is set up, use the VARPTR function to find the address of the array in memory and store it in another variable: 40 PRTSC%=VARPTR(APRTSC%(1)) Now you need only CALL that variable: 50 CALL PRTSC% and your screen will be printed. ----------------------------------------------------------------- IBM PC One-Liner (COMPUTE! December 1986 by Paul W. Carlson) Programs don't have to be long to be useful. The one-line program below can be used to entertain pre-schoolers, while teaching them the alphabet and the computer's keyboard at the same time. Adults can benefit from the program by gaining an increased understanding of how characters are produced on the IBM's video display. However it's used, the program is certainly worth the time it takes to type in the single line. When you run it, nothing seems to happen. Now press any key on the keyboard. A giant character matching the key you pressed rises from the bottom of the screen and centers itself on the display. Press another key. The same thing happens and continues happening until you press Ctrl-Break. The program enlarges all the letters (uppercase and lowercase), symbols, and punctuation that appears on the keyboard -- plus a few that don't appear on the keycaps. If you press Ctrl along with a letter or number key, you'll see one of the IBM's graphics characters. Even the function keys produce results. Try pressing F1. Despite its small size, the program contains some techniques you may find useful in other programs. 1. The program begins by setting the segment to location $FFA6. This is 14 bytes before the PEL (character definition) map in ROM. The program reads your keystroke and converts it to the corresponding ASCII code. 2. Then the program loops through eight bytes for the character, with byte zero (the first byte) containing the bits for the top row of the character and byte seven containing those for the bottom row. It stores the value of the PEL map in the variable A and initializes A$ as a null string. 3. We begin to loop through the eight bits of the current byte, resting the rightmost bit first. The variable M equals one if the bit is set and equals zero if it's not. The value of the variable W equals 32 (the ASCII code for a blank space) if the bit is not set and equals 219 (the ASCII code for a solid block) if the bit is set. Either three blanks or three solid blocks are added to the beginning of A$. Then the program shifts all bits in the current byte by subtracting the value of the rightmost bit and dividing by two. This step is repeated for each bit of the current byte. 4. The next step is to print A$ preceded by 28 blanks. This is done twice to double the character's height. Then the program branches back to step 2 and repeats the process for each byte of the PEL map that defines the character. After the last byte of the PEL map has been processed, we print two blank lines and return to step 1 to wait for another keypress. Experienced programmers may notice that the code could be made even more compact. Some statements could be combined, but this would result in parentheses several levels deep, making the program more difficult to understand. 1 KEY OFF:DEF SEG=&HFFA6:L$=INPUT$(1):N=ASC(L$):CLS:LOCATE 24,1,0:FOR L=0 TO 7:A=PEEK(N*8+L+14):A$="":FOR J=0 TO 7:M=A AND 1:W=32+M*187:A$ =CHR$(W)+CHR$(W)+CHR$(W)+A$:A=(A-M)/2:NEXT J:PRINT SPC(28)A$:PRINT SPC(28)A$:NEXT L:PRINT:PRINT:GOTO 1 (NOTE: To save and run this one-line program, copy it out and make it one continuous line by removing the carriage returns from the ends of the lines.) ----------------------------------------------------------------- Faster Slide Show (PC Magazine Vol 5 No 21 Dec 9, 1986 User-to-User) This technique produces a smooth BASIC slide show program. The BASIC BLOADer normally reads a graphics screen off a disk directly to the screen buffer. This approach gives a very slow (7.5-second) and messy screen update. However, using a combination of the PUT statement and VARPTR function with BLOAD creates a fast and smooth slide show program. Use these statements to create screen data files: 100 DIM A(4000) . . (Screen-creating code) . 200 GET (0,0)-(319,199),A 210 DEF SEG 220 ADDRESS=VARPTR(A(0)) 230 BSAVE "SCR1.",ADDRESS,16004 Then use the following statements to display the graphics screens: 100 DIM A(4000) 110 ADDRESS=VARPTR(A(0)) 120 BLOAD "SCR1.",ADDRESS 130 PUT (0,0),A,PSET This method updates the screen in less than a second. It still takes about 7.5 seconds to BLOAD the graphics information into memory, but you can display one screen while the next one loads. The full technique is implemented in DEMOLOAD.BAS: 100 'DEMOLOAD.BAS 110 DEF FNS$(Z)=MID$(STR$(Z),2) 120 SCREEN 1:CLS:KEY OFF:DIM A(4000) 130 PRINT "First we'll make 3 screens" 140 PRINT "(Hit any key to continue)" 150 WHILE INKEY$="":WEND:CLS 160 FOR B=1 TO 3:FOR C=1 TO 23 170 PRINT STRING$(40,48+B);:NEXT 180 CIRCLE (160,92),60,B,,,4/5 190 PAINT (160,92),B 200 GET (0,0)-(319,199),A 210 DEF SEG:ADDRESS=VARPTR(A(0)) 220 BSAVE "SCR"+FNS$(B),ADDRESS,16004 230 CLS:NEXT 240 ' 250 PRINT "Now we'll quick-load them" 260 PRINT "(Hit any key to continue)" 270 WHILE INKEY$="":WEND 280 ADDRESS=VARPTR(A(0)) 290 BLOAD "SCR1",ADDRESS 300 PUT (0,0),A,PSET 310 BLOAD "SCR2",ADDRESS 320 PUT (0,0),A,PSET 330 BLOAD "SCR3",ADDRESS 340 PUT (0,0),A,PSET ----------------------------------------------------------------- BASICA 3.2 Bug (PC Magazine Vol 5 No 22 Dec 23, 1986 User-to-User) DOS 3.2 BASICA.COM does not save files efficiently. If a file's size is reduced, the space allocated is fixed at the largest previously saved size. This is not true for all other IBM (Microsoft) BASICs, including DOS 3.2 BASIC.COM. The solution is to set up a macro (based on the filename being imbedded in the program at a fixed location) to first delete the current file before the updated version is saved. SmartKey is a big help here. Editor's Note: Another solution is to add two lines to the beginning of your program: 101 KILL"filename 102 SAVE"filename (substituting the actual name of your BASICA program for filename). Each time you run it, this code will delete the existing version and save the subsequent one. Actually, when you chop a large program down to a smaller one, BASICA 3.2 not only keeps the file size the same but actually seems to retain the discarded part of the program. To see this in action (assuming you're using BASIC 3.2 only), run LONG.BAS to create a 10K file: 100 'CREATE.BAS -- creates a long file 110 DEF FNST$(Z)=MID$(STR$(Z),2+(Z<0)) 120 OPEN "LONG.BAS" FOR OUTPUT AS #1 130 FOR A=100 TO 5000 STEP 10 140 PRINT #1,FNST$(A);" REM DUMMY TEXT" 150 NEXT:CLOSE:END Get into DOS and note the file size. Then get into BASICA 3.2, load LONG.BAS, and issue the command to get rid of most of the file's contents: DELETE 200- Save the shorter file, and get into DOS and you'll see that the tiny file has the same size as the older, larger one. Then get into DEBUG, and start leaning on the D key and you'll see that the part of the file you deleted is still there.