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).