BASIC Parms (PC World Star-Dot-Star Vol 1 No 8 November 1983) BASPARM.BAS shows how a Compiled BASIC program can access DOS command line parameters. Using this technique, a compiled program can pick up the parameters on the DOS command and take action. For example, if you type PROGA B:DATAFILE.TXT;NEW as a command or in a batch file, this routine will place in the variable PARM$ the string B:DATAFILE.TXT;NEW. You can then strip away any blanks and separate the individual parts of the parameter string as needed by the particular program. In this example, the program PROGA may have been written to open a file and to add data to it. The program expects to open the file as an append file unless the NEW part was specified on the command, in which case the file would be opened as an output file. The comments in the BASPARM.BAS provide detailed documentation of what the routine does. Note the warning that the routine works in a compiled program only and that it should not be altered unless you are very careful not to cause any string variables to be generated. The best way to use this routine is to place it at the beginning of the program. - - - - - 10 'BASPARM.BAS: Routine to determine parameters on DOS 20 'Calling sequence from BASIC: (compiler only) 30 'It is very important not to alter the sequence of these 40 'instructions or insert any code without full knowledge of 50 'the interface structure 60 DEF SEG 'Point data segment register to BASIC segment 70 DIM SUBR%(3) 'Array to contain machine subroutine 80 DEF USRO=VARPTR(SUBR%(0)) 'Get subroutine's segment offset 90 SUBR%(0)=&H5B59 'POP CX POP BX 100 SUBR%(1)=&H5153 'PUSH BX PUSH CX 110 SUBR%(2)=&HEB83 'SUB BX,10H 120 SUBR%(3)=&HCB10 'RETF 130 I%=0 'Dummy parameter to function call 140 PSP%=USRO(I%) 'Get program segment prefix's segment 150 DEF SEG=PSP% 'Set Base register at that segment 160 PARMLEN%=PEEK(&H80) 'Get command parameter length 170 PARM$="" 'Set up string to receive parameters 180 FOR I% = 1 TO PARMLEN% 'Loop through parameter string 190 PARM$=PARM$+CHR$(PEEK(&H80+I%)) '& concatenate characters 200 NEXT I% 'together until end of string 210 DEF SEG 'Return to normal data string 300 FIL$=MID$(PARM$,2) ---------------------------------------------------------------- BASIC Tips (PC World Star-Dot-Star Vol No 8 November 1983) You don't need to put a closing double quote around file names that are the only parameter of a command; LOAD "FILESPEC is just as acceptable as LOAD "FILESPEC". You can use square brackets to dimension and index arrays. This can make statements that have many parentheses easier to read: DIM A[20] OR A[1,2] works just fine. You don't need to put a comma, semicolon, blank, or concatenation symbol between items that constitute a parameter list for PRINT. For example, PRINT "STRING"STRING$(10,40)A$ is an acceptable entry. If you are using a color monitor, you can work in DOS in color by typing the following in BASICA: COLOR n : CLS : SYSTEM in which "n" is the number of the color you want. If you are using Boolean data and need all 16 bits that can be stored in a 2-byte integer, write your data in hexadecimal or octal. This avoids the necessity of computing the two's complement of your constant to turn on the high bit (two's complement notation is the way negative numbers are stored). For example, PRINT &H0FHOF OR &HFOFO. You don't need to put the number sign (#) in front of file numbers for file statements such as CLOSE, FIELD, GET, etc.; GET 1,10 is the same as GET #1,10. You can't put a comment at the end of a DATA statement -- DATA 10,20,30 'comment won't work. This flaw can be overcome by starting a new statement: DATA 10,20,30:'comment. ----------------------------------------------------------------- BASIC Variable Checker (PC World Star-Dot-Star Vol 2 No 4 April 1984) It is often necessary to examine the current values of variables while you are trying to debug a BASIC program. The listing VARCHKR.BAS is a BASIC subroutine that displays the scalar variables from a BASIC program. It was developed as a general debugging aid for use on any program in place of inserting the ad hoc PRINT statements usually used to help find errors. When called, the subroutine will list all the scalar (not dimen-sioned) variables and their current values. Variable type symbols (%,!,#,$) are included in the listing regardless of how the variable names appear in the main program so that no confusion exists between variables of different types with the same letter names. The line numbers are high so as not to interfere with the line numbers in your program. Also, all the subroutine's variable names contain a period (.), minimizing the chances of duplicating a variable name in your program. The subroutine's variables will not be listed. If your program uses any of this subroutine's variable names, change those names in your program to keep the subroutine from changing a program variable when it is called. After the subroutine has been entered and SAVEd (with the ASCII option) as VARCHKR, use it as follows. With your BASIC program loaded in memory, issue the command MERGE B:VARCHKR (assuming you have the subroutine on the disk in drive B) and insert GOSUB 60000 in your program wherever you want a dump. 64000 P.%=PEEK(858)-6:Q.%=PEEK(859) 64010 A.%=PEEK(856)+256*PEEK(857) 64020 T.%=PEEK(A.%):E.%=PEEK(A.%+3):L.%=T.%+E.%+3 64030 N.%$=CHR$(PEEK(A.%+1)):IF PEEK(A.%+2)>.0 THEN N.$=N.$+CHR$ (PEEK(A.%+2)) 64040 IF E.%>0 THEN FOR K.%=1 TO E.%:N.$=n.$+CHR$(PEEK (A.%+3+K.%)MOD 128):NEXT 64050 IF T.%=3 THEN 64120 64060 V.$="":FOR K.%=1 TO T.%:V.$=CHR$(PEEK(A.%+L.%-K.%+1)) +V.$:NEXT 64070 ON T.%/2 GOTO 64080,64090,64180,64100 64080 V.!=CVI(V.$):T.$="%":GOTO 64110 64090 V.!=CVS(V.$):T.$="!":GOTO 64110 64100 V.!=CVD(V.$):T.$="#" 64110 PRINT N.$;T.$;"=";V.!:GOTO 64150 64120 B.!=PEEK(A.%+L.%-1)+256*PEEK(A.%+L.%) 64130 T.$="":FOR K.%=1 TO PEEK(A.%+L.%-2):T.$+CHR$(PEEK (B.!+K.%-1)):NEXT 64140 PRINT N.$"$=";CHR$(34);T.$;CHR$(34) 64150 IF A.%+L.%s while the second shows just one <100> - The second method allows the addition of statements on the same line without any tedious IF ...THEN ...ELSE baggage, i.e.: 10 WHILE A$="":A$=INKEY$:WEND:LOCATE X,Y:PRINT A$ rather than 10 A$=INKEY$:IF A$="" THEN 10 ELSE LOCATE X,Y Make sure the value of the variable you use in the WHILE ...WEND loop is null by inserting an A$="" before you use it. INKEY$ is really designed to allow program flow to continue unimpeded if no key is pressed. If you're looping through some tedious math that can be interrupted if necessary, or running a loop to put repeated graphics images on the screen, INKEY$ can allow the program to continue running until a key is struck. But INPUT$(1) can sit and wait for a one-character input such as a menu selection of a Y or N. INKEY$ has to be used when the user might hit a key that generates an extended code (a two-character code where the first chracter is a null, or CHR$(0)). For instance, run the following programs and hit the cursor keys as inputs. INPUT$(1) grabs only the first character, the null, while INKEY$ reads in the whole code. 100 A$=INKEY$:IF A$="" THEN 100 110 PRINT "**";A$;"**" 120 GOTO 100 100 A$=INPUT$(1) 110 PRINT "**";A$;"**" 120 GOTO 100 In the following case, the WHILE ...WEND method just spins the first value that you enter down the left edge of the screen unless you first take the trouble to null it: 100 A$="" 110 WHILE A$="":A$=INKEY$:WEND 120 PRINT "**";A$;"**" 130 GOTO 100 ----------------------------------------------------------------- BASIC Debugging Aid (PC Magazine Vol 3 No 24 Dec 11, 1984 User-to-User) DEBUGGER.BAS is a program that shows you the error number and the line in which the error occurred. It will automatically set up the troublesome line to edit. 100 'DEBUGGER.BAS 110 ON ERROR GOTO 140 120 OPEN "e:test" FOR OUTPUT AS #1 130 END 140 RPINT "Error number";ERR;"in statement";ERL 150 EDIT . Editor's Note: The BASIC interpreter is good about telling you what error it encounters and which statements were involved. It is useful for programs with short lines; however, for long, complex program statements, having it say there's a syntax error somewhere within the densely packed 254 characters doesn't help much. The trick of following the word EDIT with a period is valuable. If you're going to include this in the program, however, the interpreter won't tell you what the error is. However, by adding two lines: 101 KEY 9,CHR$(31)+"RUN 10000"+CHR$(13) 10000 ERROR ERR you can have BASIC describe the error to you. After you run the original program and choke on an error, simply hit the F9 key and these new lines will spell out what the error is. As a matter of fact, you can even do this in direct mode -- just get into BASIC, type the word ERROR followed by a low integer, and then hit the Enter key. ----------------------------------------------------------------- My Favorite Stripper (PC Magazine Vol 4 No 5 Mar 5, 1985 User-to-User) NUMSTRIP.BAS simply removes the spaces on either side of a number. BASIC prints a space to the left of a number to use for a negative sign, when appropriate. The DEF FNST$(Z) defined function will do away with this space, so it really doubles as an absolute value function (except that FNST$(Z) turns numbers into strings while ABS(Z) returns numerical values). BASIC also prints a space to the right of a number, and DEF FNST$(Z) gets rid of this as well. It's much cleaner and easier when mixing text and numbers to treat everything like strings, and this makes it simple. Otherwise, BASIC pads every positive number with an extra space. - - - - - 100 'NUMSTRIP.BAS (not for negative numbers) 110 DEF FNST$(Z)=RIGHT$(STR$(Z),LEN(STR$(Z))-1) 120 INPUT "Enter a number: ",N 130 PRINT "***";N;"***" 140 PRINT "***";FNST$(N);"***" ----------------------------------------------------------------- Better Stripper (PC Magazine Vol 4 No 13 June 25, 1985 User-to-User) Since negative numbers occur just as often as positive numbers, "My Favorite Stripper" (PC Mag Vol 4 No 5) could use a little enhancement. First, add the little-used SGN() function, which returns a -1 if the number is negative and a +1 if the number is positive. This will work for all positive and negative numbers, but not zero. However, we can check to see if the number is zero, within the formula, by using another trick. If Z=0, that part of the formula evaluates to -1, which will in turn remove the leading space. - - - - - Stripper Enhancement that works for negative numbers: DEF FNST$(Z)=RIGHT$(STR$(Z),LEN(STR$(Z))-SGN(Z)) Stripper that works with both negatives and zero: DEF FNST$(Z)=RIGHT$(STR$(Z),LEN(STR$(Z))-SGN(Z)+(Z=0)) ----------------------------------------------------------------- Ultimate Stripper (PC Magazine Vol 4 No 23 Nov 12, 1985 User-to-User) There is an even better way using a feature of the MID$ function to strip leading blanks. If the third argument is omitted, MID$ returns all characters from the position indicated by the second argument to the end of the string, which means you don't have to use either the LEN or SGN function. The FNST$(Z) stripper below works because the expression 2+(Z < 0) evaluates to 2 if Z is 0 or greater and 1 if Z is negative. Thus, MID$ will strip the leading blank from STR$(Z) only if Z is not negative. - - - - - 100 'BSTSTRIP.BAS 110 DEF FNST$(Z)=MID$(STR$(Z),2+(Z<0)) 120 FOR A=-1 TO 1 STEP .5 130 PRINT "OLD: ***";A;"***";TAB(20); 140 PRINT "NEW: ###";FNST$(A);"###" 150 NEXT ----------------------------------------------------------------- A Better INKEY$ (PC Magazine Vol 4 No 14 July 9, 1985 User-to-User) It's possible to access extended functions (cursor keys, Ctrl key combinations, etc.) through the BASIC INKEY$ system variable. This takes more understanding of the mechanics of INKEY$ than IBM is willing to describe in its BASIC or Technical Reference manuals, however. In addition, INKEY$ distinguishes between uppercase and lowercase letters, something that occasionally forces the user to look for both in IF ...THEN filters. Programmers can use the machine-code subroutine in the INKEYNEW.BAS program instead of INKEY$. There are lots of ways to do this, but the one illustrated in INKEYNEW.BAS POKEs values out of DATA statements directly into memory (in this case into a protected area high in the default segment for BASIC). When CALLed, the subroutine returns the ASCII code of the key pressed in the variable ASCI and the extended code in the variable CODE. Notice that the CODE variable is the same for each key whether or not the uppercase or lowercase version of it was typed. This makes for much more direct manipulation of key inputs than INKEY$ and at a cost of only 29 bytes. Editor's Note: This technique makes it easy to use such keys as the Function keys, cursor arrows, Del, Ins, Home, and so forth for program control. And, it makes it a snap to pose "Yes/No" or "Choose A, B, or C" questions without having to use the four or five popular tricks to make the response insensitive to the case of the letter entered. - - - - - 100 'INKEYNEW.BAS 110 DATA 55,89,E5,8B,7E,6,8B,76,8,31 120 DATA C0,31,DB,CD,16,88,E3,30,E4 130 DATA 89,5,89,F7,89,1D,5D,CA,4,0 140 CLEAR,&HEFFF:DEFINT A-Z:INKE=&HF000 150 FOR X=1 TO 29 160 READ A$:A=VAL("&H"+A$):POKE X+&HEFFF,A 170 NEXT X 180 PRINT "Now start hitting keys....." 190 PRINT "CODE","ASCI";SPC(6);"CHR$(ASCI)" 200 CALL INKE (CODE,ASCI) 210 PRINT CODE,ASCI,CHR$(ASCI) 220 GOTO 200 ----------------------------------------------------------------- Better Delays (PC Magazine Vol 4 No 12 June 11, 1985 User-to-User) Custom programming for the IBM PC works nicely on the PC AT except for FOR ...NEXT timing loops to produce short delays. The same loop on the AT runs too fast for the desired effect. To solve this problem, use the following routine: 10 X = TIMER + 3.5 20 WHILE TIMER < X 30 WEND (substituting the number of seconds you want to delay for the 3.5 above). This works on all PC systems and is accurate to about a tenth of a second. Editor's Note: This solves one of the timing problems on the supercharged AT; the other is that it's more difficult now to synchronize music with routines that print characters on the AT's lightning quick screen. ----------------------------------------------------------------- Foolproof BASIC Delayer (PC Magazine Vol 4 No 16 Aug 6, 1985 User-to-User) PC Vol 4 No 12 (above) offered a way to create a time-delay loop. The BASIC manual mentions a trick using the SOUND statement that solves this problem for all PCs, XTs, ATs and compatibles in both interpretive and compiled versions. The demo program below illustrates how this works. Using the TIMER function would also do the job, except that this can't be handled by the BASIC 1.1 compiler, which is long overdue for some updates. This program produces a small speaker click which is annoying, perhaps, but it works on every PC configuration. 100 'DELAYER.BAS 110 INPUT "Enter the number of seconds to delay: ",N 120 TIME$ = "00" 130 SOUND 32767,(N * 18.2):SOUND 32767,1 140 PRINT "The delay lasted exactly ";TIME$ ----------------------------------------------------------------- Finding BASIC's Data Segment (PC Magazine Vol 4 No 16 Aug 6, 1985 PC Tutor) Take a BASIC program that uses about 8K of file space when saved. The program contains a 27-byte machine language program and is poked into supposedly reserved space just below the 64K upper boundary of the BASIC workspace. Additionally, a 16K screen display is BLOADed adjacent to the machine language, also below the 64K boundary. A CLEAR statement is used to reserve 17K of high memory for this purpose. However, when the display data is loaded, starting at absolute 48128 (segment &HBC0), everything dies. The program was altered to put the machine language program and display data just above the 64K boundary, starting at 65536 (&H1000). The program then works fine for about 50 minutes, after which the data and machine language programs get written over by new data. DOS 2.0 is used. Why can't the machine language routines be just about 64K? Why, for an 8K program with DOS and BASIC loaded, can't the routines be just below the 64K boundary? Editor's Response: You are confusing the space requirements of BASICA with the absolute addresses in the PC. There are always other programs in the PC, such as the operating system, that also take up address space. Suppose that the operating system takes up 45K. The BASIC interpreter takes up another 27K. In this case, the 64K BASIC program area would actually begin at address 72K (45 + 27) and would continue on through 136K (72 + 64). What you really need to know, then, is the actual starting address of the BASIC data segment that corresponds to the interpreted program and its storage. The simplest way to find this is with a PEEK instruction, as follows: 10 DEF SEG = 0 'Look at low memory 20 SEGMENT = PEEK(&H510) + 256 * PEEK(&H511) 30 'Now SEGMENT is the segment address of BASIC's data 40 'area (the program + data).