P8 Tech Ref Appendix A
- 1 Appendix A - The ProDOS BASIC System Program
Appendix A - The ProDOS BASIC System Program
This appendix explains aspects of the BASIC system program (BASIC.SYSTEM) that are beyond the scope of the manual BASIC Programming With ProDOS. The primary subjects discussed in this appendix are
- how the BASIC system program uses memory
- how a machine-language program can make calls to the BASIC system program
- useful locations in the BASIC system program
- how you can add commands to the BASIC system program.
A.1 - Memory Map
The arrangement of ProDOS in memory is decided when the system is started up, and it depends on your particular system configuration. Figure A-1 shows the memory organization for an Apple IIe (64K or 128K) or Apple IIc (128K).
Figure A-1. Memory Map
Main Memory Auxiliary Memory (IIc or 128K IIe only) $FFFF+---------+$FFFF+---------+ $FFFF+---------+ |.Monitor.| |#########| |.........| $F800|---------| |#########| |.........| |.........| |#########| |.........| |.........| |#########| |.........| |.........| |#########| |.........| |.........| |#########| |.........| |.........| |#########| |.........| |.........| |#ProDOS##| |.........| |Applesoft| |#########|$DFFF+---------+$E000|---------|$DFFF+---------+ |.........| |#########| |.........| | | |.........| |.........| |#########| |.........| | | |.........| |.........| |#########|$D400|---------| | | |.........| |.........| |#########| |#########| | | |.........| |.........| |#########|$D100|---------| | |$D100|---------| |.........| |#########| | | | | | | $D000|---------| +---------+ +---------+$D000+---------+ +---------+ |..Other..| $C100+---------+ ^ $BFFF+---------+ $BFFF+---------+ | |#########| |.........| This ROM area| $BF00|---------| $BF00|---------| on IIc and IIe |\\\\\\\\\| | | only! |\\\\\\\\\| | | +---------+ |\\\\\\\\\| | | |#########| |\\\\\\\\\| | | +---------+ |\\\\\\\\\| | | Used by ProDOS |\BASIC.\\| | | |\SYSTEM\\| | | |\\\\\\\\\| | | +---------+ |\\\\\\\\\| | | |\\\\\\\\\| |\\\\\\\\\| | | +---------+ |\\\\\\\\\| | | Used by |\\\\\\\\\| | | BASIC.SYSTEM $9600|---------| | | | | | | | | | | +---------+ | | | | |.........| | | | | +---------+ | | | | Other used or | | | | reserved areas | | | | | | | | | | | | +---------+ | | | | | | | | | | +---------+ | | | | Free Space | | | | /\/\/\/\/\/ /\/\/\/\/\/ /\/\/\/\/\/ /\/\/\/\/\/ | | | | | | | | | | | | | | | | | | | | $800|---------| $800|---------| |.........| |.........| |.........| |.........| |.........| |.........| |.........| $400|---------| |.........| |#########| $300|---------| |#########| | | |#########| $300|---------| |#########| |.........| $200|---------| |.........| | | $100|---------| $100|---------| | | |#########| | | $80|---------| $4F|---------| | | |#Shared/#| | | |####safe#| | | $3A|---------| | | | | | | +---------+ +---------+ $00
When ProDOS starts up the BASIC system program, it loads all the necessary programs and data into memory as shown in Figure A-1, leaves a 1K buffer on the highest available 1K boundary, and then sets HIMEM right below this buffer. This buffer is used as the file buffer for commands, such as CATALOG, that only need a temporary buffer.
Table A-1 shows the possible settings of HIMEM, and the maximum number of bytes available to a program running under such a system configuration.
Table A-1. HIMEM and Program Workspace
System Bytes Available Configuration HIMEM to Programs 64K 38400 ($9600) 36352 ($8E00) Applesoft in ROM
These settings are in effect immediately after you boot the BASIC system program. While a program is running, however, these figures may change. Each time a file is opened, ProDOS lowers HIMEM by 1K ($400), keeping the 1K temporary command buffer immediately above it, and places a buffer for the file where the old temporary buffer was. When a file is closed, ProDOS releases the file's buffer, and raises HIMEM by 1K. Figure A-2 illustrates this process.
Figure A-2. The Movement of HIMEM
_______ _______ _______ _______ _______ _______ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |_______| |_______| |_______| |_______| |_______| |_______| | | |///////| | | | | | | | | | Free | 1K |/CAT's/| 1K | Free | 1K | DOG's | 1K | DOG's | 1K | Free | 1K |_______| |_______| |_______| |_______| |_______| |_______| | | | | | | | | | | | | | HIMEM | | HIMEM | | HIMEM | | Free | 1K | CAT's | 1K | HIMEM | | | | | | | |_______| |_______| | | | | | | | | | | | | | | | | | | | | | HIMEM | | HIMEM | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |_______| |_______| |_______| |_______| |_______| |_______|
No Files During CAT After CAT Open "DOG" During CAT Close "DOG" Open
There are many times when you might want machine-language routines to coexist with ProDOS; for example, when using interrupt-driven devices, when using input/output devices that have no ROM, or when using commands that you have added to ProDOS.
BASIC.SYSTEM provides buffer management for file I/O. Those facilities can also be utilized from machine-language modules operating in the ProDOS/Applesoft environment to provide protected areas for code, data, and so on.
BASIC.SYSTEM resides from $9A00 upward, with a general-purpose buffer from $9600 (HIMEM) to $99FF. When a file is opened, BASIC.SYSTEM does garbage collection if needed, moves the general-purpose buffer down to $9200, and installs a file I/O buffer at $9600. When a second file is opened, the general-purpose buffer is moved down to $8E00 and a second file I/O buffer is installed at $9200. If an EXEC file is opened, it is always installed as the highest file I/O buffer at $9600, and all the other buffers are moved down.
Additional regular file I/O buffers are installed by moving the general-purpose buffer down and installing it below the lowest file I/O buffer. All file I/O buffers, including the general-purpose buffer, are 1K (1024 bytes) and begin on a page boundary.
BASIC.SYSTEM may be called from machine language to allocate any number of pages (256 bytes) as a buffer, located above HIMEM and protected from Applesoft BASIC programs. The ProDOS bit map is not altered, so that files can be loaded into the area without an error from the ProDOS Kernel. If you subsequently alter the bit map to protect the area, you must mark the area as free when you are finished -- BASIC.SYSTEM will not do it for you.
To allocate a buffer, simply place the number of desired pages in the accumulator and use JSR GETBUFR ($BEF5). If the carry flag returns clear, the allocation was successful and the accumulator will return the high byte of the buffer address. If the carry flag returns set, an error has occurred and the accumulator will return the error code.
Note that the X and Y registers are not preserved. The first buffer is installed as the highest buffer, just below BASIC.SYSTEM from $99FF downward, regardless of the number and type of file I/O buffers that are open. If a second allocation is requested, it is installed immediately below the first. Thus, it is possible to assemble code to run at known addresses-relocatable modules are not needed.
To de-allocate the buffers created by the above call and move the file buffers back up, just use JSR FREEBUFR ($BEF8). Although more than one buffer may be allocated by this call, they may not be selectively de-allocated.
All routines that are to be called by BASIC.SYSTEM should begin with the CLD instruction. This includes I/O routines accessed by PR# and IN# and clock/calendar routines. This allows ProDOS to spot accidental calls.
For tips on raising LOMEM to provide more memory for assembly-language routines, and protecting high-res graphics pages, see the Applesoft BASIC Programmer's Reference Manual.
The BASIC Global Page
The BASIC system program has a specific area of memory, its global page, in which it keeps its current status. This page lies in the address range $BE00 through $BEFF (48640-48895). When BASIC.SYSTEM is active, its fields are defined as follows:
BE00: CI.ENTRY JMP WARMDOS ;Reenter ProDOS/Applesoft BE03: DOSCMD JMP SYNTAX ;External entry for command string BE06: EXTRNCMD JMP XRETURN ;Called for added CMD syntaxing BE09: ERROUT JMP ERROR ;Handles ONERR or prints error BE0C: PRINTERR JMP PRTERR ;Prints error message ;Number is in accumulator BE0F: ERRCODE DFB 0 ;ProDOS error code stored here ;and $DE for Applesoft
Default I/O vectors. These may be changed by the user to remap slots for nondisk devices. When the system is booted, all slots not containing a ROM are considered not connected and the default vector is left to point at the appropriate error handling routine.
BE10: OUTVECT0 DW COUT1 ;Monitor video output routine BE12: OUTVECT1 DW NODEVERR ;Default $C100 when ROM present BE14: OUTVECT2 DW NODEVERR ;Default $C200 when ROM present BE16: OUTVECT3 DW NODEVERR ;Default $C300 when ROM present BE18: OUTVECT4 DW NODEVERR ;Default $C400 when ROM present BE1A: OUTVECT5 DW NODEVERR ;Default $C500 when ROM present BE1C: OUTVECT6 DW NODEVERR ;Default $C600 when ROM present BE1E: OUTVECT7 DW NODEVERR ;Default $C700 when ROM present BE20: INVECT0 DW CHIN1 ;Monitor keyboard input routine BE22: INVECT1 DW NODEVERR ;Default $C100 when ROM present BE24: INVECT2 DW NODEVERR ;Default $C200 when ROM present BE26: INVECT3 DW NODEVERR ;Default $C300 when ROM present BE28: INVECT4 DW NODEVERR ;Default $C400 when ROM present BE2A: INVECT5 DW NODEVERR ;Default $C500 when ROM present BE2C: INVECT6 DW NODEVERR ;Default $C600 when ROM present BE2E: INVECT7 DW NODEVERR ;Default $C700 when ROM present BE30: VECTOUT DW COUT1 ;Current character output routine BE32: VECTIN DW CHIN1 ;Current character input routine BE34: VDOSIO DW DOSOUT ;ProDOS char out intercept routine
BE36: DW DOSINP ;ProDOS char in intercept routine BE38: VSYSIO DW 0,0 ;Internal redirection of I/O BE3C: DEFSLT DFB $06 ;Default slot, set by 'S' parm BE3D: DEFDRV DFB $01 ;Default drive, set by 'D' parm BE3E: PREGA DFB 0 ;Register save area BE3F: PREGX DFB 0 BE40: PREGY DFB 0 BE41: DTRACE DFB 0 ;Applesoft trace enable BE42: STATE DFB 0 ;0=Imm, >0=Def modes BE43: EXACTV DFB 0 ;EXEC file active if bit 7 on BE44: IFILACTV DFB 0 ;Input file active if bit 7 on BE45: OFILACTV DFB 0 ;Output file active if bit 7 on BE46: PFXACTV DFB 0 ;Prefix input active if bit 7 on BE47: DIRFLG DFB 0 ;File being accessed is directory BE48: EDIRFLG DFB 0 ;End of directory encountered BE49: STRINGS DFB 0 ;Counter for free string space BE4A: TBUFPTR DFB 0 ;Temporory buffered char count (WRITE) BE4B: INPTR DFB 0 ;Input char count during kbd input BE4C: CHRLAST DFB 0 ;Last character output (for error detect) BE4D: OPENCNT DFB $00 ;Number of open file (except EXEC file) BE4E: EXFILE DFB $00 ;Flag to indicate EXEC file being closed BE4F: CATFLAG DFB $00 ;File being input is (translated) dir BE50: XTRNADDR DW 0 ;Execution address of external cmd (0) BE52: XLEN DFB 0 ;Length of command string-1, ('HELP'=3) BE53: XCNUM DFB 0 ;BASIC cmd number (external cmd if =0)
Command parameter PBITS/FBITS bit definitions:
BE54: PFIX EQU $80 ;Prefix needs fetching, pathname optional BE54: SLOT EQU $40 ;No parameters to be processed BE54: RRUN EQU $20 ;Command only valid during program BE54: FNOPT EQU $10 ;Filename is optional BE54: CRFLG EQU $08 ;CREATE allowed BE54: T EQU $04 ;File type BE54: FN2 EQU $02 ;Filename '2' for RENAME BE54: FN1 EQU $01 ;Filename expected
And for PBITS+1/FBITS+1 definitions:
BE54: AD EQU $80 ;Address BE54: B EQU $40 ;Byte BE54: E EQU $20 ;End address BE54: L EQU $10 ;Length BE54: LINE EQU $08 ;'@' line number BE54: SD EQU $04 ;Slot and drive numbers BE54: F EQU $02 ;Field BE54: R EQU $01 ;Record BE54: V EQU $00 ;Volume number ignored
When the BASIC system program recognizes one of its commands, it sets up PBITS to indicate which parameters (#S, #D, and so on) may be used with that command. Then it parses the command string, marking the found parameters in FBITS, and placing their values in locations $BE58-$BE6B. The meanings of the bit within PBITS and FBITS are discussed in the section "Adding Commands to the BASIC System Program."
BE54: PBITS DW 0 ;Allowed parameter bits BE56: FBITS DW 0 ;Found parameter bits
The following locations hold the values of the parameters for the BASIC commands. As the BASIC system program parses command options, it sets the value of the corresponding command parameters.
Previously set parameters do not change.
BE58: PVALS EQU * BE58: VADDR DW 0 ;Parameter value for 'A' parm BE5A: VBYTE DFB 0,0,0 ;Parameter value for 'B' parm BE5D: VENDA DW 0 ;Parameter value for 'E' parm BE5F: VLNTH DW 0 ;Parameter value for 'L' parm BE61: VSLOT DFB 0 ;Parameter value for 'S' parm BE62: VDRIV DFB 0 ;Parameter value for 'D' parm BE63: VFELD DW 0 ;Parameter value for 'F' parm BE65: VRECD DW 0 ;Parameter value for 'R' parm BE67: VVOLM DFB 0 ;Parameter value for 'V' parm BE68: VLINE DW 0 ;Parameter value for '@' parm BE6A: PTYPE EQU *-PVALS BE6A: VTYPE DFB 0 ;Parameter value for 'T' parm BE6B: PIOSLT EQU *-PVALS BE6B: VIOSLT DFB 0 ;Parameter value for IN# or PR# BE6C: VPATH1 DW TXBUF-1 ;Pathname 1 buffer BE6E: VPATH2 DW TXBUF2 ;Pathname 2 buffer (RENAME)
GOSYSTEM is used to make all MLI calls since errors must be translated before returning to the calling routine. On entry the Accumulator should contain the call number. The address of the parameter table is looked up and set based on the call number. Only file management calls can be made using this routine: $C0-$D3. The original implementation of this BASIC system program contains only these calls.
BE70: GOSYSTEM STA SYSCALL ;Save call number BE73: STX CALLX ;Preserve X register BE76: AND #$1F ;Strip high bits of call number BE78: TAX ; and use as lookup index BE79: LDA SYSCTBL,X ;Get low address of parm table BE7C: STA SYSPARM BE7F: LDX CALLX ;Restore X before calling BE82: JSR MLIENTRY ;Call ProDOS MLI to execute request BE85: SYSCALL DFB 0 BE86: SYSPARM DW * ;(High address should be same ; as parameter tables) BE88: BCS BADCALL ;Branch if error encountered BE8A: RTS
BADCALL converts MLI errors into BASIC system program error equivalents. Routines should be entered with error number in the Accumulator. The BADCALL routine should be used whenever a ProDOS MLI call returns an error and BASIC.SYSTEM will be used to print the error message. Returns BASIC system program error number in Accumulator. All unrecognized errors are mapped to I/O error.
X register is restored to its value before the call is made. Carry is set.
BE8B: BADCALL LDA #12 ;19 errors are mapped to BE8D: MLIERR1 CMP MLIERTBL,X ; other than I/O error BE90: BEQ MLIERR2 BE92: DEX BE93: BPL MLIERR1 BE95: LDX #$13 ;If not recognized, make it I/O error BE97: MLIERR2 LDA BIERRTBL,X ;return error in Accumulator BE9A: LDX CALLX ;Restore X register BE9D: SEC ;Set Carry to indicate error BE9E: XRETURN RTS BE9F: CISPARE1 DFB $00
The following are the system-call parameter tables. These tables must reside within the same page of memory. Only those parameters that are subject to alterations have been labeled. SYSCTBL below contains the low-order addresses of each parameter table. SYSCTBL is used by GOSYSTEM to set up the address of the parameter table for each call. (See GOSYSTEM.)
BEA0: SCREATE DFB $07 BEA1: DW TXBUF-1 ;Pointer to pathname BEA3: CRACESS DFB $C3 ;$C1 if directory create BEA4: CRFILID DFB $00 BEA5: CRAUXID DW $0000 BEA7: CRFKIND DFB 0 BEA8: DW 0 ;No predetermined date/time BEAA: DW 0 BEAC: SSGPRFX EQU * BEAC: SDSTROY DFB $01 BEAD: DW TXBUF-1 ;This call requires no modifications BEAF: SRECNAME DFB $02 BEB0: DW TXBUF-1 ;No modifications needed BEB2: DW TXBUF2 BEB4: SSGINFO DFB $00 ;P.CNT=7 if SET_FILE_INFO ;P.CNT=A if GET_FILE_INFO BEB5: DW TXBUF-1 BEB7: FIACESS DFB $00 ;Access used by lock/unlock BEB8: FIFILID DFB $00 ;FILE ID is type specifier BEB9: FIAUXID DW $0000 ;Aux_id is used for load addr ; and record length BEBB: FIFKIND DFB $00 ;Identifies trees vs. directories BEBC: FIBLOKS DW $0000 ;Used by CAT commands for root dir BEBE: FIMDATE DW $0000 ;Modification date & time BEC0: DW $0000 ;should always be zeroed before call BEC2: DW $0000 ;Create date and time ignored BEC4: DW $0000
BEC6: SONLINE EQU * BEC6: SSETMRK EQU * BEC6: SGETMRK EQU * BEC6: SSETEOF EQU * BEC6: SGETEOF EQU * BEC6: SSETBUF EQU * BEC6: SGETBUF EQU * BEC6: DFB $02 ;Parameter count BEC7: SBUFREF EQU * BEC7: SREFNUM EQU * BEC7: SUNITNUM EQU * BEC7: DFB 0 ;Unit or reference number BEC8: SDATPTR EQU * BEC8: SMARK EQU * BEC8: SEOF EQU * BEC8: SBUFADR EQU * BEC8: DFB 0,0,0 ;Some calls only use 2 bytes ;MRK & EOF use 3 bytes BECB: SOPEN DFB $03 BECC: DW TXBUF-1 BECE: OSYSBUF DW $0000 BED0: OREFNUM DFB 0 BED1: SNEWLIN DFB $03 BED2: NEWLREF DFB $00 ;Reference number BED3: NLINEBL DFB $7F ;Newline character is always CR BED4: DFB $0D ; both $0D and $8D are recognized BED5: SREAD EQU * BED5: SWRITE EQU * BED5: DFB $04 BED6: RWREFNUM DFB $00 BED7: RWDATA DW $0000 ;Pointer to data to be read/written BED9: RWCOUNT DW $0000 ;Number of bytes to be read/written BEDB: RWTRANS DW $0000 ;returned # of bytes read/written
BEDD: SCLOSE EQU * BEDD: SFLUSH EQU * BEDD: DFB $01 BEDE: CFREFNUM DFB $00 BEDF: CCCSPARE DFB $00 BEE0: ASC 'COPYRIGHT APPLE, 1983' BEF5: GETBUFR JMP GETPAGES BEF8: FREBUFR JMP FREPAGES BEF8: RSHIMEM DFB 0, 0, 0, 0, 0
BASIC.SYSTEM Commands From Assembly Language
There are times when a routine wants to perform functions that are already implemented by the BASIC system program -- deleting and renaming files, displaying a directory, and so on. The DOSCMD vector serves just this function.
First a routine should place the desired BASIC command in the input buffer ($200). It should be an ASCII string with the high bits set, followed by a carriage return ($8D), exactly as the Monitor GETLN routine would leave a string. Next the routine should do a JSR to the DOSCMD entry point ($BE03).
BASIC.SYSTEM will parse the command, set up all the parameters, (as explained in Section A.3.3), and then execute the command. If there is an error, it will return the error code in the accumulator with the carry set. If it is 0, there was no error. Otherwise it contains a BASIC system program error number.
Note: The JSR DOSCMD must be executed in deferred mode (from a BASIC program), rather than in immediate mode. This applies also to the Monitor program: from the Monitor, you can't do a $xxxxG to execute the code that contains the JSR DOSCMD. This is because BASIC.SYSTEM checks certain state flags, which are set correctly only while in deferred mode.
There are certain commands that do not work as expected when initiated via DOSCMD: RUN -(dash command), LOAD, CHAIN, READ, WRITE, APPEND, and EXEC. Use them this way at your own risk.
The commands that do work correctly are: CATALOG, CAT, PREFIX, CREATE, RENAME, DELETE, LOCK, UNLOCK, SAVE, STORE, RESTORE, PR#, IN#, FRE, OPEN, CLOSE, FLUSH, POSITION, BRUN, BLOAD, and BSAVE.
The following are: 1. An example of a BASIC program that uses the BLOAD command to load an assembly-language routine that exercises the DOSCMD routine.
2. A listing of that assembly-language routine. 3. You should review them before writing your own routine.
10 REM YOU MUST CALL THE ROUTINE FROM INSIDE A BASIC PROGRAM 11 REM 12 REM 20 PRINT CHR$(4)"BLOAD/P/PROGRAMS/CMD.0" 30 CALL 4096 40 PRINT "BACK TO THE WONDERFUL WORLD OF BASIC!" 50 END
1000: 1000 1 ORG $1000 1000: FD6F 2 GETLN1 EQU $FD6F ; MONITORS INPUT ROUTINE 1000: BE03 3 DOSCMD EQU $BE03 ; BASIC.SYSTEM GLBL PG DOS CMD ENTRY 1000: FDED 4 COUT EQU $FDED ; MONITORS CHAR OUT ROUTINE 1000: BE0C 5 PRERR EQU $BE0C ; PRINT THE ERROR 1000: 6 * 1000: 7 * 1000: 8 * 1000:A2 00 9 START LDX #0 ; DISPLAY PROMPT... 1002:BD 1F 10 10 L1 LDA PROMPT,X ; 1005:F0 06 100D 11 BEQ CONT ; BRANCH IF END OF STRING 1007:20 ED FD 12 JSR COUT ; 100A:E8 13 INX ; 100B:D0 F5 1002 14 BNE L1 ; LOOP UNTIL NULL TERMINATOR HIT 100D: 15 * 100D:20 6F FD 16 CONT JSR GETLN1 ; ACCEPT COMMAND FROM KB 1010:20 03 BE 17 JSR DOSCMD ; AND EXECUTE COMMAND 1013:2C 10 C0 18 BIT $C010 ; CLEAR STROBE 1016:B0 02 101A 19 BCS ERROR ; BRANCH IF ERROR DETECTED 1018:90 E6 1000 20 BCC START ; OTHERWISE RESTART 101A: 21 * 101A: 22 * 101A: 23 * NOTE: AFTER HANDLING YOUR ERROR YOU MUST CLEAR THE CARRY 101A: 24 * BEFORE RETURNING TO BASIC OR BASIC WILL DO 101A: 25 * STRANGE TO YOU. 101A: 26 * 101A:20 0C BE 27 ERROR JSR PRERR ; PRINT 'ERR' 101D:18 28 CLC ; 101E:60 29 RTS ; RETURN TO BASIC 101F: 30 * 101F: 31 MSB ON 101F: 32 * 101F:8D 33 PROMPT DB $8D ; OUTPUT A RETURN FIRST 1020:C5 CE D4 C5 34 ASC 'ENTER BASIC.SYSTEM COMMAND --> ' 103F:00 35 DB 0
DOSCMD is merely a way to perform some BASIC.SYSTEM commands from assembly language, and is not a substitute for performing the commands in BASIC. Keep in mind the consequences of the command you are executing. For example, when doing a BRUN or BLOAD, make sure the code is loaded at proper addresses.
After you call DOSCMD, the carry bit will be set if an error has occurred. The accumulator will have the error number.
There are three ways to handle DOSCMD errors:
- Do a JSR ERROUT ($BE09). This returns control to your
BASIC ONERR routine, where you can handle the error.
- Do a JSR PRINTERR ($BE0C). This prints Out the error and
returns control to the point just after the JSR.
- Handle the error yourself. Be sure to clear the carry (CLC) before
returning control to BASIC.SYSTEM. If you don't, an error will be assumed, and the results are unpredictable.
Adding Commands to the BASIC System Program
The EXTRNCMD location in the global page allows you to add your own commands to the ProDOS command set. Once you attach a command, it is treated as if it were one of the BASIC.SYSTEM commands, except that the original commands have preference. To execute your command in immediate mode, just enter it. To execute it in deferred mode, preface it with 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 sends the command to the external command handlers, if any are connected. If no external command handler claims the command, BASIC.SYSTEM passes control to Applesoft, which returns an error if the command is not recognized.
If you have frequent need for special commands, you can write your own command handler and attach it to BASIC.SYSTEM through the EXTRNCMD jump vector. First, save 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 (low byte first). Your routine must do three things:
- It must check for the presence of your command(s) by inpecting the
GETLN input buffer. If the command is not yours, you must set the carry (SEC) and JMP to the initial EXTRNCMD vector you saved to continue the search.
- If the command is yours, 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$, and so
on) to parse, or if you're going to parse them yourself, 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.
- If there are parameters to parse, it is easiest to let BASIC.SYSTEM
parse them for you (unless you want to use some undefined parameters). 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.
- It must execute the instructions expected of the command, and it
should RTS with the carry cleared.
Note: Having BASIC.SYSTEM parse your external command parameters was initially intended only for its own use. As it happens, not all parameters can be parsed separately. The low byte of PBITS ($BE54) must have a nonzero value to have BASIC.SYSTEM parse parameters.
This means that regardless of the parameters you need parsed, you must also elect to parse some parameter specified by the low byte of PBITS. For example, set PBITS to $10, filename optional (this parameter need not be known by the user).
The following are two sample routines, BEEP and BEEPSLOT. They can reside together as external commands. BEEP handles everything itself, while BEEPSLOT lets you pass a slot and drive parameter (,S#,D#) where the drive is ignored.
A.3.2.1 - BEEP Example
************************************************************** * * * BRUN BEEP.0 TO INSTALL THE ROUTINE'S ADDRESS IN EXTRNCMD. * * THEN TYPE BEEP AS AN IMMEDIATE COMMAND OR USE PRINT * * CHR$(4);"BEEP" IN A PROGRAM. * * * ************************************************************** * * ORG $300 INBUF EQU $200 ;GETLN input buffer. WAIT EQU $FCA8 ;Monitor wait routine. BELL EQU $FF3A ;Monitor bell routine. EXTRNCMD EQU $BE06 ;External cmd JMP vector. XTRNADDR EQU $BE50 ;Ext cmd implementation addr. XLEN EQU $BE52 ;length of command string-1. XCNUM EQU $BE53 ;CI cmd no. (ext cmd - 0). PBITS EQU $BE54 ;Command parameter bits. XRETURN EQU $BE9E ;Known RTS instruction. MSB ON ;Set high bit on ASCII * * FIRST SAVE THE EXTERNAL COMMAND ADDRESS SO YOU WON'T * DISCONNECT ANY PREVIOUSLY CONNECTED COMMAND. * LDA EXTRNCMD+1 STA NXTCMD LDA EXTRNCMD+2 STA NXTCMD+1 * LDA #>BEEP ;Install the address of our STA EXTRNCMD+1 ; command handler in the LDA #<BEEP ; external command JMP STA EXTRNCMD+2 ; vector. RTS * BEEP LDX #0 ;Check for our command. NXTCHR LDA INBUF,X ;Get first character. CMP CMD,X ;Does it match? BNE NOTOURS ;No, back to CI. INX ;Next character CPX #CMDLEN ;All characters yet? BNE NXTCHR ;No, read next one. * LDA #CMDLEN-1 ;Our cmd! Put cmd length-1 STA XLEN ; in CI global XLEN. LDA #>XRETURN ;Point XTRNADDR to a known STA XTRNADDR ; RTS since we'll handle LDA #<XRETURN ; at the time we intercept
STA XTRNADDR+1 ; our command. LDA #0 ;Mark the cmd number as STA XCNUM ; zero (external). STA PBITS ;And indicate no parameters STA PBITS+1 ; to be parsed. * LDX #5 ;Number of desired beeps. NXTBEEP JSR BELL ;Else, beep once. LDA #$80 ;Set up the delay JSR WAIT ; and wait. DEX ;Decrement index and BNE NXTBEEP ; repeat until X = 0. * CLC ;All done successfully. RTS ; RETURN WITH THE CARRY CLEAR. * NOTOURS SEC ; ALWAYS SET CARRY IF NOT YOUR JMP (NXTCMD) ; CMD AND LET NEXT COMMAND TRY * ; TO CLAIM IT. CMD ASC "BEEP" ;Our command CMDLEN EQU *-CMD ;Our command length * NXTCMD DW 0 ; STORE THE NEXT EXT CMD'S ; ADDRESS HERE.
************************************************************* * * * BRUN BEEPSLOT.0 TO INSTALL THE ROUTINE'S ADDRESS IN * * EXTRNCMD. THEN ENTER BEEPSLOT,S(n),D(n). ONLY A LEGAL * * SLOT AND DRIVE NUMBERS ARE ACCEPTABLE. IF NO SLOT NUMBER * * IT WILL USE THE DEFAULT SLOT NUMBER. ANY DRIVE NUMBER IS * * SIMPLY IGNORED. THE COMMAND MAY ALSO BE USED IN A * * PROGRAM PRINT CHR$(4) STATEMENT. * * * ************************************************************* * * ORG $2000 INBUF EQU $200 ;GETLN input buffer. WAIT EQU $FCA8 ;Monitor wait routine. BELL EQU $FF3A ;Monitor bell routine EXTRNCMD EQU $BE06 ;External cmd JMP vector. XTRNADDR EQU $BE50 ;Ext cmd implementation addr. XLEN EQU $BE52 ;Length of command string-1. XCNUM EQU $BE53 ;CI cmd no. (ext cmd = 0). PBITS EQU $BE54 ;Command parameter bits. VSLOT EQU $BE61 ;Verified slot parameter. MSB ON ;Set high bit on ASCII. * * REMEMBER TO SAVE THE PREVIOUS COMMAND ADDRESS. * LDA EXTRNCMD+1 STA NXTCMD LDA EXTRNCMD+2 STA NXTCMD+1 * LDA #>BEEPSLOT ;Install the address of our STA EXTRNCMD+1 ; command handler in the LDA #<BEEPSLOT ; external command JMP STA EXTRNCMD+2 ; vector. RTS * BEEPSLOT LDX #0 ;Check for our command. NXTCHR LDA INBUF,X ;Get first character. CMP CMD,X ;Does it match? BNE NOTOURS ;NO, SO CONTINUE WITH NEXT CMD. INX ;Next character CPX #CMDLEN ;All characters yet? BNE NXTCHR ;No, read next one. * LDA #CMDLEN-1 ;Our cmd! Put cmd length-1 STA XLEN ; in CI global XLEN. LDA #>EXECUTE ;Point XTRNADDR to our
STA XTRNADDR ; command execution LDA #<EXECUTE ; routine STA XTRNADDR+1 LDA #0 ;Mark the cmd number as STA XCNUM ; zero (external). * LDA #%00010000 ;Set at least one bit STA PBITS ; in PBITS low byte! * LDA #%00000100 ;And mark PBITS high byte STA PBITS+1 ; that slot & drive are legal. CLC ;Everything is OK. RTS ;Return to BASIC.SYSTEM * EXECUTE LDA VSLOT ;Get slot parameter. TAX ;Transfer to index reg. NXTBEEP JSR BELL ;Else, beep once. LDA #$80 ;Set up the delay JSR WAIT ; and wait. DEX ;decrement index and BNE NXTBEEP ; repeat until x = 0. CLC ;All done successfully. RTS ;Back to BASIC.SYSTEM. * * IT'S NOT OUR COMMAND SO MAKE SURE YOU LET BASIC * CHECK WHETER OR NOT IT'S THE NEXT COMMAND. * NOTOURS SEC ;SET CARRY AND LET JMP (NXTCMD) ; NEXT EXT CMD GO FOR IT. * CMD ASC "BEEPSLOT" ;Our command CMDLEN EQU *-CMD ;Our command length NXTCMD DW 0 ; STORE THE NEXT COMMAND'S ; ADDRESS HERE.
Command String Parsing
First, the external command must tell the BASIC system program which parameters are allowed for the command. It does this by assigning the appropriate values to the two PBITS bytes, which have the following meanings:
Address: $BE54 $BE55 _______________________ _______________________ | | | | | | | | | | | | | | | | | | PBITS: | | | | | | | | | | | | | | | | | | |__|__|__|__|__|__|__|__| |__|__|__|__|__|__|__|__| Bit #: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Bit # - Meaning 15 - Prefix needs fetching. Pathname is optional 14 - No parameters to be processed 13 - Command only valid during program execution 12 - Filename is optional 11 - Create allowed if file doesn't exist 10 - File type (Ttype) optional 9 - A second filename expected 8 - A first filename expected 7 - Address (A#) allowed 6 - Byte (B#) allowed 5 - End address (E#) allowed 4 - Length (L#) allowed 3 - Line number (@#) allowed 2 - Slot and Drive (S# and D#) allowed 1 - Field (F#) allowed 0 - Record (R#) allowed
Having done this, the routine should place the length of the recognized command word minus one into XLEN ($BE52). It should also place a $00 into XCNUM ($BE53), indicating that an external command was found, and it should place the address within the routine at which further processing of the parsed command will take place into XTRNADDR ($BE50). Then it should RTS back to the BASIC system program.
The BASIC system program will see that the command was recognized, and it will parse the string according to PBITS. For each parameter that was used in the command, it will set the corresponding bit in FBITS ($BE56) and update the value of that parameter in the global page. Finally, it will do a JSR to the location indicated in XTRNADDR ($BE50).
The routine can now process the command. All parameters are stored in the global page except the filenames which are stored in the locations indicated by VPATH1 and VPATH2.
The HELP command is such a routine. When you type -HELP, the help command is loaded into memory at $2000, it moves HIMEM down and places itself above HIMEM, then it marks itself in the bit map.
Finally it places the start address of the routine in the EXTRNCMD vector. The BASIC system program now recognizes a series of HELP commands as well as the NOHELP command.
The NOHELP command removes the help routine's address from the EXTRNCMD vector, unmarks the routine from the bit map, and moves HIMEM back up.
Figure A-3 is a memory map that shows the locations used by the Monitor, Applesoft, the Device Drivers, and the ProDOS MLI. The owner of each location is shown by a letter: M, A, D, or P.
Figure A-3. Zero Page Memory Map Use by the Monitor (M), Applesoft (A), Disk Drivers (D), and ProDOS MLI (P) is shown.
Decimal---0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 , Hex---$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $A $B $C $D $E $F 0 $00 DA DA A A A A A A A A A A 16 $10 A A A A A A A A A A 32 $20 M M M M M M M M M M M M M M M M 48 $30 M M M M M M M M M M PMD PMD PMD PMD PMD DM 64 $40 PMD PMD PMD PMD PMD PMD PMD PM PM PM P P P P PM M 80 $50 MA MA MA MA MA MA A A A A A A A A A A 96 $60 A A A A A A A A A A A A A A A A 112 $70 A A A A A A A A A A A A A A A A 128 $80 A A A A A A A A A A A A A A A A 144 $90 A A A A A A A A A A A A A A A A 160 $A0 A A A A A A A A A A A A A A A A 176 $B0 A A A A A A A A A A A A A A A A 192 $C0 A A A A A A A A A A A A A A 208 $D0 A A A A A A A A A A A A A A 224 $E0 A A A A A A A A A A 240 $F0 A A A A A A A A A A
If you need many zero-page locations for your routines, choose a region of already-used locations, save them at the beginning of the routine, and then restore them at the end.
The Extended 80-Column Text Card
The Apple IIe computer can optionally contain an Extended 80-Column Text Card, giving the computer access to an additional 64K of RAM.
(The Apple IIc has the equivalent of such a card built in.) ProDOS uses this extra RAM as a volume, just like a small disk volume. This volume is initially given the name /RAM, but it can be renamed.
The 64K of RAM on the card is logically partitioned into 127 512-byte blocks of information. The contents of these blocks are:
Blocks 00-01 - Unavailable Block 02 - Volume directory Block 03 - Volume bit map Blocks 04-07 - Unavailable Blocks 08-126 - Directories and files
A detailed description of the way these blocks are used on a disk volume is in Appendix B. The major differences between a disk volume and /RAM are:
- On a disk volume, blocks 0 and 1 are used for the loader program.
Since /RAM is not a bootable volume, these blocks are not used.
- On a disk volume, there are usually four blocks reserved for the
volume directory, with a maximum capacity of 51 files in the volume directory. On /RAM, there is only one block of volume directory: it can hold 12 files (any or all of them can be subdirectory files).
- Normal disk devices are associated with a given slot and drive.
/RAM is placed in the device list as slot 3, drive 2.
This arrangement gives you a total of 119 blocks of file storage.