Maximum Function (PC Magazine Vol 5 No 1 January 14, 1986 User-to-User) BASIC lacks two very fundamental functions -- maximum and minimum. Instead of using repetitive IF ...THEN ...ELSE statements. Two user- defined functions can handle this (MINMAX.BAS). As an example, if you wanted to create a formula "Principal payment = Cash flow available - Interest" but had to be sure the principal payment could not be a negative and could not be greater than the loan opening balance, create the following line: PRIN=FNMIN((FNMAX(CF-INTRST),0),LOAN). 100 'MINMAX.BAS 110 DEF FNMAX(V1,V2)=INSTR(STR$(V1-V2),"-")*V2+(1-INSTR(STR$(V1-V2),"-"))*V1 120 DEF FNMIN(V1,V2)=INSTR(STR$(V1-V2),"-")*V1+(1-INSTR(STR$(V1-V2),"-"))*V2 130 A=10:B=40:X=FNMAX(A,B):PRINT"A=";A,"B=";B,"MAX(A,B)=";X 140 A=-20:B=10:X=FNMIN(A,B):PRINT"A=";A,"B=";B,"MIN(A,B)'";X ----------------------------------------------------------------- Faster Arrays (PC Magazine Vol 5 No 1 January 14, 1986 User-to-User) It can be necessary to load and save large arrays of numbers in BASIC programs that were stored as sequential files. Even with the AT's RAM drive and fast procesing speed, it takes forever to do this. The trick is to save the array information in binary format. First, you have to know the size of the array. This is determined by computing the product of each of the array's dimension plus 1. For example, if an array is defined by DIM ARRAY(250,20), the actual size of the array is calculated by SIZE=(250+1)*(20+1)=5271. Next, determine the length of memory used to store the array. The PC uses 2 bytes to store each element of an integer array, 4 bytes for each element of a single-precision (!) array, and 8 bytes for each element of a double-precision (#) array. Therefore, the amount of memory used is calculated by computing LENGTH=BYTES*SIZE where BYTES is 2, 4 or 8, depending on the array type, and SIZE is the figure computed earlier. Finally, know the array's starting address -- where it is located in memory. This is found by using the VARPTR function, with a statement ADDRESS=VARPTR(ARRAY(0,0)). It is very important to set the program variables before calculating the ADDRESS with VARPTR, since BASIC will move the location of an array whenever it encounters a new nondimemsioned variable. After running the BSAVER.BAS program, your array will be stored as a binary file. To reload the array from the binary file, run the accompanying BLOADER.BAS program. This will work on any precision numbers and arrays with any legal number of dimensions. Editor's Note: This technique does indeed save an enormous amount of time and disk space. We ran a 5,000-element integer test on a PC AT running at 8 MHz. The FILMAKER.BAS program used a 250 x 20 loop to create a 5,000-element sequential file (called SEQFILE) consisgint of the sequential integers from 1 to 5,000. It took 15 seconds to create the 38K sequential file. The BSAVER.BAS program loaded the SEQFILE sequential file in 13 seconds and wrote it back to disk as a 10K binary file called YOURFILE.EXT in just 1 second. The BLOADER.BAS program reloaded the 5,000-element array from the YOURFILE.EXT file in one second. To text this yourself, after running the third program, type PRINT ARRAY(1,1) and PRINT ARRAY(250,20). You should see 1 and 5000, respectively. This happens so fast that you might want to verify that the array was not lurking in memory the whole time; you can do this by entering CLEAR or going in and out of DOS between program steps. 100 'FILMAKER.BAS 110 DEFINT A-Z:TIME$="0" 120 OPEN "seqfile" FOR OUTPUT AS #1 130 FOR A=0 TO 249 140 FOR B=1 TO 20 150 PRINT #1,A*20+B 160 NEXT:NEXT:CLOSE 170 PRINT "Seq file time ";TIME$ 100 'BSAVER.BAS 110 DEFINT A-Z:TIME$="0" 120 D1=250:D2=20:ADDRESS=0 130 FILE$="yourfile.ext" 140 DIM ARRAY(D1,D2) 150 OPEN "I",#1,"SEQFILE" 160 FOR N=1 TO D1 170 FOR J=1 TO D2 180 INPUT #1,ARRAY(N,J) 190 NEXT:NEXT 200 PRINT "LOADing TIME ";TIME$ 210 TIME$="0" 220 SIZE=(D1+1)*(D2+1) 230 LENGTH=2*SIZE 240 ADDRESS=VARPTR(ARRAY(0,0)) 250 BSAVE FILE$,ADDRESS,LENGTH 260 PRINT "BSAVEing time ";TIME$ 100 'BLOADER.BAS 110 DEFINT A-Z:TIME$="0" 120 D1=250:D2=20:ADDRESS=0 130 FILE$="yourfile.ext" 140 DIM ARRAY(D1,D2) 150 ADDRESS=VARPTR(ARRAY(0,0)) 160 BLOAD FILE$,ADDRESS 170 PRINT "BLOADer time ";TIME$ ----------------------------------------------------------------- BASIC Vocabulary (COMPUTE! Magazine date unknown) You can write a "vocabulary" in BASIC so the computer understands dozens of words without using IF statements to check for every word. Create the vocabulary with arrays. Unlike a simple variable which equates to a single numeric value (A=1) or string of characters (A$="HELLO"), an array is a group of related data items. This short example creates a rudimentary vocabulary with string arrays. 100 DIM VB$(4),OB$(4):FOR J=1 TO 4:READ A$,B$ 110 VB$(J)=A$:OB$(J)=B$:NEXT J 120 DATE TAKE,GOLD,PUT,SWORD,EAT,FOOD,THROW,ROCK 130 SP=0:I$="":VB$="";OB$="":PRINT "YOUR COMMAND: "; 140 INPUT I$:FOR J=1 TO LEN(I$):T$=MID$(I$,J,1) 150 IF T$=CHR$(32) THEN SP=1:GOTO 180 160 IF SP=1 THEN OB$=OB$+T$ 170 IF SP=0 THEN VB$=VB$+T$ 180 NEXT J:VB=0:FOR J=1 TO 4 190 IF VB$=VB$(J) THEN VB=J 200 NEXT J 210 IF VB=0 THEN PRINT "DON'T UNDERSTAND ";VB$:GOTO 130 220 OB=0:FOR J=1 TO 4:IF OB$=OB$(J) THEN OB=J 230 NEXT J 240 IF OB=0 THEN PRINT "DON'T UNDERSTAND ";OB$:GOTO 130 250 PRINT "VERB #";VB;", OBJECT #";OB:GOTO 130 Lines 100-120 store the vocabulary in two string arrays. The array named VB$() holds four verb strings (TAKE,PUT,EAT and THROW) and the OB$() array holds four object strings (GOLD, SWORD, FOOD and ROCK). Once the arrays are set up, each word can be referenced by the index number that identifies its position within the array. For instance, in response to the statement PRINT V$(1), the computer prints TAKE. The statement IF A$=OB$(1) is true when A$ equals GOLD, and so on. Since the vocabulary has been reduced to reference numbers, you can cycle through the arrays with simple FOR-NEXT loops, testing whether your input words match anything in the vocabulary. This is far more efficient than using a multitude of IF statements. Lines 140-180 bring the input sentence into the computer as one string (I$) and break it into two parts: the verb string V$ and the object string O$. Note that simple (nonarray) variables like V$ and O$ are distinct from array variables like V$() and O$(). The program uses a primitive method to extract verb and object from the input sentence. It looks for the space character that separates the words, assigning every character before the space to V$, and everything after it to O$. Once the verb and object have been extracted, lines 180-200 compare the verb string V$ to every verb in the vocabulary array V$(). The variable V signifies the verb number. As soon as V$ matches up with a word in V$(), V records the V$() index number for future reference. If V$ isn't found in the vocabulary, line 210 prints the unknown word and permits another attempt. A similar loop in lines 220-230 compares the object string O$ to each word in the O$() array, and records the object number in the variable O. Using arrays makes your program far easier to modify. For instance, if you've written an adventure using dozens (or hundreds) of separate IF statements, and then decide to change one of the vocabulary words. It could take hours to locate and change every line that uses that word. If your vocabulary is stored in arrays, you can make the same change in seconds, by replacing the word in a DATA statement. To expand the vocabulary, just add more DATA items and increase the values in the DIM statement and FOR-NEXT loops accordingly. Of course, there's much more to writing a playable adventure. But arrays can help there, too. Use a room description array to store the description strings for each room, and a room connection array to show the connections between them. The location of each object can be stored in an object location array, and so on. You'll want a more sophisticated parsing routine, however, to pick apart the input sentence. ----------------------------------------------------------------- New BASIC Functions (PC World October 1985 Star-Dot-Star) The MIN function, which returns the lesser of two values supplied, and the MAX function, which returns the greater, are not available in BASIC(A). For example, if you set variable A to 3 and variable B to 7, executing the statement X=MIN(A,B) sets the value of X to 3. The statement X=MAX(A,B) would set X to 7. The two lines of BASIC shown below are BASIC statements to create user-defined functions for MIN and MAX. Due to the design of the PC's BASIC, the letters FN must precede the name of each function. Also, the lines must be executed before the functions are used, so it's a good idea to put them near the top of your program. 10 DEF FNMIN(A,B)=-B*(BA)-A*(A>B) MIN and MAX Revisions (PC World January 1986 Star-Dot-Star) Both functions in "New BASIC Functions" (above) return 0 if inputs are equal. Here are corrected versions: 10 DEF FNMIN(A,B)=A+(A-B)*(AB) FNMIN(A,B) returns the smaller value of A and B, and FNMAX(A,B) returns the larger value. ----------------------------------------------------------------- BASIC Conversions (PC Magazine Vol 5 No 3 Feb 11, 1986 PC Tutor) When you save a BASIC program, it is normally stored on disk in a tokenized format -- that is, all the keywords are replaced with 1-byte numbers to save space. You can save a BASIC program in ASCII format by following the filename in the SAVE command with a comma and an A. The ASCII files take up more space on disk, but you can use TYPE and PRINT on them. You can use redirection of standard output to make a file that the BASIC interpreter can read with redirection of standard input to convert all BASIC files to ASCII format. First, you create a small file called LOADSAVE.BAT: echo load "%1" >> convert echo save "%1",A >> convert The double angle bracket means append the text following the ECHO command to the file CONVERT. The "text" written into CONVERT will consist of just BASIC commands. Next, create a two-line batch file called MAKEFILE.BAT: del convert for %%x in (*.bas) do command /c loadsave %%x The DEL command gets rid of any existing CONVERT file so you start from scratch. When you run MAKEFILE.BAT on the DOS command level, the FOR command will execute LOADSAVE.BAT for every BASIC file on your disk (or hard disk subdirectory), passing to it a parameter with the name of the batch file. LOADSAVE.BAT will create a file called CONVERT that contains BASIC commands to load each file and save it with the ASCII option. After you run MAKEFILE.BAT to create CONVERT, all you have to do is execute the command: BASICA < CONVERT. BASICA will load all your BASIC files and save them in ASCII format. Then you can use the PRINT command. If you don't want to convert them to ASCII format but just want to print them all out, you can do something similar by creating the following two files. First, LOADLIST.BAT: echo load "%1" >> listall echo llist >> listall echo lprint chr$(12); >> listall Second, MAKEFILE.BAT: del listall for %%x in (*.bas) do command /c loadlist %%x Run MAKEFILE.BAT to create LISTALL and then execute: BASICA < LISTALL. Notice that a printer form feed using CHR$(12) is included so each BASIC file listing will start on a new page when printing a disk full of BASIC files. ----------------------------------------------------------------- Recurring Intruder (PC World February 1986 The Help Screen) Attempts to use the Epson FX-100 printers' "bit image mode" have failed. A small, twisted line appears at regular intervals within an image. These tiny squiggles are the result of the carriage return and line feed codes (ASCII 13 and 10) that BASIC sends to the printer after every 80 characters. (In bit image mode, a "character" is a vertical pattern one dot wide and up to eight dots high.) Although these printer control codes are necessary to print text properly in text mode, they are interpreted as graphics data when the printer is in bit image mode. Editor's solution: You may already be using the BASIC statements WIDTH "LPT1:",80 and WIDTH "LPT1:",132, thereby switching the printer's line width to accomodate 80- and 132-column paper. You can use any from 0 to 254 to specify the number of characters on a line; BASIC will insert a carriage return/line feed sequence after the requisite number of characters has been sent to the printer. But using a value of 255 disables line folding (inserting the CR/LF sequence). Before you send bit image mode data to the printer, add the statement WIDTH "LPT1:",255 and remember to reset the printer's line width before printer in text mode. ----------------------------------------------------------------- Dummy Numbers (PC Magazine Vol 5 No 6 Mar 25, 1986 User-to-User) One way to trace the flow of BASIC programs that contain lots of GOTO statements is to use a technique called backtracking. If lines 300, 500, and 700 all contain a GOTO 900, simply insert a line 899 that says: 899 ZZ=9:ON ZZ GOTO 300,500,700. ZZ can be a numbered junction point in a program flow chard, or it can be a dummy number. However, it must be larger than the number of program lines after the GOTO. (In this example, there were three line numbers after GOTO, so ZZ could have just as easily been equal to 4.) This trick lets you figure out which possible lines sent the program execution to that point. Editor's Note: This is a variation of an old trick to start off programs with a cascade of unused GOTOs with descriptive REM statements following them. For instance, you could identify the set up, main loop, etc. by beginning code with the lines: 100 GOTO 170 110 GOTO 160 'SETUP 120 GOTO 300 'MAIN LOOP 130 GOTO 350 'INPUT ROUTINE 140 GOTO 400 'ERROR TRAPS 150 GOTO 500 'PRINTER 160 'SETUP STARTS NEXT LINE 170 DEFINT A-Z:CLS The initial GOTO 170 in line 100 jumps the program execution around all the lines from 110 through 160, which contain remarks that label the various parts of the program and which will continue to refer to the proper lines even after renumbering. Renumbering can often cause devastating results if done too casually. One of the most dreaded messages is an "Undefined line 620 in 850" right after you've just removed 20 lines of code; after renumbering, these two line numbers could be anywhere. And if you don't catch the missing line, your program is destined for doom. The obvious solution is to save early and often, especially right before you start meddling with the code. Then if the renumbering process uncovers undefined lines, you can load the previous version and start again. A crafty solution that's been around is to type in two very high dummy RENUM numbers before you try the real thing. If you code is short, typing in RENUM 9999,9999 will uncover undefined lines before actually changing anything. This gives you a chance to correct the mistake before it's too late. If your program is larger, you might have to use RENUM 65000,65000. One advantage of the shorter technique is that you can put the whole RENUM 9999,9999 on a single function key -- to do this to F10, add a line at the beginning or the program: 10 KEY 10,"RENUM 9999,9999" Since BASIC will assign a maximum of 15 keystrokes to each function key and the new definition is exactly 15 characters long, you'll have to hit Enter after hitting F10. ----------------------------------------------------------------- Popping to DOS (PC World March 1986 Star-Dot-Star) POP2DOS.BAS lets users interrupt BASIC programs at any time to execute DOS commands. This technique works with BASICA v2.0 and v2.0 of the IBM BASIC Compiler. When you press F1 the routine saves and clears the screen and then loads DOS. You can execute any DOS command and even run other programs such as DISKCOPY and FORMAT. When you're done, type EXIT and press Enter to return to the program, restoring the screen and putting you back where you began. Because there is no easy way for the routine to determine the current screen mode, your program must set the variable SCRNFLAG% to note the current mode. Setting SCRNFLAG% to 0 indicates an 80-column text mode; set SCRNFLAG% to 1 just before working with medium- resolution graphics, and set it to 2 before any high-resolution graphics. Be sure to reset SCRNFLAG% to 0 when you reset the screen to text mode. When exiting to DOS, be sure that your programs have not left open any partially written files, lest the user decide to engage in disk operations that adversely affect the open files (for example, changing disks or deleting files). 100 DIM GRAPHARRAY%(8004) 110 SCRNFLAG%=0:ON KEY(1) GOSUB 25000:KEY(1) ON . . 25000 IF SCRNFLAG%<>1 THEN 25050 25010 GET (0,0)-(319,199),GRAPHARRAY% 25020 SCREEN 0,1,0,0:WIDTH 80:CLS:SHELL 25030 CLS:SCREEN 1,0:PUT (0,0),GRAPHARRAY%,PSET 25050 RETURN 25050 IF SCRNFLAG%<>2 THEN 25100 25060 GET (0,0)-(639,199),GRAPHARRAY% 25070 SCREEN 0,1,0,0:CLS:SHELL 25080 CLS:SCREEN 1,0:PUT (0,0),GRAPHARRAY%,PSET 25090 RETURN 25100 SCRNY%=CSRLIN:SCRNX%=POS(0) 25110 SCREEN 0,1,1,1:CLS:SHELL 25120 CLS:SCREEN 0,1,0,0:LOCATE SCRNY%,SCRNX% 25130 RETURN Window Wonder (PC World March 1986 Star-Dot-Star) UPDOWN.BAS shows how easily BASICA v2.0 can be made to print upside down. Line 140 of the program uses the GET statement to capture a part of the screen, line 150 uses the WINDOW statement to reverse the direction of the screen's y coordinate, and line 160 PUTs the captured image back onto the screen. Because the y coordinate has been transposed, the resulting image appears upside down. 100 'Upside down graphics 110 DIM IMAGE(100) 120 SCREEN 1,0:CLS:KEY OFF 130 LOCATE 12,15:PRINT "BASIC EXPERT" 140 GET (112,80)-(207,95),IMAGE 150 WINDOW (0,0)-(319,199( 160 PUT (112,96),IMAGE 170 A$=INPUT$(1) 180 END ----------------------------------------------------------------- BASIC Values (PC World April 1986 The Help Screen) Rounding errors occur in IBM BASIC. For example, if you type PRINT 9.4, BASIC prints 9.399999. It performs similar transformations on hundreds of numbers (8.4, 8.86, 9.02, 9.1 and 9.31 to list a few). This is because BASIC stores numbers as binaries (base 2). When a numeric variable is assigned a number as in A = 10, BASIC converts the ASCII character string 10 into a binary value, then stores that value in memory as a string of on and off bits representing 1s and 0s. When the value of a numeric variable is printed, BASIC converts the stored binary value into a string of decimal digits. For whole numbers, this presents no problem; every whole decimal number can be converted to a unique binary number consisting of a finite number of binary digits, and vice versa. For fractions, however, this translation creates problems. Take, for example, the value .1 in base 3. This value cannot be represented with a finite number of base 10 digits and is thus written as an approximation: .33 or .33333 or .33333333, etc., depending on the number of digits allowed. This idiosyncrasy persists with double-precision numbers, but because they are stored with a greater number of binary digits, conversion errors are less critical, particularly in money matters. Even double-precision numbers can ruin a programmer's day: Imagine your check-balancing program warning that your checkbook is out of whack when the program finds that the last balance plus the total value of deposits (say $9.40) doesn't equal the remaining balance plus the total value of withdrawals (say $9.399999). Programs that demand decimal accuracy rely on another method of numeric storage. That technique is called binary coded decimal (BCD); with it, every digit of a decimal number (or, in an alternate form of BCD, every two digits) is stored in a single byte. The BCD approach avoids both errors and approximations, but like double-precision numbers in BASIC, calculations are slow and storage requirements increase. You can use single-precision values for monetary dealings if you're willing to keep sums below $100,000.00 (or $10,000,000 is you can manage to skip the pennies). Just work in whole values and use PRINT USING "$$####.##";x/100 (or use PRINT USING "$$######";x) when you need to print. ----------------------------------------------------------------- Matching BASIC Functions (PC World April 1986 The Help Screen) The following code executes flawlessly: 10 INPUT A$ 20 A$ = CHR$(ASC(A$)-32) 30 PRINT A$ The respective uppercase character is printed when a lowercase character is entered. If the identical coding for line 20 is entered into a define function (DEF FN), as in: 10 DEF FNupper(LOW$) = CHR$(ASC(LOW$)-32) 20 INPUT A$ 30 A$ = FNupper(A$) 40 PRINT A$ a "Type mismatch in 30" error message occurs. The problem is that the string variable A$ and the (incorrect) numeric variable FNupper are the mismatched types in the code. Change line 10 to: DEF FNupper $(LOW$) = CHR$(ASC(LOW$)-32) and line 30 to: A$ = FNupper$(A$). These differ from the original lines only in that the dollar signs are inserted directly after the name of the string function and before the opening parenthesis. ----------------------------------------------------------------- Leaving Work Early (PC Magazine Vol 5 No 7 Apr 15, 1986 PC Tutor) It is possible to run a batch job at a set time each day. One way to do this is by using the statement: BASICA GOHOME.BAS as the first line in a batch file. The GOHOME.BAS program below simply loops until 17:30 (5:30 p.m.), at which point it exits to the system so the batch file can continue. Line 20 is not strictly needed. However, it serves the function of giving coworkers the impression that your PC is doing some important work and that you've stepped away from your desk for a moment. They will be less inclined to turn off your machine. 10 WHILE TIME$ < "17:30:00" 20 PRINT RND, 30 WEND 40 SYSTEM Free Stamps (PC Magazine Vol 5 No 8 Apr 29, 1986 User-to-User) STAMP.BAS automatically updates the date and time in its own REM statements each time you run it and then writes the new information to disk. The program first looks for the beginning of the program in memory by peeking at addresses &h30 and &h31, then searching memory for the { } brackets. Once it's found them, it pokes the appropriate bytes of date and time into memory and saves the changed program. The subroutine in lines 140 through 190 can be put into any program to add the current date and time automatically. If you do this, be sure to insert an initial {dddddddddd} and {tttttttt} near the beginning of the program. Afterward, each time you run the program the subroutine will log your use. The DOS directory listing will log the creation or change of a file, but not its normal running. 100 'STAMP.BAS: Auto Date-Time stamper 110 'Last Run Date {dddddddddd} 120 'Last Run Time {tttttttt} 130 GOSUB 140:END 140 DEF SEG:C=PEEK(&H30)+256*PEEK(&H31):L=10:A$=DATE$ 150 IF PEEK(C)=123 AND PEEK(C+L+1)=125 THEN 160 ELSE C=C1+1:GOTO 150 160 FOR I=1 TO L:POKE I+C,ASC(MID$(A$,I,1)):NEXT:C=C+12 170 IF L=10 THEN L=8:A$=TIME$:GOTO 150 180 SAVE "STAMP 190 RETURN ----------------------------------------------------------------- Scan Code Shortcut (PC Magazine Vol 5 No 10 May 27, 1986 User-to-User) Most programmers trap extended key combinations such as PgUp, Shift-F1, Ins or Ctrl-PrtSc with routines like: 100 I$=INKEY$ 110 IF I$="" THEN 100 120 IF LEN(I$)<2 THEN 100 130 IF ASC(MID$(I$,2))=82 THEN .... A simple solution relies on the MKI$ function to convert an integer into a 2-byte string. Recode lines 120 and 130 as: 120 IF I$=MKI$(&H5200) THEN .... to simplify the expression and pick out what is going on much faster. When INKEY$ returns a two-character ("extended") code, the first is always a null, or CHR$(0). In the &H5200 example above, the 00 at the end represents CHR$(0), while the &H52 is the hex representation of 82 (the Ins key). To use this technique, look up the decimal value of the key or key combination in the BASIC manual chart, convert it to hex, precede it with a "&H", and follow it with a pair of zeros. This results in BASIC code that is easier to read, easier for the machine to execute, and a lot less typing. Editor's Note: If you're testing many keys with extended codes, you can do a single check for the initial null and use a long INSTR string to identify all the keys quickly. SCANNER.BAS is an example: 100 'SCANNER.BAS 110 PRINT "Hit cursor pad keys ..."; 120 PRINT "Or hit Esc to quit." 130 FOR A=1 TO 10:READ A$:B$(A)=A$:NEXT 140 I$=INKEY$:IF I$="" THEN 140 150 IF I$=CHR$(27) THEN END 160 IF LEN(I$)<2 THEN BEEP:GOTO 140 170 S=INSTR("GHIKRMOPQS",RIGHT$(I$,1)) 180 PRINT B$(S);" key":GOTO 140 190 DATA Home,CsrUp,PgUp,CsrLeft,Ins 200 DATA CsrRight,End,CsrDown,PgDn,Del ----------------------------------------------------------------- .EXE to .COM Conversion (PC Magazine Vol 5 No 10 May 27, 1986 PC Tutor) Compiled BASIC programs in .EXE format cannot be converted to a .COM format. This is not really a problem, however, since the .EXE format files are directly executable by DOS without conversion. Only .EXE files that have been specially prepared (generally in assembly language) can successfully be turned into .COM files. These progarms must not contain a stack segment, they must have no references to relocatable segments, and they must begin execution at offset 100h in the file. Since an executable .EXE file must have a stack segment and generally uses separate code and data segments, the two formats are essentially incompatible. The .COM format has a few advantages over the .EXE format, notably a smaller size and slightly easier assembly language programming for small programs. However, .COM files are limited to about 65,000 bytes. (The .COM fornat was adapted from the CP/M operating system, which had only a 64K address space.) Don't be too eager to convert your programs to .COM files. The COM format is an endangered species. The mix of code and data in the same segment and the calculation of the segment address outside the code segment are two of the major blocks that limit PC programs to one megabyte of addressable memory and prevent them from running under the PC AT 80286 protected mode. If a future version of PC-DOS supports the 80286 protected mode, it will probably not support the .COM file format at the same time. ----------------------------------------------------------------- Different BASICs (PC Magazine Vol 5 No 10 May 27, 1986 PC Tutor) The PC-DOS BASIC interpreter for the IBM PC and the MS-DOS BASIC interpreter for IBM-compatibles both come from Microsoft and function similarly. But structurally they are quite different. In general, techniques that access specific memory locations within the interpreter (as does the "unprotect" scheme) can not be used on both versions of BASIC. The IBM PC has an entire self-contained (but limited) BASIC interpreter coded in a ROM inside the PC. For the very early PCs, IBM included this under the assumption that lots of people might be using the PC without disk drives and storing BASIC programs to cassette tape. With marketing assumptions like this, no wonder IBM was surprised at how the PC took off! Even on the PC AT, however, ROM BASIC is still included with the machine. The PC-DOS versions of BASIC and BASICA uses routines in the ROM BASIC module. In fact, the PC-DOS BASIC and BASICA interpreters are sometimes referred to as "extensions" of ROM BASIC. This makes the BASIC.COM and BASICA.COM files shorter, but it limits their use to the IBM PC. IBM-compatible machines cannot include the IBM ROM version of BASIC because it is proprietary. Versions of MS-DOS for compatibles generally come with a version of BASIC called GWBASIC but may also include files named BASIC and BASICA, which are identical copies of the GWBASIC interpreter. (On the PC, BASIC and BASICA are different.) The "GW" of GWBASIC is either the reversed initials of Microsoft Chairman William Gates (better known as Bill Gates), or it stands for "Gee Whiz." Since PC-DOS runs on many compatibles, many IBM-compatible users have switched to PC-DOS. The only real problem is that the PC-DOS BASIC interpreters won't work, so they have to use the GWBASIC included with their compatibles' MS-DOS. Microsoft does not sell DOS to the general public (instead it licenses it to companies making MS-DOS computers), but PC-DOS is available at any IBM Product Center and other computer stores. For the PC-compatible owners running PC-DOS, Microsoft sells separately a version of GWBASIC that does not need the PC ROM BASIC. ----------------------------------------------------------------- IBM BASIC Directory (COMPUTE! Magazine June 1986) There are two ways to read and display the disk directory on an IBM PC from with a BASIC program. The first is simply to print the directory to the screen at the appropriate time in your BASIC program. This routine asks you from which drive A: or B:) you want to read the directory. If you have a single-drive system (drive A: only), remove the REM from line 100. 100 REM FSPEC$="A:*.*":GOTO 140 110 PRINT:PRINT "Select drive: (";:COLOR 16,15:PRINT"A B";:COLOR 7,0: PRINT CHR$(29)CHR$(29)"/"CHR$(28)")" 120 DRIVE$=INKEY$+":":A=ASC(DRIVE$):IF (A OR 32)<97 OR (A OR 32)>98 THEN 120 130 DRIVE$=CHR$(A AND 223)+":":FSPEC$=DRIVE$+"*.*" 140 ON ERROR GOTO 150:FILES FSPEC$:ON ERROR GOTO 0:END 150 BEEP:COLOR 31:CLS:PRINT "Cannot read directory":COLOR 7:ON ERROR GOTO 0:END The second method reads the directory into a string array for use by your program at some later point. This routine reads the filenames from the disk directory into an array named F$. One advantage of this method is that you need to look only once at the directory. Once the directory information is stored in a string, you can extract the file- names whenever it's convenient and print them in any format you like. With a little more programming, you could cursor through the directory to access a particular file, sort the directory entries alphabetically, catalog all your disks, or whatever. Remove the REM from line 1000 if you have a single-drive system. 1000 REM FSPEC$="A:*.*":GOTO 1040 1010 PRINT:PRINT "Select drive: (";:COLOR 16,15:PRINT"A B";:COLOR 7,0:PRINT CHR$929)CHR$(29)"/"CHR$(28)")" 1020 DRIVE$=INKEY$+":":A=ASC(DRIVE$):IF (A OR 32)<97 OR (A OR 32)>98 THEN 1020 1030 DRIVE$=CHR$(A AND 223)+":":FSPEC$=DRIVE$+"*.*" 1040 DEF SEG=0:WIDTH 80 1050 HEAD=1050:TAIL=1052:BUFFER=1054 1060 CLS:COLOR 23,0,0:PRINT"Reading disk directory" 1070 COLOR 0:ON ERROR GOTO 1090 1080 FILES FSPEC$:ON ERROR GOTO 1100 1090 BEEP:COLOR 31:CLS:PRINT "Cannot read directory":COLOR 7:ON ERROR GOTO 0:END 1100 DIM TT$(24):LOCATE 3,1:COLOR 7:ROWS=0 1110 POKE HEAD,30:POKE TAIL,34:POKE BUFFER,0:POKE BUFFER+1,79:POKE BUFFER+2,13:POKE BUFFER+3,28:'Put code for End, Enter into keyboard 1120 LINE INPUT TT$(ROWS):IF TT$(ROWS)<>"" THEN ROWS=ROWS+1:GOTO 1110 1130 IF NOT DIMMED THEN DIM F$(ROWS*4-1):DIMMED=1 1140 ROWS=ROWS-1:FOR I=0 TO ROWS:FOR J=0 TO 3 1150 T$=MID$(TT$(I),J*18+1,12) 1160 IF T$<>"" THEN F$(ENTRIES)=T$:ENTRIES=ENTRIES+1 1170 NEXT J:NEXT I:ERASE TT$:ENTRIES=ENTRIES-1:DEF SEG:RETURN ----------------------------------------------------------------- Screen Pages in BASIC (PC Magazine Vol 5 No 11 June 10, 1986 PC Tutor) When using the color/graphics adapter in 80-column text mode, you can store four video pages. (In 40-column text mode, you can store eight pages.) Only one page is visible at a time, but you can write to the other three (or seven) pages and then quickly flip to them later. The SCREEN command is used to select both which video page you want to write to and which page you want to have displayed. SCREEN takes four parameters. In text mode, the first parameter is a 0. The second parameter is a 1 to enable color. (You'll probably use 0 to disable color only if you use a black-and-white display driven through the composite output jack on the display adapter.) The third parameter selects which of the four (or eight) pages subsequent screen output statements will write to. The fourth parameter determines which of these four (or eight) pages is displayed. The pages are numbered 0, 1, 2, and 3 (or up to 7 with a 40-column display). The short BASIC program below demonstrates this. Lines 10 and 20 write one line on each of the four pages while continuing to display page 0. Line 40 writes another line on page 0. By pressing the 0, 1, 2, or 3 keys, you can see the other three pages. Note that BASIC does not maintain a different cursor for each of the pages. Each of the text lines in this demonstration program is spaced a line lower, just as if they were all written to the same page. You have to handle the cursor problem yourself when using video pages. You can get the current cursor position with the POS and CSRLIN functions and set it with LOCATE. Graphics present a whole different story. The color/graphics adapter has only enough memory for one video page when using either the 320-dot by 200-line four-color graphics mode or the 640-dot by 200-line two-color graphics mode. BASIC has various built-in commands to work with graphics and to perform some crude animation, so you might consider programming a simulated operation of your products rather than flip different images to the screen. The BASIC command BSAVE and BLOAD can, respectively, save a graphics screen image to a disk file and retrieve it, but the speed of these commands, even in a compiled BASIC program, will probably be unsatisfactory. (IBM's Enhanced Graphics Adapter can store multiple graphics pages, but BASIC does not yet support it.) One compromise that may prove easier to use may be "Character Graphics." In character mode, you have access to a variety of lines, blocks, and other graphics symbols that can often be successfully combined to make effective and easily manipulated 16-color drawings. The available graphics symbols are shown in Appendix G of the BASIC 2.0 manual and Appendix D of the BASIC 3.0 manual. For an example of the variety of images that you can make using these characters, take a look at IBM's monochrome display version of the "Introduction to the IBM PC" disk. ----------------------------------------------------------------- Run BASIC Programs From DOS (PC World The Help Screen June 1986) This procedure simplifies the procedure for running a BASIC program without having to learn how to load the BASIC interpreter and then loading and running a particular BASIC program. Create RUN.BAT (below), place the program disk in drive B:, insert the disk containing BASIC and RUN.BAT in drive A:, and type A:RUN progname, where progname is the name of the desired BASIC program in drive B:. If RUN is entered without a program name, RUN.BAT will display a list of the BASIC programs in drive B:. echo off cls a: if x==%1x goto noname basic b:%1 goto end :noname echo BASIC programs in drive B: are dir b:*.bas/w/p :end ----------------------------------------------------------------- BASIC Unprotect (PC World June 1986 Star-Dot-Star) BASIC files saved with the /P option can be unprotected with the 2-byte program UNPROT.BAS. Because of its brevity, loading UNPROT.BAS resets the flag that indicates a program's protection status without overwriting the program already in memory. UNPROT.BAS also works with programs that have been accidentally erased by the NEW command. To use UNPROT.BAS, load the protected program first, then load UNPROT.BAS, and your program can be listed, edited and saved. A>debug.com -e 100 ff 1a -n unprot.bas -r cx CX 0000 :2 -w Writing 0002 bytes -q