****************************************************************** * * * DOS's Command Parsing and Processing Routines * * * * * *----------------------------------------------------------------* * * * This disassembly is critical to understanding DOS 3.3 * * because the following routines and tables define all DOS * * commands and the entire syntax of the operating system. * * The command parsing and processing routines are * * responsible for (1) recognizing all DOS commands, (2) insuring * * that the commands are issued in the correct context and (3) * * doing a "stack jump" to the handlers that actually execute the * * commands. If a DOS command is recognized but its syntax is * * not valid, execution flows to DOS's error handling routines. * * Failure to recognize a DOS command causes a return to the * * Applesoft calling routine. (This routine, which is not part * * of the following disassembly, subsequently parses the input * * line for any BASIC commands.) * * A written blow-by-blow description of the different * * execution patterns associated with this disassembly is pre- * * cluded by the fact that the command parsing and processing * * routines contain over 70 branch instructions. However, * * because a special effort has been made to liberally comment * * each section of code, you should be able to clearly understand * * this disassembly simply by reading it over a few times. * * (P.S. You may find it helpful to construct a crude flow * * diagram by drawing arrows to connect each branch or jump * * instruction with its target location.) * * The following routines make extensive use of several * * tables that are shown at the end of this disassembly. These * * tables are used to define: (1) the name and attributes of * * each DOS command, (2) the various option character codes and * * valid ranges that can be issued with each command and (3) the * * command handler entry point addresses. Simply by altering a * * few bytes in any of these tables you can customize DOS to do * * many weird and wonderful things. As a matter of fact, several * * (often over-priced) DOS-customizing programs do little more * * than alter these tables. By investing very little time and * * effort almost anyone can write such a program. * * * ****************************************************************** (9FCD) PARSECMD LDY #$FF ;Initialize index to the command text table. STY NDX2CMD INY ;(y) = 0. STY NEXTCMD ;Signal no pending command for next time. GETCHR1 INC NDX2CMD ;Index to command text table. (0 on entry.) LDX #0 ;Initialize index to input chars. (9FDB) PHP ;Save status (with z = 1) on the stack. ;(Default condition to assume chars match.) (9FDC) LDA BUF200,X ;Get the first char in the input buffer. CMP DCTRLCHR ;If it is not DOS's ctrl char, set index BNE SAVLINDX ;to 0. If it is DOS's ctrl char, set (9FE4) INX ;index to 1 so we skip the ctrl char. * Does the input char = the char in the * command text table (CMDTXTBL, $A884 - $A909)? * (Note: The last char in @ command is * negative ASCII whereas the rest of the * chars in a given command are positive ASCII.) (9FE5) SAVLINDX STX NDX2INBF ;Save index to the input buffer. INVSCMD JSR PURGECMD ;Get char from the input buffer (9FE8) ;(ignore spaces). * Get the first non-space character from the * input buffer. Set the z-flag if it is a * carriage return or a comma. (A1A4) PRUGECMD JSR CMDCHAR ;Get the first char. * Get character from the input buffer. * Set z-flag if it is a or comma. (A193) CMDCHAR LDX NDX2INBF ;(x)=index input buf. LDA BUF200,X ;Get next char. CMP #$8D ;Is it a ? BEQ CMDCHRTS ;Yes. INX ;(x) = index 4 next STX NDX2INBF ;anticipated char. CMP #"," ;Is the char a comma? CMDCHRTS RTS (A1A3) (A1A7) BEQ CMDCHRTS ;Exit if or comma. CMP #" " ;Is char a space? BEQ PURGECMD ;Yes - ignore leading spaces. (A1AD) RTS ============= CMDCHRTS RTS (A1A3) ============ (9FEB) AND #$7F ;Strip the hi bit off the char. (9FED) EOR CMDTXTBL,Y ;Does the input char match a command char? ;If positive input char / positive ASCII cmd ;char match, then (a) = 0. ;If positive input char / negative ASCII cmd ;char match, then (a) = $80. (9FF0) INY ;Kick up the index to the next char in the ;command text table. (9FF1) ASL ;If pos/pos match, (a) = 0 & (c) = 0. ;If pos/neg match, (a) = $80 & (c) = 1. (9FF2) BEQ CKIFCHRS ;Char matched, so go check carry. * Chars didn't match. (9FF4) PLA ;Pull the saved status off the stack to ;clear the z-flag (cause if it has (z)=1 ;or (c)=1, then the new (a) will have at ;least one bit set so then (a) < > 0.) (9FF5) PHP ;Push the status on the stack (with (z)=0 ;and (c) conditioned as per the above "ASL" ;instruction). * See if there are more characters to * check in the text of a given command. (9FF6) CKIFCHRS BCC INVSCMD ;If (c) = 0, more chars to check in a given ;command listed in the table. ;If (c) = 1, we just checked the last char ;in the table. * Finished checking the text of * a particular command. (9FF8) PLP ;Get saved status register off the stack. * Did command match? (9FF9) BEQ PRPDOCMD ;If the last character matched, then the ;entire command matched, so go process ;the command. * Check if we searched the entire * table yet. (9FFB) LDA CMDTXTBL,Y ;The last character didn't match, so (9FFE) BNE GETCHR1 ;this isn't the correct command. Therefore, ;go check if the next character byte in the ;table is a $00. If it is a $00, then we ;checked the entire table and no commands ;matched. If is isn't a $00, then go check ;the remaining commands in the table. * Checked entire table & no matching * commands were found. Therefore, check * if DOS's control char was used. (A000) CKIFCTRL LDA BUF200 ;Is the FIRST character in the input buffer CMP DCTRLCHR ;equal to DOS's control character? BEQ CHKIFCR ;Yes (A008) JMP DSPLYALL ;No - go display the character & exit DOS. ------------ ;Execution will eventually return to the ;calling Applesoft routine which will then ;parse the input line for BASIC commands. * Was DOS's control character the only * character on the line? (A00B) CHKIFCR LDA BUF200+1 ;Get second char in input buffer. CMP #$8D ;Is it a carriage return? (A010) BNE PRSYNERR ;Wasn't just DOS's ctrl char (and a ) ;so go issue a syntax error message. (A012) JSR RESTAT0 ;Got a lone DOS ctrl char, set condition 0. * Reset CONDNFLG & OPUTCOND to 0. (A75B) RESTAT0 LDY #0 STY CONDNFLG STY OPUTCOND (A763) RTS (A015) JMP DSPLYCMD ;Go display the carriage return. ------------ PRSYNERR JMP SYNTXERR ;CTRL CHAR DENOTED THAT A DOS COMAND WAS (A018) ------------ ;INTENDED. HOWEVER, NO DOS COMMAND COULD ;BE MATCHED UP WITH THE INPUT CHARACTERS ;so go issue a syntax-error message. ;(See dis'mbly of errors.) * Prepare to execute the DOS command. * On entry - A DOS command was parsed. * - NDX2CMD = command code. * - The I/O hooks point to the * true input/output handlers. * * ********** NOTE ********** * This routine makes extensive use of a table * (CMDATTRB, $A909 - $A940) which contains an encoded * list of the attributes associated with * each command. (The table is shown at the end of * this disassembly.) (A01B) PRPDOCMD ASL NDX2CMD ;Double the index because there are 2 bytes (A01E) LDY NDX2CMD ;per address in the table of DOS command ;entry points. (A021) JSR CKBSCRUN ;Check if basic is running a program: * Check if basic is running a program. (A65E) CKBSCRUN PHA ;Save (a) on stack. LDA ACTBSFLG ;Check which basic is presently up. (A662) BEQ INTBASIC ;Branch if using integer. * Using Applesoft so now check if in immediate * or deferred mode. If the hi byte of the line * number is > 65288 ($FF in hi byte), then we * are using the immediate mode as far as * Applesoft is concerned. (A664) LDX CURLIN+1 ;Check the hi byte of the line number. INX ;If $FF --> $00, then line # > 65288. (A667) BEQ IMEDMODE ;Branch if using the immediate mode. * Applesoft appears to be running a program. * However, maybe CURLIN+1 was zapped as * part of a protection scheme, so better also * check to see if using Applesoft prompt. (A669) LDX PROMPT ;What char are we using for a prompt? CPX #$DD ;Is it an Applesoft prompt (RH brackett)? BEQ IMEDMODE ;Yes - so must be in the immediate mode. RUNNING PLA ;Get the saved (a) back from the stack. CLC ;Clear carry to signal prgm running. RTS (A671) ============= (A672) INTBASIC LDA RUNMODE ;Check Integer basic's run mode flag. BMI RUNNING ;If negative, integer in deferred mode. IMEDMODE PLA ;Get the saved (a) back from the stack. SEC ;Set carry to signal in immediate mode. (A678) RTS ============= (A024) BCC CHKIFRUN ;Branch if basic is running a program: ; (c)=0=basic is running. ; (c)=1=basic not running. * Using immediate mode, so now check if the * parsed command is legal in that mode. (A026) IMMED LDA #%00000010 ;Check bit1 of the cmd's attributes to see AND CMDATTRB,Y ;if the command is legal in that mode. BEQ CHKIFRUN ;Branch if command is legal in that mode. NODIRCMD LDA #15 ;Set the return code to signal error. (A02F) JMP ERRHNDLR ;Go handle the not-direct-command error. ------------ ;(See dis'mbly of errors.) * We are either running a program, or else the * command complies with the immediate mode. (A032) CHKIFRUN CPY #6 ;Check to see if command was a "RUN". BNE TST4NAME ;Branch if cmd wasn't a "RUN". (A036) STY PROMPT ;Put a $06 in prompt if command was a "RUN". * Check to see if a filename * is applicable to the command. (A038) TST4NAME LDA #%001000000 ;Bit 5 = 1 if file name is applicable. AND CMDATTRB,Y (A03D) BEQ FNOTAPPL ;Branch if a file name is not applicable. ;(Example commands: CATALOG, PR#, IN#, ;MON, NOMON, MAXFILES, FP and INT.) * Filename is applicable to the command issued * so go blank out the file name buffers in * anticipation of receiving a name. (A03F) FNXPCTD JSR BLNKFNBF * Blank out both the primary (PRIMFNBF, $AA75) and * secondary (SCNDFNBF, $AA93) file name buffers. (A095) BLNKFNBF LDY #60 ;30 bytes in each buffer. BLNK1RST LDA #" " STORBLNK STA PRIMFNBF-1,Y DEY BNE STORBLNK ;More bytes to blank out. (A09F) RTS (A042) PHP ;Save status register (z-flag) on the stack. ;Note: Z-flag used to signal if dealing (A043) ; with primary or secondary name bufs. FNAMCHR1 JSR PURGECMD ;Get the first char in the name (ignore any ;leading spaces). * Get the first non-space character from the * name in the input buffer. Set the z-flag if * it is a carriage return or a comma. (A1A4) PURGECMD JSR CMDCHAR ;Get the first char. * Get character from the input buffer. * Set z-flag if it is a or comma. (A193) CMDCHAR LDX NDX2INBF ;(x)=index input buf. LDA BUF200,X ;Get next character. CMP #$8D ;Is it a ? BEQ CMDCHRTS ;Yes. INX ;(x) = index 4 next STX NDX2INBF ;anticipated char. CMP #"," ;Is the char a comma? CMDCHRTS RTS (A1A3) (A1A7) BEQ CMDCHRTS ;Exit if or comma. CMP #" " ;Is char a space? BEQ PURGECMD ;Yes - ignore leading spaces. (A1AD) RTS ============ CMDCHRTS RTS (A1A3) ============ (A046) BEQ DONEFN ;Got a "," or so go check if we're done ;the name cause these aren't legal tender ;for a filename. * Check if character is legal tender for the name. * (We know it wasn't a space, comma or carriage * return, but it still may not be legal.) (A048) ASL ;(c) = hi bit of character. (A049) BCC LGLFNCHR ;If inverse or flashing char, OK for name. ;However, note that they can only be poked ;into the input buffer from a RUNNING prgm. ;(This technique is used in many protection ;schemes.) (A04B) BMI LGLFNCHR ;Actually testing bit 6 cause we just did ;an "ASL". If the byte is 11xxxxxx (that ;is, $C0, normal "@" or greater) the char ;is legal tender for a file name. (A04D) JMP CKIFCTRL ;Got an illegal name character so go do a ------------ ;few more checks on control chars, etc. and ;then exit DOS. (That is, was illegal char ;a control char or one of the following ;NORMAL characters: spc, !, ", #, $, %, &, ;', (, ), *, +, comma, ., /, :, ;, <, -, >, ; or a question mark?) * Character is legal tender for a file name. (A050) LGLFNCHR ROR ;Restore the name character. (A051) JMP SVFNCHAR ;Save character in the primary or secondary ------------ ;name buffer. * Process the rest of the characters. (A054) NCHR2ETC JSR CMDCHAR ;Get second and subsequent chars in name. * Get character from the input buffer. * Set z-flag if it is a or ",". (A193) CMDCHAR LDX NDX2INBF ;(x) = index to the input buffer. LDA BUF200,X ;Get the next character. CMP #$8D ;Is it a carriage return? BEQ CMDCHRTS ;Yes. INX ;(x) = index for next anticipated char. STX NDX2INBF CMP #"," ;Is the char a comma? CMDCHRTS RTS (A1A3) (A057) BEQ DONEFN ;Got a carriage return or comma, so go check ;if we just finished the secondary file name. * Put the characters in the file name buffer. (A059) SVFNCHAR STA PRIMFNBF,Y ;(y) = offset from the primary name buff. INY CPY #60 ;Total of 60 chars in both buffers. (A05F) BCC NCHR2ETC ;We haven't hit a comma, EOL marker ;(ie. a carriage return) or done all 60 ;characters yet. * Done all 60 characters, so ignore the * rest of the characters until we encounter * a comma or a carriage return. (A061) PURGEFN JSR CMDCHAR ;Get the next character. * Get character from the input buffer. * Set z-flag if it is a or comma. (A193) CMDCHAR LDX NDX2INBF ;(x) = index to the input buffer. LDA BUF200,X ;Get the next character. CMP #$8D ;Is it a carriage return? BEQ CMDCHRTS ;Yes. INX ;(x) = index for next anticipated char. STX NDX2INBF CMP #"," ;Is the char a comma? CMDCHRTS RTS (A1A3) (A064) BNE PURGEFN ;Not a $00 yet, so go get next character. * Just finished a name, so check if it * was a primary or secondary file name. (A066) DONEFN PLP ;Retrieve the status from the stack. (A067) BNE CKFN2LGL ;If z-flag is clear, just done 2nd name. * Just finished the first file name, so see * if a second file name is also required. * (That is, are we dealing with the RENAME cmd?) (A069) FINFIRST LDY NDX2CMD ;Get the index associated with cmd issued. LDA #%00010000 ;Check bit 4 of CMDATTRB to see if a AND CMDATTRB,Y ;2nd name is required (ie. RENAME?). (A071) BEQ CKFN1LGL ;A 2nd name is not applicable, so go and ;check the primary file name buffer. * A secondary file name is applicable so * we are dealing with the RENAME command. (A073) LDY #30 ;(y) = index to start of 2nd name. PHP ;Put status on the stack (with z-flag clear) (A076) BNE FNAMCHR1 ;to signal that we're dealing with the ;secondary file name & then go back to get ;its characters. (ALWAYS take branch.) * We're done processing associated with the * secondary file name so now check if any of * these characters were legal & therefore * stuck in the secondary file name buffer. (A078) CKFN2LGL LDA SCNDFNBF ;Check first byte of secondary file name. CMP #" " ;If it contains a space, then no legal chars (A07D) BEQ GOXITDOS ;were issued for the 2nd name and therefore ;a second name was required but not issued. * Only the primary file name is applicable. * Check if any legal chars were issued and * therefore put in the primary file name buf. (A07F) CKFN1LGL LDA PRIMFNBF ;If the first character in the buffer is a (A082) CMP #" " ;space, a legal primary file name was not ;issued so fall through to see if a primary ;name was required or optional. (A084) BNE DFLTPRSD ;Branch if we got a primary file name. * A primary file name was not issued. * Therefore, check if it was required or * optional. (If optional, command may * be a CLOSE, LOAD, SAVE OR RUN.) (A086) LDY NDX2CMD ;Get the index associated with the command. LDA #%11000000 ;Check bits 7 & 6 to see if name required. AND CMDATTRB,Y (A08E) BEQ GOXITDOS ;A PRIMARY FILE NAME IS REQUIRED FOR THE ;COMMAND. HOWEVER, NO PRIMARY NAME WAS ;ISSUED. Go exit DOS. * Was command a CLOSE? (A090) BPL DFLTPRSD ;File name wasn't present, but it is no big ;deal because it was optional. GOXITDOS JMP CKIFCTRL ;An optional file name was not issued. (A092) ----------- ;Therefore the command must have been a ;LOAD, RUN or SAVE. These commands can ;also be basic commands. * A file name was not applicable to the * command (ex. CATALOG, MON, NOMON, FP, * INT, PR#, IN# or MAXFILES). (A0A0) FNOTAPPL STA PRIMFNBF ;Put a $00 in the first byte of the primary ;file name buffer. NOTE: Although this ;seems like a begnin instruction, it is ;important in that IT INSURES THAT A ;MATCHING DOS FILE NAME BUFFER WON'T BE ;FOUND WHEN THE GETBUFF ROUTINE IS LATER ;USED BY VARIOUS COMMANDS. As a result, ;the highest numbered (lowest in memory) DOS ;file name buffer will be selected. * The command didn't require a file name, * so now check if it expects any numeric * arguments. (Ie. Was it a PR#, IN# or * MAXFILES command?) (A0A3) LDA #%00001100 ;Test bits 2 & 3 to see if IN#, PR# or AND CMDATTRB,Y ;MAXFILES numeric operand is expected. (A0A8) BEQ DFLTPRSD ;Branch if not expected. * An IN#, PR# or MAXFILES numeric * operand is expected. (A0AA) INPRMAX JSR CNVRTASC ;Convert ASCII number argument to hex. * Convert ASCII to hex or dec. * On entry :NDX2INBF indexes the input buffer. * On exit: A5L/H and (x,a) = low/hi bytes of result. * (c) = 0 = good conversion. * (c) = 1 = invalid chars. (A1B9) CNVRTASC LDA #0 ;Zero out locations to hold result. STA A5L ;Low byte of result. STA A5L+1 ;Hi byte of result. (A1BF) JSR PURGECMD ;Get 1rst non-space char. (A1A4) PURGECMD . . (See dis'mbly above.) . . (RTS) (A1C2) PHP ;Save status (z-flag) on stack. ;z-flag = 1 if or comma. * Check to see if want to convert * ASCII to hex or ASCII to dec. (A1C3) CMP #"$" ;Is the hex symbol present? (A1C5) BEQ ASC2HEX ;Yes - branch 2 convert ASCII to hex. * ASCII to DECIMAL conversion wanted. (A1C7) PLP ;Get status denoting if or ",". (A1C8) JMP CKIFDONE ;Begin decimal conversion of first char. ------------ (A1CB) ASC2DEC JSR PURGECMD ;Get 2nd and subsequent ASCII chars ;to be converted to decimal. (Ignore ;any spaces.) (A1A4) PURGECMD . . (See dis'mbly above.) . . (RTS) (A1CE) CKIFDONE BNE SUBTRASC ;Branch if not or ",". ;(ALWAYS FALL THRU IF ACCESSED FROM ;THE HEX CONVERSION ROUTINE.) * Successful conversion. Exit with * A5L/+1 and (x,a) containing the * low/hi bytes of result. (A1D0) LDX A5L ;Result low. LDA A5L+1 ;Result hi. CLC ;(c) = 0 = good conversion. (A1D5) RTS ;Exit to caller of CNVRTASC. ============= * Check validity of ASCII chars for * representation of decimal numbers. (A1D6) SUBTRASC SEC SBC #$B0 ;Subtract ASCII "0". BMI NOTASCII ;Error, cause less than 0. CMP #$0A (A1DD) BCS NOTASCII ;Error, cause greater than 9. * Multiply running result times ten * and then add new digit. (A1DF) JSR DOUBLE ;Get result * 2. * Multiply two-byte result times two. * ("Roll" hi and low bytes as a unit. * Pick up (c) in hi byte.) (A1FE) DOUBLE ASL A5L ROL A5L+1 (A202) RTS (A1E2) ADC A5L TAX ;(x) = low result * 2 + new digit. LDA #0 ADC A5L+1 ;Add (c) to hi byte of result. TAY ;(y) = hi byte of result * 2 + (c). JSR DOUBLE ;(a) = result * 8. JSR DOUBLE ;(See dis'mbly above.) TXA ADC A5L STA A5L ;(result*2+new digit) + (result * 8). TYA ADC A5L+1 ;Add (c) to update hi byte. STA A5L+1 (A1FA) BCC ASC2DEC ;Branch if # < 65536. * Error - invalid ASCII number. (A1FC) NOTASCII SEC ;Exit with (c) = 1 to signal error. (A1FD) RTS ;Return to caller of CNVRTASC. ============= * Convert ASCII chars to HEX. (A203) ASC2HEX PLP ;Throw saved status off stack. GETASCII JSR PURGECMD ;Get first & subsequent chars that (A204) ;occur after the hex symbol ("$"). ;(See dis'mlby above.) (A207) BEQ CKIFDONE ;Go exit if or ",". * Check validity of ASCII chars * to be converted to hex. (A209) SEC SBC #$B0 ;Subtract ASCII "0". BMI NOTASCII ;Error cause less than 0. CMP #$0A BCC PRP2DUBL ;Valid: 0 <------> 9. SBC #7 ;Check hi range of hex numbers. BMI NOTASCII ;Error cause > $09 and < $0A. CMP #$10 (A218) BCS NOTASCII ;Error cause > $0F. * Move result in A5L/+1 up a nibble * by rolling it as a unit (ie. *16). (A21A) PRP2DUBL LDX #4 ;(x) = # of times to double unit. TIMES2 JSR DOUBLE ;Multiply result * 2. (A21C) ;(See dis'mbly above.) (A21F) DEX (A220) BNE TIMES2 ;More multiplication to do. * Merge hex representation of digit * into low nibble position of result. * * NOTE BUG: No check is made to trap * numbers > $FFFF. If too many numbers * are input, only the last four digits * are reflected in the result. (A222) ORA A5L STA A5L (A226) JMP GETASCII ;Go get next char to convert. ------------- (A0AD) BCS TOSYNTX ;Character not #, exit with syntax error. TAY ;(y) = hi byte of converted char. BNE ARGRNGER ;Range error - cause value > 255. CPX #17 (A0B4) BCS ARGRNGER ;Range error because the argument for ;MAXFILES or IN# or PR# was too large ;(ie. greater than 16). * Was the command a PR# or IN#? (A0B6) LDY NDX2CMD ;Check to see if a slot value is applicable LDA #%00001000 ;to the command. AND CMDATTRB,Y (A0BE) BEQ MAXFMIN ;Slot value is not applicable, therefore we ;must be dealing with the MAXFILES command. * The command was PR# or IN# so now check * if the slot value is too large or not. * (Legal range is 0 to 7.) (A0C0) CPX #8 ;Too large? BCS GOXITDOS ;Yes - bail out. (A0C4) BCC DFLTPRSD ;No -set defaults & continue parsing. ------------ * Check the minimum value for the * MAXFILES command. (Range is 1 to 16.) (A0C6) MAXFMIN TXA (A0C7) BNE DFLTPRSD ;Not 0, so okay. * Got a range error because the MAXFILES argument * or slot values for IN# or PR# were illegal. (A0C9) ARGRNGER LDA #2 ;Set the return code for a range error. (A0CB) JMP ERRHNDLR ;Go handle the error. ------------ ;(See dis'mbly of errors.) TOSYNTX JMP SYNTXERR ;Exit via syntax error. (A0CE) ------------ ;(See dis'mbly of errors.) * Initialize CUMLOPTN and parsed table. (A0D1) DFLTPRSD LDA #0 (A0D3) STA CUMLOPTN ;Set the cummulative option byte to a ;default value of 0 to assume that no ;options were issued. (A0D6) STA MONPRSD ;Set the default values in the parsed table. STA VOLPRSD ;That is assume that: STA LENPRSD ; - C, I, O weren't issued. STA LENPRSD+1; - volume # and length are zero. (A0E2) JSR ZEROPTCH ;Set TEMPBYT & BYTPRSD to 0 defaults. (BFDC) ZEROPTCH STA TEMPBYT STA BYTPRSD STA BYTPRSD+1 (BFE5) RTS (A0E5) LDA NDX2INBF ;Irrelevant, might as well be 3 "NOP"s. ;(Made obsolete by ZEROPTCH.) * Do more parsing of the command line. * (Ignore any commas or spaces.) (A0E8) NXCMDCHR JSR PURGECMD ;Get the next command character. * Get the first non-space character from the * name in the input buffer. Set the z-flag if * it is a carriage return or a comma. (A1A4) PRUGECMD JSR CMDCHAR ;Get the first char. * Get character from the input buffer. * Set z-flag if or comma. (A193) CMDCHAR LDX NDX2INBF ;(x)=index input buf. LDA BUF200,X ;Get next character. CMP #$8D ;Is it a ? BEQ CMDCHRTS ;Yes. INX ;(x)=index 4 next STX NDX2INBF ;anticipated char. CMP #"," ;Is the char a comma? CMDCHRTS RTS (A1A3) (A1A7) BEQ CMDCHRTS ;Exit if or comma. CMP #" " ;Is char a space? BEQ PURGECMD ;Yes - ignore leading spaces. (A1AD) RTS ============== CMDCHRTS RTS (A1A3) ============= (A0EB) BNE CHKOPTNS ;If it isn't a carriage return or comma, ;maybe it is an option, so take the branch ;to check it out. (A0ED) CMP #$8D ;Was it a carriage return? (A0EF) BNE NXCMDCHR ;No - so must have been a comma. ;Branch back to ignore commas. * Got a carriage return (ie. an End-Of-Line marker), * so now we're done parsing and must make sure that * the cummulative record of the options we encountered * is applicable to the command. (A0F1) LDX NDX2CMD LDA CUMLOPTN ;Check if options are legal. ORA CMDATTRB+1,X EOR CMDATTRB+1,X (A0FD) BNE GOXITDOS ;Illegal, so go exit. * Cummulative record of options can legally * be associated with the command. (A0FF) LDX TEMPBYT ;TEMPBYT = 0 as set in ZEROPTCH. (A102) BEQ TODOSCMD ;ALWAYS. * Meaningless instructions (made obsolete * by the inclusion of ZEROPTCH). (A104) STA TEMPBYT STX NDX2INBF (A10A) BNE NXCMDCHR * Check if the character represents an option * (ie. A, B, R, L, S, D, V, C, I, or O). (A10C) CHKOPTNS LDX #10 CKNXOPTN CMP OPTNTXT-1,X BEQ OPTNOK ;Found an option. DEX (A114) BNE CKNXOPTN ;Haven't checked all options yet. TOTOSYNT BEQ TOSYNTX ;Couldn't find a match. (Syntax err - char (A116) ------------ ;doesn't represent an option.) * Found an option character, so check if it * was a "C", "I" or "O". (That is, is a * numeric argument expected?) (A118) OPTNOK LDA OPTNISSD-1,X (A11B) BMI CIOPTNS ;If the hi bit = 0, then a "C", "I" or "O" ;was detected and therefore no numeric ;argument is required. * Update the cummulative options byte to * reflect the latest option. (A11D) ORA CUMLOPTN (A120) STA CUMLOPTN * Now check if the numeric argument that * was issued with the option is legal. (A123) DEX ;Reduce the counter that was kicked up in ;anticipation of getting more chars in the ;CMDCHAR routine. (A124) STX NDX2OPTN ;Save the index to the option. (A127) JSR CNVRTASC ;Convert ASCII number to hex. * Convert ASCII to hex or dec. * On entry : NDX2INBF indexes the input buffer. * On exit: A5L/H and (x,a) = low/hi bytes of result. * (c) = 0 = good conversion. * (c) = 1 = invalid chars. (A1B9) CNVRTASC . . (See dis'mbly above.) . . (RTS) * Was the character numeric? (A12A) BCS TOSYNTX ;No - got a syntax error. * Character was numeric. (A12C) LDA NDX2OPTN ;Retrieve the index to the option. ASL ;Times 4 cause going to check minimum and (A130) ASL ;maximum values of the legal ranges ;associated with option (2 bytes each). (A131) TAY ;(y) = index to the legal range table. * Check if the argument is too large. (A132) LDA A5L+1 ;Get the hi byte of the argument. BNE CKMAXVAL ;Branch if hi byte is a non-zero. LDA A5L ;Hi byte was 0, so now check the low byte. CMP OPTNRNG,Y (A13B) BCC ARGRNGER ;Range error -argument < minimum legal val. * Check if the argument is less than or * equal to the maximum legal value plus 1. (A13D) LDA A5L+1 CKMAXVAL CMP OPTNRNG+3,Y ;Compare hi byte to the max legal val. BCC SVALOPTN ;Less than maximum, so argument is okay. TOARGRNG BNE ARGRNGER ;Argument greater than maximum legal value, (A144) ;so got range error. (A146) LDA A5L ;Now check if the low byte of the argument CMP OPTNRNG+2,Y ;complies to the maximum legal low byte. BCC SVALOPTN ;Argument is legal. (A14D) BNE TOARGRNG ;Argument is illegal. * Save the option value in the parsed table. (A14F) SVALOPTN LDA TEMPBYT ;Obsolete, because TEMPBYT was set to zero BNE NXCMDCHR ;in ZEROPTCH so ALWAYS FALL THROUGH. TYA ;(y) --> (a) = index to option ranges. (A155) LSR ;Divide by two because each option range ;table had 4 bytes, but each parsed value ;entry is only two bytes long. (A156) TAY ;Put the index to the parsed table in (y). LDA A5L+1 ;Store the argument in the parsed table. STA VOLPRSD+1,Y LDA A5L (A15E) STA VOLPRSD,Y * Go see if any more options are present * on the command line. TONXOPTN JMP NXCMDCHR (A161) ------------ * Option characters issued were "C", "I" or "O". (A164) CIOPTNS PHA ;Put (a) = OPTNISSD on the stack. LDA #%10000000 ;Update the CUMLOPTN to signal that "C", ORA CUMLOPTN ;"I" or "O" options were issued. STA CUMLOPTN PLA ;Get (a) = OPTNISSD back from the stack. AND #%01111111 ;Turn the hi bit off. ORA MONPRSD ;Update MONPRSD in the parsed table. STA MONPRSD BNE TONXOPTN ;Go see if any more options. (A178) BEQ TOTOSYNT ;Irrelevant. ------------ * FINAL PROCESSING OF THE DOS COMMAND. (A17A) TODOSCMD JSR DODOSCMD ;Go do the DOS command. * DO THE DOS COMMAND. (A180) DODOSCMD JSR RESTAT0 ;Reset to condition 0. * Reset CONDNFLG & OPUTCOND to 0. (A75B) RESTAT0 LDY #0 STY CONDNFLG STY OPUTCOND (A763) RTS * Zero out the FM parameter list so we * can customize it in accordance with * the specific DOS cmd handler called. (A183) JSR CLRFMPRM * Clear out the FM parameter list. (A1AE) CLRFMPRM LDA #0 LDY #$16 ZFMPARM STA FMPRMLST-1,Y DEY BNE ZFMPARM (A1B8) RTS * Do a stack jump to process the command. * (NOTE: THE COMMAND HANDLER IS ALWAYS * ENTERED WITH CONDNFLG & OPUTCOND = 0.) (A186) LDA NDX2CMD ;Get (a) = index to the command. TAX ;(x) = index to the table of entry points. LDA CMDTBL+1,X ;Get the address of the command's routine PHA ;and put it on the stack (hi byte first). LDA CMDTBL,X PHA (A192) RTS ;Do a "stack jump" to execute the command. . . ******************************** * * * COMMAND HANDLER EXECUTES * * THE DOS COMMAND. * * * * (See formatted disassembly * * of the appropriate command * * handler.) * * * ******************************** . . (RTS) *:::::::::::::::::::::::::::::::::::::::::::::::::::::* * * * NOTE - MOST, BUT NOT ALL, DOS COMMANDS RETURN HERE. * * - If an error is encountered, execution exits DOS's * * error handler via RESTART ($D43C) or basic's * * error-handling routine (BSCERHLR, $D865). * * - FP exits back into DOS's coldstart routine * * (DOSCOLD, $9D84). * * - INT & CHAIN go into Integer basic if the integer * * language is available. * * - The WRITE & READ commands return to the FINSHCMD * * ($9F83) routine shown below. * * - BLOAD returns here if it was not called by the * * BRUN command. Otherwise, BLOAD returns to the * * BRUN command handler at $A391. * * - BRUN executes the binary file before returning. * * IF the BRUNed prgm performed any input or ouput, * * or if the binary file was BRUN with MON in effect,* * the program gets hung up in an infinite loop. * * (See formatted disassembly of the BRUN command * * handler for details.) * * - The LOAD command goes into Applesoft (at $D4F2) * * to reset the program link pointers and then on to * * the RESTART routine ($D43C). If the LOAD cmd was * * called from a RUN, execution jumps back into the * * RUN command handler at RUNFPINT ($A4DC). * * - The RUN cmd exits into Applesoft at STKINI * * ($D683) and eventually returns to the RESTART * * routine ($D43C). * * * *:::::::::::::::::::::::::::::::::::::::::::::::::::::* (A17D) AFTRCMD JMP FINSHCMD ------------ * Common routine used to finish off MOST DOS commands. * The write and read command handlers return here. * CMDWRITE ($A510) sets the output condition (OPUTCOND, * $AA51) to 5 before returning. CMDREAD ($A51B) returns * with the condition flag (CONDNFLG, $AA52) set to 1. (9F83) FINSHCMD LDA BUF200 ;Get the first character in the input buf. CMP DCTRLCHR ;Was the command done via DOS's ctrl char? (9F89) BEQ DSPLYCMD ;Yes. * Cancel the command by replacing the * character with a carriage return and * then fall through to continue the exit * sequence. (9F8B) LDA #$8D ;Place a at the start of STA BUF200 ;the input buffer. LDX #0 ;Set the index to the input buffer to zero. (9F92) STX XSAVED * Display the character output conditionally. * (That is, prepare to send the char to the * ouput device.) (9F95) DSPLYCMD LDA #%01000000 ;Set bit 6 to see if using "MON C". BNE DSPLYCHR ;ALWAYS. DSPLYOUT LDA #%00010000 ;Set bit 4 to see if using "MON O". BNE DSPLYCHR ;ALWAYS. DSPLYINP LDA #%00100000 ;Set bit 5 to see if using "MON I". DSPLYCHR AND CIOCUMUL ;Test the flag to see if should display. (9FA2) BEQ DOSEXIT ;Don't display - the specific bit of ;interest was off. (MON/NOMON = clr/set.) * Display the character. (9FA4) DSPLYALL JSR RESTOREG * Restore (a), (y) & (x) registers. (9FBA) RESTOREG LDA ASAVED LDY YSAVED LDX XSAVED SEC ;Why????? (9FC4) RTS (9FA7) JSR GODSPLY (9FC5) GODSPLY JMP (CSW) ;************** N O T E **************** ----------- ;* If a BRUN cmd was just executed, * ;* CSW points to OPUTINCP. The saved * ;* stack pointer (STKSAVED, $AA75) gets* ;* reset by OPUTINCP when the char is * ;* printed. As a result, the computer * ;* gets hung up in an infinite loop * ;* running between $9FAA and $9FC4. * ;* (See dis'mbly below.) * ;*************************************** * Print the character through the true * output handler. (FDF0) COUT1 . . (See dis'mbly in APPLE II REFERENCE MANUAL.) . . (RTS) * Save the registers. (9FAA) STA ASAVED ;Save (a), (y) & (x) registers. STY YSAVED (9FB0) STX XSAVED * Routine to exit DOS. * Reset hooks & stack pointer. (9FB3) DOSEXIT JSR INITIOHK ;Reset DOS hooks. * Initialize the I/O hooks so that DOS intercepts * all input & output. For instance, if a routine * encounters a "COUT JMP (CSW)", then execution will * actually flow to DOS's output routine (OPUTINCP, * $9EBD). Similarly, any routine that refers to * "RDKEY JMP (KSW)" will actually jump to DOS's * input routine (INPTINCP, $9E81). * * The true (ie. normal) hooks are saved, ex: * KSW: KEYIN --> KSWTRUE: KEYIN. * CSW: COUT1 --> CSWTRUE: COUT1. * The intercepts are then set as follows: * ADINPTCP: INPTINCP --> KSW: INPTINCP. * ADOPUTCP: OPUTINCP --> CSW: OPUTINCP. * Check if input hook needs to be reset. (A851) INITIOHK LDA KSW+1 CMP ADINPTCP+1 (A856) BEQ CKOUTHK ;Input hook already points to DOS's ;input handler, so go check output hook. * Reset input hook to point to DOS. (A858) STA KSWTRUE+1 ;KSW: KEYIN --> KSWTRUE: KEYIN. LDA KSW STA KSWTRUE LDA ADINPTCP ;ADINPTCP: INPTINCP --> KSW: INPTINCP. STA KSW LDA ADINPTCP+1 (A868) STA KSW+1 * Check if output hook needs to be reset. (A86A) CKOUTHK LDA CSW+1 CMP ADOPUTCP+1 (A86F) BEQ SETHKRTN ;Output hook already points to DOS's ;output handler, so go exit. * Reset output hook to point to DOS. (A871) STA CSWTRUE+1 ;CSW: COUT1 --> CSWTRUE: COUT1. LDA CSW STA CSWTRUE LDA ADOPUTCP ;ADOPUTCP: OPUTINCP --> CSW: OPUTINCP. STA CSW LDA ADOPUTCP+1 STA CSW+1 SETHKRTN RTS (A883) * Reset stack pointer & save registers. (9FB6) LDX STKSAVED ;Retrieve the saved stack pointer value TXS ;& reset the stack to return to caller. RESTOREG LDA ASAVED ;Restore (a), (y) & (x) registers. LDY YSAVED LDX XSAVED SEC ;Return to routine that called routine (9FC4) RTS ;that contained "JSR PREP4DOS" instruc. ;NORMALLY RETURNS TO $D533. *================================================================* * * * TABLES USED BY DOS'S COMMAND PARSING * * AND PROCESSING ROUTINES * * * *----------------------------------------------------------------* * * * The following tables are a hacker's dream. By simply * * altering just a few bytes here and there, you can easily * * customize DOS to frustrate most fluffwear users. * * * ****************************************************************** * DOS'S COMMAND TEXT TABLE ($A884 - $A908). * * The names of the different DOS commands * can readily be changed by altering the * information in this table. If you decide * to mess around with this table, be sure * to: - let the first command create a new * file. (For explanation, see the FMXTNTRY * routine ($AAFD) in the linear dis'mbly.) * - avoid creating new DOS command names * that duplicate basic command names. * - use positive ASCII characters for * all but the last character of each name. * - enter the last character in each name * in negative ASCII form. * - shift subsequent names to keep all characters * contiguous if you create shorter names. * - don't expand the table beyond $A908. * - end the table with a $00 byte. (A884) CMDTXTBL DCI 'INIT' ;($A884 - $A887) DCI 'LOAD' ;($A888 - $A88B) DCI 'SAVE' ;($A88C - $A88F) DCI 'RUN' ;($A890 - $A892) DCI 'CHAIN' ;($A893 - $A897) DCI 'DELETE' ;($A898 - $A89D) DCI 'LOCK' ;($A89E - $A8A1) DCI 'UNLOCK' ;($A8A2 - $A8A7) DCI 'CLOSE' ;($A8A8 - $A8AC) DCI 'READ' ;($A8AD - $A8B0) DCI 'EXEC' ;($A8B1 - $A8B4) DCI 'WRITE' ;($A8B5 - $A8B9) DCI 'POSITION' ;($A8BA - $A8C1) DCI 'OPEN' ;($A8C2 - $A8C5) DCI 'APPEND' ;($A8C6 - $A8CB) DCI 'RENAME' ;($A8CC - $A8D1) DCI 'CATALOG' ;($A8D2 - $A8D8) DCI 'MON' ;($A8D9 - $A8DB) DCI 'NOMON' ;($A8DC - $A8E0) DCI 'PR#' ;($A8E1 - $A8E3) DCI 'IN#' ;($A8E4 - $A8E6) DCI 'MAXFILES' ;($A8E7 - $A8EE) DCI 'FP' ;($A8EF - $A8F0) DCI 'INT' ;($A8F1 - $A8F3) DCI 'BSAVE' ;($A8F4 - $A8F8) DCI 'BLOAD' ;($A8F9 - $A8FD) DCI 'BRUN' ;($A8FE - $A901) DCI 'VERIFY' ;($A902 - $A907) (A908) HEX 00 ;Zero byte signals end of table. * TABLE OF COMMAND ATTRIBUTES ($A909 - $A940). * * Note: Any alterations to this table should be * done with a degree of caution because several * parameters acquire certain default values in * the actual command handling routines. With * just a bit of inspection of the handling routines * however, you should be able to safely make * extensive changes if you so desire. * * This table is indexed by a value corresponding * to the parsed DOS command. Two bytes of info * are associated with each comand. A SET bit * denotes the following: * * * 7 6 5 4 3 2 1 0 * . . . . . . . ...Cmd can create new file if file not found. * . . . . . . ......Cmd restricted to deferred mode. * . . . . . .........Value for MAXFILES required. * . . . . ............Slot value for PR# or IN# required. * . . . ...............Secondary file name required (RENAME command). * . . ..................A file name is applicable to the command (ie. * . . a file name is expected but not necessarily * . . required). * . .....................Execute the DOS comand (even if no file * . name was issued with the command). * ........................Interpret the command as a basic command * if no file name is issued with command. * * * 7 6 5 4 3 2 1 0 * . . . . . . . ...A(ddress) parameter is allowed. * . . . . . . ......B(yte) parameter is allowed. * . . . . . .........R(ecord) number or R(elative field * . . . . . pos'n) parameter is allowed. * . . . . ............L(ength) parameter is allowed. * . . . ...............S(lot) parameter is allowed. * . . ..................D(rive) parameter is allowed. * . .....................V(olume) parameter is allowed. * ........................C, I or O parameters are allowed. (A909) CMDATTRB HEX 2170 ;INIT 0010 0001 0111 0000 HEX A070 ;LOAD 1010 0000 0111 0000 HEX A170 ;SAVE 1010 0001 0111 0000 HEX A070 ;RUN 1010 0000 0111 0000 HEX 2070 ;CHAIN 0010 0000 0111 0000 HEX 2070 ;DELETE 0010 0000 0111 0000 HEX 2070 ;LOCK 0010 0000 0111 0000 HEX 2070 ;UNLOCK 0010 0000 0111 0000 HEX 6000 ;CLOSE 0110 0000 0000 0000 HEX 2206 ;READ 0010 0010 0000 0110 HEX 2074 ;EXEC 0010 0000 0111 0100 HEX 2206 ;WRITE 0010 0010 0000 0110 HEX 2204 ;POSITION 0010 0010 0000 0100 HEX 2378 ;OPEN 0010 0011 0111 1000 HEX 2270 ;APPEND 0010 0010 0111 0000 HEX 3070 ;RENAME 0011 0000 0111 0000 HEX 4070 ;CATALOG 0100 0000 0111 0000 HEX 4080 ;MON 0100 0000 1000 0000 HEX 4080 ;NOMON 0100 0000 1000 0000 HEX 0800 ;PR# 0000 1000 0000 0000 HEX 0800 ;IN# 0000 1000 0000 0000 HEX 0400 ;MAXFILES 0000 0100 0000 0000 HEX 4070 ;FP 0100 0000 0111 0000 HEX 4000 ;INT 0100 0000 0000 0000 HEX 2179 ;BSAVE 0010 0001 0111 1001 HEX 2071 ;BLOAD 0010 0000 0111 0001 HEX 2071 ;BRUN 0010 0000 0111 0001 (A93F) HEX 2070 ;VERIFY 0010 0000 0111 0000 * OPTION CHARACTER SYMBOL TABLE ($A941 - $A94A). * These chars are frequently changed as part of * a protection scheme. (A941) ASC "VDSLRBACIO" * OPTIONS ISSUED TABLE ($A94B - $A954). * A set bit in the following positions * (of CUMLOPTN, $AA65) denotes the options * that were issued with a command. (A94B) OPTNISSD HEX 40 ;V(olume) parameter. HEX 20 ;D(rive) parameter. HEX 10 ;S(lot) parameter. HEX 08 ;L(ength) parameter. HEX 04 ;R(ecord) # or R(elative field pos'n) parameter. HEX 02 ;B(yte) parameter. HEX 01 ;A(ddress) parameter. HEX C0 ;C(ommand) display parameter. HEX A0 ;I(nput) display parameter. (A954) HEX 90 ;O(utput) display parameter. * OPTION RANGES ($A955 - $A970). * Table of valid ranges associated * with each option character. * (The upper range associated with the * L-parameter is often changed to allow * DOS to handle larger files.) (A955) OPTNRNG HEX 0000FE00 ;V: (0 - 254). HEX 01000200 ;D: (1 - 2). HEX 01000700 ;S: (1 - 7). HEX 0100FF7F ;L: (1 - 32767). HEX 0000FF7F ;R: (0 - 32767). HEX 0000FF7F ;B: (0 - 32767). (A96D) HEX 0000FFFF ;A: (0 - 65535). * PARSED OPTION VALUES TABLE * ($AA66 = $AA93). * A record of the option values that * were issued with the command. Some * of these location are often defaulted * to a given value. ($AA66) VOLPRSD DS 2 ;Parsed volume number value. DRVPRSD DS 2 ;Parsed drive number value. SLOTPRSD DS 2 ;Parsed slot number value. LENPRSD DS 2 ;Parsed length value. RECPRSD DS 2 ;Parsed record number or relative field pos'n value. BYTPRSD DS 2 ;Parsed byte offset value. ADRPRSD DS 2 ;Parsed address value. MONPRSD DS 2 ;Parsed MON/NOMON value. ($AA91) * Command handler entry point table * ($9D1E - $9D54). * * (All addresses are one less than the * actual entry point because these routines * are entered via a "stack jump".) * If you create a new command, be sure to * alter the entry point address of the old * command. Some authors simply disable commands * by pointing them at the coldstart routine or * placing returns at their entry points. (9D1E) CMDTBL DA CMDINIT-1 ;$A54F-1 DA CMDLOAD-1 ;$A413-1 DA CMDSAVE-1 ;$A397-1 DA CMDRUN-1 ;$A4D1-1 DA CMDCHAIN-1 ;$A4F0-1 DA CMDELTE-1 ;$A263-1 DA CMDLOCK-1 ;$A271-1 DA CMDUNLOK-1 ;$A275-1 DA CMDCLOSE-1 ;$A2EA-1 DA CMDREAD-1 ;$A51B-1 DA CMDEXEC-1 ;$A5C6-1 DA CMDWRITE-1 ;$A510-1 DA CMDPOSN-1 ;$A5DD-1 DA CMDOPEN-1 ;$A2A3-1 DA CMDAPPND-1 ;$A298-1 DA CMDRENAM-1 ;$A281-1 DA CMDCATLG-1 ;$A56E-1 DA CMDMON-1 ;$A233-1 DA CMDNOMON-1 ;$A23D-1 DA CMDPR-1 ;$A229-1 DA CMDIN-1 ;$A22E-1 DA CMDMXFIL-1 ;$A251-1 DA CMDFP-1 ;$A57A-1 DA CMDINT-1 ;$A59E-1 DA CMDBSAVE-1 ;$A331-1 DA CMDBLOAD-1 ;$A35D-1 DA CMDBRUN-1 ;$A38E-1 (9D53) DA CMDVERFY-1 ;$A27D-1