Latest release of ProDOS is 2.4.3

ProDOS 2.5 alpha 8 pre-release is available

TechRef - Appendix - The ProDOS BASIC System Program

ProDOS 8 Technical Reference Manual 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|.........|                     |#########|
                      |.........|                     |#########|
                      |.........|                     |#########|
                      |.........|                 $200|---------|
                      |.........|                     |         |
                  $100|---------|                 $100|---------|
                      |         |                     |#########|
                      |         |                  $80|---------|
                   $4F|---------|                     |         |
                      |#Shared/#|                     |         |
                      |####safe#|                     |         |
                   $3A|---------|                     |         |
                      |         |                     |         |
                      +---------+                     +---------+
                   $00

A.2 - HIMEM

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

A.2.1 - Buffer Management

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.

Important! 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.

A.3 - 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           ;Temporary 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 CIERRTBL,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
 BEFB:  RSHIMEM   DFB 0, 0, 0, 0, 0

A.3.1 - 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.

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.

A.3.2 - 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 inspecting 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.

A.3.2.2 - BEEPSLOT Example

 *************************************************************
 *                                                           *
 * 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  #&#60BEEPSLOT ; 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.

A.3.3 - 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.

A.4 - Zero Page

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.

A.5 - 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.