* AUXMEM.OSCLI.S * (c) BOBBI 2021-2022 GPLv3 * * Handle OSCLI system calls * 22-Aug-2021 Uses dispatch table * Prepares parameters and hands on to API call * 24-Aug-2021 Combined *LOAD and *SAVE, full address parsing. * 02-Sep-2021 *LOAD/*SAVE now uses GSTRANS. * 12-Sep-2021 *HELP uses subject lookup, *HELP MOS, *HELP HOSTFS. * 25-Oct-2021 Implemented *BASIC. * 07-Oct-2022 *CLOSE is a host command, fixed *EXEC. * 08-Oct-2022 Rewrote *TYPE, *DUMP, *SPOOL, shares code with *EXEC. * Sorted command table, added *HELP FILE. * Optimised CLILOOK dispatcher. * COMMAND TABLE *************** * Table structure is: { string, byte OR $80, destword-1 } $00 * Commands are entered with A=command byte with b7=1 * EQ=no parameter * b6=0 - Enter with XY=>parameters * b6=1 - Enter with LPTR,Y=>parameters * * Ok, let's get around to sorting these CMDTABLE CMDFILE ASC 'CAT' ; Must be first command so matches '*.' DB $85 DW STARFSC-1 ; CAT -> FSC 5, XY=>params ASC 'CDIR' DB $88 DW STARFILE-1 ; CDIR -> OSFILE 08, CBLK=>filename ASC 'CLOSE' DB $FF DW CMDCLOSE-1 ; CLOSE -> (LPTR)=>params ASC 'DELETE' DB $86 DW STARFILE-1 ; DELETE -> OSFILE 06, CBLK=>filename ASC 'DUMP' DB $81 ; TO DO DW CMDDUMP-1 ; DUMP -> XY=>params ASC 'EXEC' DB $81 ; TO DO DW CMDEXEC-1 ; EXEC -> XY=>params ASC 'EX' DB $89 DW STARFSC-1 ; EX -> FSC 9, XY=>params ASC 'INFO' DB $8A DW STARFSC-1 ; INFO -> FSC 10, XY=>params ASC 'LOAD' DB $81 DW STARLOAD-1 ; LOAD -> OSFILE FF, CBLK=>filename ASC 'MKDIR' DB $88 DW STARFILE-1 ; MKDIR -> OSFILE 08, CBLK=>filename ASC 'OPT' DB $8B DW STARBYTE-1 ; OPT -> OSBYTE &8B,X,Y XY=>params ASC 'RUN' DB $84 DW STARFSC-1 ; RUN -> FSC 4, XY=>params ASC 'RENAME' DB $8C DW STARFSC-1 ; RENAME -> FSC 12, XY=>params ASC 'SAVE' DB $81 DW STARSAVE-1 ; SAVE -> OSFILE 00, CBLK=>filename ASC 'SPOOL' DB $81 ; TO DO DW CMDSPOOL-1 ; SPOOL -> XY=>params ASC 'TYPE' DB $81 ; TO DO DW CMDTYPE-1 ; TYPE -> XY=>params * Split between HELP lists DB $00 * CMDMOS ASC 'BASIC' DB $FF DW STARBASIC-1 ; BASIC -> (LPTR)=>params ASC 'ECHO' DB $FF DW CMDECHO-1 ; ECHO -> (LPTR)=>params ASC 'FX' DB $FF DW STARFX-1 ; FX -> OSBYTE A,X,Y (LPTR)=>params ASC 'FAST' DB $FF DW CMDFAST-1 ; FAST -> (LPTR)=>params ASC 'HELP' DB $81 DW STARHELP-1 ; HELP -> XY=>params ASC 'KEY' DB $FF DW STARKEY-1 ; KEY -> (LPTR)=>params ASC 'QUIT' DB $FF DW STARQUIT-1 ; QUIT -> (LPTR)=>params ASC 'SLOW' DB $FF DW CMDSLOW-1 ; SLOW -> (LPTR)=>params * Table terminator DB $FF * *HELP TABLE ************* HLPTABLE ASC 'MOS' DB $80 DW HELPMOS-1 ; *HELP MOS ASC 'FILE' DB $80 DW HELPFILE-1 ; *HELP FILE ASC 'HOSTFS' DB $80 DW HELPHOSTFS-1 ; *HELP HOSTFS DB $FF * Command table lookup * On entry, (OSLPTR)=>command string * XY=>command table * On exit, A=0 done, command called * A<>0 no match * * Search command table CLILOOKUP STX OSTEXT+0 ; Start of command table STY OSTEXT+1 LDX #0 ; (ZP,X)=>command table CLILP4 LDY #0 ; Start of command line CLILP5 LDA (OSTEXT,X) BMI CLIMATCH ; End of table string EOR (OSLPTR),Y AND #$DF ; Force upper case match BNE CLINOMATCH JSR CLISTEP ; Step to next table char INY ; Step to next command char BNE CLILP5 ; Loop to check CLINOMATCH LDA (OSLPTR),Y CMP #'.' ; Abbreviation? BEQ CLIDOT CLINEXT JSR CLISTEP ; No match, step to next entry BPL CLINEXT CLINEXT2 JSR CLISTEP ; Step past byte, address JSR CLISTEP JSR CLISTEP BNE CLINEXT3 JSR CLISTEP CLINEXT3 BPL CLILP4 ; Loop to check next RTS ; Exit, A>$7F CLIDOT LDA (OSTEXT,X) BMI CLINEXT2 ; Dot after full word, no match CLIDOT2 JSR CLISTEP ; Step to command address BPL CLIDOT2 INY ; Step past dot BNE CLIMATCH2 ; Jump to this command CLIMATCH LDA (OSLPTR),Y CMP #'.' BEQ CLINEXT ; Longer abbreviation, eg 'CAT.' CMP #'A' BCS CLINEXT ; More letters, eg 'HELPER' CLIMATCH2 JSR CLIMATCH3 ; Call the routine LDA #0 RTS ; Return A=0 to claim CLIMATCH3 JSR SKIPSPC ; (OSLPTR),Y=>parameters PHP ; Save EQ=end of line LDA (OSTEXT,X) ; Command byte STA OSTEMP JSR CLISTEP ; Address low byte PHA JSR CLISTEP ; Address high byte TAX ; X=high PLA ; A=low PLP ; EQ=end of line PHX ; SP->high PHA ; SP->low, high PHP ; SP->flg, low, high * BIT OSTEMP * BVS CLICALL LDA OSTEMP ASL A ; Move bit 6 into bit 7 BMI CLICALL ; If b6=1 don't convert LPTR BEQ CLICALL ; *TEMP* If $80, skip - remove when HOSTFS updated * JSR LPTRtoXY ; XY=>parameters CLICALL LDA OSTEMP ; A=command parameter PLP ; EQ=no parameters RTS ; Call command routine CLISTEP INC OSTEXT+0,X ; Point to next table byte BNE CLISTEP2 INC OSTEXT+1,X CLISTEP2 LDA (OSTEXT,X) ; Get next byte RTS * OSCLI HANDLER * On entry, XY=>command string * On exit, AXY corrupted or error generated * CLIHND JSR XYtoLPTR ; LPTR=>command line CLILP1 LDA (OSLPTR),Y CMP #$0D BEQ CLI2 INY BNE CLILP1 CLIEXIT1 RTS ; No terminating CLI2 LDY #$FF CLILP2 JSR SKIPSPC1 ; Skip leading spaces CMP #'*' ; Skip leading stars BEQ CLILP2 CMP #$0D BEQ CLIEXIT1 ; Null string CMP #'|' BEQ CLIEXIT1 ; Comment CMP #'/' BEQ CLISLASH JSR LPTRtoXY ; Add Y to LPTR JSR XYtoLPTR ; LPTR=>start of actual command LDX #command table LDY #>CMDTABLE JSR CLILOOKUP ; Look for command BNE CLIUNKNOWN ; No match CLIDONE RTS CLISLASH JSR SKIPSPC1 BEQ CLIDONE ; */ LDA #$02 BNE STARFSC2 ; FSC 2 = */filename * CLIUNKNOWN LDA #$04 JSR SERVICE ; Offer to sideways ROM(s) BEQ CLIDONE ; Claimed LDA #$03 ; FSC 3 = unknown command STARFSC2 PHA JSR LPTRtoXY ; XY=>command PLA STARFSC AND #$7F ; A=command, XY=>parameters JSR CALLFSCV ; Hand on to filing system TAX BEQ CLIDONE ; A=0, FSC call implemented ERRBADCMD BRK DB $FE ASC 'Bad command' ERRBADNUM BRK DB $FC ASC 'Bad number' ERRBADADD BRK DB $FC ASC 'Bad address' BRK * MOS COMMANDS ************** * *FX num(,num(,num)) * ------------------- STARFX JSR SCANDEC BRA STARBYTE1 * Commands passed to OSBYTE * ------------------------- STARBYTE JSR XYtoLPTR STARBYTE1 STA OSAREG ; Save OSBYTE number LDA #$00 ; Default X and Y STA OSXREG STA OSYREG JSR SKIPCOMMA ; Step past any comma/spaces BEQ STARBYTE2 ; End of line, do it JSR SCANDEC ; Scan for X param STA OSXREG ; Store it JSR SKIPCOMMA ; Step past any comma/spaces BEQ STARBYTE2 ; End of line, do it JSR SCANDEC ; Scan for Y param STA OSYREG ; Store it JSR SKIPSPC BNE ERRBADCMD ; More params, error STARBYTE2 LDY OSYREG LDX OSXREG LDA OSAREG JSR OSBYTE BVS ERRBADCMD RTS * Line scanning * ------------- * Scan decimal number SCANDEC JSR SKIPSPC JSR SCANDIGIT ; Check first digit BCS ERRBADNUM ; Doesn't start with a digit SCANDECLP STA OSTEMP ; Store as current number JSR SCANDIGIT ; Check next digit BCS SCANDECOK ; No more digits PHA LDA OSTEMP CMP #26 BCS ERRBADNUM ; num>25, num*25>255 ASL A ; num*2 ASL A ; num*4 ADC OSTEMP ; num*4+num = num*5 ASL A ; num*10 STA OSTEMP PLA ADC OSTEMP ; num=num*10+digit BCC SCANDECLP BCS ERRBADNUM ; Overflowed SCANDECOK LDA OSTEMP ; Return A=number SCANDIG2 SEC RTS SCANDIGIT LDA (OSLPTR),Y CMP #'0' BCC SCANDIG2 ; <'0' CMP #'9'+1 BCS SCANDIG2 ; >'9' INY AND #$0F RTS HEXDIGIT JSR SCANDIGIT BCC HEXDIGIT2 ; Decimal digit AND #$DF CMP #'A' BCC SCANDIG2 ; Bad hex character CMP #'G' BCS HEXDIGIT2 ; Bad hex character SBC #$36 ; Convert 'A'-'F' to $0A-$0F INY CLC HEXDIGIT2 RTS * Scan hex address * (OSLPTR),Y=>first character * $200,X = 4-byte accumulator SCANHEX JSR HEXDIGIT ; Get first digit BCS ERRBADADD1 ; Not a hex character STA $200,X ; Store first digit LDA #0 STA $201,X ; Clear rest of accumulator STA $202,X STA $203,X SCANHEXLP1 JSR HEXDIGIT ; Get next digit BCS SKIPSPC ; Done, exit by skipping spaces STY OSTEMP LDY #4 ; Four bits to rotate SCANHEXLP2 ASL $200,X ; Multiple accumulator by 16 ROL $201,X ROL $202,X ROL $203,X BCS ERRBADADD1 ; Overflowed DEY BNE SCANHEXLP2 ; Loop for four bits ORA $200,X ; Add in current digit STA $200,X LDY OSTEMP ; Get Y back BNE SCANHEXLP1 ERRBADADD1 JMP ERRBADADD SKIPCOMMA LDA (OSLPTR),Y CMP #$2C BNE SKIPSPC ; Drop through * * Skip spaces SKIPSPC1 INY ; Step past a character SKIPSPC LDA (OSLPTR),Y CMP #' ' BEQ SKIPSPC1 CMP #$0D ; Return EQ= RTS * Skip a string SKIPWORD CLC JSR GSINIT SKIPWORDLP JSR GSREAD BCC SKIPWORDLP RTS * Convert (LPTR),Y to XY LPTRtoXY CLC TYA ADC OSLPTR+0 TAX LDA #0 ADC OSLPTR+1 TAY RTS * Convert XY to (LPTR),Y XYtoLPTR STX OSLPTR+0 STY OSLPTR+1 LDY #0 STARHELP9 RTS * *BASIC * ------ STARBASIC LDX MAXROM :BASICLP JSR ROMSELECT BIT $8006 BPL :BASICGO ; No service, must be BASIC DEX BPL :BASICLP JMP ERRBADCMD ; No BASIC, give an error :BASICGO JMP BYTE8E * *ECHO * ---------------------- CMDECHO SEC ECHO0 JSR GSINIT :ECHOLP1 JSR GSREAD BCS STARHELP9 JSR OSWRCH JMP :ECHOLP1 * *HELP () * ----------------- STARHELP JSR XYtoLPTR ; Update OSLPTR=>parameters JSR PRHELLO ; Unify version message LDX #command table LDY #>HLPTABLE JSR CLILOOKUP ; Look for *HELP subject BEQ STARHELP9 ; Matched LDA $8006 ; Does ROM have service entry? BMI STARHELP6 ; Yes, skip to send service call JSR OSNEWL LDX #$09 ; Language name LDY #$80 ; *TO DO* make this and BYTE8E JSR OSPRSTR ; use same code JSR OSNEWL STARHELP6 LDY #0 ; (OSLPTR),Y=>parameters LDA #9 JMP SERVICE ; Pass to sideways ROM(s) * Print *HELP text HELPHOSTFS LDX #FSCCOMMAND BNE HELPLIST HELPFILE LDX #CMDFILE BNE HELPLIST HELPMOS LDX #CMDMOS * HELPLIST STX OSTEXT+0 ; Start of command table STY OSTEXT+1 LDX #0 HELPLP1 LDA #32 JSR OSWRCH JSR OSWRCH HELPLP2 LDY #10 HELPLP3 LDA (OSTEXT,X) BMI HELPLP4 JSR OSWRCH DEY JSR CLISTEP BPL HELPLP3 HELPLP4 LDA #32 JSR OSWRCH DEY BNE HELPLP4 JSR CLISTEP JSR CLISTEP JSR CLISTEP BEQ STARHELP4 BPL HELPLP2 STARHELP4 LDA #$08 JSR OSWRCH JSR OSWRCH JMP FORCENL * *KEY * --------------------------- STARKEY RTS * *QUIT command * ------------- STARQUIT >>> XF2MAIN,QUIT * FILING COMMANDS * *************** * *LOAD and *SAVE * --------------- STARLOAD LDA #$7E ; Set here to A=$7E -> $FF, LOAD STARSAVE EOR #$81 ; Entered with A=$81 -> $00, SAVE * Can tweek this after HOSTFS command table updated PHA JSR XYtoLPTR ; Update OSLPTR=>filename JSR SKIPWORD ; Step past filename BNE STARLDSV3 ; filename followed by addr * * filename followed by no address, must be *LOAD name STARLDSV1 LDA #$FF ; $FF=load to file's address STARLOAD2 STA OSFILECB+6 PLA BEQ ERRBADADD2 ; *save name LDA #$7F ; Will become A=$FF JMP STARLDSVGO ; Do the load * At least one address specified STARLDSV3 LDX #OSFILECB+2-$200 ; X=>load JSR SCANHEX BNE STARSAVE3 ; Another address LDA #$00 ; $00=load to supplied address BEQ STARLOAD2 ; Only one address, must be *LOAD * More than one address, must be *SAVE STARSAVE3 PLA BNE ERRBADADD2 ; Can't be *LOAD LDX #3 STARSAVE4 LDA OSFILECB+2,X ; Get load STA OSFILECB+6,X ; copy to exec STA OSFILECB+10,X ; and to start DEX BPL STARSAVE4 LDA (OSLPTR),Y CMP #'+' PHP BNE STARSAVE5 ; Not start+length JSR SKIPSPC1 ; Step past '+' and spaces STARSAVE5 LDX #OSFILECB+14-$200 JSR SCANHEX ; Get end or length PLP BNE STARSAVE7 ; Not +length LDX #0 CLC STARSAVE6 LDA OSFILECB+10,X ; end=start+length ADC OSFILECB+14,X STA OSFILECB+14,X INX TXA AND #3 BNE STARSAVE6 * load =start * exec =start * start=start * end =end or start+length STARSAVE7 JSR SKIPSPC BEQ STARSAVE10 ; No more, do it LDX #OSFILECB+6-$200 JSR SCANHEX ; Get exec BEQ STARSAVE10 ; No more, do it LDX #OSFILECB+2-$200 JSR SCANHEX ; Get load BEQ STARSAVE10 ; No more, do it ERRBADADD2 JMP ERRBADADD ; Too many parameters STARSAVE10 LDA #$80 ; Will become $00 - SAVE STARLDSVGO LDX OSLPTR+0 LDY OSLPTR+1 ; Continue through... * * Commands passed to OSFILE * ------------------------- STARFILE EOR #$80 STX OSFILECB+0 STY OSFILECB+1 LDX #OSFILECB JSR OSFILE TAX BNE STARDONE JMP ERRNOTFND * *CLOSE * ------ CMDCLOSE LDA #$00 TAY JSR OSFIND ; Close all files STA FXEXEC ; Ensure Spool/Exec handles cleared STA FXSPOOL STARDONE RTS * *TYPE * ------------ * XY=>parameters string, EQ=no parameters * CMDTYPE BEQ ERRTYPE ; No filename JSR OPENINFILE ; Try to open file :LOOP JSR OSBGET ; Read a byte BCS TYPDMPEND ; EOF CMP #$0A BEQ :LOOP ; Ignore TAX ; Remember last character JSR OSASCI ; Print the character BIT ESCFLAG BPL :LOOP ; No Escape, keep going TYPEESC JSR TYPCLOSE ERRESCAPE BRK DB $11 ASC 'Escape' BRK TYPDMPEND CPX #$0D BEQ TYPCLOSE JSR OSNEWL TYPCLOSE LDA #$00 JMP OSFIND ; Close file ERRTYPE BRK DB $DC ASC 'Syntax: TYPE ' ERRDUMP BRK DB $DC ASC 'Syntax: DUMP ' BRK * Handle *DUMP command * -------------------- * XY=>parameters string, EQ=no parameters * CMDDUMP BEQ ERRDUMP ; No filename JSR OPENINFILE ; Try to open file STZ OSNUM+0 ; Offset = zero STZ OSNUM+1 :LOOP1 BIT ESCFLAG BMI TYPEESC ; Escape pressed PHY ; Save handle TYA TAX ; X=handle LDA #$7F JSR OSBYTE ; Read EOF PLY ; Get handle back TXA BNE TYPCLOSE ; At EOF LDA OSNUM+1 ; Print file offset JSR PRHEX LDA OSNUM+0 JSR PRHEX JSR PRSPACE LDA #8 ; 8 bytes to dump STA OSNUM+2 TSX ; Reserve bytes on stack TXA SEC SBC OSNUM+2 TAX TXS ; X=>space on stack :LOOP2 JSR OSBGET ; Read a byte BCS :DUMPEOF STA $0101,X ; Store on stack JSR PRHEX ; Print as hex JSR PRSPACE INX DEC OSNUM+2 BNE :LOOP2 ; Loop to do 8 bytes CLC ; CLC=Not EOF BCC :DUMPCHRS ; Jump to display characters :DUMPEOF LDA #'*' ; EOF met, pad with '**' JSR OSWRCH JSR OSWRCH JSR PRSPACE LDA #$00 STA $0101,X INX DEC OSNUM+2 BNE :DUMPEOF ; Loop to do 8 bytes SEC ; SEC=EOF :DUMPCHRS LDX #8 ; 8 bytes to print :LOOP4 PLA ; Get character PHP ; Save EOF flag CMP #$7F BEQ :DUMPDOT CMP #' ' BCS :DUMPCHR :DUMPDOT LDA #'.' :DUMPCHR JSR OSWRCH ; Print character INC OSNUM+0 ; Increment offset BNE :DUMPNXT INC OSNUM+1 :DUMPNXT PLP ; Get EOF flag back DEX BNE :LOOP4 ; Loop to do 8 bytes PHP JSR OSNEWL PLP BCC :LOOP1 JMP TYPCLOSE ; Close and finish * *SPOOL () * --------------- * XY=>parameters string, EQ=no parameters * CMDSPOOL PHP ; Save EQ/NE PHY ; Save Y LDY FXSPOOL ; Get SPOOL handle BEQ :SPOOL1 ; Wasn't open, skip closing LDA #$00 ; A=CLOSE STA FXSPOOL ; Clear SPOOL handle JSR OSFIND ; Close SPOOL file :SPOOL1 PLY ; Get Y back, XY=>filename PLP ; Get NE=filename, EQ=no filename BEQ :DONE ; No filename, all done LDA #$80 ; A=OPENOUT, for writing JSR OPENAFILE ; Try to open file STA FXSPOOL ; Store SPOOL handle :DONE RTS * *EXEC () * -------------- * XY=>parameters string, EQ=no parameters * CMDEXEC0 LDA #$00 ; Close EXEC file CMDEXEC PHP ; Save EQ/NE PHY ; Save Y LDY FXEXEC ; Get EXEC handle BEQ :EXEC1 ; Wasn't open, skip closing it LDA #$00 ; A=CLOSE STA FXEXEC ; Clear EXEC handle JSR OSFIND ; Close EXEC file :EXEC1 PLY ; Get Y back, XY=>filename PLP ; Get NE=filename, EQ=no filename BEQ EXECDONE ; No filename, all done JSR OPENINFILE ; Try to open file STA FXEXEC ; Store EXEC file handle EXECDONE RTS OPENINFILE LDA #$40 ; Open for input OPENAFILE JSR OSFIND ; Try to open file TAY ; Was file opened? BNE EXECDONE ; File opened EXECNOTFND JMP ERRNOTFND ; File not found * ZIP SPEED COMMANDS * ================== * Handle *FAST command * -------------------- * Turn Apple II accelerators on CMDFAST LDA #$80 ; Apple IIgs TSB CYAREG STA GSSPEED JSR ZIPUNLOCK ; ZipChip JSR ZIPDETECT BCC :NOZIP STA $C05B ; Enable BCS ZIPLOCK :NOZIP STA $C05C ; Ultrawarp fast RTS * Handle *SLOW command * -------------------- * Turn Apple II accelerators off CMDSLOW LDA #$80 ; Apple IIgs TRB CYAREG STZ GSSPEED JSR ZIPUNLOCK ; ZipChip JSR ZIPDETECT BCC :NOZIP STZ $C05A ; Disable BCS ZIPLOCK :NOZIP STA $C05D ; Ultrawarp slow RTS * Detect a ZipChip * Set carry is ZipChip found DETECTZIP ZIPDETECT LDA $C05C ; ZipChip manual p25 EOR #$FF STA $C05C CMP $C05C BNE :NOZIP EOR #$FF STA $C05C CMP $C05C BEQ :ZIPOK ; BEQ already has SEC * BNE :NOZIP * SEC * RTS :NOZIP CLC :ZIPOK RTS * Unlock ZipChip registers UNLOCKZIP ZIPUNLOCK PHP SEI ; Timing sensitive LDA #$5A STA $C05A STA $C05A STA $C05A STA $C05A PLP RTS * Lock ZipChip registers LOCKZIP ZIPLOCK LDA #$A5 STA $C05A RTS