REM > C64Host/src REM Host interface between BBC BASIC and Commodore 64 Kernal : load% =&B000:REM Start of Host code basic%=&B800:REM Start of BBC BASIC code basic$="BasC64_"+STR$~(basic%DIV256) outfile$="bbcbasic/prg" version$="2":ver$="0.23":date$=TIME$ date$="Tue,26 Feb 2013"+MID$(date$,16) REM v0.21 - Moved OSRDCH, OSWORD, OSBYTE to high memory REM VDU control dispatch implemented REM COLOUR, MODE, VDU 20, TAB implemented, all MODEs are MODE 1 REM Cursor removed during character output, ADVAL reads C64 memory REM v0.22 - SAVE done REM v0.23 - Screen moved to top of memory, uses standard BASIC with ws at &0400 : ON ERROR REPORT:P." at line ";ERL:END DIM mcode% 21*1024 : REM C64 Kernal addresses C64WRCH=&FFD2:C64RDCH=&FFE4:BRKV=&316 C64SETNAM=&FFBD:C64SETLFS=&FFBA:C64OPEN=&FFC0 C64READST=&FFB7:C64CHKIN=&FFC6 :C64CHKOUT=&FFC9 C64CHROUT=&FFD2:C64CHRIN=&FFCF :C64CLOSE=&FFC3 C64CLRCHN=&FFCC : REM Host workspace locations ScrBase =(load%AND-&400)-&400 :REM Location of Kernal text screen LowPage =&0800 :REM Default start of BASIC memory PrInline=&F9CF :REM Will later scan code to find ESCFLG=&FF FAULT=&FD :REM &50-1 - LOMEM wksp =&52 :REM &52 - OSWORD workspace regP =wksp+4 :REM &56 - also wksp+4 - Register - also OSFILE regA =regP+1 :REM &57 - also wksp+5 - store - fname pointer regX =regA+1 :REM &58 - - for regY =regX+1 :REM &59 - - BYTE/WORD/etc drv =regY+1 :REM &5A - default drive vduQ =drv+1 :REM &5B - Length of VDU queue VduQueue=&259 :REM Base of VDU queue REM &24E-&24F VDU destination REM &250 char REM &251 p1 n1 REM &252 p2 n2 REM &253 p3 n3 REM &254 l p4 n4 k REM &255 p p5 n5 x1 x1 x1 REM &256 r p6 n6 x2 y1 x2 REM &257 mask g p7 n7 y1 x2 y1 x REM &258 char col col b mode p8 n8 y2 y2 y2 y REM 1 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 REM PRN CLG COLOUR GCOL PALET RST NOVDU MODE DEFCHR GRWIN PLOT NOWIN ESC TXWIN ORIG HOME TAB : FOR opt%=4 TO 7 STEP 2 O%=mcode% PROCc64_Header O%=mcode%+basic%-load%+c64base-c64start OSCLI "Load "+basic$+" "+STR$~O% FOR addr%=O% TO O%+(16*1024-1) IF (!addr% AND &FFFFFF)=&02028D:addr%?1=&16:addr%?2=&03 :REM STA BRKV+0 IF (!addr% AND &FFFFFF)=&02038D:addr%?1=&17:addr%?2=&03 :REM STA BRKV+1 IF (!addr% AND &FFFFFF)=&002A6C:addr%?0=&4C:addr%?1=&9B:addr%?2=&FF :REM JMP (&002A) IF (!addr% AND &FFFFFF)=&020E6C:addr%?0=&4C:addr%?1=&EE:addr%?2=&FF :REM JMP (WRCHV) IF !addr%=&68378568:PrInline=addr%-O%+basic% :REM PrText REM Look for &01, &01 access REM If screen at &400, need to also relocate &04xx-&07xx access NEXT addr% O%=O%+16*1024 PROCc64_Footer NEXT opt% : A$="SAVE "+outfile$+" "+STR$~mcode%+"+"+STR$~(O%-mcode%) PRINT A$;:OSCLI A$:OSCLI "SetType "+outfile$+" 064" PRINT:END : : DEFPROCc64_Header P%=&7FF [OPT opt% \ C64 PRG header \ ============== .c64start EQUW &0801 :\ Load address .L0801 EQUW c64next:EQUW 10 :\ Line 10 EQUS CHR$&9E+STR$c64exec:EQUB 0 :\ SYS 2061 .c64next EQUW 0 :\ End of program .c64exec SEI:LDA #&30:STA &01 :\ Page everything out LDA #c64base AND 255:STA &04 :\ copy code to high memory LDA #c64base DIV 256:STA &05 LDY #load% AND 255:STY &06:\STY &283 :\ Also set top of memory LDA #load% DIV 256:STA &07:\STA &284 \STY &281:\LDA #&0C:\STA &282 :\ Set bottom of memory LDX #((end%-load%)DIV256) AND 255 .c64copylp LDA (&04),Y:STA (&06),Y INY:BNE c64copylp INC &07:INC &05 DEX:BNE c64copylp :\ Copy all bytes JMP (load%) :\ Jump to entry code EQUS STRING$(((P%AND-16)+15)-P%+1,CHR$0) :\ A bit of padding to 16-byte boundary : .c64base ]:P%=load%:[OPT opt% \ Commodore 64 Code Header \ ======================== EQUW c64cold :\ Cold entry EQUW c64warm :\ Warm entry EQUS "BBCBASIC" :\ Title : .c64vectors EQUW c64nmi EQUW c64reset EQUW c64irq : \ Cold and Warm start \ =================== .c64cold SEI:LDX #6:LDY #&30:STY &01 :\ Page in all RAM .c64veclp LDA c64vectors-1,X:STA &FFFA-1,X :\ Ensure hardware vectors set up DEX:BNE c64veclp .c64warm LDA #&36:STA &01 :\ Page in I/O and Kernal STX vduQ:STX &CC :\ Clear VDU queue, enable flashing cursor : \ Move screen memory LDA #3-(ScrBase DIV 16384)+148:STA &DD00 :\ VIC Screen address b14-b15 LDA #((ScrBase DIV 1024)*16+7) AND 255:STA &D018 :\ VIC Screen address b10-b13 LDA #(ScrBase DIV 256):STA &288 :\ Kernal screen address b8-b15 STA &284:STX &283 :\ Set top of memory : LDA &BA:STA drv :\ drive= STY &01 :\ Page in all RAM back in JSR PrInline :\ Print inline text EQUB 12 :\ Must do a CLS to reset VDU drivers EQUS "Commodore 64 BBC BASIC "+STRING$(VALversion$,"I")+" Version "+ver$+CHR$13 EQUS "Assembled at "+date$+CHR$13 EQUS "Starting with "+STR$(ScrBase-LowPage-2)+" bytes free."+CHR$13+CHR$13 NOP LDA #1:JMP basic% :\ Jump to enter BASIC : : : \ Commodore 64 Kernel Interface \ ============================= \ This part has to be below &D000 in order to be able to page I/O and Kernal \ into memory : : \ Interrupt veneers \ ================= .c64irq PHA:TXA:PHA:TYA:PHA:SEC :\ Save registers, CS=IRQ TSX:LDA &104,X :\ Get stacked P AND #&10:BEQ c64irqGo :\ BRK or IRQ : PLA:PLA:PLA :\ Drop stacked registers PLA:CLD:CLI:PLA :\ Drop flags, pop return low byte SEC:SBC #&01:STA FAULT+0 :\ Point to error block PLA:SBC #&00:STA FAULT+1 : LDA #&30:STA &01 :\ Page in all RAM JMP (BRKV) :\ Pass on to BRK handler .c64nmi PHA:TXA:PHA:TYA:PHA:CLC :\ Save registers, CC=NMI .c64irqGo LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O LDA #c64irqRet DIV 256:PHA :\ Return address back to here LDA #c64irqRet AND 255:PHA LDA #4:PHA :\ Stack 'noIRQ'+'not BRK' BCC c64nmiGo JMP (&FFFE) :\ Vector to Kernal IRQ handler .c64nmiGo JMP (&FFFA) :\ Vector to Kernal NMI handler .c64irqRet PLA:STA &01 :\ Restore memory mapping LDA &91:BMI P%+6 EOR #&80:STA ESCFLG :\ Set Esc flag if STOP pressed PLA:TAY:PLA:TAX:PLA:RTI :\ Restore registers and return .c64reset LDA #&36:STA &01:JMP (&FFFC) :\ Pass on to Kernal : : \ Paged call \ ========== \ On entry, Cy,A,X,Y=data to pass to call \ &002A-D =addr to call .c64pagedcall STA regA :\ Save A LDA &2D:BNE c64pageIn :\ dest>=&01xxxxxx, page in Kernal and I/O LDA &2C:AND #&C0:BEQ c64call :\ dest< &00400000, call in BBC environment .c64pageIn LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O JSR c64call :\ Make the call STA regA:PHP:PLA:STA regP :\ Save results PLA:STA &01 :\ Restore memory mapping LDA regP:PHA:LDA regA:PLP :\ Restore results RTS :\ Return .c64call LDA regA:JMP (&002A) :\ Jump to address in IntA : : \ Poll input stream for a character, and translate \ ================================================ .c64Inkey LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O JSR C64RDCH:TAX :\ Call Kernal PLA:STA &01 :\ Restore memory mapping TXA:BEQ c64InkeyZero :\ No key pending LDX &99:BNE c64InkeyZero :\ Input not keyboard, return raw character CMP #ASC"a"+&60:BCC c64Inkey2 CMP #ASC"z"+&61:BCS c64Inkey2 EOR #&A0:RTS :\ Translate to lower case letters .c64Inkey2 LDX #(c64keys2-c64keys1-1)AND255 :\ Scan through translation table .c64InkeyLp CMP c64keys1,X:BEQ c64InkeyMap :\ Match found DEX:BPL c64InkeyLp :\ Loop until no match found .c64InkeyZero RTS :\ EQ=no key, NE=keypress in A .c64InkeyMap CPX #c64keys1a-c64keys1 BCS c64InkeyMap80 :\ &7B-&8F range LDA c64keys2,X:TAX :\ Misc keys .c64InkeyMap80 TXA:ADC #ASC"{"-8:CMP #&8B BCC c64InkeyOk:EOR #&40 .c64InkeyOk RTS :\ NE=keypress in A : .c64keys1 EQUB 148:EQUB 3:EQUB 219:EQUB 169 :\ BS STOP \ Sh£ EQUB 92:EQUB 95:EQUB 160 :\ £ <- ShSpc .c64keys1a EQUB 186:EQUB 221:EQUB 192:EQUB 222:EQUB 20 :\ { | } ~ DEL EQUB 147:EQUB 133:EQUB 137:EQUB 134:EQUB 138 :\ ShHome f1 f2 f3 f4 EQUB 135:EQUB 139:EQUB 136:EQUB 140:EQUB 141 :\ f5 f6 f7 f8 ShRet EQUB 131:EQUB 19:EQUB 157:EQUB 29:EQUB 17 :\ ShStop Hm Lft Rgh Dn EQUB 145 :\ Up .c64keys2 EQUB 8+&8D:EQUB 27+&8D:EQUB 92+&8D:EQUB 95+&8D :\ BS Esc \ _ EQUB 96+&8D:EQUB 9+&8D:EQUB 32+&8D :\ £ TAB ShSpc : : \ Print XY in hex \ =============== .Pr2Hex TYA:JSR PrHex:TXA : \ Print A in hex \ ============== \ Has to be here to be able to page Kernal in .PrHex PHA:LSR A:LSR A:LSR A:LSR A:JSR PrNyb:PLA .PrNyb AND #15:CMP #10:BCC P%+4:ADC #6:ADC #48:\JMP c64WRCH : \ OSWRCH - Send character to current output stream \ ================================================ .c64WRCH PHP:PHA:TYA:PHA:TXA:PHA :\ Stack everything LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O TSX:LDA &104,X :\ Get stacked character LDX &9A:CPX #3:BNE wrchNoSwap :\ Output not VDU, send raw LDX vduQ:BNE wrchQueue :\ Add to VDU queue STX &D4 :\ Ensure quotes flag is clear TAX:BEQ wrchNull :\ VDU &00 - NULL \ \ Remove cursor while outputting character SEI:LDA &CC:BNE wrchCursor :\ Cursor disabled LSR &CF:BCC wrchCursor :\ Cursor is off LDY &D3:LDA (&D1),Y :\ Get character under cursor EOR #&80:STA (&D1),Y :\ Remove cursor .wrchCursor : TXA:CMP #1:BEQ wrchCtrl01 :\ VDU 1,n - sent to printer CMP #16:BCC wrchCtrl00 :\ Single-byte control characters CMP #32:BCC wrchCtrl10 :\ Multiple-byte control characters CMP #ASC"A":BCC wrchNoSwap :\ Punctuation, numbers CMP #ASC"[":BCC wrchSwap :\ Swap upper & lower case letters CMP #ASC"a":BCC wrchRemapUpper :\ Remap square bracket characters CMP #ASC"{":BCC wrchSwap :\ Swap upper & lower case letters CMP #127:BCC wrchRemapLower :\ Remap curly bracket characters CMP #160:BCC wrchColours :\ Translate to colour characters BCS wrchNoSwap :\ &A0+ - no translation .wrchSwap EOR #32 :\ Swap upper & lower case .wrchNoSwap JSR C64WRCH :\ Output to C64 Kernal LDX &D6:LDA &D9,X ORA #&80:STA &D9,X :\ Ensure text lines not linked .wrchNull PLA:STA &01 :\ Restore memory mapping .wrchUnstack PLA:TAX:PLA:TAY:PLA:PLP:RTS :\ Restore everything : .wrchQueue STA VduQueue-256,X :\ Store byte in queue INC vduQ:BNE wrchNull :\ Inc. queue, exit if not done LDA VduQueue-1 :\ Get last parameter .wrchDispatch JMP (VduQueue-11) :\ Dispatch : .wrchHome LDA #125 :\ Translates to offset of 0 for HOME .wrchDel SBC #25 :\ Translates to offset of 1 for DEL .wrchRemapLower SBC #(ASC"{"-ASC"a") :\ Subtract for "{"+ .wrchRemapUpper SBC #(ASC"Z"-16) :\ Subtract for "["+ .wrchCtrl00 TAX:LDA wrchConv00,X :\ Index into control char & bracket translation BNE wrchNoSwap :\ And output it \ \ &0D - , needs a bit of fiddling to get this working LDX &D6:LDY #0 :\ Get Y, set X=0 (yes, XY opposite way around) STY &D8:STY &C7 :\ Turn various things off CLC:JSR &FFF0:JMP wrchNull :\ Set to (0,Y) .wrchColours TAX:BPL wrchDel:AND #15:TAX :\ Jump back for &7F - DEL LDA wrchConv80,X:JSR C64WRCH :\ Translate to C64 colour code LDA #32:BNE wrchNoSwap :\ Follow with a space : .wrchCtrl01 LDA #14 :\ Use offset 14 .wrchCtrl10 CMP #30:BEQ wrchHome AND #15:ASL A:TAX :\ Index into table LDA wrchAddrs+0,X:STA VduQueue-11 LDA wrchAddrs+1,X:PHA:AND #&0F ORA #&B0:STA VduQueue-10 :\ Set dispatch address PLA:LSR A:LSR A:LSR A:LSR A BEQ wrchDispatch :\ If no parameters, jump to it ORA #&F0:STA vduQ:BNE wrchNull :\ Set length of VDU queue : .wrchAddrs EQUW (wrch16 AND &FFF) OR &0000 :\ CLG EQUW (wrch17 AND &FFF) OR &F000 :\ COLOUR EQUW (wrch18 AND &FFF) OR &E000 :\ GCOL EQUW (wrch19 AND &FFF) OR &B000 :\ Set Palette EQUW (wrch20 AND &FFF) OR &0000 :\ Reset Colours EQUW (wrch21 AND &FFF) OR &0000 :\ Disable VDU EQUW (wrch22 AND &FFF) OR &F000 :\ MODE EQUW (wrch23 AND &FFF) OR &7000 :\ DEFCHR$ EQUW (wrch24 AND &FFF) OR &8000 :\ Define Graphics Window EQUW (wrch25 AND &FFF) OR &B000 :\ PLOT EQUW (wrch26 AND &FFF) OR &0000 :\ Clear Windows EQUW (wrch27 AND &FFF) OR &0000 :\ Escape EQUW (wrch28 AND &FFF) OR &C000 :\ Define Text Window EQUW (wrch29 AND &FFF) OR &C000 :\ ORIGIN EQUW (wrch01 AND &FFF) OR &F000 :\ Send to Printer EQUW (wrch31 AND &FFF) OR &E000 :\ TAB : \ Control character and square/squiggly bracket conversion .wrchConv00 EQUB &13:EQUB &14:EQUB &FF:EQUB &FF:EQUB &FF:EQUB &FF:EQUB &FF:EQUB &FF :\ &1E,&7F,&02-07 EQUB &9D:EQUB &1D:EQUB &11:EQUB &91:EQUB &93:EQUB &00:EQUB &FF:EQUB &FF :\ &08-&0F EQUB &5B:EQUB &BF:EQUB &5D:EQUB &5E:EQUB &A4:EQUB &5C :\ " [ \ ] ^ _ ` " EQUB 179:EQUB 125:EQUB 171:EQUB &A3 :\ " { | } ~ " : \ Colour character conversion .wrchConv80 EQUB 144:EQUB 149:EQUB 30:EQUB 129:EQUB 31:EQUB 156:EQUB 150:EQUB 152 EQUB 151:EQUB 28:EQUB 153:EQUB 158:EQUB 154:EQUB 159:EQUB 155:EQUB 5 : \ Colour number conversion .wrchConvRGB EQUB 00:EQUB 09:EQUB 05:EQUB 08:EQUB 06:EQUB 04:EQUB 10:EQUB 12 EQUB 11:EQUB 02:EQUB 13:EQUB 07:EQUB 14:EQUB 03:EQUB 15:EQUB 01 : \ VDU 17,n - COLOUR n \ ------------------- .wrch17 BMI wrch17bkg :\ >&7F - background AND #15:TAX:LDA wrchConv80,X :\ Convert to C64 text colour JSR C64WRCH:JMP wrchNull :\ Set C64 text colour .wrch17bkg CMP #&C0 :\ Cy=Background/Border AND #15:TAX:LDA wrchConvRGB,X :\ Convert RGB to C64 colour BCS wrch17border .wrch17set STA &D021:BCC wrch17exit :\ Set background colour .wrch17border STA &D020 :\ Set border colour .wrch17exit JMP wrchNull : \ VDU 22,n - MODE n \ ----------------- .wrch22 LDA #&93:JSR C64WRCH :\ Do a CLS : \ VDU 20 - Reset colours \ ---------------------- .wrch20 LDA #&05:JSR C64WRCH :\ White text LDA #0:SEC:BCS wrch17set :\ Exit by setting border and background : \ VDU 31,x,y - Set text position \ ------------------------------ .wrch31 :\ TAB TAX:LDY VduQueue-2 :\ X=Ycoord, Y=Xcoord CLC:JSR &FFF0:JMP wrchNull :\ Set to (X,Y) : \ All others, pass to UnknownVDU vector \ ------------------------------------- .wrch16:\ CLG .wrch18:\ GCOL .wrch19:\ Set Palette .wrch21:\ Disable VDU .wrch23:\ Define Character .wrch24:\ Define Graphics Window .wrch25:\ PLOT .wrch26:\ Clear Windows .wrch27:\ Escape .wrch28:\ Define Text Window .wrch29:\ ORIGIN .wrch01:\ Next To Printer PLA:STA &01 :\ Restore memory mapping JSR UKVDU:JMP wrchUnstack :\ Pass on to Unknown VDU : : \ .errNoAddr BRK:EQUB 255:EQUS "Load address missing":BRK \ OSFILE - Load/Save whole files \ ============================== \ On entry, XY=>control block \ .c64FILE STX regX:STY regY:STA regA :\ Store registers TAX \CMP #8:\BEQ File2 :\ CDir \CMP #6:\BEQ File2 :\ Delete \CMP #5:\BEQ File2 :\ Info CMP #0:BEQ File2 :\ Save CMP #&FF:BEQ File1 :\ Load LDX regX .c64Unsupported RTS \ 8 -- -- -- -- \ 6 -- -- -- -- \ 5 -- -- -- -- \ 0 -- -- start end \ FF load 00 \ .File1 LDY #6:LDA (regX),Y :\ Check for load address BNE errNoAddr :\ Must specify load address .File2 \ regX=>control block \ X=OSFILE action : LDY #1:LDA (regX),Y:STA regP+1 DEY:LDA (regX),Y:STA regP+0 :\ regP=>filename DEY :\ Y=&FF \ regX=>control block \ regP=>filename \ X=OSFILE action : \ Parse filename .NameLp INY:LDA (regP),Y :\ Look for end of filename CMP #ASC"!":BCS NameLp :\ Y=name length STY wksp+0 :\ wksp+0=name length LDA drv:BNE P%+4:LDA #8 :\ Default drive, ensure not zero STA wksp+1 :\ wksp+1=drive to use LDY #1:LDA (regP),Y:DEY :\ Look at second character CMP #ASC":":BNE NameNoDrive :\ No colon, use default drive, Y=0 LDA (regP),Y :\ Get drive number CMP #ASC"1":BCC NameNoDrive :\ <'1', use default drive CMP #ASC"9"+1:BCC NameDrive :\ <='9', use it AND #&DF:SBC #7 :\ Convert 'A'+ to 10+ .NameDrive SEC:SBC #ASC"0":STA wksp+1 :\ Set drive to use DEC wksp+0:DEC wksp+0:LDY #2 :\ Reduce name length by two .NameNoDrive STX regA :\ regA=OSFILE action TYA:CLC:LDY #0 :\ Adjust pointer to start of name ADC (regX),Y:TAX:TYA:INY ADC (regX),Y:TAY :\ XY=>filename+0 or +2 : :\ wksp+0=filename length : :\ wksp+1=device number : \ We now need the Kernal paged in LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O LDA wksp+0 :\ A=name length JSR &FFBD :\ Call SETNAM LDA regA:EOR #&FF:AND #&01 :\ A=00 - Load, A=&01 - Save TAY:LDX wksp+1:LDA #&02 :\ A=Channel 2, X=Device , Y=Address 0=READ, 1=WRITE, 2=WRITE AT EOF JSR &FFBA :\ Call SETLFS JSR &FFC0:BCS c64FileErrorA1 :\ Call OPEN - this prompts for RECORD with tape on writing JSR &FFB7:BNE c64FileErrorST :\ Call READST LDA regA:BEQ File4 LDX #&02:JSR &FFC6 :\ Call CHKIN for &FF=Load LDY #2:BNE File5 :\ Y=>load address .File4 LDX #&02:JSR &FFC9 :\ Call CHKOUT for &00=Save LDY #10 :\ Y=>start address .File5 LDA (regX),Y:STA regP+0 INY:LDA (regX),Y:STA regP+1 :\ regP=>load or start address CPY #3:BEQ FileLoad : LDY #14 :\ Save file LDA (regX),Y:STA wksp+0 INY:LDA (regX),Y:STA wksp+1 :\ wksp=>end address LDY #0 .FileSaveLp LDA wksp+0:CMP regP+0:BNE FileSaveByte LDA wksp+1:CMP regP+1:BEQ FileDone .FileSaveByte LDA (regP),Y:JSR &FFD2 :\ Call CHROUT JSR &FFB7:BNE FileDoneEof :\ Call READST INC regP+0:BNE FileSaveLp INC regP+1:BNE FileSaveLp BEQ FileDone : .FileLoad :\ Load file LDY #0 .FileLoadLp JSR &FFB7:BNE FileDoneEof :\ Call READST JSR &FFCF :\ Call CHRIN STA (regP),Y:INY:BNE FileLoadLp INC regP+1:BNE FileLoadLp .FileDone LDA #&40 :\ &40=Ok at EOF : .FileDoneEof TAX:AND #&40:BEQ c64FileErrorX LDA #2:JSR &FFC3 :\ Call CLOSE JSR &FFCC :\ Call CLRCHN PLA:STA &01 :\ Restore memory mapping LDA #&01:RTS :\ &01=File : \ \ Convert C64 error returned in Status bitmap \ =========================================== \ Assumes SP=>previous page register \ b7 Device not present -> &05 -> 214 \ b6 End of file -> &0A -> 223 \ b5 Checksum error (tape) -> &0 -> 216 \ b4 Verify error -> &0 -> ??? \ b3 Block too long (tape) -> &0 -> 218 \ b2 Block too short (tape) -> &0 -> 218 \ b1 Timeout (file not found) -> &04 -> 214 \ b0 Timout on 0=reading 1=writing \ .c64FileErrorA1 BCS c64FileErrorA : .c64FileErrorX TXA .c64FileErrorST LDX #0 .c64FileErrorLp ASL A:BCS c64FileErrorBit :\ Convert bitmap to bit number INX:BNE c64FileErrorLp .c64FileErrorBit LDA c64errorbits,X :\ Get byte and continue into next \ \ Conver C64 error returned in A \ ============================== \ Assumes SP=>previous page register \ &00 escape/stop \ &01 too many files \ &02 file open \ &03 file not open \ &04 file not found \ &05 device not present \ &06 not input file \ &07 not output file \ &08 file name missing \ &09 illegal device no. \ extra errors \ &0A end of file (b6) \ &0B checksum error (b5) \ &0C verify error (b4) \ &0D block error (b4,b2) \ &0E \ &0F \ .c64FileErrorA PHA:LDA #2:JSR &FFC3 :\ Call CLOSE JSR &FFCC :\ Call CLRCHN PLA:TAX:PLA:STA &01 :\ Restore memory mapping CPX #&10:BCC P%+4:LDX #&0B :\ Unknown error becomes &00 LDA #c64err00 DIV 256:PHA :\ Push high byte of error block LDA c64errors,X:PHA :\ Push low byte of error block RTS :\ Jump to it via stack \ \ Error translation can be in high memory if Kernal paged out first \ ::EQUS STRING$(140,CHR$0) .c64errors EQUB c64err00-1:EQUB c64err01-1:EQUB c64err02-1 EQUB c64err03-1:EQUB c64err04-1:EQUB c64err05-1 EQUB c64err06-1:EQUB c64err07-1:EQUB c64err08-1 EQUB c64err09-1:EQUB c64err0A-1:EQUB c64err10-1 .c64errorbits EQUB &05:EQUB &0A:EQUB &FF:EQUB &FF EQUB &FF:EQUB &FF:EQUB &04:EQUB &FF .c64err00:BRK:EQUB 17:EQUS "Escape" :\ &00 stop .c64err01:BRK:EQUB 192:EQUS "Too many files" :\ &01 too many files .c64err02:BRK:EQUB 194:EQUS "Already open" :\ &02 file open .c64err03:BRK:EQUB 222:EQUS "File not open" :\ &03 file not open .c64err04:BRK:EQUB 214:EQUS "File not found" :\ &04 file not found .c64err05:BRK:EQUB 210:EQUS "Device not present" :\ &05 device not present .c64err06:BRK:EQUB 189:EQUS "Not open for input" :\ &06 not input file .c64err07:BRK:EQUB 193:EQUS "Not open for output" :\ &07 not output file .c64err08:BRK:EQUB 204:EQUS "Bad filename" :\ &08 file name missing .c64err09:BRK:EQUB 205:EQUS "Bad device" :\ &09 illegal device no. .c64err0A:BRK:EQUB 223:EQUS "EOF" .c64err10:BRK:EQUB 255:EQUS "Unknown error" :\ &10+ unknown BRK ]:IF(opt% AND 2):IF (c64err00 AND &FF00)<>(c64err10 AND &FF00):P."*** WARNING *** Error table at &";~c64err00;" overruns by ";(c64err10+1) AND 255;" bytes." [OPT opt% : : \ OSWORD &01 - Read TIME \ ====================== .Word01 LDA &01:PHA:LDA #&36:STA &01 :\ Page in Kernal and I/O JSR &FFDE :\ Read Jiffy clock to &YYXXAA STA wksp+0:STA wksp+3 :\ Prepare to multiply by 1+2/3 STX wksp+1:STX wksp+4 :\ to convert 1/60ths to 1/100ths STY wksp+2:STY wksp+5 .Word01lp1 :\ Can probably optimise this LSR wksp+5:ROR wksp+4:ROR wksp+3 :\ add=add/2 LDA wksp+0:ADC wksp+3:STA wksp+0 :\ num=num+add LDA wksp+1:ADC wksp+4:STA wksp+1 LDA wksp+2:ADC wksp+5:STA wksp+2 LSR wksp+5:ROR wksp+4:ROR wksp+3 :\ add=add/2 LDA wksp+5:ORA wksp+4:ORA wksp+3 BNE Word01lp1 :\ Loop until nothing left to add LDY #4 :\ Usefully, wksp+3/4 is now zero .Word01lp2 LDA wksp,Y:STA (regX),Y :\ Copy to control block DEY:BPL Word01lp2 .Word01exit PLA:STA &01:RTS :\ Restore memory mapping : \ OSWORD &05 - Read I/O memory \ ============================ .Word05 CLC : \ OSWORD &06 - Write I/O memory \ ============================= \ addr>&003Fxxxx - C64 Kernal/IO memory \ addr<&0040xxxx - BASIC memory \ .Word06 STA regP:INY:LDA (regX),Y:STA regA :\ regP=>address LDA &01:PHA :\ Save memory paging INY:INY:LDA (regX),Y:BNE Word06a :\ >&00xxxxxx - C64 memory DEY:LDA (regX),Y AND #&C0:BEQ Word06b :\ <&0040xxxx - BASIC memory .Word06a LDA #&36:STA &01 :\ Page in Kernal and I/O .Word06b BCC Word05Read LDY #4:LDA (regX),Y :\ Get byte from control block LDY #0:STA (regP),Y :\ Store in memory BCS Word01exit .Word05Read LDY #0:LDA (regP),Y :\ Get byte from memory LDY #4:STA (regX),Y :\ Store in control block BCC Word01exit : \ OSBYTE &80 - ADVAL \ ================== .Byte80 STY wksp+1:LDA #wksp STA wksp+3:STA regX LDY #0:STY regY TXA:JSR Word05 :\ Read from I/O memory LDA (regX),Y:TAX LDY #0:RTS : : ]:IF(opt% AND 2):IF P%>basic%:P."*** WARNING *** Low code overruns BASIC at &";~P%;"." [OPT opt% : \ Align to start of page EQUS STRING$(((P%+255)AND&FF00)-P%,CHR$0) EQUS STRING$((&B600-P%) AND (P%<&B600),CHR$0) EQUS STRING$((&B680-P%) AND (P%<&B680),CHR$0) EQUS STRING$((&B700-P%) AND (P%<&B700),CHR$0) EQUS STRING$((&B780-P%) AND (P%<&B780),CHR$0) EQUS STRING$((&B800-P%) AND (P%<&B800),CHR$0) ] ENDPROC : : DEFPROCc64_Footer P%=basic%+16384 [OPT opt% \ OSWORD - OS function with control block \ ======================================= .c64WORD STA regA :\ Store A CMP #&10:BCS c64WordNull :\ >&0F - exit with C=1, V=1 PHA:ADC #(&87-&7C+&00+2) :\ Index to start of OSWORD table SEC:BCS ByteDispatch :\ Jump to dispatch : : \ OSBYTE - OS function with byte data \ =================================== .c64BYTE STA regA:PHA :\ Store A CMP #&01:BCC ByteDispatch :\ <&01 - dispatch CMP #&7C:BCC ByteNull :\ <&7C - null CMP #&88:BCS ByteNull :\ >&87 - null SBC #&7C-2:CLC :\ Reduce index .ByteDispatch JSR c64Dispatch :\ Jump to dispatch PLA:RTS :\ Restore A : .c64Dispatch \ A=index into table, CC=OSBYTE, CS=OSWORD \ STX regX:STY regY :\ Store X,Y PHP:ASL A:TAX:PLP :\ Save Cy, index into table LDA ByteTable+1,X:PHA LDA ByteTable+0,X:PHA LDA regA :\ Restore A BMI ByteDispatch2 :\ If A>&7F, use X and Y LDY #0:BCC ByteDispatch2 :\ If A<&80, force Y=0 LDA (regX),Y :\ If OSWORD, fetch first byte .ByteDispatch2 SEC:LDX regX:RTS :\ CS, EQ=X, A=OSBYTE function or OSWORD (XY),0 .ByteNull PLA .c64WordNull BIT ByteNull-1:RTS :\ Set V and return : .ByteTable EQUW Byte00-1 :\ Read host type EQUW Byte7C-1 :\ Clear Escape state EQUW Byte7D-1 :\ Set Escape state EQUW Byte7E-1 :\ Ack. Escape state EQUW Byte7F-1 :\ =EOF EQUW Byte80-1 :\ =ADVAL() EQUW Byte81-1 :\ =INKEY() EQUW Byte82-1 :\ CPU high word EQUW Byte83-1 :\ Bottom of memory EQUW Byte84-1 :\ Top of memory EQUW Byte85-1 :\ Top of memory for MODE EQUW Byte86-1 :\ POS and VPOS EQUW Byte87-1 :\ Character and MODE : EQUW c64Word00-1 :\ ReadLine EQUW Word01-1 :\ =TIME EQUW c64Word02-1 :\ TIME= EQUW c64Word03-1 :\ =Timer2 EQUW c64Word04-1 :\ Timer2= EQUW Word05-1 :\ =mem() - read C64/BBC memory EQUW Word06-1 :\ mem()= - write C64/BBC memory EQUW c64Word07-1 :\ SOUND - needs to be in <&D000 memory to access I/O EQUW c64Word08-1 :\ ENVELOPE - needs to be in <&D000 memory to access I/O EQUW c64Word09-1 :\ =POINT - needs to be in <&D000 memory to access Kernal EQUW c64Word0A-1 :\ =DEFCHR$ - needs to be in <&D000 memory EQUW c64Word0B-1 :\ Read palette - no palette EQUW c64Word0C-1 :\ Write palette - no palette EQUW c64Word0D-1 :\ Read graphics coords EQUW c64Word0E-1 :\ =TIME$ EQUW c64Word0F-1 :\ TIME$= : : \ OSBYTE &00 - Read host type \ =========================== .Byte00 BNE Byte00a :\ X<>0 - return value BRK:EQUB 247:EQUS "C64 BBC BASIC OS "+ver$:BRK .Byte00a LDX #28:RTS :\ Return %xx0x1xxx : \ OSBYTE &7C-&7E - Manipulate Escape state \ ======================================== .Byte7D DEY:BMI Byte7Ca :\ Set Escape .Byte7C:.Byte7E :\ Clear Escape, Ack. Escape STY &C6 :\ Clear keyboard buffer .Byte7Ca LDX ESCFLG:STY ESCFLG :\ Clear Escape, return old state LDA &91:BPL P%-2:RTS :\ Wait until STOP flag clears : \ OSBYTE &86 - Return POS and VPOS \ ================================ .Byte86 LDA &D3:LDY &D6 :\ Fetch POS, VPOS CMP #40:BCC P%+4:SBC #40 :\ Ensure 0-39 as C64 wraps to 40-79 TAX:RTS : \ OSBYTE &81 - INKEY \ ================== .Byte81 LDX #&00 INY:BNE P%+4:LDX #&C0 :\ INKEY-256 returns &00C0 LDY #&00:CLC:RTS : .Byte7F:LDX #&00:LDY #&00:RTS :\ EOF .Byte82:LDX #&00:LDY #&00:RTS :\ Memory high word .Byte83:LDX &281:LDY &282:RTS :\ Bottom of memory .Byte84:LDX &283:LDY &284:RTS :\ Top of memory .Byte85:LDX &283:LDY &284:RTS :\ Top of memory for MODE .Byte87:LDX #&00:LDY #&01:RTS :\ Character, MODE : : \ OSWORD &00 - ReadLine \ ===================== \ On entry, Y=0, A=??, X=?? \ (regX)=> buf.lo buf.hi maxlen minchar maxchar \ On exit, Y=len, CC=ok, CS=Escape, X=??, A=?? \ .c64Word00 LDA (regX),Y:STA wksp,Y :\ Copy control block to w/s INY:CPY #5:BNE c64Word00 LDY #0 .c64Word00Lp1 JSR OSRDCH :\ Wait for character \\BCS c64Word00Esc :\ Escape CMP #&1B:BEQ c64Word00Esc :\ Escape CMP #&7F:BNE c64Word00Char :\ Not Delete CPY #&00:BEQ c64Word00Lp1 :\ Nothing to delete JSR OSWRCH :\ VDU 127 DEY:JMP c64Word00Lp1 :\ Dec. counter, loop back .c64Word00Char CMP #&15:BNE c64Word00Ins :\ Not Ctrl-U TYA:BEQ c64Word00Lp1 :\ Nothing to delete LDA #&7F .c64Word00Lp2 JSR OSWRCH:DEY :\ Delete characters BNE c64Word00Lp2 BEQ c64Word00Lp1 :\ Jump back to start .c64Word00Ins STA (wksp),Y :\ Store character CMP #&0D:CLC:BEQ c64Word00cr :\ Return - finish CPY wksp+2:BCS c64Word00max :\ Maximum length CMP #&20:BCS c64Word00ctrl :\ Control character DEY :\ Cancel following INY .c64Word00ctrl INY:JSR OSWRCH :\ Inc. counter, print character .c64Word00max JMP c64Word00Lp1 :\ Loop for more .c64Word00cr JSR OSNEWL:CLC .c64Word00Esc RTS : : : : .c64Word02:\LDX #2:\RTS :\ TIME= .c64Word03:\LDX #3:\RTS :\ =Timer2 .c64Word04:\LDX #4:\RTS :\ Timer2= .c64Word05:\LDX #5:\RTS :\ =mem() .c64Word06:\LDX #6:\RTS :\ mem()= .c64Word07:\LDX #7:\RTS :\ SOUND .c64Word08:\LDX #8:\RTS :\ ENVELOPE .c64Word09:\LDX #9:\RTS :\ =POINT .c64Word0A:\LDX #10:\RTS :\ =DEFCHR$ .c64Word0B:\LDX #11:\RTS :\ .c64Word0C:\LDX #12:\RTS :\ .c64Word0D:\LDX #13:\RTS :\ .c64Word0E:\LDX #14:\RTS :\ =TIME$ .c64Word0F:\LDX #15:\RTS :\ TIME$= .c64null RTS : : \ OSRDCH - Wait for a character from input stream \ =============================================== .c64RDCH CLC:PHP PHA:TYA:PHA:TXA:PHA .c64RDCHlp JSR c64Inkey:BEQ c64RDCHlp :\ Loop until key pressed TSX:STA &103,X :\ Place keypress on stack \ROR &104,X :\ Drop stacked Carry \LDA ESCFLG:\ASL A:\ROL &104,X :\ Copy ESCFLG into stacked Carry JMP wrchUnstack :\ Jump to unstack and return : : \ Align to start of page &FFxx \ ---------------------------- EQUS STRING$((&FA00-P%) AND (P%<&FA00),CHR$0) EQUS STRING$((&FA80-P%) AND (P%<&FA80),CHR$0) EQUS STRING$((&FB00-P%) AND (P%<&FB00),CHR$0) EQUS STRING$((&FB80-P%) AND (P%<&FB80),CHR$0) EQUS STRING$((&FC00-P%) AND (P%<&FC00),CHR$0) EQUS STRING$((&FC80-P%) AND (P%<&FC80),CHR$0) EQUS STRING$((&FD00-P%) AND (P%<&FD00),CHR$0) EQUS STRING$((&FD80-P%) AND (P%<&FD80),CHR$0) EQUS STRING$((&FE00-P%) AND (P%<&FE00),CHR$0) EQUS STRING$((&FE80-P%) AND (P%<&FE80),CHR$0) EQUS STRING$((&FF00-P%) AND (P%<&FF00),CHR$0) : \ NB! Can't use this space to call directly from code with Kernal paged out! : \ BBC MOS Entry Block \ ================== EQUS STRING$((P% AND &FF00)+&80-P%,CHR$0) .LFF80 :EQUB &00 :\ Z80 Escape flag .LFF81 :EQUB &00 :\ Z80 TempA .LFF82 :EQUW &0000 :\ Z80 Fault pointer .LFF84 :EQUW &0000 :\ Z80 Default error handler .LFF86 :EQUW &0000 :\ Z80 Command line tail pointer .LFF88 :EQUW &0000 :\ Z80 Bottom of available memory .LFF8A :EQUW &0000 :\ Z80 Top of available memory .LFF8C :EQUD &0000 :\ Z80 Transfer address .LFF90 :EQUW &0000 :\ Z80 Control block .LFF92 :EQUW &0000 :\ Z80 Current program : .LFF94 :\ &FF94 :EQUB 0 .OSSERV :\ &FF95 :JMP c64null :\ SERVICE .OSCOLD :\ &FF98 :JMP c64cold :\ COLD .OSCALL :\ &FF9B :JMP c64pagedcall :\ OSPRSTR .OSRDDEC :\ &FF9E :JMP c64null :\ Read in decimal .OSRDHEX :\ &FFA1 :JMP c64null :\ Read in hex .LFFA4 :\ &FFA4 :JMP c64null :\ .OSQUIT :\ &FFA7 :JMP c64null :\ Quit to *command loop .PR1HEX :\ &FFAA :JMP PrHex :\ Print A in hex .PR2HEX :\ &FFAD :JMP Pr2Hex :\ Print XY in hex .USERINT :\ &FFB0 :JMP c64null :\ IRQ stub .PRTEXT :\ &FFB3 :JMP PrInline :\ Print inline text BBC=OSWRSC .VECDEF :\ &FFB6 :EQUB 0:EQUW 0 :\ Pointer to default vectors .LFFB9 :\ &FFB9 :JMP c64null :\ BBC=OSRDRM .UKVDU :\ &FFBC :JMP c64null :\ Unknown VDU BBC=VDUCHR Z80/6809/etc=Generate error .OSINIT :\ &FFBF :JMP c64null :\ Reset vectors, etc. BBC=OSEVNT .SPARE1 :\ &FFC2 :JMP c64null :\ BBC=GSINIT .SPARE2 :\ &FFC5 :JMP c64null :\ BBC=GSREAD .SPARE3 :\ &FFC8 :JMP c64null :\ BBC=NVRDCH : \ Filing system entry points \ -------------------------- .OSFSC :\ &FFC5 :JMP c64null :\ BBC=NVWRCH .OSFIND :\ &FFCE :JMP c64null .OSGBPB :\ &FFD1 :JMP c64null .OSBPUT :\ &FFD4 :JMP c64null .OSBGET :\ &FFD7 :JMP c64null .OSARGS :\ &FFDA :JMP c64null .OSFILE :\ &FFDD :JMP c64FILE : \ MOS I/O entry points \ -------------------- .OSRDCH :\ &FFE0 :JMP c64RDCH .OSASCI :\ &FFE3 :CMP #&0D:BNE OSWRCH .OSNEWL :\ &FFE7 :LDA #&0A:JSR OSWRCH .OSWRCR :\ &FFEC :LDA #&0D .OSWRCH :\ &FFEE :JMP c64WRCH .OSWORD :\ &FFF1 :JMP c64WORD .OSBYTE :\ &FFF4 :JMP c64BYTE .OS_CLI :\ &FFF7 :JMP c64null : EQUW c64nmi :\ &FFFA EQUW c64reset :\ &FFFC EQUW c64irq :\ &FFFE .end% ] IF P%<>&10000:P."*** WARNING *** Code ends at ";~P%-1;" - should end at &FFFF" ENDPROC