[comp.sys.apple] Apple Tech Note: Basic System

ranger@ecsvax.UUCP (05/07/87)

Apple II ProD TN #6: BASIC.SYSTEM External Commands
 
Written:                                                1/85 
Modified by:                Pete McDonald               12/85                
 
BASIC.SYSTEM has a facility that allows a user to attach his own commands to 
the existing commands that BASIC.SYSTEM implements.  Once you attach a command,
your command will be treated as if it was one of the BASIC.SYSTEM commands 
(with its own commands having preference).  Therefore the command will execute 
in immediate and deferred modes as does the CATALOG command.  In immediate mode
all you must do to execute the command is type it in.  In deferred mode (eg. 
from within a program), to access the command you must preface the command with
a PRINT CHR$(4).  
 
Whenever BASIC.SYSTEM receives a command, it first checks its command list for 
a match.  If the command is not recognized, BASIC.SYSTEM then sends the command
out to the external command handlers (if any are connected).  Finally, if no 
external command handler has recognized the command, BASIC.SYSTEM passes 
control to Applesoft which will return an error if the command is not 
recognized.  If you find regular need for an additional command, you can write 
your own command handler and attach it to BASIC.SYSTEM through the EXTRNCMD 
jump vector.  To do this, you must first save off the current EXTRNCMD vector 
(to JMP to if the command is not yours), and install the address of your 
routine in EXTRNCMD+1 and +2 (lo-byte first).  There are essentially three 
functions that your routine must perform: 
 
(1) It must check for the presence of your command(s) by inpecting the GETLN 
input buffer.  If it is not your command, you must set the carry (SEC) and JMP 
to the initial EXTRNCMD vector you saved off to continue the search.  
 
(2) If it is your command, you must let BASIC.SYSTEM know.  You must zero XCNUM
($BE53) to indicate an external command and set XLEN ($BE52) equal to the 
length of your command string minus one.  
 
If there are no associated parameters (such as slot, drive, A$, etc.) to 
parse, you must set all 16 parameter bits in PBITS ($BE54,$BE55) to zero.  And,
if you're going to handle everything yourself before returning control to 
BASIC.SYSTEM you must point XTRNADDR ($BE50,$BE51) at an RTS 
instruction...XRETURN ($BE9E) is a good location.  Now just "fall through" to 
your execution routines (3).  
 
If there are parameters to parse, it is easiest to let BASIC.SYSTEM parse them 
for you (unless you want to use some parameters that BASIC.SYSTEM doesn't use).
 
By setting up the bits in PBITS ($BE54,$BE55), and setting XTRNADDR 
($BE50,$BE51) equal to the location where execution of your command begins, you
can return control to BASIC.SYSTEM, with an RTS, and let it parse and verify 
the parameters and return them to you in the global page.  
 
(3) It must execute the desired instructions expected of the command and should
RTS with the carry cleared.  
 
NOTE: The facility to have BASIC.SYSTEM parse your external command parameters 
was initially intended for only its own use.  It turns out that not all the 
parameters can be parsed separately.  The low byte of 'PBITS' ($BE54) must have
a non-zero value in it to have the BASIC.SYSTEM parse parameters.  THIS MEANS 
THAT REGARDLESS OF WHAT PARAMETERS YOU NEED PARSED, YOU MUST ALSO ELECT TO 
OPTIONALLY PARSE SOME PARAMETER SPECIFIED BY THE LOW BYTE OF PBITS.(eg. Set 
PBITS to $10, filename optional, and this parameter need not be known by the 
user.) 
 
Attached are two example routines, BEEP and BEEPSLOT which can reside together 
as external commands.  BEEP handles everything itself and BEEPSLOT will let you
pass a slot & drive parameter (,S#,D#), where the drive is ignored.  
 
Suggestion: Save the EXTRNCMD address on entry in your routine AS IF there 
already was an external command attached.  This will create a linked list of 
external commands in which the first command asking to be installed will be the
last one asked to see if it's his command.  Of course if you don't know the 
loading address of the other command's code, you may overwrite it.  
 
The problem of where external commands are stored in memory is left up to your 
own discretion (See Tech Note #9 for buffer allocation above HIMEM).