diff --git a/jgh/applecorn.s b/jgh/applecorn.s new file mode 100644 index 0000000..f3af3be --- /dev/null +++ b/jgh/applecorn.s @@ -0,0 +1,164 @@ +* Load an Acorn BBC Micro ROM in aux memory and +* Provide an environment where it can run +* (c) Bobbi 2021 GPLv3 +* +* Assembled with the Merlin 8 assembler. + + XC ; 65c02 + ORG $2000 ; Load addr of loader in main memory + +* Monitor routines +BELL EQU $FBDD +PRBYTE EQU $FDDA +COUT1 EQU $FDED +CROUT EQU $FD8E +AUXMOVE EQU $C311 +XFER EQU $C314 + +* Monitor ZP locations +A1L EQU $3C +A1H EQU $3D +A2L EQU $3E +A2H EQU $3F +A4L EQU $42 +A4H EQU $43 + +* Used by XFER +STRTL EQU $3ED +STRTH EQU $3EE + +* Reset vector (2 bytes + 1 byte checksum) +RSTV EQU $3F2 + +* IRQ vector +A2IRQV EQU $3FE + +* MLI entry point +MLI EQU $BF00 + +* ProDOS MLI command numbers +QUITCMD EQU $65 +GTIMECMD EQU $82 +CREATCMD EQU $C0 +DESTCMD EQU $C1 +ONLNCMD EQU $C5 +SPFXCMD EQU $C6 +GPFXCMD EQU $C7 +OPENCMD EQU $C8 +READCMD EQU $CA +WRITECMD EQU $CB +CLSCMD EQU $CC +FLSHCMD EQU $CD +SMARKCMD EQU $CE +GMARKCMD EQU $CF +GEOFCMD EQU $D1 + +* IO Buffer for reading file (1024 bytes) +IOBUF0 EQU $4000 ; For loading ROM, OSFILE, *. +IOBUF1 EQU $4400 ; Four open files for langs +IOBUF2 EQU $4800 +IOBUF3 EQU $4C00 +IOBUF4 EQU $5000 + +* 512 byte buffer sufficient for one disk block +BLKBUF EQU $5200 +BLKBUFEND EQU $5400 + +* Address in aux memory where ROM will be loaded +AUXADDR EQU $8000 + +* Address in aux memory where the MOS shim is located +AUXMOS1 EQU $2000 ; Temp staging area in Aux +EAUXMOS1 EQU $3000 ; End of staging area +AUXMOS EQU $D000 ; Final location in aux LC + +* Macro for calls from aux memory to main memory +XFMAIN MAC + CLC ; Use main memory + CLV ; Use main ZP and LC + JMP XFER + EOM + +* Called by code running in main mem to invoke a +* routine in aux memory +XF2AUX MAC + PHA + LDA $C08B ; R/W LC RAM, bank 1 + LDA $C08B + LDA #<]1 + STA STRTL + LDA #>]1 + STA STRTH + PLA + SEC ; Use aux memory + BIT $FF58 ; Set V: use alt ZP and LC + JMP XFER + EOM + +* Called by code running in aux mem to invoke a +* routine in main memory +XF2MAIN MAC + TSX + STX $0101 ; Save alt SP + PHA + LDA STRTL + STA STRTBCKL + LDA STRTH + STA STRTBCKH + LDA #<]1 + STA STRTL + LDA #>]1 + STA STRTH + PLA + CLC ; Use main mem + CLV ; Use main ZP and LC + JMP XFER + EOM + +* Macro called on re-entry to aux memory +ENTAUX MAC + LDX $0101 ; Recover alt SP + TXS + PHA + LDA STRTBCKL + STA STRTL + LDA STRTBCKH + STA STRTH + PLA + EOM + +* Macro called on re-entry to main memory +ENTMAIN MAC + LDX $0100 ; Recover SP + TXS + PHA ; Preserve parm in A + LDA $C081 ; Bank in ROM + LDA $C081 + PLA + EOM + +* Enable writing to main memory (for code running in aux) +WRTMAIN MAC + SEI ; Keeps IRQ handler easy + STA $C004 ; Write to main memory + EOM + +* Go back to writing to aux (for code runnign in aux) +WRTAUX MAC + STA $C005 ; Write to aux memory + CLI ; Normal service resumed + EOM + +* Code is all included from PUT files below ... +* ... order matters! + PUT LOADER + PUT MAINMEM + PUT AUXMEM.MOSEQU + PUT AUXMEM.INIT + PUT AUXMEM.VDU + PUT AUXMEM.HOSTFS + PUT AUXMEM.KERNEL +* PUT AUXMEM.OSBW.ORIG + PUT AUXMEM.BYTWRD + PUT AUXMEM.CHARIO + PUT AUXMEM.MISC diff --git a/jgh/auxmem.bytwrd.s b/jgh/auxmem.bytwrd.s new file mode 100644 index 0000000..74164ae --- /dev/null +++ b/jgh/auxmem.bytwrd.s @@ -0,0 +1,384 @@ +* KERNEL/OSWOSB.S +***************** + + XC ; 65c02 + +************************* +* OSBYTE DISPATCH TABLE * +************************* + +BYTWRDADDR DW BYTE00 ; OSBYTE 0 - Machine host +* DW BYTE01 ; OSBYTE 1 - User flag +* DW BYTE02 ; OSBYTE 2 - OSRDCH source +* DW BYTE03 ; OSBYTE 3 - OSWRCH dest +* DW BYTE04 ; OSBYTE 4 - Cursor keys +BYTWRDLOW +BYTESZLO EQU BYTWRDLOW-BYTWRDADDR +BYTELOW EQU BYTESZLO/2-1 ; Maximum low OSBYTE +BYTEHIGH EQU $7C ; First high OSBYTE + DW BYTE7C ; OSBYTE 124 - Clear Escape + DW BYTE7D ; OSBYTE 125 - Set Escape + DW BYTE7E ; OSBYTE 126 - Ack. Escape + DW BYTE7F ; OSBYTE 127 - Read EOF + DW BYTE80 ; OSBYTE 128 - ADVAL + DW BYTE81 ; OSBYTE 129 - INKEY + DW BYTE82 ; OSBYTE 130 - Memory high word + DW BYTE83 ; OSBYTE 131 - MEMBOT + DW BYTE84 ; OSBYTE 132 - MEMTOP + DW BYTE85 ; OSBYTE 133 - MEMTOP for MODE + DW BYTE86 ; OSBYTE 134 - POS, VPOS + DW BYTE87 ; OSBYTE 135 - Character, MODE +* DW BYTE88 ; OSBYTE 136 - *CODE +* DW BYTE89 ; OSBYTE 137 - *MOTOR +* DW BYTE8A ; OSBYTE 138 - Buffer insert +* DW BYTE8B ; OSBYTE 139 - *OPT +* DW BYTE8C ; OSBYTE 140 - *TAPE +* DW BYTE8D ; OSBYTE 141 - *ROM +* DW BYTE8E ; OSBYTE 142 - Enter language +* DW BYTE8F ; OSBYTE 143 - Service call +BYTWRDTOP + DW BYTEVAR ; OSBYTE 166+ - Read/Write OSBYTE variable +* Maximum high OSBYTE +BYTESZHI EQU BYTWRDTOP-BYTWRDLOW +BYTEMAX EQU BYTESZHI/2+BYTEHIGH-1 + +************************* +* OSWORD DISPATCH TABLE * +************************* +OSWBASE DW WORD00 ; OSWORD 0 - Read input line + DW WORD01 ; OSWORD 1 - Read elapsed time + DW WORD02 ; OSWORD 2 - Write eleapsed time + DW WORD03 ; OSWORD 3 - Read interval timer + DW WORD04 ; OSWORD 4 - Write interval timer + DW WORD05 ; OSWORD 5 - Read I/O memory + DW WORD06 ; OSWORD 6 - Write I/O memory +* DW WORD07 ; OSWORD 7 - SOUND +* DW WORD08 ; OSWORD 8 - ENVELOPE +* DW WORD09 ; OSWORD 9 - POINT +* DW WORD0A ; OSWORD 10 - Read character bitmap +* DW WORD0B ; OSWORD 11 - Read palette +* DW WORD0C ; OSWORD 12 - Write palette +* DW WORD0D ; OSWORD 13 - Read coordinates +OSWEND + DW WORDE0 ; OSWORD &E0+ - User OSWORD + +* Offset to start of OSWORD table +WORDSZOFF EQU OSWBASE-BYTWRDADDR +WORDOFF EQU WORDSZOFF/2 +* Maximum OSWORD +WORDSZ EQU OSWEND-OSWBASE +WORDMAX EQU WORDSZ/2-1 + + +************************ +* OSWORD/OSBYTE dispatch +************************ +* OSWORD: +* On entry, A=action +* XY=>control block +* On exit, A=preserved +* X,Y,Cy trashed (except OSWORD 0) +* control block updated +* +WORDHND PHA + PHP + SEI + STA OSAREG ; Store registers + STX OSCTRL+0 ; Point to control block + STY OSCTRL+1 + LDX #$08 ; X=SERVWORD + CMP #$E0 ; User OSWORD + BCS WORDGO1 + CMP #WORDMAX+1 + BCS BYTWRDFAIL ; Pass on to service call + ADC #WORDOFF + BCC BYTWRDCALL ; Call OSWORD routine +WORDGO1 LDA #WORDOFF+WORDMAX+1 + BCS BYTWRDCALL ; Call User OSWORD routine + +* OSBYTE: +* On entry, A=action +* X=first parameter +* Y=second parameter if A>$7F +* On exit, A=preserved +* X=first returned result +* Y=second returned result if A>$7F +* Cy=any returned status if A>$7F +* +BYTEHND PHA + PHP + SEI + STA OSAREG ; Store registers + STX OSXREG + STY OSYREG + LDX #$07 ; X=SERVBYTE + CMP #$A6 + BCS BYTEGO1 ; OSBYTE &A6+ + CMP #BYTEMAX+1 + BCS BYTWRDFAIL ; Pass on to service call + CMP #BYTEHIGH + BCS BYTEGO2 ; High OSBYTEs + CMP #BYTELOW+1 + BCS BYTWRDFAIL ; Pass on to service call + STZ OSYREG ; Prepare Y=0 for low OSBYTEs + BCC BYTEGO3 + +BYTEGO1 LDA #BYTEMAX+1 ; Index for BYTEVAR +BYTEGO2 SBC #BYTEHIGH-BYTELOW-1 ; Reduce OSBYTE number +BYTEGO3 ORA #$80 ; Will become CS=OSBYTE call + +BYTWRDCALL ASL A ; Index into dispatch table + TAY ; Y=offset into dispatch table +* BIT FXNETCLAIM ; Check Econet intercept flag +* BPL BYTWRDNONET ; No intercept, skip past +* TXA ; Set A=BYTE or WORD call +* CLV ; Clear V +* JSR CALLNET ; Call Econet with X=call type +* BVS BYTWRDEXIT ; V now set, claimed by NETV + +BYTWRDNONET LDA BYTWRDADDR+1,Y ; Get routine address + STA OSINTWS+1 + LDA BYTWRDADDR+0,Y + STA OSINTWS+0 + LDA OSAREG ; Get A parameter back + LDY OSYREG ; Get Y parameter back + LDX OSXREG ; Get X parameter, set EQ from it + BCS BYTWRDGO ; Skip if OSBYTE call + LDY #$00 ; OSWORD call, enter with Y=0 + LDA (OSCTRL),Y ; and A=first byte in control block + SEC ; Enter routine with CS +BYTWRDGO JSR JMPADDR ; Call the routine +* Routines are entered with: +* A=OSBYTE call or first byte of OSWORD control block +* X=X parameter +* Y=OSBYTE Y parameter for A>$7F +* Y=$00 for OSBYTE A<$80 +* Y=$00 for OSWORD so (OSCTRL),Y => first byte +* Carry Set +* EQ set from OSBYTE X or from OSWORD first byte +* X,Y,Cy from routine returned to caller + +BYTWRDEXIT ROR A ; Move Carry to A + PLP ; Restore original flags + ROL A ; Move Carry back to flags + PLA ; Restore A + CLV ; Clear V = Actioned + RTS + +BYTWRDFAIL +* JSR SERVICE ; Offer to sideways ROMs +* LDX OSXREG ; Get returned X +* CMP #$00 +* BEQ BYTWRDEXIT ; Claimed, return + JSR UNSUPBYTWRD ; *DEBUG* + LDX #$FF ; X=&FF if unclaimed + PLP ; Restore IRQs + PLA ; Restore A + BIT SETV ; Set V = Not actioned + RTS + +SETV ; JMP() is $6C, bit 6 set to set V +* JMPADDR JMP (OSINTWS) +* Merlin doesn't like the above +JMPADDR JMP ($00FA) + +* OSWORD &00 - Read a line of input +*********************************** +* On entry, (OSCTRL)=>control block +* Y=0, A=(OSCTRL) +* On exit, Y=length of line, offset to +* CC = Ok, CS = Escape +* + + XC ; 65c02 +WORD00 IF MAXLEN-OSTEXT-2 + LDY #$04 +:WORD00LP1 LDA (OSCTRL),Y ; Copy MAXLEN, MINCH, MAXCH to workspace + STA MAXLEN-2,Y + DEY + CPY #$02 + BCS :WORD00LP1 +:WORD00LP2 LDA (OSCTRL),Y ; (OSTEXT)=>line buffer + STA OSTEXT,Y + DEY + BPL :WORD00LP2 + INY ; Initial line length = zero + ELSE + LDA (OSCTRL),Y ; Copy control block + STA OSTEXT,Y ; 0,1 => text + INY ; 2 = MAXLEN + CPY #$05 ; 3 = MINCHAR + BCC WORD00 ; 4 = MAXCHAR + LDY #$00 ; Initial line length = zero + FIN +; STY FXLINES ; Reset line counter + CLI + BEQ :WORD00LP ; Enter main loop + +:WORD00BELL LDA #$07 ; $07=BELL + DEY ; Balance next INY +:WORD00NEXT INY ; Step to next character +:WORD00ECHO JSR OSWRCH ; Print character + +:WORD00LP JSR OSRDCH + BCS :WORD00ESC ; Escape +* TAX ; Save character in X +* LDA FXVAR03 ; Get FX3 destination +* ROR A +* ROR A ; Move bit 1 into Carry +* TXA ; Get character back +* BCS :WORD00TEST ; VDU disabled, ignore +* LDX FXVDUQLEN ; Get length of VDU queue +* BNE :WORD00ECHO ; Not zero, just print +:WORD00TEST CMP #$7F ; Delete + BNE :WORD00CHAR + CPY #$00 + BEQ :WORD00LP ; Nothing to delete + DEY ; Back up one character + BCS :WORD00ECHO ; Loop back to print DEL +:WORD00CHAR CMP #$15 ; Ctrl-U + BNE :WORD00INS ; No, insert character + LDA #$7F ; Delete character + INY ; Balance first DEY +:WORD00ALL DEY ; Back up one character + BEQ :WORD00LP ; Beginning of line + JSR OSWRCH ; Print DELETE + JMP :WORD00ALL ; Loop to delete all +:WORD00INS STA (OSTEXT),Y ; Store the character + CMP #$0D + BEQ :WORD00CR ; CR - Done + CPY MAXLEN + BCS :WORD00BELL ; Too long, beep + CMP MINCHAR + BCC :WORD00ECHO ; MAXCHAR, don't step to next + +:WORD00CR JSR OSNEWL +; JSR CALLNET ; Call Econet Vector, A=13 +:WORD00ESC LDA ESCFLAG ; Get Escape flag + ROL A ; Carry=Escape state + RTS + +* OSWORD &01 - Read elapsed time +* OSWORD &02 - Write elapsed time +* OSWORD &03 - Read countdown timer +* OSWORD &04 - Write countdown timer +************************************ +* On entry, (OSCTRL)=>control block +* Y=0 + +WORD01 TYA ; Dummy, just return zero +:WORD01LP STA (OSCTRL),Y + INY + CPY #$05 + BCC :WORD01LP +WORD04 +WORD03 +WORD02 RTS ; Dummy, do nothing + +* OSWORD &05 - Read I/O memory +* OSWORD &06 - Write I/O memory +*********************************** +* On entry, (OSCTRL)+0 address +* (OSCTRL)+4 byte read or written +* Y=0, A=(OSCTRL) + + XC ; 65c02 +WORD05 JSR GETADDR ; Point to address, set X and Y +; needs to switch to main memory + LDA (OSINTWS) ; Get byte +; needs to switch back + STA (OSCTRL),Y ; Store it + RTS +WORD06 JSR GETADDR ; Point to address, set X and Y + LDA (OSCTRL),Y ; Get byte +; needs to switch to main memory + STA (OSINTWS) ; Store it +; needs to switch back + RTS +GETADDR STA OSINTWS+0 ; (OSINTWS)=>byte to read/write + INY + LDA (OSCTRL),Y + STA OSINTWS+1 + LDY #$04 ; Point Y to data byte + RTS + +* KERNEL/BWMISC.S +***************** +* Here until tidied + + +BYTE00 LDX #$0A ; $00 = identify Host + RTS + +BYTE7C LDA ESCFLAG ; $7C = clear escape condition + AND #$7F ; Clear MSbit + STA ESCFLAG + RTS + +BYTE7D ROR ESCFLAG ; $7D = set escape condition + RTS + +BYTE7E LDA ESCFLAG ; $7E = ack detection of ESC + AND #$7F ; Clear MSB + STA ESCFLAG + LDX #$FF ; Means ESC condition cleared + RTS + +BYTE7F PHY ; $7F = check for EOF + JSR CHKEOF + PLY + RTS + +BYTE82 LDY #$00 ; $82 = read high order address + LDX #$00 ; $0000 for language processor + RTS + +BYTE83 LDY #$0E ; $83 = read bottom of user mem + LDX #$00 ; $0E00 + RTS + +BYTE84 LDY #$80 ; $84 = read top of user mem + LDX #$00 + RTS + +BYTE85 LDY #$80 ; $85 = top user mem for mode + LDX #$00 + RTS + +BYTE8B LDA #$00 ; $8B = *OPT + JMP ((FSCV)) ; Hand over to filing system + +BYTEDA RTS ; $DA = clear VDU queue + +BYTEEA LDX #$00 ; No tube + RTS ; $EA = Tube presence + +UNSUPBYTWRD + LDA #OSBYTEM + CPX #7 + BEQ UNSUPGO + LDA #OSWORDM +UNSUPGO JSR PRSTR + LDA OSAREG + JSR OUTHEX + LDA #OSBM2 + JMP PRSTR + +OSBYTEM ASC 'OSBYTE($' + DB $00 +OSWORDM ASC 'OSWORD($' + DB $00 +OSBM2 ASC ').' + DB $00 + +BYTEVAR LDX #$00 + LDY #$00 +WORDE0 RTS + diff --git a/jgh/auxmem.chario.s b/jgh/auxmem.chario.s new file mode 100644 index 0000000..de8be29 --- /dev/null +++ b/jgh/auxmem.chario.s @@ -0,0 +1,181 @@ +********************************************************* +* Kernel / Misc +********************************************************* + +* KERNEL/CHARIO.S +***************** +* Character read and write +* + +* OSWRCH handler +* All registers preserved +WRCHHND PHA + PHX + PHY +* TODO Check any output redirections +* TODO Check any spool output + JSR OUTCHAR +* TODO Check any printer output + PLY + PLX + PLA + RTS + +* OSRDCH handler +* All registers preserved except A, Carry +* Read a character from the keyboard +RDCHHND PHX + PHY + JSR GETCHRC + STA OLDCHAR +:L1 LDA CURS+1 ; Skip unless CURS=$8000 + CMP #$80 + BNE :S1 + LDA CURS + BNE :S1 + + STZ CURS + STZ CURS+1 + LDA CSTATE + ROR + BCS :S2 + LDA #'_' + BRA :S3 +:S2 LDA OLDCHAR +:S3 JSR PRCHRC + INC CSTATE +:S1 INC CURS + BNE :S4 + INC CURS+1 +:S4 LDA $C000 ; Keyboard data/strobe + BPL :L1 + LDA OLDCHAR ; Erase cursor + JSR PRCHRC + LDA $C000 + AND #$7F + STA $C010 ; Clear strobe + PLY + PLX + CMP #$1B ; Escape pressed? + BNE :S5 + SEC ; Return CS + ROR ESCFLAG + SEC + RTS +:S5 CLC + RTS +CURS DW $0000 ; Counter +CSTATE DB $00 ; Cursor on or off +OLDCHAR DB $00 ; Char under cursor + + +BYTE81 JSR GETKEY ; $81 = Read key with time lim + RTS + + +* Performs OSBYTE $81 INKEY$ function +* X,Y has time limit +* On exit, CC, Y=$00, X=key - key pressed +* CS, Y=$FF - timeout +* CS, Y=$1B - escape +GETKEY TYA + BMI NEGKEY ; Negative INKEY +:L1 CPX #$00 + BEQ :S1 + LDA $C000 ; Keyb data/strobe + AND #$80 + BNE :GOTKEY + JSR DELAY ; 1/100 sec + DEX + BRA :L1 +:S1 CPY #$00 + BEQ :S2 + DEY + LDX #$FF + BRA :L1 +:S2 LDA $C000 ; Keyb data/strobe + AND #$80 + BNE :GOTKEY + LDY #$FF ; No key, time expired + SEC + RTS +:GOTKEY LDA $C000 ; Fetch char + AND #$7F + STA $C010 ; Clear strobe + CMP #27 ; Escape + BEQ :ESC + TAX + LDY #$00 + CLC + RTS +:ESC ROR ESCFLAG + LDY #27 ; Escape + SEC + RTS +NEGKEY LDX #$00 ; Unimplemented + LDY #$00 + RTS + +* KERNEL/KEYBOARD.S +******************* + +KBDREAD +KEYPRESS LDA $C000 + TAY + CMP #$80 + BCC KEYNONE ; No key pressed + AND #$7F + STA $C010 ; Ack. keypress + BIT $C061 + BMI KEYLALT ; Left Apple pressed + BIT $C062 + BMI KEYRALT ; Right Apple pressed + CMP #$09 + BEQ KEYTAB + CMP #$08 + BCC KEYOK ; <$08 not cursor key + CMP #$0C + BCC KEYCURSR + CMP #$15 + BEQ KEYCUR15 +KEYOK SEC ; SEC=Ok +KEYNONE RTS + +KEYTAB LDA #$C9 +; If cursors active, COPY +; else TAB + SEC + RTS + +KEYRALT ; Right Apple key pressed +KEYLALT CMP #$40 ; Left Apple key pressed + BCS KEYCTRL + CMP #$30 + BCC KEYOK ; <'0' + CMP #$3A + BCS KEYOK ; >'9' +KEYFUNC AND #$0F ; Convert Apple-Num to function key + ORA #$80 + BIT $C062 + BPL KEYFUNOK ; Left+Digit -> $8x + ORA #$90 ; Right+Digit -> $9x + BIT $C061 + BPL KEYFUNOK + EOR #$30 ; Left+Right+Digit -> $Ax +KEYFUNOK SEC + RTS +KEYCTRL AND #$1F ; Apple-Letter -> Ctrl-Letter + RTS + +KEYCUR15 +; BIT $C062 +; BPL KEYCUR16 ; Right Apple not pressed +; LDA #$C9 ; Solid+Right -> COPY? +; SEC +; RTS +KEYCUR16 LDA #$09 ; Convert RGT to $09 +KEYCURSR AND #$03 + ORA #$CC ; Cursor keys $CC-$CF + SEC ; SEC=Ok + RTS + diff --git a/jgh/auxmem.hostfs.s b/jgh/auxmem.hostfs.s new file mode 100644 index 0000000..395caaf --- /dev/null +++ b/jgh/auxmem.hostfs.s @@ -0,0 +1,437 @@ +* FILESYS.S +********************************************************* +* AppleMOS Host File System +********************************************************* + + +* OSFIND - open/close a file for byte access +FINDHND PHX + PHY + PHA + STX ZP1 ; Points to filename + STY ZP1+1 + CMP #$00 ; A=$00 = close + BEQ :CLOSE + PHA + LDA #MOSFILE+1 + STA ZP2+1 + LDY #$00 +:L1 LDA (ZP1),Y + >>> WRTMAIN + STA (ZP2),Y + >>> WRTAUX + INY + CMP #$0D ; Carriage return + BNE :L1 + DEY + >>> WRTMAIN + STY MOSFILE ; Length (Pascal string) + >>> WRTAUX + PLA ; Recover options + >>> XF2MAIN,OFILE +:CLOSE >>> WRTMAIN + STY MOSFILE ; Write file number + >>> WRTAUX + >>> XF2MAIN,CFILE +OSFINDRET + >>> ENTAUX + PLY ; Value of A on entry + CPY #$00 ; Was it close? + BNE :S1 + TYA ; Preserve A for close +:S1 PLY + PLX + RTS + +* OSGBPB - Get/Put a block of bytes to/from an open file +GBPBHND LDA #OSGBPBM + JMP PRSTR +OSGBPBM ASC 'OSGBPB.' + DB $00 + +* OSBPUT - write one byte to an open file +BPUTHND PHX + PHY + PHA ; Stash char to write + >>> WRTMAIN + STY MOSFILE ; File reference number + >>> WRTAUX + >>> XF2MAIN,FILEPUT +OSBPUTRET + >>> ENTAUX + CLC ; Means no error + PLA + PLY + PLX + RTS + +* OSBGET - read one byte from an open file +BGETHND PHX + PHY + >>> WRTMAIN + STY MOSFILE ; File ref number + >>> WRTAUX + >>> XF2MAIN,FILEGET +OSBGETRET + >>> ENTAUX + CLC ; Means no error + CPY #$00 ; Check error status + BEQ :NOERR + SEC ; Set carry for error + BRA :EXIT +:NOERR CLC +:EXIT PLY + PLX + RTS + +* OSARGS - adjust file arguments +* On entry, A=action +* X=>4 byte ZP control block +* Y=file handle +ARGSHND PHA + PHX + PHY + CPY #$00 + BNE :HASFILE + CMP #$00 ; Y=0,A=0 => current file sys + BNE :S1 + PLY + PLX + PLA + LDA #105 ; 105=AppleFS filing system + RTS +:S1 CMP #$01 ; Y=0,A=1 => addr of CLI + BNE :S2 +* TODO: Implement this for *RUN and *command + JSR BEEP + BRA :IEXIT +:S2 CMP #$FF ; Y=0,A=FF => flush all files + BNE :IEXIT + >>> WRTMAIN + STZ MOSFILE ; Zero means flush all + >>> WRTAUX + BRA :IFLUSH +:HASFILE >>> WRTMAIN + STY MOSFILE ; File ref num + STX MOSFILE+1 ; Pointer to ZP control block + >>> WRTAUX + CMP #$00 ; Y!=0,A=0 => read seq ptr + BNE :S3 + >>> WRTMAIN + STZ MOSFILE+2 ; 0 means get pos + >>> WRTAUX + >>> XF2MAIN,TELL +:IEXIT BRA :IEXIT2 +:IFLUSH BRA :FLUSH +:S3 CMP #$01 ; Y!=0,A=1 => write seq ptr + BNE :S4 + >>> WRTMAIN + LDA $00,X + STA MOSFILE+2 + LDA $01,X + STA MOSFILE+3 + LDA $02,X + STA MOSFILE+4 + >>> WRTAUX + >>> XF2MAIN,SEEK +:IEXIT2 BRA :EXIT +:S4 CMP #$02 ; Y!=0,A=2 => read file len + BNE :S5 + >>> WRTMAIN + STA MOSFILE+2 ; Non-zero means get len + >>> WRTAUX + >>> XF2MAIN,TELL +:S5 CMP #$FF ; Y!=0,A=FF => flush file + BNE :EXIT +:FLUSH >>> XF2MAIN,FLUSH +:EXIT PLY + PLX + PLA + RTS +OSARGSRET + >>> ENTAUX + PLY + PLX + PLA + RTS + +* OSFILE - perform actions on entire files +* On entry, A=action +* XY=>control block +* On exit, A=preserved if unimplemented +* A=0 object not found (not load/save) +* A=1 file found +* A=2 directory found +* XY preserved +* control block updated +FILEHND PHX + PHY + PHA + + STX ZP1 ; LSB of parameter block + STY ZP1+1 ; MSB of parameter block + LDA #FILEBLK + STA ZP2+1 + LDY #$00 ; Copy to FILEBLK in main mem +:L1 LDA (ZP1),Y + >>> WRTMAIN + STA (ZP2),Y + >>> WRTAUX + INY + CPY #$12 + BNE :L1 + + LDA (ZP1) ; Pointer to filename->ZP2 + STA ZP2 + LDY #$01 + LDA (ZP1),Y + STA ZP2+1 + LDA #MOSFILE+1 + STA ZP1+1 + LDA (ZP2) ; Look at first char of filename + CMP #'9'+1 + BCS :NOTDIGT + CMP #'0' + BCC :NOTDIGT + LDA #'N' ; Prefix numeric with 'N' + >>> WRTMAIN + STA (ZP1) + >>> WRTAUX + LDY #$01 ; Increment Y + DEC ZP2 ; Decrement source pointer + LDA ZP2 + CMP #$FF + BNE :L2 + DEC ZP2+1 + BRA :L2 +:NOTDIGT LDY #$00 +:L2 LDA (ZP2),Y + >>> WRTMAIN + STA (ZP1),Y + >>> WRTAUX + INY + CMP #$21 ; Space or Carriage return + BCS :L2 + DEY + >>> WRTMAIN + STY MOSFILE ; Length (Pascal string) + >>> WRTAUX + + PLA ; Get action back + PHA + BEQ :S1 ; A=00 -> SAVE + CMP #$FF + BEQ :S2 ; A=FF -> LOAD + + LDA #OSFILEM + JSR PRSTR + PLA + PHA + JSR OUTHEX + LDA #OSFILEM2 + JSR PRSTR + PLA ; Not implemented, return unchanged + PLY + PLX + RTS +:S1 >>> XF2MAIN,SAVEFILE +:S2 >>> XF2MAIN,LOADFILE +OSFILERET + >>> ENTAUX + PLY ; Value of A on entry + CPY #$FF ; LOAD + BNE :S4 ; Deal with return from SAVE + + CMP #$01 ; No file found + BNE :SL1 + BRK + DB $D6 ; $D6 = Object not found + ASC 'File not found' + BRK + +:SL1 CMP #$02 ; Read error + BNE :SL2 + BRK + DB $CA ; $CA = Premature end, 'Data lost' + ASC 'Read error' + BRK + +:SL2 LDA #$01 ; Return code - file found + BRA :EXIT + +:S4 CPY #$00 ; Return from SAVE + BNE :S6 + CMP #$01 ; Unable to create or open + BNE :SS1 + BRK + DB $C0 ; $C0 = Can't create file to save + ASC 'Can' + DB $27 + ASC 't save file' + BRK + +:SS1 CMP #$02 ; Unable to write + BNE :S6 + BRK + DB $CA ; $CA = Premature end, 'Data lost' + ASC 'Write error' + BRK + +:S6 LDA #$00 +:EXIT PLY + PLX + RTS + +OSFILEM ASC 'OSFILE($' + DB $00 +OSFILEM2 ASC ')' + DB $00 + +* OSFSC - miscellanous file system calls +***************************************** +* On entry, A=action, XY=>command line +* or A=action, X=param1, Y=param2 +* On exit, A=preserved if unimplemented +* A=modified if implemented +* X,Y=any return values +* +FSCHND CMP #$01 + BEQ CHKEOF ; A=0 - Read EOF + CMP #$02 + BEQ FSCRUN ; A=2 - */filename + CMP #$04 + BEQ FSCRUN ; A=4 - *RUN + CMP #$05 + BEQ FSCCAT ; A=5 - *CAT + CMP #$09 + BEQ FSCCAT ; A=9 - *EX + CMP #$0A + BEQ FSCCAT ; A=10 - *INFO + CMP #$0C + BEQ FSCREN ; A=12 - *RENAME +FSCRUN +FSCREN + LDA #OSFSCM + JSR PRSTR + RTS +OSFSCM ASC 'OSFSC.' + DB $00 + +* Performs OSBYTE $7F EOF function +* File ref number is in X +CHKEOF >>> WRTMAIN + STX MOSFILE ; File reference number + >>> WRTAUX + >>> XF2MAIN,FILEEOF +CHKEOFRET + >>> ENTAUX + TAX ; Return code -> X + RTS + +* Perform CAT +* A=5 *CAT, A=9 *EX, A=10 *INFO +FSCCAT >>> XF2MAIN,CATALOG +STARCATRET + >>> ENTAUX + JMP OSNEWL + RTS + +* Print one block of a catalog. Called by CATALOG +* Block is in AUXBLK +PRONEBLK >>> ENTAUX + LDA AUXBLK+4 ; Get storage type + AND #$E0 ; Mask 3 MSBs + CMP #$E0 + BNE :NOTKEY ; Not a key block + LDA #<:DIRM + LDY #>:DIRM + JSR PRSTR + SEC +:NOTKEY LDA #$00 +:L1 PHA + PHP + JSR PRONEENT + PLP + BCC :L1X + JSR OSNEWL +:L1X PLA + INC + CMP #13 ; Number of dirents in block + CLC + BNE :L1 + >>> XF2MAIN,CATALOGRET +:DIRM ASC 'Directory: ' + DB $00 + +* Print a single directory entry +* On entry: A = dirent index in AUXBLK +PRONEENT TAX + LDA #AUXBLK+4 + STA ZP3+1 +:L1 CPX #$00 + BEQ :S1 + CLC + LDA #$27 ; Size of dirent + ADC ZP3 + STA ZP3 + LDA #$00 + ADC ZP3+1 + STA ZP3+1 + DEX + BRA :L1 +:S1 LDY #$00 + LDA (ZP3),Y + BEQ :EXIT ; Inactive entry + AND #$0F ; Len of filename + TAX + LDY #$01 +:L2 CPX #$00 + BEQ :S2 + LDA (ZP3),Y + JSR OSWRCH + DEX + INY + BRA :L2 +:S2 LDA #$20 + JSR OSWRCH + INY + CPY #$15 + BNE :S2 +:EXIT RTS + +* Handle *DIR (directory change) command +* On entry, ZP1 points to command line +STARDIR JSR EATSPC ; Eat leading spaces + BCC :S1 ; If no space found + RTS ; No argument +:S1 LDX #$01 +:L3 LDA (ZP1),Y + CMP #$0D + BEQ :S3 + >>> WRTMAIN + STA MOSFILE,X + >>> WRTAUX + INY + INX + BRA :L3 +:S3 DEX + >>> WRTMAIN + STX MOSFILE ; Length byte + >>> WRTAUX + >>> XF2MAIN,SETPFX +STARDIRRET + >>> ENTAUX + RTS + diff --git a/jgh/auxmem.init.s b/jgh/auxmem.init.s new file mode 100644 index 0000000..5cc0f72 --- /dev/null +++ b/jgh/auxmem.init.s @@ -0,0 +1,143 @@ +*********************************************************** +* BBC Micro 'virtual machine' in Apple //e aux memory +*********************************************************** + +ZP1 EQU $90 ; $90-$9f are Econet space + ; so safe to use +ZP2 EQU $92 + +ZP3 EQU $94 + +ROW EQU $96 ; Cursor row +COL EQU $97 ; Cursor column +STRTBCKL EQU $9D +STRTBCKH EQU $9E +WARMSTRT EQU $9F ; Cold or warm start + +MAGIC EQU $BC ; Arbitrary value + +MOSSHIM + ORG AUXMOS ; MOS shim implementation + +* +* Shim code to service Acorn MOS entry points using +* Apple II monitor routines +* This code is initially loaded into aux mem at AUXMOS1 +* Then relocated into aux LC at AUXMOS by MOSINIT +* +* Initially executing at $3000 until copied to $D000 + +MOSINIT STA $C005 ; Make sure we are writing aux + STA $C000 ; Make sure 80STORE is off + + LDA $C08B ; LC RAM Rd/Wt, 1st 4K bank + LDA $C08B + + LDA WARMSTRT ; Don't relocate on restart + CMP #MAGIC + BEQ :NORELOC + + LDA #AUXMOS1 + STA A1H + LDA #EAUXMOS1 + STA A2H + LDA #AUXMOS + STA A4H +:L1 LDA (A1L) + STA (A4L) + LDA A1H + CMP A2H + BNE :S1 + LDA A1L + CMP A2L + BNE :S1 + BRA :S4 +:S1 INC A1L + BNE :S2 + INC A1H +:S2 INC A4L + BNE :S3 + INC A4H +:S3 BRA :L1 + +:S4 LDA #MOSVEC-MOSINIT+AUXMOS1 + STA A1H + LDA #MOSVEND-MOSINIT+AUXMOS1 + STA A2H + LDA #MOSAPI + STA A4H +:L2 LDA (A1L) + STA (A4L) + LDA A1H + CMP A2H + BNE :S5 + LDA A1L + CMP A2L + BNE :S5 + BRA :S8 +:S5 INC A1L + BNE :S6 + INC A1H +:S6 INC A4L + BNE :S7 + INC A4H +:S7 BRA :L2 + +:NORELOC +:S8 STA $C00D ; 80 col on + STA $C003 ; Alt charset off + STA $C055 ; PAGE2 + + STZ ROW + STZ COL + JSR CLEAR + + STZ ESCFLAG + + LDX #$35 +:INITPG2 LDA DEFVEC,X + STA $200,X + DEX + BPL :INITPG2 + + LDA #<:HELLO + LDY #>:HELLO + JSR PRSTR + + LDA #$09 ; Print language name at $8009 + LDY #$80 + JSR PRSTR + JSR OSNEWL + JSR OSNEWL + + LDA WARMSTRT + CMP #MAGIC + BNE :S9 + LDA #<:OLDM + LDY #>:OLDM + JSR PRSTR + +:S9 LDA #MAGIC ; So we do not reloc again + STA WARMSTRT + + CLC ; CLC=Entered from RESET + LDA #$01 ; $01=Entering application code + JMP AUXADDR ; Start Acorn ROM +* No return +:HELLO ASC 'Applecorn MOS v0.01' + DB $0D,$0D,$00 +:OLDM ASC '(Use OLD to recover any program)' + DB $0D,$0D,$00 + diff --git a/jgh/auxmem.kernel.s b/jgh/auxmem.kernel.s new file mode 100644 index 0000000..d5b67d1 --- /dev/null +++ b/jgh/auxmem.kernel.s @@ -0,0 +1,438 @@ +********************************************************* +* AppleMOS Kernel +********************************************************* + +* KERNEL/SWROM.S +**************** + +* OSBYTE $8E - Enter language ROM +* +BYTE8E PHP ; Save CLC=RESET, SEC=Not RESET + LDA #$08 + STA FAULT+0 + LDA #$80 + STA FAULT+1 + JSR PRERR ; Print ROM name with PRERR to set FAULT + JSR OSNEWL + JSR OSNEWL + PLP ; Get entry type back + LDA #$01 + JMP AUXADDR + +* OSBYTE $8F - Issue service call +* X=service call, Y=parameter +* +BYTE8F TXA +SERVICE LDX #$0F + BIT $8006 + BPL :SERVSKIP ; No service entry + JSR $8003 ; Call service entry + TAX + BEQ :SERVDONE +:SERVSKIP LDX #$FF +:SERVDONE RTS + + +* KERNEL/OSCLI.S +**************** + +* OSCLI HANDLER +* On entry, XY=>command string +* TO DO: line pointer in XY should be in LPTR +* +CLIHND PHX + PHY + STX ZP1+0 ; Pointer to CLI + STY ZP1+1 +:L1 LDA (ZP1) + CMP #'*' ; Trim any leading stars + BEQ :NEXT + CMP #' ' ; Trim any leading spaces + BEQ :NEXT + BRA :TRIMMED +:NEXT INC ZP1 + BNE :L1 + INC ZP1+1 + BRA :L1 +:TRIMMED CMP #'|' ; | is comment + BEQ :IEXIT + CMP #$0D ; Carriage return + BEQ :IEXIT + LDA #<:QUIT + STA ZP2 + LDA #>:QUIT + STA ZP2+1 + JSR STRCMP + BCS :S1 + JSR STARQUIT + BRA :IEXIT +:S1 LDA #<:CAT + STA ZP2 + LDA #>:CAT + STA ZP2+1 + JSR STRCMP + BCS :S2 + JSR STARCAT + BRA :IEXIT +:S2 LDA #<:CAT2 + STA ZP2 + LDA #>:CAT2 + STA ZP2+1 + JSR STRCMP + BCS :S3 + JSR STARCAT + BRA :IEXIT +:S3 LDA #<:DIR + STA ZP2 + LDA #>:DIR + STA ZP2+1 + JSR STRCMP + BCS :S4 + JSR STARDIR + BRA :IEXIT +:S4 LDA #<:LOAD + STA ZP2 + LDA #>:LOAD + STA ZP2+1 + JSR STRCMP + BCS :S5 + JSR STARLOAD + BRA :EXIT +:S5 LDA #<:SAVE + STA ZP2 + LDA #>:SAVE + STA ZP2+1 + JSR STRCMP + BCS :S6 + JSR STARSAVE +:IEXIT BRA :EXIT +:S6 LDA #<:RUN + STA ZP2 + LDA #>:RUN + STA ZP2+1 + JSR STRCMP + BCS :S7 + JSR STARRUN + BRA :EXIT +:S7 LDA #<:HELP + STA ZP2 + LDA #>:HELP + STA ZP2+1 + JSR STRCMP + BCS :ASKROM + JSR STARHELP + BRA :EXIT +:ASKROM LDA $8006 ; Check for service entry + BPL :UNSUPP ; No service entry +; LDA $8003 ; Check for service entry +; CMP #$4C ; Not a JMP? +; BNE :UNSUPP ; Only BASIC has no srvc entry + LDA ZP1 ; String in (OSLPTR),Y + STA OSLPTR + LDA ZP1+1 + STA OSLPTR+1 + LDY #$00 + LDA #$04 ; Service 4 Unrecognized Cmd + LDX #$0F ; ROM slot + JSR $8003 ; Service entry point + TAX ; Check return + BEQ :EXIT ; Call claimed + +:UNSUPP LDA #<:OSCLIM + LDY #>:OSCLIM + JSR PRSTR + PLY + PLX + STX ZP3 + STY ZP3+1 + LDY #$00 +:PL1 LDA (ZP3),Y + CMP #$0D + BEQ :PS1 + CMP #$00 + BEQ :PS1 + JSR $FFEE ; OSWRCH + INY + BRA :PL1 +:PS1 LDA #<:OSCLIM2 + LDY #>:OSCLIM2 + JSR PRSTR + RTS +:EXIT PLY + PLX + RTS +:QUIT ASC 'QUIT' + DB $00 +:CAT ASC 'CAT' + DB $00 +:CAT2 ASC '.' + DB $00 +:DIR ASC 'DIR' + DB $00 +:LOAD ASC 'LOAD' + DB $00 +:SAVE ASC 'SAVE' + DB $00 +:RUN ASC 'RUN' + DB $00 +:HELP ASC 'HELP' + DB $00 +:OSCLIM ASC 'OSCLI(' + DB $00 +:OSCLIM2 ASC ').' + DB $00 + +* String comparison for OSCLI +* Compares str in ZP1 with null-terminated str in ZP2 +* Clear carry if match, set carry otherwise +* Leaves (ZP1),Y pointing to char after verb +STRCMP LDY #$00 +:L1 LDA (ZP2),Y + BEQ :PMATCH + CMP (ZP1),Y + BNE :MISMTCH + INY + BRA :L1 +:PMATCH LDA (ZP1),Y + CMP #$0D + BEQ :MATCH + CMP #' ' + BEQ :MATCH + CMP #'"' + BEQ :MATCH + BRA :MISMTCH +:MATCH CLC + RTS +:MISMTCH SEC + RTS + +* Print *HELP test +STARHELP LDA #<:MSG + LDY #>:MSG + JSR PRSTR + LDA #$09 ; Language name + LDY #$80 + JSR PRSTR + LDA #<:MSG2 + LDY #>:MSG2 + JSR PRSTR + RTS +:MSG DB $0D + ASC 'Applecorn MOS v0.01' + DB $0D,$0D,$00 +:MSG2 DB $0D,$00 + +* Handle *QUIT command +STARQUIT >>> XF2MAIN,QUIT + +* Handle *CAT / *. command (list directory) +STARCAT JMP FSCCAT + +* Consume spaces in command line. Treat " as space! +* Return C set if no space found, C clear otherwise +* Command line pointer in (ZP1),Y +EATSPC LDA (ZP1),Y ; Check first char is ... + CMP #' ' ; ... space + BEQ :START + CMP #'"' ; Or quote mark + BEQ :START + BRA :NOTFND +:START INY +:L1 LDA (ZP1),Y ; Eat any additional ... + CMP #' ' ; ... spaces + BEQ :CONT + CMP #'"' ; Or quote marks + BNE :DONE +:CONT INY + BRA :L1 +:DONE CLC + RTS +:NOTFND SEC + RTS + +* Consume chars in command line until space or " is found +* Command line pointer in (ZP1),Y +* Returns with carry set if EOL +EATWORD LDA (ZP1),Y + CMP #' ' + BEQ :SPC + CMP #'"' + BEQ :SPC + CMP #$0D ; Carriage return + BEQ :EOL + INY + BRA EATWORD +:SPC CLC + RTS +:EOL SEC + RTS + +* Add Y to ZP1 pointer. Clear Y. +ADDZP1Y CLC + TYA + ADC ZP1 + STA ZP1 + LDA #$00 + ADC ZP1+1 + STA ZP1+1 + LDY #$00 + RTS + +* Decode ASCII hex digit in A +* Returns with carry set if bad char, C clear otherwise +HEXDIGIT CMP #'F'+1 + BCS :BADCHAR ; char > 'F' + CMP #'A' + BCC :S1 + SEC ; 'A' <= char <= 'F' + SBC #'A'-10 + CLC + RTS +:S1 CMP #'9'+1 + BCS :BADCHAR ; '9' < char < 'A' + CMP #'0' + BCC :BADCHAR ; char < '0' + SEC ; '0' <= char <= '9' + SBC #'0' + CLC + RTS +:BADCHAR SEC + RTS + +* Decode hex constant on command line +* On entry, ZP1 points to command line +HEXCONST LDX #$00 +:L1 STZ :BUF,X ; Clear :BUF + INX + CPX #$04 + BNE :L1 + LDX #$00 + LDY #$00 +:L2 LDA (ZP1),Y ; Parse hex digits into + JSR HEXDIGIT ; :BUF, left aligned + BCS :NOTHEX + STA :BUF,X + INY + INX + CPX #$04 + BNE :L2 + LDA (ZP1),Y ; Peek at next char +:NOTHEX CPX #$00 ; Was it the first digit? + BEQ :ERR ; If so, bad hex constant + CMP #' ' ; If whitespace, then okay + BEQ :OK + CMP #$0D + BEQ :OK +:ERR SEC + RTS +:OK LDA :BUF-4,X + ASL + ASL + ASL + ASL + ORA :BUF-3,X + STA ADDRBUF+1 + LDA :BUF-2,X + ASL + ASL + ASL + ASL + ORA :BUF-1,X + STA ADDRBUF + CLC + RTS +:ZEROPAD DB $00,$00,$00 +:BUF DB $00,$00,$00,$00 + +ADDRBUF DW $0000 ; Used by HEXCONST + +* Handle *LOAD command +* On entry, ZP1 points to command line +STARLOAD JSR CLRCB + JSR EATSPC ; Eat leading spaces + BCS :ERR + JSR ADDZP1Y ; Advance ZP1 + LDA ZP1 ; Pointer to filename + STA OSFILECB + LDA ZP1+1 + STA OSFILECB+1 + JSR EATWORD ; Advance past filename + BCS :NOADDR ; No load address given + LDA #$0D ; Carriage return + STA (ZP1),Y ; Terminate filename + INY + JSR EATSPC ; Eat any whitespace + JSR ADDZP1Y ; Update ZP1 + JSR HEXCONST + BCS :ERR ; Bad hex constant + LDA ADDRBUF + STA OSFILECB+2 ; Load address LSB + LDA ADDRBUF+1 + STA OSFILECB+3 ; Load address MSB +:OSFILE LDX #OSFILECB + LDA #$FF ; OSFILE load flag + JSR OSFILE +:END RTS +:NOADDR LDA #$FF ; Set OSFILECB+6 to non-zero + STA OSFILECB+6 ; Means use the file's addr + BRA :OSFILE +:ERR JSR BEEP + RTS + +* Handle *SAVE command +* On entry, ZP1 points to command line +STARSAVE JSR CLRCB + JSR EATSPC ; Eat leading space + BCS :ERR + JSR ADDZP1Y ; Advance ZP1 + LDA ZP1 ; Pointer to filename + STA OSFILECB + LDA ZP1+1 + STA OSFILECB+1 + JSR EATWORD + BCS :ERR ; No start address given + LDA #$0D ; Carriage return + STA (ZP1),Y ; Terminate filename + INY + JSR EATSPC ; Eat any whitespace + JSR ADDZP1Y ; Update ZP1 + JSR HEXCONST + BCS :ERR ; Bad start address + LDA ADDRBUF + STA OSFILECB+10 + LDA ADDRBUF+1 + STA OSFILECB+11 + JSR EATSPC ; Eat any whitespace + JSR ADDZP1Y ; Update ZP1 + JSR HEXCONST + BCS :ERR ; Bad end address + LDA ADDRBUF + STA OSFILECB+14 + LDA ADDRBUF+1 + STA OSFILECB+15 + LDX #OSFILECB + LDA #$00 ; OSFILE save flag + JSR OSFILE +:END RTS +:ERR JSR BEEP + RTS + +* Handle *RUN command +* On entry, ZP1 points to command line +STARRUN JSR ADDZP1Y + LDX ZP1+0 + LDY ZP1+1 + LDA #$04 +CALLFSCV JMP (FSCV) ; Hand on to filing system + +* Clear OSFILE control block to zeros +CLRCB LDA #$00 + LDX #$00 +:L1 STA OSFILECB,X + INX + CPX #18 + BNE :L1 + RTS + diff --git a/jgh/auxmem.misc.s b/jgh/auxmem.misc.s new file mode 100644 index 0000000..9cd6c02 --- /dev/null +++ b/jgh/auxmem.misc.s @@ -0,0 +1,282 @@ +* MISC.S +******** +* Misc functions and API entry block +* + +* OSBYTE $80 - ADVAL +************************************ +* Read input device or buffer status + +BYTE80 LDY #$00 ; Prepare return=&00xx + TXA ; X<0 - info about buffers + BMI :ADVALBUF ; X>=0 - read input devices +* +* TEST CODE + CPX #$7F + BNE :ADVALNONE +ADVALWAIT JSR KBDREAD + BCC ADVALWAIT + TAX + RTS +* TEST CODE +* +:ADVALNONE LDX #$00 ; Input, just return 0 + RTS +:ADVALBUF INX + BEQ :ADVALKBD ; Fake keyboard buffer + INX + BEQ :ADVALOK ; Serial input, return 0 + LDX #$01 ; For outputs, return 1 char free + RTS +:ADVALKBD BIT $C000 ; Test keyboard data/strobe + BPL :ADVALOK ; No Strobe, return 0 + INX ; Strobe, return 1 +:ADVALOK RTS + + +****************** +* Helper functions +****************** + +* Beep +BEEP PHA + PHX + LDX #$80 +:L1 LDA $C030 + JSR DELAY + INX + BNE :L1 + PLX + PLA + RTS + +* Delay approx 1/100 sec +DELAY PHX + PHY + LDX #$00 +:L1 INX ; 2 + LDY #$00 ; 2 +:L2 INY ; 2 + CPY #$00 ; 2 + BNE :L2 ; 3 (taken) + CPX #$02 ; 2 + BNE :L1 ; 3 (taken) + PLY + PLX + RTS + +* Print string pointed to by X,Y to the screen +OUTSTR TXA + +* Print string pointed to by A,Y to the screen +PRSTR STA OSTEXT+0 ; String in A,Y + STY OSTEXT+1 +:L1 LDA (OSTEXT) ; Ptr to string in ZP3 + BEQ :S1 + JSR OSASCI + INC OSTEXT + BNE :L1 + INC OSTEXT+1 + BRA :L1 +:S1 RTS + +* Print XY in hex +OUT2HEX TYA + JSR OUTHEX + TAX ; Continue into OUTHEX + +* Print hex byte in A +OUTHEX PHA + LSR + LSR + LSR + LSR + AND #$0F + JSR PRNIB + PLA + AND #$0F ; Continue into PRNIB +; JSR PRNIB +; RTS + +* Print hex nibble in A +PRNIB CMP #$0A + BCC :S1 + CLC ; >= $0A + ADC #'A'-$0A + JSR OSWRCH + RTS +:S1 ADC #'0' ; < $0A + JMP OSWRCH + + +********************************************************** +* Interrupt Handlers, MOS redirection vectors etc. +********************************************************** + +* IRQ/BRK handler +IRQBRKHDLR + PHA +; >>> WRTMAIN +; STA $45 ; A->$45 for ProDOS IRQ handlers +; >>> WRTAUX +; Mustn't enable IRQs within the IRQ handler +; Do this manually, as we have complete control at this point + STA $C004 ; Write to main memory + STA $45 ; Store A in $45 for ProDOS IRQ handlers + STA $C005 ; Write to aux memory +; + TXA + PHA + CLD + TSX + LDA $103,X ; Get PSW from stack + AND #$10 + BEQ :IRQ ; IRQ + SEC + LDA $0104,X + SBC #$01 + STA FAULT + LDA $0105,X + SBC #$00 + STA FAULT+1 + PLA + TAX + PLA + CLI + JMP (BRKV) ; Pass on to BRK handler + +:IRQ >>> XF2MAIN,A2IRQ ; Bounce to Apple IRQ handler +IRQBRKRET + PLA ; TODO: Pass on to IRQ1V + TAX + PLA +NULLRTI RTI + +PRERR LDY #$01 +PRERRLP LDA (FAULT),Y + BEQ PRERR1 + JSR OSWRCH + INY + BNE PRERRLP +NULLRTS +PRERR1 RTS + +MOSBRKHDLR LDA #MSGBRK + JSR PRSTR + JSR PRERR + JSR OSNEWL + JSR OSNEWL +STOP JMP STOP ; Cannot return from a BRK + +MSGBRK DB $0D + ASC "ERROR: " + DB $00 + +RDROM LDA #OSRDRMM + JMP PRSTR +OSRDRMM ASC 'OSRDDRM.' + DB $00 + +EVENT LDA #OSEVENM + JMP PRSTR +OSEVENM ASC 'OSEVEN.' + DB $00 + +GSINTGO LDA #OSINITM + JMP PRSTR +OSINITM ASC 'GSINITM.' + DB $00 + +GSRDGO LDA #OSREADM + JMP PRSTR +OSREADM ASC 'GSREAD.' + DB $00 + + +* Default page 2 contents +DEFVEC DW NULLRTS ; $200 USERV + DW MOSBRKHDLR ; $202 BRKV + DW NULLRTI ; $204 IRQ1V + DW NULLRTI ; $206 IRQ2V + DW CLIHND ; $208 CLIV + DW BYTEHND ; $20A BYTEV + DW WORDHND ; $20C WORDV + DW WRCHHND ; $20E WRCHV + DW RDCHHND ; $210 RDCHV + DW FILEHND ; $212 FILEV + DW ARGSHND ; $214 ARGSV + DW BGETHND ; $216 BGETV + DW BPUTHND ; $218 BPUTV + DW GBPBHND ; $21A GBPBV + DW FINDHND ; $21C FINDV + DW FSCHND ; $21E FSCV +ENDVEC + +* +* Acorn MOS entry points at the top of RAM +* Copied from loaded code to high memory +* + +MOSVEC ; Base of API entries here in loaded code +MOSAPI EQU $FFB6 ; Real base of API entries in real memory + ORG MOSAPI + +* OPTIONAL ENTRIES +* ---------------- +*OSSERV JMP NULLRTS ; FF95 OSSERV +*OSCOLD JMP NULLRTS ; FF98 OSCOLD +*OSPRSTR JMP OUTSTR ; FF9B PRSTRG +*OSFF9E JMP NULLRTS ; FF9E +*OSSCANHEX JMP RDHEX ; FFA1 SCANHX +*OSFFA4 JMP NULLRTS ; FFA4 +*OSFFA7 JMP NULLRTS ; FFA7 +*PRHEX JMP OUTHEX ; FFAA PRHEX +*PR2HEX JMP OUT2HEX ; FFAD PR2HEX +*OSFFB0 JMP NULLRTS ; FFB0 +*OSWRRM JMP NULLRTS ; FFB3 OSWRRM + +* COMPULSARY ENTRIES +* ------------------ +VECSIZE DB ENDVEC-DEFVEC ; FFB6 VECSIZE Size of vectors +VECBASE DW DEFVEC ; FFB7 VECBASE Base of default vectors +OSRDRM JMP RDROM ; FFB9 OSRDRM Read byte from paged ROM +OSCHROUT JMP OUTCHAR ; FFBC CHROUT Send char to VDU driver +OSEVEN JMP EVENT ; FFBF OSEVEN Signal an event +GSINIT JMP GSINTGO ; FFC2 GSINIT Init string reading +GSREAD JMP GSRDGO ; FFC5 GSREAD Parse general string +NVWRCH JMP WRCHHND ; FFC8 NVWRCH Nonvectored WRCH +NVRDCH JMP RDCHHND ; FFCB NVRDCH Nonvectored RDCH +OSFIND JMP (FINDV) ; FFCE OSFIND +OSGBPB JMP (GBPBV) ; FFD1 OSGBPB +OSBPUT JMP (BPUTV) ; FFD4 OSBPUT +OSBGET JMP (BGETV) ; FFD7 OSBGET +OSARGS JMP (ARGSV) ; FFDA OSARGS +OSFILE JMP (FILEV) ; FFDD OSFILE +OSRDCH JMP (RDCHV) ; FFE0 OSRDCH +OSASCI CMP #$0D ; FFE3 OSASCI + BNE OSWRCH +OSNEWL LDA #$0A ; FFE7 OSNEWL + JSR OSWRCH +OSWRCR LDA #$0D ; FFEC OSWRCR +OSWRCH JMP (WRCHV) ; FFEE OSWRCH +OSWORD JMP (WORDV) ; FFF1 OSWORD +OSBYTE JMP (BYTEV) ; FFF4 OSBYTE +OSCLI JMP (CLIV) ; FFF7 OSCLI +NMIVEC DW NULLRTI ; FFFA NMIVEC +RSTVEC DW STOP ; FFFC RSTVEC +IRQVEC + +* Assembler doesn't like running up to $FFFF, so we bodge a bit +MOSEND + ORG MOSEND-MOSAPI+MOSVEC + DW IRQBRKHDLR ; FFFE IRQVEC +MOSVEND + +* Buffer for one 512 byte disk block in aux mem +AUXBLK DS $200 + diff --git a/jgh/auxmem.mosequ.s b/jgh/auxmem.mosequ.s new file mode 100644 index 0000000..d38375e --- /dev/null +++ b/jgh/auxmem.mosequ.s @@ -0,0 +1,61 @@ +* MOSEQU.S +******************************* +* BBC MOS WORKSPACE LOCATIONS * +******************************* + +* $00-$8F Language workspace +* $90-$9F Network workspace +* $A0-$A7 NMI workspace +* $A8-$AF Non-MOS *command workspace +* $B0-$BF Temporary filing system workspace +* $C0-$CF Persistant filing system workspace +* $D0-$DF VDU driver workspace +* $E0-$EE Internal MOS workspace +* $EF-$FF MOS API workspace + +FSFLAG1 EQU $E2 +FSFLAG2 EQU $E3 +GSFLAG EQU $E4 +GSCHAR EQU $E5 +OSTEXT EQU $E6 ; $E6 => text string +MAXLEN EQU OSTEXT+2 ; $E8 +MINCHAR EQU OSTEXT+3 ; $E9 +MAXCHAR EQU OSTEXT+4 ; $EA +OSTEMP EQU $EB ; $EB +OSKBD1 EQU $EC ; $EC kbd ws +OSKBD2 EQU OSKBD1+1 ; $ED kbd ws +OSKBD3 EQU OSKBD1+2 ; $EE kbd ws +OSAREG EQU $EF ; $EF A register +OSXREG EQU OSAREG+1 ; $F0 X register +OSYREG EQU OSXREG+1 ; $F1 Y register +OSCTRL EQU OSXREG ; $F0 (XY)=>control block +OSLPTR EQU $F2 ; $F2 => command line +; +OSINTWS EQU $FA ; $FA IRQ ZP pointer, use when IRQs off +OSINTA EQU $FC ; $FC IRQ register A store +FAULT EQU $FD ; $FD Error message pointer +ESCFLAG EQU $FF ; $FF Escape status + + +* $0200-$0235 Vectors +* $0236-$028F OSBYTE variable +* $0290-$02ED +* $02EE-$02FF MOS control block + +USERV EQU $200 ; USER vector +BRKV EQU $202 ; BRK vector +CLIV EQU $208 ; OSCLI vector +BYTEV EQU $20A ; OSBYTE vector +WORDV EQU $20C ; OSWORD vector +WRCHV EQU $20E ; OSWRCH vector +RDCHV EQU $210 ; OSRDCH vector +FILEV EQU $212 ; OSFILE vector +ARGSV EQU $214 ; OSARGS vector +BGETV EQU $216 ; OSBGET vector +BPUTV EQU $218 ; OSBPUT vector +GBPBV EQU $21A ; OSGBPB vector +FINDV EQU $21C ; OSFIND vector +FSCV EQU $21E ; FSCV misc file ops + +OSFILECB EQU $2EE ; OSFILE control block + diff --git a/jgh/auxmem.vdu.s b/jgh/auxmem.vdu.s new file mode 100644 index 0000000..94b9c56 --- /dev/null +++ b/jgh/auxmem.vdu.s @@ -0,0 +1,327 @@ +* VDU.S +**************************************************** +* Apple //e VDU Driver for 40/80 column mode (PAGE2) +**************************************************** + +********************************** +* VDU DRIVER WORKSPACE LOCATIONS * +********************************** +* $00D0-$00DF VDU driver zero page workspace + +VDUSTATUS EQU $D0 ; $D0 VDU status +VDUZP1 EQU VDUSTATUS+1 ; $D1 +VDUCOL EQU VDUSTATUS+2 ; $D2 text column +VDUROW EQU VDUSTATUS+3 ; $D3 text row +VDUADDR EQU VDUSTATUS+4 ; $D4 address of current char cell + +FXVDUQLEN EQU $D1 ; TEMP HACK +VDUCHAR EQU $D6 ; TEMP HACK +VDUQ EQU $D7 ; TEMP HACK + + +* Clear to EOL +CLREOL LDA ROW + ASL + TAX + LDA SCNTAB,X ; LSB of row + STA ZP1 + LDA SCNTAB+1,X ; MSB of row + STA ZP1+1 + LDA COL + PHA + STZ COL +:L1 LDA COL + LSR + TAY + BCC :S1 + >>> WRTMAIN +:S1 LDA #" " + STA (ZP1),Y + >>> WRTAUX + LDA COL + CMP #79 + BEQ :S2 + INC COL + BRA :L1 +:S2 PLA + STA COL + RTS + +* Clear the screen +CLEAR STZ ROW + STZ COL +:L1 JSR CLREOL +:S2 LDA ROW + CMP #23 + BEQ :S3 + INC ROW + BRA :L1 +:S3 STZ ROW + STZ COL + RTS + +* Print char in A at ROW,COL +PRCHRC PHA + LDA $C000 ; Kbd data/strobe + BMI :KEYHIT +:RESUME LDA ROW + ASL + TAX + LDA SCNTAB,X ; LSB of row address + STA ZP1 + LDA SCNTAB+1,X ; MSB of row address + STA ZP1+1 + LDA COL + BIT $C01F + BPL :S1A ; 40-col + LSR +; TAY + BCC :S1 +:S1A >>> WRTMAIN +:S1 TAY + PLA + EOR #$80 + STA (ZP1),Y ; Screen address + >>> WRTAUX + RTS +:KEYHIT STA $C010 ; Clear strobe + AND #$7F + CMP #$13 ; Ctrl-S + BEQ :PAUSE + CMP #$1B ; Esc + BNE :RESUME +:ESC SEC + ROR ESCFLAG ; Set ESCFLAG + BRA :RESUME +:PAUSE STA $C010 ; Clear strobe +:L1 LDA $C000 ; Kbd data/strobe + BPL :L1 + AND #$7F + CMP #$11 ; Ctrl-Q + BEQ :RESUME + CMP #$1B ; Esc + BEQ :ESC + BRA :PAUSE + +* Return char at ROW,COL in A and X, MODE in Y +BYTE87 +GETCHRC LDA ROW + ASL + TAX + LDA SCNTAB,X + STA ZP1 + LDA SCNTAB+1,X + STA ZP1+1 + LDA COL + BIT $C01F + BPL :S1A ; 40-col + LSR + BCC :S1 +:S1A STA $C002 ; Read main memory +:S1 TAY + LDA (ZP1),Y + EOR #$80 + STA $C003 ; Read aux mem again + TAX + LDY #$00 + BIT $C01F + BMI :GETCHOK + INY +:GETCHOK RTS + +BYTE86 LDY ROW ; $86 = read cursor pos + LDX COL + RTS + +* Perform backspace & delete operation +DELETE JSR BACKSPC +; LDA COL +; BEQ :S1 +; DEC COL +; BRA :S2 +;:S1 LDA ROW +; BEQ :S3 +; DEC ROW +; LDA #79 +; STA COL +:S2 LDA #' ' + JSR PRCHRC +:S3 RTS + +* Perform backspace/cursor left operation +BACKSPC LDA COL + BEQ :S1 + DEC COL + BRA :S3 +:S1 LDA ROW + BEQ :S3 + DEC ROW + LDA #39 + BIT $C01F + BPL :S2 + LDA #79 +:S2 STA COL +:S3 RTS + +;* Perform cursor right operation +;CURSRT LDA COL +; CMP #78 +; BCS :S1 +; INC COL +; RTS +;:S1 LDA ROW +; CMP #22 +; BCS :S2 +; INC ROW +; STZ COL +;:S2 RTS + +* Output character to VDU driver +* All registers trashable +OUTCHAR +; +; Quick'n'nasty VDU queue + LDX FXVDUQLEN + BNE ADDTOQ + CMP #$01 + BEQ ADDQ ; One param + CMP #$11 + BCC OUTCHARGO ; Zero param + CMP #$20 + BCS OUTCHARGO ; Print chars +ADDQ STA VDUCHAR ; Save initial character + AND #$0F + TAX + LDA QLEN,X + STA FXVDUQLEN + BEQ OUTCHARGO1 + RTS +QLEN DB -0,-1,-2,-5,-0,-0,-1,-9 + DB -8,-5,-0,-0,-4,-4,-0,-2 +ADDTOQ STA VDUQ-256+9,X + INC FXVDUQLEN + BNE :IDONE +OUTCHARGO1 LDA VDUCHAR +; end nasty hack +; +OUTCHARGO CMP #$00 ; NULL + BNE :T1 + BRA :IDONE +:T1 CMP #$07 ; BELL + BNE :T2 + JSR BEEP + BRA :IDONE +:T2 CMP #$08 ; Backspace + BNE :T3 + JSR BACKSPC + BRA :IDONE +:T3 CMP #$09 ; Cursor right + BNE :T4 +; JSR CURSRT + JSR :VDU09 + BRA :IDONE +:T4 CMP #$0A ; Linefeed + BNE :T5 + LDA ROW + CMP #23 + BEQ :SCROLL + INC ROW +:IDONE BRA :DONE +:T5 CMP #$0B ; Cursor up + BNE :T6 + LDA ROW + BEQ :DONE + DEC ROW + BRA :DONE +:T6 CMP #$0D ; Carriage return + BNE :T7 +; JSR CLREOL + STZ COL + BRA :DONE +:T7 CMP #$0C ; Ctrl-L + BEQ :T7A + CMP #$16 + BNE :T8 + LDA VDUQ+8 + EOR #$07 + AND #$01 + TAX + STA $C00C,X +:T7A JSR CLEAR + BRA :DONE +:T8 CMP #$1E ; Home + BNE :T9 + STZ ROW + STZ COL + BRA :DONE +:T9 CMP #$7F ; Delete + BNE :T10 + JSR DELETE + BRA :DONE +:T10 CMP #$20 + BCC :DONE + JSR PRCHRC + +* Perform cursor right operation +:VDU09 LDA COL + CMP #39 + BCC :S2 + BIT $C01F + BPL :T11 + CMP #79 + BCC :S2 +:T11 STZ COL + LDA ROW + CMP #23 + BEQ :SCROLL + INC ROW + BRA :DONE +:S2 INC COL + BRA :DONE +:SCROLL JSR SCROLL +; STZ COL + JSR CLREOL +:DONE RTS + +* Scroll whole screen one line +SCROLL LDA #$00 +:L1 PHA + JSR SCR1LINE + PLA + INC + CMP #23 + BNE :L1 + RTS + +* Copy line A+1 to line A +SCR1LINE ASL ; Dest addr->ZP1 + TAX + LDA SCNTAB,X + STA ZP1 + LDA SCNTAB+1,X + STA ZP1+1 + INX ; Source addr->ZP2 + INX + LDA SCNTAB,X + STA ZP2 + LDA SCNTAB+1,X + STA ZP2+1 + LDY #$00 +:L1 LDA (ZP2),Y + STA (ZP1),Y + STA $C002 ; Read main mem + >>> WRTMAIN + LDA (ZP2),Y + STA (ZP1),Y + STA $C003 ; Read aux mem + >>> WRTAUX + INY + CPY #40 + BNE :L1 + RTS + +* Addresses of screen rows in PAGE2 +SCNTAB DW $800,$880,$900,$980,$A00,$A80,$B00,$B80 + DW $828,$8A8,$928,$9A8,$A28,$AA8,$B28,$BA8 + DW $850,$8D0,$950,$9D0,$A50,$AD0,$B50,$BD0 + diff --git a/jgh/loader.s b/jgh/loader.s new file mode 100644 index 0000000..e19bf08 --- /dev/null +++ b/jgh/loader.s @@ -0,0 +1,112 @@ +* LOADER.S +* (c) Bobbi 2021 GPLv3 +* +* Applecorn loader code + +* Loads Acorn ROM file (16KB) from disk and writes it +* to aux memory starting at $08000. Copies Applecorn MOS +* to aux memory starting at AUXMOS1 and jumps to it. +* (Note that the MOS code will relocate itself to $D000.) +START STZ :BLOCKS + LDX #$00 +:L1 LDA HELLO,X ; Signon message + BEQ :S1 + JSR COUT1 + INX + BRA :L1 +:S1 JSR CROUT + JSR SETPRFX + JSR DISCONN + + STA $C009 ; Alt ZP on + STZ $9F ; WARMSTRT - set cold! + STA $C008 ; Alt ZP off + + LDA #ROMFILE + STA OPENPL+2 + JSR OPENFILE ; Open ROM file + BCC :S2 + LDX #$00 +:L2 LDA CANTOPEN,X + BEQ :ER1 + JSR COUT1 + INX + BRA :L2 + BRA :S2 +:ER1 JSR CROUT + JSR BELL + RTS + +:S2 LDA OPENPL+5 ; File reference number + STA READPL+1 + +:L3 LDA #'.'+$80 ; Read file block by block + JSR COUT1 + JSR RDFILE + BCS :S3 ; EOF (0 bytes left) or some error + + LDA # A1L,A1H + STA A1L + LDA #>BLKBUF + STA A1H + + LDA # A2L,A2H + STA A2L + LDA #>BLKBUFEND + STA A2H + + LDA # A4L, A4H + STA A4L + LDA #>AUXADDR + LDX :BLOCKS +:L4 CPX #$00 + BEQ :S25 + INC + INC + DEX + BRA :L4 +:S25 STA A4H + + SEC ; Copy Main -> Aux + JSR AUXMOVE + + INC :BLOCKS + BRA :L3 + +:S3 LDA OPENPL+5 ; File reference number + STA CLSPL+1 + JSR CLSFILE + + LDA #MOSSHIM + STA A1H + + LDA #MOSSHIM+$1000 + STA A2H + + LDA #AUXMOS1 + STA A4H + + SEC ; Copy MOS from Main->Aux + JSR AUXMOVE + + LDA #RESET + STA RSTV + LDA #>RESET + STA RSTV+1 + EOR #$A5 ; Checksum + STA RSTV+2 + + TSX ; Save SP at $0100 + STX $0100 + >>> XF2AUX,AUXMOS1 + +:BLOCKS DB 0 ; Counter for blocks read + diff --git a/jgh/mainmem.s b/jgh/mainmem.s new file mode 100644 index 0000000..96d60bb --- /dev/null +++ b/jgh/mainmem.s @@ -0,0 +1,743 @@ +* MAINMEM.S +* (c) Bobbi 2021 GPL v3 +* +* Code that runs on the Apple //e in main memory. +* This code is mostly glue between the BBC Micro code +* which runs in aux mem and Apple II ProDOS. + +* Trampoline in main memory used by aux memory IRQ handler +* to invoke Apple II / ProDOS IRQs in main memory +A2IRQ >>> ENTMAIN + JSR A2IRQ2 + >>> XF2AUX,IRQBRKRET +A2IRQ2 PHP ; Fake things to look like IRQ + JMP (A2IRQV) ; Call Apple II ProDOS ISR + +* Set prefix if not already set +SETPRFX LDA #GPFXCMD + STA :OPC7 ; Initialize cmd byte to $C7 +:L1 JSR MLI +:OPC7 DB $00 + DW GSPFXPL + LDX $0300 + BNE RTSINST + LDA $BF30 + STA ONLPL+1 ; Device number + JSR MLI + DB ONLNCMD + DW ONLPL + LDA $0301 + AND #$0F + TAX + INX + STX $0300 + LDA #$2F + STA $0301 + DEC :OPC7 + BNE :L1 +RTSINST RTS + +* Disconnect /RAM ramdrive to avoid aux corruption +* Stolen from Beagle Bros Extra K +DISCONN LDA $BF98 + AND #$30 + CMP #$30 + BNE :S1 + LDA $BF26 + CMP $BF10 + BNE :S2 + LDA $BF27 + CMP $BF11 + BEQ :S1 +:S2 LDY $BF31 +:L1 LDA $BF32,Y + AND #$F3 + CMP #$B3 + BEQ :S3 + DEY + BPL :L1 + BMI :S1 +:S3 LDA $BF32,Y + STA $0302 +:L2 LDA $BF33,Y + STA $BF32,Y + BEQ :S4 + INY + BNE :L2 +:S4 LDA $BF26 + STA $0300 + LDA $BF27 + STA $0301 + LDA $BF10 + STA $BF26 + LDA $BF11 + STA $BF27 + DEC $BF31 +:S1 RTS + +* Reset handler - invoked on Ctrl-Reset +* XFER to AUXMOS ($C000) in aux, AuxZP on, LC on +RESET TSX + STX $0100 + >>> XF2AUX,AUXMOS + RTS + +* Copy 512 bytes from BLKBUF to AUXBLK in aux LC +COPYAUXBLK + LDA $C08B ; R/W LC RAM, bank 1 + LDA $C08B + STA $C009 ; Alt ZP (and Alt LC) on + + LDY #$00 +:L1 LDA BLKBUF,Y + STA $C005 ; Write aux mem + STA AUXBLK,Y + STA $C004 ; Write main mem + CPY #$FF + BEQ :S1 + INY + BRA :L1 + +:S1 LDY #$00 +:L2 LDA BLKBUF+$100,Y + STA $C005 ; Write aux mem + STA AUXBLK+$100,Y + STA $C004 ; Write main mem + CPY #$FF + BEQ :S2 + INY + BRA :L2 + +:S2 STA $C008 ; Alt ZP off + LDA $C081 ; Bank the ROM back in + LDA $C081 + RTS + +* ProDOS file handling for MOS OSFIND OPEN call +* Options in A: $40 'r', $80 'w', $C0 'rw' +OFILE >>> ENTMAIN + PHA ; Preserve arg for later + CMP #$80 ; Write mode + BNE :S0 + + LDA #MOSFILE + STA DESTPL+2 + JSR MLI + DB DESTCMD + DW DESTPL + + LDA #MOSFILE + STA CREATEPL+2 + STA OPENPL+2 + LDA #$C3 ; Access unlocked + STA CREATEPL+3 + LDA #$06 ; Filetype BIN + STA CREATEPL+4 + LDA #$00 ; Auxtype + STA CREATEPL+5 + LDA #$00 + STA CREATEPL+6 + LDA #$01 ; Storage type - file + STA CREATEPL+7 + LDA $BF90 ; Current date + STA CREATEPL+8 + LDA $BF91 + STA CREATEPL+9 + LDA $BF92 ; Current time + STA CREATEPL+10 + LDA $BF93 + STA CREATEPL+11 + JSR CRTFILE + +:S0 LDA #$00 ; Look for empty slot + JSR FINDBUF + STX BUFIDX + CPX #$00 + BNE :S1 + LDA #IOBUF1 + BRA :S4 +:S1 CPX #$01 + BNE :S2 + LDA #IOBUF2 + BRA :S4 +:S2 CPX #$02 + BNE :S3 + LDA #IOBUF3 + BRA :S4 +:S3 CPX #$03 + BNE :NOTFND ; Out of buffers really + LDA #IOBUF4 + +:S4 STA OPENPL2+3 + STY OPENPL2+4 + + LDA #MOSFILE + STA OPENPL2+2 + JSR MLI + DB OPENCMD + DW OPENPL2 + BCS :NOTFND + LDA OPENPL2+5 ; File ref number + LDX BUFIDX + CPX #$FF + BEQ FINDEXIT + STA FILEREFS,X ; Record the ref number + BRA FINDEXIT +:NOTFND LDA #$00 +FINDEXIT >>> XF2AUX,OSFINDRET +BUFIDX DB $00 + +* ProDOS file handling for MOS OSFIND CLOSE call +CFILE >>> ENTMAIN + LDA MOSFILE ; File ref number + STA CLSPL+1 + JSR CLSFILE + LDA MOSFILE + JSR FINDBUF + CPX #$FF + BEQ :S1 + LDA #$00 + STA FILEREFS,X +:S1 JMP FINDEXIT + +* Map of file reference numbers to IOBUF1..4 +FILEREFS DB $00,$00,$00,$00 + +* Search FILEREFS for value in A +FINDBUF LDX #$00 +:L1 CMP FILEREFS,X + BEQ :END + INX + CPX #$04 + BNE :L1 + LDX #$FF ; $FF for not found +:END RTS + +* ProDOS file handling for MOS OSBGET call +* Returns with char read in A and error num in Y (or 0) +FILEGET >>> ENTMAIN + LDA MOSFILE ; File ref number + STA READPL2+1 + JSR MLI + DB READCMD + DW READPL2 + BCC :NOERR + TAY ; Error number in Y + BRA :EXIT +:NOERR LDY #$00 + LDA BLKBUF +:EXIT >>> XF2AUX,OSBGETRET + +* ProDOS file handling for MOS OSBPUT call +* Enters with char to write in A +FILEPUT >>> ENTMAIN + STA BLKBUF ; Char to write + + LDA MOSFILE ; File ref number + STA WRITEPL+1 + LDA #$01 ; Bytes to write + STA WRITEPL+4 + LDA #$00 + STA WRITEPL+5 + JSR WRTFILE + >>> XF2AUX,OSBPUTRET + +* ProDOS file handling for OSBYTE $7F EOF +* Returns EOF status in A ($FF for EOF, $00 otherwise) +FILEEOF >>> ENTMAIN + + LDA MOSFILE ; File ref number + STA GEOFPL+1 + STA GMARKPL+1 + JSR MLI + DB GEOFCMD + DW GEOFPL + BCS :ISEOF ; If error, just say EOF + + JSR MLI + DB GMARKCMD + DW GMARKPL + BCS :ISEOF ; If error, just say EOF + + LDA GEOFPL+2 ; Subtract Mark from EOF + SEC + SBC GMARKPL+2 + STA :REMAIN + LDA GEOFPL+3 + SBC GMARKPL+3 + STA :REMAIN+1 + LDA GEOFPL+4 + SBC GMARKPL+4 + STA :REMAIN+2 + + LDA :REMAIN ; Check bytes remaining + BNE :NOTEOF + LDA :REMAIN+1 + BNE :NOTEOF + LDA :REMAIN+2 + BNE :NOTEOF +:ISEOF LDA #$FF + BRA :EXIT +:NOTEOF LDA #$00 +:EXIT >>> XF2AUX,CHKEOFRET +:REMAIN DS 3 ; Remaining bytes + +* ProDOS file handling for OSARGS flush commands +FLUSH >>> ENTMAIN + LDA MOSFILE ; File ref number + STA FLSHPL+1 + JSR MLI + DB FLSHCMD + DW FLSHPL + >>> XF2AUX,OSARGSRET + +* ProDOS file handling for OSARGS set ptr command +SEEK >>> ENTMAIN + LDA MOSFILE ; File ref number + STA GMARKPL+1 ; GET_MARK has same params + LDA MOSFILE+2 ; Desired offset in MOSFILE[2..4] + STA GMARKPL+2 + LDA MOSFILE+3 + STA GMARKPL+3 + LDA MOSFILE+4 + STA GMARKPL+4 + JSR MLI + DB SMARKCMD + DW GMARKPL + >>> XF2AUX,OSARGSRET + +* ProDOS file handling for OSARGS get ptr command +* and for OSARGs get length command +TELL >>> ENTMAIN + LDA MOSFILE ; File ref number + STA GMARKPL+1 + LDA MOSFILE+2 ; Mode (0=pos, otherwise len) + CMP #$00 + BEQ :POS + JSR MLI + DB GEOFCMD + DW GMARKPL ; MARK parms same as EOF parms + BRA :S1 +:POS JSR MLI + DB GMARKCMD + DW GMARKPL +:S1 LDX MOSFILE+1 ; Pointer to ZP control block + BCS :ERR + LDA $C08B ; R/W LC RAM, bank 1 + LDA $C08B + STA $C009 ; Alt ZP on + LDA GMARKPL+2 + STA $00,X + LDA GMARKPL+3 + STA $01,X + LDA GMARKPL+4 + STA $02,X + STZ $03,X + STA $C008 ; Alt ZP off + LDA $C081 ; Bank the ROM back in + LDA $C081 +:EXIT >>> XF2AUX,OSARGSRET +:ERR LDX MOSFILE+1 ; Address of ZP control block + LDA $C08B + LDA $C08B + STA $C009 + STZ $00,X + STZ $01,X + STZ $02,X + STZ $03,X + STZ $C008 + LDA $C081 + LDA $C081 + BRA :EXIT + +* ProDOS file handling for MOS OSFILE LOAD call +* Return A=0 if successful +* A=1 if file not found +* A=2 if read error +LOADFILE >>> ENTMAIN + STZ :BLOCKS + LDA #MOSFILE + STA OPENPL+2 + JSR OPENFILE + BCS :NOTFND ; File not found +:L1 LDA OPENPL+5 ; File ref number + STA READPL+1 + JSR RDFILE + BCC :S1 + CMP #$4C ; EOF + BEQ :EOF + BRA :READERR +:S1 LDA #BLKBUF + STA A1H + LDA #BLKBUFEND + STA A2H + LDA FBEXEC ; If FBEXEC is zero, use addr + CMP #$00 ; in the control block + BEQ :CBADDR + ; Otherwise use the file addr +* TODO Issue GET_FILE_INFO MLI call to get file load addr +:CBADDR LDA FBLOAD + STA A4L + LDA FBLOAD+1 + LDX :BLOCKS +:L2 CPX #$00 + BEQ :S2 + INC + INC + DEX + BRA :L2 +:S2 STA A4H + SEC ; Main -> AUX + JSR AUXMOVE + INC :BLOCKS + BRA :L1 +:NOTFND LDA #$01 ; Nothing found + PHA + BRA :EXIT +:READERR LDA #$02 ; Read error + BRA :EOF2 +:EOF LDA #$00 ; Success +:EOF2 LDA OPENPL+5 ; File ref num + STA CLSPL+1 + JSR CLSFILE +:EXIT >>> XF2AUX,OSFILERET +:BLOCKS DB $00 + +* ProDOS file handling for MOS OSFILE SAVE call +* Return A=0 if successful +* A=1 if unable to create/open +* A=2 if error during save +SAVEFILE >>> ENTMAIN + LDA #MOSFILE + STA DESTPL+2 + JSR MLI + DB DESTCMD + DW DESTPL + STZ :BLOCKS + LDA #MOSFILE + STA CREATEPL+2 + STA OPENPL+2 + LDA #$C3 ; Access unlocked + STA CREATEPL+3 + LDA #$06 ; Filetype BIN + STA CREATEPL+4 + LDA FBLOAD ; Auxtype = load address + STA CREATEPL+5 + LDA FBLOAD+1 + STA CREATEPL+6 + LDA #$01 ; Storage type - file + STA CREATEPL+7 + LDA $BF90 ; Current date + STA CREATEPL+8 + LDA $BF91 + STA CREATEPL+9 + LDA $BF92 ; Current time + STA CREATEPL+10 + LDA $BF93 + STA CREATEPL+11 + JSR CRTFILE + BCS :FWD1 ; :CANTOPEN error + JSR OPENFILE + BCS :FWD1 ; :CANTOPEN error + SEC ; Compute file length + LDA FBEND + SBC FBSTRT + STA :LEN + LDA FBEND+1 + SBC FBSTRT+1 + STA :LEN+1 +:L1 LDA FBSTRT ; Setup for first block + STA A1L + STA A2L + LDA FBSTRT+1 + STA A1H + STA A2H + INC A2H ; $200 = 512 bytes + INC A2H + LDA OPENPL+5 ; File ref number + STA WRITEPL+1 + LDA #$00 ; 512 byte request count + STA WRITEPL+4 + LDA #$02 + STA WRITEPL+5 + LDX :BLOCKS +:L2 CPX #$00 ; Adjust for subsequent blks + BEQ :S1 + INC A1H + INC A1H + INC A2H + INC A2H + DEX + BRA :L2 + +:FWD1 BRA :CANTOPEN ; Forwarding call from above + +:S1 LDA :LEN+1 ; MSB of length remaining + CMP #$02 + BCS :S2 ; MSB of len >= 2 (not last) + CMP #$00 ; If no bytes left ... + BNE :S3 + LDA :LEN + BNE :S3 + BRA :NORMALEND + +:S3 LDA FBEND ; Adjust for last block + STA A2L + LDA FBEND+1 + STA A2H + LDA :LEN + STA WRITEPL+4 ; Remaining bytes to write + LDA :LEN+1 + STA WRITEPL+5 + +:S2 LDA #BLKBUF + STA A4H + + CLC ; Aux -> Main + JSR AUXMOVE + + LDA OPENPL+5 ; File ref number + STA WRITEPL+1 + JSR WRTFILE + BCS :WRITEERR + + BRA :UPDLEN + +:ENDLOOP INC :BLOCKS + BRA :L1 + +:UPDLEN SEC ; Update length remaining + LDA :LEN + SBC WRITEPL+4 + STA :LEN + LDA :LEN+1 + SBC WRITEPL+5 + STA :LEN+1 + BRA :ENDLOOP + +:CANTOPEN + LDA #$01 ; Can't open/create + BRA :EXIT +:WRITEERR + LDA OPENPL+5 ; File ref num + STA CLSPL+1 + JSR CLSFILE + LDA #$02 ; Write error + BRA :EXIT +:NORMALEND + LDA OPENPL+5 ; File ref num + STA CLSPL+1 + JSR CLSFILE + LDA #$00 ; Success! + BCC :EXIT ; If close OK + LDA #$02 ; Write error +:EXIT >>> XF2AUX,OSFILERET +:LEN DW $0000 +:BLOCKS DB $00 + +* Quit to ProDOS +QUIT INC $3F4 ; Invalidate powerup byte + STA $C054 ; PAGE2 off + JSR MLI + DB QUITCMD + DW QUITPL + RTS + +* Obtain catalog of current PREFIX dir +CATALOG >>> ENTMAIN + + JSR MLI ; Fetch prefix into BLKBUF + DB GPFXCMD + DW GPFXPL + BNE CATEXIT ; If prefix not set + + LDA #BLKBUF + STA OPENPL+2 + JSR OPENFILE + BCS CATEXIT ; Can't open dir + +CATREENTRY + LDA OPENPL+5 ; File ref num + STA READPL+1 + JSR RDFILE + BCC :S1 + CMP #$4C ; EOF + BEQ :EOF + BRA :READERR +:S1 JSR COPYAUXBLK + >>> XF2AUX,PRONEBLK +:READERR +:EOF LDA OPENPL+5 ; File ref num + STA CLSPL+1 + JSR CLSFILE +CATEXIT >>> XF2AUX,STARCATRET + +* PRONEBLK call returns here ... +CATALOGRET + >>> ENTMAIN + BRA CATREENTRY + +* Set the prefix +SETPFX >>> ENTMAIN + JSR MLI + DB SPFXCMD + DW SPFXPL + BCC :S1 + JSR BELL ; Beep on error +:S1 >>> XF2AUX,STARDIRRET + +* Create disk file +CRTFILE JSR MLI + DB CREATCMD + DW CREATEPL + RTS + +* Open disk file +OPENFILE JSR MLI + DB OPENCMD + DW OPENPL + RTS + +* Close disk file +CLSFILE JSR MLI + DB CLSCMD + DW CLSPL + RTS + +* Read 512 bytes into BLKBUF +RDFILE JSR MLI + DB READCMD + DW READPL + RTS + +* Write data in BLKBUF to disk +WRTFILE JSR MLI + DB WRITECMD + DW WRITEPL + RTS + +HELLO ASC "Applecorn - (c) Bobbi 2021 GPLv3" + HEX 00 +CANTOPEN ASC "Unable to open BASIC.ROM" + HEX 00 +ROMFILE STR "BASIC.ROM" + +* ProDOS Parameter lists for MLI calls +OPENPL HEX 03 ; Number of parameters + DW $0000 ; Pointer to filename + DW IOBUF0 ; Pointer to IO buffer + DB $00 ; Reference number returned + +OPENPL2 HEX 03 ; Number of parameters + DW $0000 ; Pointer to filename + DW $0000 ; Pointer to IO buffer + DB $00 ; Reference number returned + +CREATEPL HEX 07 ; Number of parameters + DW $0000 ; Pointer to filename + DB $00 ; Access + DB $00 ; File type + DW $0000 ; Aux type + DB $00 ; Storage type + DW $0000 ; Create date + DW $0000 ; Create time + +DESTPL HEX 01 ; Number of parameters + DW $0000 ; Pointer to filename + +READPL HEX 04 ; Number of parameters + DB $00 ; Reference number + DW BLKBUF ; Pointer to data buffer + DW 512 ; Request count + DW $0000 ; Trans count + +READPL2 HEX 04 ; Number of parameters + DB #00 ; Reference number + DW BLKBUF ; Pointer to data buffer + DW 1 ; Request count + DW $0000 ; Trans count + +WRITEPL HEX 04 ; Number of parameters + DB $01 ; Reference number + DW BLKBUF ; Pointer to data buffer + DW $00 ; Request count + DW $0000 ; Trans count + +CLSPL HEX 01 ; Number of parameters + DB $00 ; Reference number + +FLSHPL HEX 01 ; Number of parameters + DB $00 ; Reference number + +ONLPL HEX 02 ; Number of parameters + DB $00 ; Unit num + DW $301 ; Buffer + +GSPFXPL HEX 01 ; Number of parameters + DW $300 ; Buffer + +GPFXPL HEX 01 ; Number of parameters + DW BLKBUF ; Buffer + +SPFXPL HEX 01 ; Number of parameters + DW MOSFILE ; Buffer + +GMARKPL HEX 02 ; Number of parameters + DB $00 ; File reference number + DB $00 ; Mark (24 bit) + DB $00 + DB $00 + +GEOFPL HEX 02 ; Number of parameters + DB $00 ; File reference number + DB $00 ; EOF (24 bit) + DB $00 + DB $00 + +QUITPL HEX 04 ; Number of parameters + DB $00 + DW $0000 + DB $00 + DW $0000 + +* Buffer for Acorn MOS filename +MOSFILE DS 64 ; 64 bytes max prefix/file len + +* Acorn MOS format OSFILE param list +FILEBLK +FBPTR DW $0000 ; Pointer to name (in aux) +FBLOAD DW $0000 ; Load address + DW $0000 +FBEXEC DW $0000 ; Exec address + DW $0000 +FBSTRT DW $0000 ; Start address for SAVE + DW $0000 +FBEND DW $0000 ; End address for SAVE + DW $0000 +