; 6809 Serial Tube Client Code ; ============================ ; Copyright (C)2010-2012, 2014, 2017 J.G.Harston ; This can be assembled with: ; DOS as9: as9 6809.src -l now bin > errors.txt ; Kingswood as09: as09-dos -i -lSerial09.lst -0 Serial09.bin Serial09.src ; 6809.org.uk asm6809: (not yet checked) ; ; v0.10 12-Jun-2011 JGH: Converted from Tube client ; Based on 6809 Tube Client and Z80 Serial Tube Client ; v0.11 07-Jan-2012 JGH: Software Reset sent on startup ; Added client error handler ; v0.12 07-Jul-2012 JGH: SendCommand adds personality bits, incoming control ; block handler, OSWORD handler. Added hardware vectors, ; workspace in RAM for ROM-only code. ; v0.23 30-Nov-2014 JGH: Brought up to same version as hardware Tube client. ; Added PRHEX and HR2HEX, execute enters at PROGRAM ; MEMTOP moved below code if code is in high memory. ; 02-Dec-2014 JGH: Added PR_STRING. Implemented KBDTST for 6809 BIOS calls. ; STARTUP and OSCLI use internal stack. Execute checks ; code header. WARM resets MEMBOT & MEMTOP. ; v0.24 07-Dec-2014 JGH: EXECUTE enters code with A=1 or A=0, PROG saved and restored, ; soft Break will re-enter at PROG. OSCLI saves all registers. ; Raw 6809 code is not made the current program. Supervisor ; prompt makes itself current program. Full header (C) check. ; RESET avoids overwriting PROG so Soft Reset will re-enter ; current program. ; 14-Dec-2014 JGH: When EXECUTE called on RESET, does not report 'Not 6809 code' ; error, but jumps directly to CLICOM. Gives a cleaner startup ; display, and this is hook for future check for booting disk ; operating system. ; v0.25 20-Dec-2014 JGH: Added local *GO and *HELP commands, LPTR set to command line, ; OSBYTE 142 sets null LPTR. OSBYTE $82-$84 return address/256 in Y. ; Testing TxRDY uses ASLA instead of AND #$40. Squashed some JSRs ; into BSRs. ; v1.00 01-Jan-2015 JGH: Rolled over to version 1.00. ; v1.01 22-Jun-2017 JGH: EXECUTE passes caller's stack to callee. EXECUTE does not set ; code as current program, that is now responsibility of ; INITERR/OSINIT. OSCLI stack saving is now recursable. ; 23-Jun-2017 JGH: Precheck for '*RUN' to point LPTR to correct parameters. ; More stack space for transient OSCLI commands. ; v1.02 05-Aug-2017 JGH: Added test for 6309 to SWI code, optimised PRHEX routines. ; Only Transfer Type &Cx updates ADDRESS, uses 16-bit address. ; Slight optimisation to BYTE_HI. ; v1.03 06-Aug-2017 JGH: Rolled over to version 1.03 to match Tube client. ; 08-Aug-2017 JGH: Command table re-ordered to optimise dispatch. Optimised some ; ADD #$80 into ASLA. Bugfix for */(space)filename. ; 12-Aug-2017 JGH: *HELP always displays help message, optimised *HELP message. ; SWI checks for 6309. ; 15-Aug-2017 JGH: Forced JMP opcode at RAWVDU entry. ; v1.04 22-Aug-2017 JGH: Rolled over to version 1.04. ; 24-Aug-2017 JGH: Optimised SEND_BLOCK and WAIT_BLOCK. ; v1.05 25-Aug-2017 JGH: OSFILE/OSGBPB/OSARGS use big-endian control blocks ; 22-Sep-2017 JGH: PRTEXT preserves X as with Z80 Client. ; 12-Jul-2019 JGH: KBDTST/KBDIN routines use pending character input KBDPEND: EQU 1 ; KBDTST/KBDIN pending input ; v1.06 25-Jun-2017 JGH: Removed superflous use of U in EXECUTE. VERSION: EQU $0106 BUILD: EQU 0 ; ; BIGENDIAN: EQU 1 ; Big-endian control block contents for OSFILE/OSGBPB/OSARGS ; ; ; This code may be freely reused. ; ; Acknowledgements to: ; Johan Heuseveldt: mentioning 6809 CoPros on the BBC Mailing List inspired me to ; complete the code. ; David Banks: has tested the Tube version on the Matchbox CoPro "real" hardware. ; ; Notes: ; 07-Jan-2012: When I initially wrote this I didn't have any 6809 hardware, so didn't ; know if this executed. ; 20-Nov-2014: DMB has tested the Tube version on emulated hardware and shown it to work. ; I have tested this on 6809Tube emulator. ; ; Memory layout: ; +-------------+--------------------------------------------------------+ ; | 0000- | vvv User memory vvv | ; | 00C0-00FF | Internal variables if in low RAM | ; | 0100-01FF | Text buffers if in low RAM | ; | | | ; | -wwFF | ^^^ User stack ^^^ | ; | xx00-xxBF | Text buffers if in high RAM | ; | xxC0-xxFF | Internal variables if in high RAM | ; | -xxFF | ^^^ User stack if Client in RAM ^^^ | ; +-------------+--------------------------------------------------------+ ; | F800-FFFF | Client ROM, copied to RAM on startup | ; | F800-F815 | 6809 BIOS entry vectors | ; | | | ; | FEC0-FEEF | I/O addresses if in ROM memory area | ; | FEF0-FEFF | Hardware vectors, remapped from &FFF0-&FFFF with BA/BS | ; | FF00-FF7F | Text buffers and internal stack if running from RAM | ; | FF80- | Internal variables if running from RAM | ; | -FFFF | BBC MOS entry block | ; +-------------+--------------------------------------------------------+ ; ; BA/BS Signals are used to remap hardware vectors from &FFFx to &FEFx. ; ROM copied to RAM at startup, then ROM paged out by accessing any I/O. Any ; write-only I/O hidden "under" ROM must be disabled until I/O at IOADDR is ; accessed. ; Client error implemented with SWI (equivalent of 6502 BRK, Z80 RST &38, etc.) ; SWI2 and SWI3 reserved for applications (eg FLEX, OS-9, etc). ; ; To distinguish between 6809 and 6309 the SWI handler checks that U does not ; equal where PC is expected on the stack. This will normally be under program ; control when you call SWI to generate an error. If your code does not know ; what the contents of U is when generating an error, ensure U is cleared with ; CLR U:SWI:error block. ; ; In all cases JSR OSERROR can be used to generate an error. ; ; Tube client system configuration tweekables ; =========================================== ; 64K RAM with ROM copied to RAM and internal workspace ; ----------------------------------------------------- RAMSTART: EQU $0000 ; Start of user RAM ROMSTART: EQU $F800 ; Start of code RAMEND: EQU ROMSTART ; End of user RAM+1 WORKSP: EQU $FF80 ; Internal workspace STACK: EQU RAMEND ; Default stack ; This memory map looks like the SWTPC MC-09 ; ------------------------------------------ ;; RAMSTART: EQU $0000 ; Start of user RAM ;; RAMEND: EQU $DF00 ; End of user RAM+1 ;; ROMSTART: EQU $F800 ; Start of code ;; WORKSP: EQU RAMEND+$C0 ; Workspace at $DFC0 ;; STACK: EQU RAMEND ; Default stack ;; ; NB, the MC-09 can map up to 1M of memory in 4K pages via DAT ; Alternatively, put the workspace in zero page like the 6502 ; ----------------------------------------------------------- ;; RAMSTART: EQU $0200 ; Start of user RAM ;; RAMEND: EQU $E000 ; End of user RAM+1 ;; ROMSTART: EQU $F800 ; Start of code ;; WORKSP: EQU $0100-$40 ; Workspace at top of zero page ;; STACK: EQU RAMEND ; Default stack ; Serial I/O Addresses ; ==================== ; These are suitable for a 6850 ;RxTxBASE EQU $E004 ; SWTPC ;RxTxBASE EQU $FEE0 ; Tube RxTxBASE EQU $FEE8 ; ACIA TxSTATUS EQU RxTxBASE+0 TxDATA EQU RxTxBASE+1 RxSTATUS EQU RxTxBASE+0 RxDATA EQU RxTxBASE+1 TxRDY EQU 1 RxRDY EQU 2 RxINIT EQU 3 TxINIT EQU $15 ; Serial Tube system values ; ========================= esc EQU $9B ; Client MOS Workspace ; ==================== ; If code runs from ROM, workspace is in user RAM ; ----------------------------------------------- IF WORKSP < ROMSTART IF WORKSP > $0100 ERRBLK: EQU WORKSP-$C0 ; $DF00 - Buffer to store host error block CLIBUF: EQU WORKSP-$90 ; $DF30 - Space to enter command line from CLI prompt CLIEND: EQU WORKSP-$00 ENDIF ENDIF IF WORKSP < ROMSTART RESV: EQU WORKSP+$00 ; $DFC0 or $00C0 ; Same location as SWTPC SWI3V: EQU WORKSP+$02 ; $DFC2 or $00C2 SWI2V: EQU WORKSP+$04 ; $DFC4 or $00C4 FIRQV: EQU WORKSP+$06 ; $DFC6 or $00C6 IRQV: EQU WORKSP+$08 ; $DFC8 or $00C8 SWIV: EQU WORKSP+$0A ; $DFCA or $00CA NMIV: EQU WORKSP+$0C ; $DFCC or $00CC DMA_DONE: EQU WORKSP+$0E ; $DFCE or $00CE ; Transfer completion flag PENDA: EQU WORKSP+$0F ; $DFCF or $00CF ; Pending keypress EVENTV: EQU WORKSP+$10 ; $DFD0 or $00D0 BRKV: EQU WORKSP+$12 ; $DFD2 or $00D2 xxx1V: EQU WORKSP+$14 ; $DFD4 or $00D4 IRQ2V: EQU WORKSP+$16 ; $DFD6 or $00D6 CLIV: EQU WORKSP+$18 ; $DFD8 or $00D8 BYTEV: EQU WORKSP+$1A ; $DFDA or $00DA WORDV: EQU WORKSP+$1C ; $DFDC or $00DC WRCHV: EQU WORKSP+$1E ; $DFDE or $00DE RDCHV: EQU WORKSP+$20 ; $DFE0 or $00E0 FILEV: EQU WORKSP+$22 ; $DFE2 or $00E2 ARGSV: EQU WORKSP+$24 ; $DFE4 or $00E4 BGETV: EQU WORKSP+$26 ; $DFE6 or $00E6 BPUTV: EQU WORKSP+$28 ; $DFE8 or $00E8 GBPBV: EQU WORKSP+$2A ; $DFEA or $00EA FINDV: EQU WORKSP+$2C ; $DFEC or $00EC PROGRAM: EQU WORKSP+$2E ; $DFEE or $00EE ; Program entry address MEMBOT: EQU WORKSP+$30 ; $DFF0 or $00F0 ; Bottom of user memory MEMTOP: EQU WORKSP+$32 ; $DFF2 or $00F2 ; Top of user memory ADDRESS: EQU WORKSP+$34 ; $DFF4 or $00F4 ; Tube execution address TRANSFER: EQU WORKSP+$36 ; $DFF6 or $00F6 ; Tube transfer address LPTR: EQU WORKSP+$38 ; $DFF8 or $00F8 ; Command line tail ADDRHI: EQU WORKSP+$3A ; $DFFA or $00FA ; Memory address high word CTRL: EQU WORKSP+$3A ; $DFFA or $00FA ; Control address DMA_TYPE: EQU WORKSP+$3C ; $DFFC or $00FC ; TempA/Client personality FAULT: EQU WORKSP+$3D ; $DFFD or $00FD ; Last error message ESCFLG: EQU WORKSP+$3F ; $DFFF or $00FF ; Escape flag ENDIF IF WORKSP < $0100 ERRBLK: EQU $0100 ; $0100 - Buffer to store host error block CLIBUF: EQU $0180 ; $0180 - Space to enter command line from CLI prompt CLIEND: EQU $0200 ENDIF ; If ROM copied to RAM, workspace within ROM area ; ----------------------------------------------- IF WORKSP > ROMSTART ESCFLG: EQU WORKSP+$00 ; $FF80 - Escape flag DMA_TYPE: EQU WORKSP+$01 ; $FF81 - TempA/Client personality FAULT: EQU WORKSP+$02 ; $FF82 - Last error message ;DEFERR: EQU WORKSP+$04 ; $FF84 - Default error handler LPTR: EQU WORKSP+$06 ; $FF86 - Command line tail MEMBOT: EQU WORKSP+$08 ; $FF88 - Bottom of user memory MEMTOP: EQU WORKSP+$0A ; $FF8A - Top of user memory ADDRESS: EQU WORKSP+$0C ; $FF8C - Tube transfer address TRANSFER: EQU WORKSP+$0E ; $FF8E - Transfer address PROGRAM: EQU WORKSP+$10 ; $FF90 - Program entry address ADDRHI: EQU WORKSP+$12 ; $FF92 - Memory address high word DMA_DONE: EQU WORKSP+$14 ; $FF94 - Transfer completion flag ENDIF ; START OF ROM CODE ; ================= ORG ROMSTART ; 6809 BIOS entry block always at F800 ; ==================================== ORG $F800 COLD: FDB RESET ; $F800 - cold start WARM: FDB WARMS ; $F802 - warm start IF KBDPEND INCH: FDB INCHAR ; $F804 - char input ELSE INCH: FDB OSRDCH ; $F804 - char input ENDIF INCHE: FDB INECHO ; $F806 - char input with echo INCHECK: FDB KBDTST ; $F808 - test for char input OUTCH: FDB OSWRCH ; $F80A - char output PDATA: FDB PRDAT ; $F80C - output string until EOT PCRLF: FDB OSNEWL ; $F80E - output CR/LF PSTRING: FDB PRTST ; $F810 - output CR/LF then string until EOT LRA: FDB LREAL ; $F812 - Load Real Address BRA WARMS ; $F814 - for FLEX compatibility BANNER: FCB 13 FCC "6809 SERIAL TUBE 64K " FCB ((VERSION >> 8) & 15) + $30 FCB "." FCB ((VERSION >> 4) & 15) +$30 FCB (VERSION & 15) + $30 IF BUILD FCB BUILD+96 ELSE FCB 32 ENDIF IF DEBUG FCC " DEBUG" ENDIF FCB 13,0 RESET: IF WORKSP > ROMSTART ORCC #$50 ; Ensure interupts disabled LDS #STACK ; Put stack just under ROM IF STACK = ROMSTART TFR S,X ; Start copying from start of ROM ELSE LDX #ROMSTART ; Start copying from start of ROM ENDIF RESETLP1: LDD ,X ; Get two bytes from ROM STD ,X++ ; Store to RAM and increment X CMPX #IOADDRS BNE RESETLP2 ; Loop until hit I/O space LDX #ROMHIGH ; Point to ROM after I/O space RESETLP2: CMPX #ADDRESS ; Don't modify ADDRESS and PROG BNE RESETLP3 LDX #DMA_DONE RESETLP3: CMPX #0 BNE RESETLP1 ; Copy top part of ROM to RAM ENDIF ; STARTUP ; ======= ; Reset all vectors, initialise Client state, tell Host we've restarted ; ; Serial Tube data: $18 $00 $FF &FF -- Cy Y X ; followed by string ; ; Hardware Tube data: via R1: string $00 -- via R2: $7F or $80 ; STARTUP: ORCC #$50 ; Disable interupts IF WORKSP < ROMSTART LDS DEFTOP ; Put stack at default top of user memory LDX #VECTORS ; Copy default vectors and workspace contents LDY #WORKSP LDB #$2E ; 46 bytes to copy VECLOOP: LDA ,X+ ; Get byte from default vectors STA ,Y+ ; Store in vectors DECB ; Decrement number of bytes BNE VECLOOP ; Loop until add done ELSE LDS #CLISTK ; Use internal stack ENDIF LDA #TxINIT STA >TxSTATUS ; Initialise data port and page ROM out LDA #RxINIT STA >RxSTATUS ; Initialise data port ; Accessing I/O registers will page ROM out if running ; from RAM. Once ROM is paged out we can do subroutine ; calls as we can now read from stack in RAM BSR MEM_INIT ; Reset user memory limits and error handler LDX PROGRAM ; Copy current PROGRAM to ADDRESS STX ADDRESS ; so will re-enter on Soft Break ; At this point, if 6309 could change character at BANNER+2 and PROMPT+1 ANDCC #$00 ; Clear all flags, enable interupts ; Tell the Host that we've restarted ; ---------------------------------- ; Serial Tube data: $18 $00 $FF &FF -- Cy Y X ; followed by string ; ; Note for Host authors: Host MUST NOT respond by echoing back a SoftReset as ; the Client will be trapped in an infinite STARTUP loop. ; LDA #$FF ; As we are using a serial link, we don't share a LDX #$00 ; hardware Reset, so we have to tell Host we have LDY #$FF ; restarted. JSR FSC ; Send FSC &FF,&00,&FF - Soft Reset command TFR X,D ; Copy returned X to B PSHS B ; Save Soft Reset result, will be &00 if no response JSR PR_HELP ; Print banner via Tube WRCH protocol JSR OSNEWL ; If we share a hardware Reset, we wait for an Ack here ; JSR OSWRCH ; Send terminating zero byte ; CLRA ; Clear Carry Flag to indicate Reset ; STA >ESCFLG ; Clear Escape flag ; ECSFLG will have been cleared when copying ROM/initialising workspace ; JSR CLI_WAIT ; Wait for result byte CLRA ; Clear Carry Flag to indicate Reset ; STA >ESCFLG ; Clear Escape flag ; ECSFLG will have been cleared when copying ROM/initialising workspace PULS A ; We sent a Software Reset, so use the Ack from that JSR CLI_CHECK ; Check result byte ; ; Fall through to CLICOM if nothing executed ; Supervisor command line prompt ; ============================== ; Allow user to enter *commands ; WARMS: CLILOOP: LDS #CLISTK ; Initially use internal stack BSR COM_INIT ; Reset user memory limits and error handler LDS MEMTOP ; Reset stack to top of user memory ; LDX #CLICOM ; Make Supervisor the current program to re-enter LDX #WARMS ; Make Supervisor the current program to re-enter STX PROGRAM ; on soft reset ANDCC #$00 ; Clear all flags, enable interupts CLILOOP2: LDX #PROMPT JSR SEND_TXT ; Display prompt ; CLRA ; Set A to zero (SEND_TXT returns A=0) ; LDX #COM_BLK ; Point to control block (SEND_TXT returned X=COM_BLK) JSR OSWORD ; Read a line of text BCS COM_ESC ; Escape pressed LDX #CLIBUF JSR OS_CLI ; Execute command BRA CLILOOP2 ; Loop back for another line PROMPT: FCC "6809>*" ; Command prompt FCB 0 COM_BLK: FDB CLIBUF ; Input buffer FCB CLIEND-CLIBUF-1 ; Buffer size FCB 32 ; Lowest acceptable CHR$32 FCB 255 ; Highest acceptable CHR$255 COM_ESC: LDA #126 JSR OSBYTE ; Acknowledge Escape ESCAPE: SWI FCB 17 FCC "Escape" FCB 0 COM_ERR: LDS MEMTOP ; Reset stack to top of user memory JSR OSNEWL LDA ,X+ ; Step X past error number JSR SEND_TXT ; Print text at X JSR OSNEWL BRA CLILOOP2 ; Return to command prompt MEM_INIT: ; This has already been done by copying ROM to RAM. But, when entering ; Supervisor we want to ensure full memory available. IF WORKSP < ROMSTART CLR ESCFLG LDA $FF81 STA DMA_TYPE LDD #BANNER STD FAULT STD LPTR ENDIF COM_INIT: IF WORKSP > ROMSTART LDD #RAMSTART STD MEMBOT ; Initialise bottom of user memory LDD #RAMEND STD MEMTOP ; Initialise top of user memory ELSE LDD DEFBOT STD MEMBOT ; Initialise bottom of user memory LDD DEFTOP STD MEMTOP ; Initialise top of user memory ENDIF COM_BRKV: LDD #COM_ERR ; Get Supervisor error handler address STD BRKV ; Set error handler ERR_INIT: TSTA BNE ERR_INIT2 ; A<>$00, just read values LDD TRANSFER ; Set last entered code as current program STD PROGRAM ERR_INIT2: IF WORKSP > ROMSTART LDD #SWI_HANDLE ELSE LDD DEFSWI ; Get default SWI address = Error handler ENDIF STD SWIV ; Point SWI vector to error handler LDX #BRKV LDY #ESCFLG ; Return X=>BRKV, Y=>ESCFLG, MI=little-endian FS calls IF BIGENDIAN CLRB CLRA ; Return PL=big-endian filing system calls ENDIF TFR D,U ; Set U to a defined value so SWI works NULL: ; Null routines PREND: LREAL: RTS ; FLEX/OS-9 BIOS code ; =================== IF KBDPEND PRTST: JSR OSNEWL PRDAT: LDA ,X+ ; Get character CMPA #4 ; EOT character? BEQ PREND ; End printing JSR OSWRCH ; Print character BRA PRDAT ; Loop to next INECHO: BSR INCHAR JMP OSWRCH INCHAR: BSR KBDTST BEQ INCHAR ; Loop until character available BSR KBDFLAG ; Clear 'pending character' flag IF WORKSP > ROMSTART _PENDA: LDA #0 ; Get pending character PENDA: EQU _PENDA+1 ; Pending character is parameter of LDA ELSE LDA PENDA ; Get pending character ENDIF RTS KBDTST: PSHS D,X,Y ; Save registers LDA ESCFLG ANDA #$40 BNE KBDTST2 ; Character pending, return with NE LDX #0 TFR X,Y LDA #$81 JSR OSBYTE ; INKEY(0) - get any pending character ; Y=$00 - character, X=character ; Y=$1B - escape ; Y=$FF - nothing pending TFR X,D ; B=character CMPY #$FF BEQ KBDTST2 ; Nothing pending, return with EQ STB PENDA BSR KBDFLAG ; Set 'pending character' flag ; Fall through with NE=character pending KBDTST2: PULS D,X,Y,PC KBDFLAG: PSHS CC ORCC #$50 ; Disable interupts LDA #$40 ; Toggle key pending flag EORA ESCFLG STA ESCFLG PULS CC,PC ; Restore IRQs and return ELSE INECHO: JSR OSRDCH JMP OSWRCH PRTST: JSR OSNEWL PRDAT: LDA ,X+ ; Get character CMPA #4 ; EOT character? BEQ PREND ; End printing JSR OSWRCH ; Print character BRA PRDAT ; Loop to next KBDTST: PSHS A,X,Y LDA #$80 LDX #$FFFF ; Should check ADVAL(-2) if Serial is current input stream TFR X,Y JSR OSBYTE ; ADVAL(-1) - keyboard input buffer CMPX #0 ; Set Z/NZ from X PULS A,X,Y,PC ENDIF ; ***************** ; Printout Routines ; ***************** ; Print X as 4-digit hex ; ====================== ; API allows A to be corrupted ; All other registers preserved (v1.04 including B) PR_HEX2: PSHS B ; Save B TFR X,D ; Copy X to D, so copy b8-b15 to A BSR PR_HEX1 ; Print it TFR B,A ; Copy b0-b7 to A PULS B ; Restore B ; Fall through into PR_HEX1 ; Print A as 2-digit hex ; ====================== ; API allows A to be corrupted ; All other registers preserved PR_HEX1: PSHS A ; Standard hex-print code LSRA LSRA LSRA LSRA BSR PR_NYBBLE PULS A ; Get A back ; Fall through into PR_NYBBLE PR_NYBBLE: ANDA #$0F CMPA #$0A BCS PR_DIGIT ADDA #7 PR_DIGIT: ADDA #$30 JMP OSWRCH ; Print inline text ; ================= ; On exit: A=$00 ; X= corrupted ; from v1.05 ; X= preserved PR_TEXT: IF VERSION*16+BUILD>$1050 PSHS X ; Save X LDX 2,S ; Get stacked PC to X BSR SEND_TXT ; Print text STX 2,S ; Update stacked PC PULS X,PC ; Restore X and return to updated PC ELSE PULS X ; Pop PC to X BSR SEND_TXT ; Print text PSHS X ; Push updated X SEND_END: RTS ; And return to it ENDIF ; Print text string at X ; ====================== ; On entry: X=>zero-terminated text string ; On exit: X=>byte after zero byte terminator ; A=$00 ; PR_HELP: LDX #BANNER ; Print startup banner as help message SEND_TXT: LDA ,X+ ; Get byte from X, increment X BEQ SEND_END ; End if $00 byte JSR OSASCI ; Send to OSASCI BRA SEND_TXT ; Loop until $00 byte ; ********************** ; Line scanning Routines ; ********************** ; Scan hex string ; =============== ; On entry, X=>start of hex string ; On exit, X=>first non-hex character ; Y=hex value ; A=terminating character ; B=corrupted ; CS if number terminated, eg 123 X or 123 ; CC if number not terminated, eg 123X ; RD_HEX: LDY #0 ; Clear hex accumulator RD_HEXLP: LDA ,X+ ; Get current character CMPA #$30 ; <'0', exit BCS RD_HEXDONE CMPA #$3A ; '0'..'9', add to accumulator BCS RD_HEXADD ANDA #$DF ; Ensure upper case letter SUBA #$07 ; Convert letter, if <'A', exit BCS RD_HEXDONE CMPA #$40 BCC RD_HEXDONE ; If >'F', exit RD_HEXADD: ANDA #$0F ; AB=0Nxx, X=>line Y=acc EXG D,Y ; AB=acc, X=>line Y=0Nxx ASLB ROLA ASLB ROLA ASLB ROLA ASLB ROLA ; AB=acc*16, X=>line, Y=0Nxx EXG X,Y ; AB=acc*16, X=0Nxx, Y=>line EXG D,X ; AB=0Nxx, X=acc*16, Y=>line EXG A,B ; AB=xx0N, X=acc*16, Y=>line ABX ; AB=xx0N, X=acc*16+N, Y=>line EXG X,Y ; AB=xx0N, X=>line, Y=acc*16+N BRA RD_HEXLP ; Move to next character ; Skip parameter word ; =================== ; On entry, X=>command line ; On exit, X=>first non-space character after current parameter word ; A=first non-space character SKIPWORD: LDA ,X+ ; Step past parameter CMPA #'!' BCC SKIPWORD LEAX -1,X ; Step back, then skip spaces ; Skip spaces ; =========== ; On entry, X=>command line ; On exit, X=>first non-space character ; A=first non-space character SKIPSPC: LDA ,X+ CMPA #' ' BEQ SKIPSPC ; Skip space characters RD_HEXDONE: LEAX -1,X ; Point to non-hex/non-space char CMPA #'!' ; Return CS if no following character IF VERSION*16+BUILD>$1050 SEND_END: ENDIF RTS ; ********************** ; MOS Interface Routines ; ********************** ; OSCLI - Execute command ; ======================= ; On entry: X=>command string ; On exit: A holds any return value ; ; First check for local commands, then pass on to host ; CLI: PSHS B,DP,X,Y,U ; Save everything on caller's stack, except A and CC IF VERSION*16+BUILD<$1060 LEAU -8,S ; Make a note of caller's stack with space for calls ELSE TFR S,U ; Make a note of caller's stack with space for calls ENDIF IF WORKSP < $0100 CMPS #CLISTK ; Check where the stack is BCS CLI_SYSSTK1 ; We're already using internal stack ELSE CMPS #BOTSTK ; Check where the stack is BCC CLI_SYSSTK1 ; We're already using internal stack ENDIF LDS #CLISTK ; Use internal stack CLI_SYSSTK1: ; As a *command may result in data transfer, that data may end up overwriting ; a stack in user memory, so use a temporary stack to do OS_CLI. If OS_CLI ; ends up jumping to a new process, a new stack will need to be be set up by ; that new process. There is space on the CLISTK for about seven recursive ; OS_CLI calls. PSHS U ; Save caller's stack pointer BSR CLI_GO ; Process the *command PULS U ; Get caller's stack pointer back IF VERSION*16+BUILD<$1060 LEAS 8,U ; Restore caller's stack ELSE TFR U,S ; Restore caller's stack ENDIF PULS B,DP,X,Y,U,PC ; Restore everything and return contents of A. ; Local *commands ; --------------- CLI_TABLE: FCC "GO" FCB $80 FCC "HELP" FCB $81 FCC "RUN" FCB $82 FCB 0 ; OSCLI - Check for local commands ; -------------------------------- ; On entry: X=>command string ; CLI_GO: CLI_LP1: BSR SKIPSPC ; Skip leading spaces LDA ,X+ CMPA #'*' BEQ CLI_LP1 ; Skip leading '*'s LEAX -1,X PSHS X ; Save start of command text CMPA #'/' ; Skip to parameters for '*/name' and '*/ name' BEQ CMD_SLASH BSR SKIPWORD ; Step past command STX LPTR ; Point LPTR to command parameters LDY #CLI_TABLE ; Point to command table CLI_LOOP0: LDX ,S ; Get start of command text back LDA ,X ; Get first character CMPA #'A' BCS CLI_TUBE ; Not a letter, pass to host CLI_LOOP2: LDA ,X+ ; Get character from command line ANDA #$DF ; Force to upper case CMPA ,Y+ ; Compare with table entry BEQ CLI_LOOP2 ; Characters match, check next LDA ,-Y ; Step to command token BMI CLI_MATCH ; Match LDA -1,X ; Get character from command line CMPA #'.' BEQ CLI_DOT ; Abbreviated command CLI_NEXT: LDA ,Y+ ; Search for command token BPL CLI_NEXT ; Step to end of table entry LDA ,Y BNE CLI_LOOP0 ; Not end of table BRA CLI_TUBE ; Pass to host CLI_DOT: LDA ,Y+ ; Search for command token BPL CLI_DOT ; Step to end of table entry BRA CLI_MATCH2 CLI_MATCH: LDB ,-X ; Get current character CMPB #'!' BCC CLI_TUBE ; Command line longer than table entry CLI_MATCH2: CMPA #$81 BEQ CMD_HELP BCC CMD_RUN ; *GO ; parameters ; ----------------------- CMD_GO: BSR SKIPSPC LDY PROGRAM ; Default to re-enter current program CMPA #13 BEQ CLI_GO2 ; *GO - enter current program CMPA #';' BEQ CLI_GO1 ; *GO ;params - enter current program JSR SCANHEX ; Get hex address BCC CLI_TUBE ; Malformed hex address, pass to host BSR SKIPSPC CMPA #';' ; Check for ';' parameter marker BEQ CLI_GO1 ; *GO ;params or *GO ;params CMPA #13 BNE CLI_TUBE ; *GO ... or *GO .... LEAX -1,X ; Balance following 1,X CLI_GO1: LEAX 1,X ; Step past ';' CLI_GO2: STX LPTR ; Save pointer to command parameters PULS X ; Drop old line pointer TFR Y,X ; X=entry address ORCC #$01 ; Set Carry to indicate OSCLI BRA EXECUTE2 ; *HELP () ; --------------- CMD_HELP: JSR PR_HELP ; Print banner as help message ; Fall through to CLI_TUBE, harmlessly updating LPTR ; *RUN - updates LPTR to point to correct parameters ; -------------------------------------------------------------------------- CMD_SLASH: LEAX 1,X ; Step past '/' CMD_RUN: BSR SKIPSPC BSR SKIPWORD ; Step past *RUN filename STX LPTR ; Update LPTR to parameters ; OSCLI - Send command line to host ; ================================= ; On entry: stacked X=>command string ; LPTR=>command parameters, prepared for EXECUTE ; On exit: All registers possibly corrupted ; Registers should be preserved by calling code ; ; Tube data: $02 string $0D -- $7F or $80 ; CLI_TUBE: PULS X ; Get command string back LDA #2 JSR SendCommand ; Send command $02 = OSCLI JSR SEND_STR ; Send string at X CLI_WAIT1: ORCC #$01 ; Set Carry to indicate OSCLI CLI_WAIT: JSR WaitByte ; Wait for result via Tube R2 (preserves Cy) ; Data transfer may happen while waiting CLI_CHECK: TSTA ; Check return code BPL CLI_DONE ; Nothing to execute, return ; EXECUTE - Enter code at ADDRESS ; =============================== ; Checks for possible code header, makes code current PROGRAM. ; On entry, ADDRESS=code entry address ; CS=entering from OSCLI ; CC=entering from RESET ; ; If code in high memory, MEMTOP moved to below code. ; If code returns, MEMTOP and PROGRAM restored to previous value. ; ; If code has a header it is entered with A=1. If not, it is entered with A=0. ; Code has a small initial stack with 100 bytes available on it (eg 50 subroutine ; calls) unless OSCLI calls OSCLI recursively. If the code calls OSINIT it ; becomes the current program and is re-entered at soft reset. Code that becomes ; the current program must set up their own stack in user space. ; ; If called from RESET and not 6809 code, error not reported, jumps directly ; to CLICOM. In future this is the point where a disk operating system would ; be checked for and booted. ; EXECUTE: LDX ADDRESS ; Get transfer address, note: big-endian EXECUTE2: PSHS CC ; Save RESET/OSCLI flag TFR X,Y ; Save address in Y LDB 7,X ; Get (C) offset ABX ; X=>(C) string LDU #EXEC_CHK+4 ; U=>check string LDB #4 ; 4 bytes to check EXEC_LP: LDA ,X+ ; Get byte from header CMPA ,-U ; Compare with check string BNE EXEC_JUMP ; No match, enter as raw code DECB BNE EXEC_LP ; Loop to check all four bytes LDA 6,Y ; Get code type ASLA ; Check b6 by moving it into b7 BPL ERR_NOTCODE ANDA #2*15 ; Byte has been moved up, so mask and compare with 2*n CMPA #2*3 BNE ERR_NOT6809 IF FALSE LDA ,Y ; Get entry opcode. Check if 6809 BRA or JMP to ADDA #2 ; filter out 68000 code with ROMTYPE=3 ANDA #$5D ; Error if not 6809 BRA or JMP BNE ERR_NOT6809 ENDIF LEAX -4,X STX FAULT ; Point FAULT to (C) string (should be version string) LDU PROGRAM LDX MEMTOP PULS A ; Get RESET/OSCLI flag to A PSHS X,U ; Save current MEMTOP and PROGRAM CMPY #$8000 BCS EXEC_ENTER ; Entering code in low memory, leave MEMTOP where it is STY MEMTOP ; Entering in high memory, put MEMTOP below entered code EXEC_ENTER: STY TRANSFER ; Set as last entered code LDX LPTR ; X=>command line IF VERSION*16+BUILD<$1060 LDU 6,S ; U=>caller's stack *BUG* Faulty if not called from OSCLI ENDIF RORA ; Move RESET/OSCLI flag back into Carry LDA #1 ; A=1 for code with a header JSR ,Y ; Call program execution address PULS Y,U STY MEMTOP ; Restore previous MEMTOP if code returns STU PROGRAM ; Restore previous PROGRAM CLI_DONE: RTS ; Return EXEC_JUMP: LDX LPTR ; X=>command line IF VERSION*16+BUILD<$1060 LDU 3,S ; U=>caller's stack *BUG* Faulty if not called from OSCLI ENDIF CLRA ; A=0 for raw code PULS CC ; Get RESET/OSCLI flag JMP ,Y ; Enter raw code EXEC_CHK: FCC ")C(" ; Deliberately backwards so doesn't match itself FCB 0 ERR_NOTCODE: ; PULS CC ; Get RESET/OSCLI flag ; LBCC CLICOM ; If called from RESET, drop straight into Supervisor ; JSR COM_BRKV ; SWI ; FCB 249 ; FCC "Not code" ; FCB 0 ERR_NOT6809: PULS CC ; Get RESET/OSCLI flag LBCC CLICOM ; If called from RESET, drop straight into Supervisor ; Here is where we would check if this is hard reset, ; and look for a disk operating system to boot instead. JSR COM_BRKV ; Error handler may have been overwritten, so set up SWI ; Supervisor error handler FCB 249 FCC "Not 6809 code" FCB 0 ; OSRDCH - Wait for character from input stream ; ============================================= ; On exit: A=char, Cy=carry ; RDCH: RDCH_RAW: JSR WaitByte ; Wait for character PSHS A ; Save character LDA >ESCFLG ; Get Escape flag ASLA ; Copy b7 of ESCFLG to Carry PULS A ; Get character back BYTE_DONE: RTS ; OSRDCH_IO - Request character via Tube API ; ========================================== ; Tube data: $00 -- Carry Char ; ; On exit: A=char, Cy=carry ; RDCH_IO: CLRA JSR SendCommand ; Send command $00 - OSRDCH WAIT_CHAR: JSR WaitByte ; Get returned byte ASLA ; Move b7 of ESCFLG to Carry JMP WaitByte ; Jump to get character ; OSBYTE ; ====== ; On entry: A,X,Y=OSBYTE parameters ; On exit: A preserved ; If A<$80, X=returned value ; If A>$7F, X, Y, Carry=returned values ; ; Tube data: $04 X A -- X ; $06 X Y A -- Cy Y X ; BYTE: PSHS A,B TSTA BMI BYTE_HI LDA #4 JSR SendCommand ; Send command $04 - short BYTE TFR X,D ; B=X JSR SendByteB ; Send second parameter from B PULS A,B PSHS A,B JSR SendByte ; Send first parameter JSR WaitByte ; Wait for response TFR A,B ; Move result to low byte CLRA ; Ensure AB is only 8-bit value TFR D,X PULS A,B,PC ; OSFSC ; ===== ; On entry: A,X,Y=OSFSC parameters ; NB: Only implemented for A>$7F ; On exit: A preserved ; X, Y, Carry=returned values ; ; Tube data: $18 X Y A -- Cy Y X ; FSC: PSHS A,B LDA #$18 ; Set command $18 - FSC BRA BYTE_CMD ; Jump to send command BYTE_WAIT: LDX #BANNER ; Point LPTR to STX LPTR BRA CLI_WAIT1 ; Wait for program start ; OSBYTE >$7F ; ----------- BYTE_HI: CMPA #$82 ; A bit of optimisation BCS BYTE_HI1 ; <$82, not a memory OSBYTE BEQ MEM82 ; =$82, fetch address high word CMPA #$85 BCS MEM83 ; <$85, fetch low/high memory limit BYTE_HI1 LDA #6 BYTE_CMD: JSR SendCommand ; Send command TFR X,D ; B=X - second parameter JSR SendByteB ; Send second parameter from B JSR SendByteY ; Send third parameter from Y PULS A,B JSR SendByte ; Send first parameter CMPA #$9D ; Was it fast BPUT? BEQ BYTE_DONE ; Don't wait for response CMPA #$8E ; Was it start language? BEQ BYTE_WAIT ; Wait for program start PSHS A,B JSR WaitByte ; Wait for response ASLA ; Move b7 into carry PSHS CC ; Save flags JSR WaitByte ; Wait for response TFR A,B ; Move result to low byte CLRA ; Ensure AB is only 8-bit value TFR D,Y ; Return result in Y JSR WaitByte ; Wait for response, high byte still in B EXG A,B ; Swap so high byte is Y, low byte is fetched byte TFR D,X ; Return result in X is returned Y*256+X value PULS CC,A,B,PC ; Get flags, A and B back and return MEM82: LDA #$88 MEM83: MEM84: ASLA ; A=16,8,10 LDX #MEMBOT-6 ; Point to addresses LDD A,X ; Fetch address value TFR D,X ; Return X=address TFR A,B ; Copy top byte to bottom byte CLRA ; Clear top byte TFR D,Y ; Return Y=address DIV 256 PULS A,B,PC ; OSWORD ; ====== ; On entry: A=OSWORD number ; X=>control block ; WORD: TSTA BEQ RDLINE ; Jump with OSWORD 0 - RDLINE ; OSWORD <>&00 ; ------------ ; Tube data: &08 function in_length block out_length -- block ; PSHS A,B,Y ; Save A, B and Y PSHS X ; Save X=>control block TFR A,B ; B=function LDA #$08 JSR SendCommand ; Send command $08 - OSWORD JSR SendByteB ; Send OSWORD function from B TSTB ; Check OSWORD function BMI WORD_SEND ; Jump to send control block with functions>&7F WORD_TXLO: LDA #$10 ; Default SendBlock length CMPB #$15 ; Check OSWORD function BCC WORD_SEND1 ; Use 16 bytes for OSWORD &15 to &7F LDX #WORDTX-1 ; X=>send length table for OSWORD 1 to &14 ABX ; X=X+B, X=>send length WORD_SEND: LDA ,X ; Get block length from control block or table WORD_SEND1: TFR A,B ; B=control block length LDX ,S ; Get X=>control block back JSR SendByteB ; Send outward block length DECB BMI WORD_NOTX ; Only send length 1 to 128 INCB JSR SEND_BLOCK ; B=length, X=>block, returns B=0, X=>block WORD_NOTX: LDB 2,S ; Get OSWORD function BMI WORD_RECV ; Jump to send control block with function>&7F WORD_RXLO: LDA #$10 ; Default ReceiveBlock length CMPB #$15 ; Check OSWORD function BCC WORD_RECV1 ; Use 16 bytes for OSWORD &15 to &7F LDX #WORDRX-2 ; X=>receive length table ABX ; X=X+B, X=>receive length for OSWORD 1 to &14 WORD_RECV: LDA 1,X ; Get receive length from table or control block WORD_RECV1: TFR A,B ; B=send block length PULS X ; Get X=>control block back JSR SendByteB ; Send inward block length DECB BMI WORD_NORX ; Only receive length 1 to 128 INCB JSR WAIT_BLOCK ; Wait for returned control block WORD_NORX: PULS A,B,Y,PC ; Restore A,B,Y and return ; OSWORD 0 - Read a line of text ; ------------------------------ ; On entry: X=>addr.hi, addr.lo, maxlen, charlo, charhi ; On exit: Cy=0: Ok, Cy=1: Escape ; Y=length of returned string in buffer at addr ; ; Note: Address of text string in control block is local big-endian address ; All other OSWORD calls use little-endian addresses to match Host ; ; Tube data: $0A block -- $FF or $7F string $0D ; RDLINE: LDA #10 JSR SendCommand ; Send Command $0A - RDLINE PSHS B LEAX 2,X ; X=X+2, point to parameters LDB #3 JSR SEND_BLOCK ; Send 3-byte control block LDA #7 JSR SendByte ; Send $0700 JSR SendByteB ; Send $00 as B=0 from SEND_BLK JSR WaitByte ; Wait for response ASLA ; Move b7 into Carry BCS RD_DONE LDX ,--X ; Get text pointer from control block ; CLRB ; B=number received - B already zero from SEND_BLOCK RD_STR: JSR WaitByte ; Wait for byte from Tube STA ,X+ ; Store in text buffer, increment X INCB ; Increment character count CMPA #13 ; Check current byte BNE RD_STR ; Loop until DECB ; Decrement character count to balance ; ANDCC #$FE ; Clear carry (already CC from CMPA #13) RD_DONE: LDA #0 ; Clear A without clearing Carry TFR D,Y ; Y=length PULS B ; Restore B RTS ; Table of OSWORD control block lengths for &01-&14 ; ------------------------------------------------- WORDTX: FCB $00,$05,$00,$05,$04 FCB $05,$08,$0E,$04,$01 FCB $01,$05,$00,$10,$20 FCB $10,$0D,$00,$08,$80 WORDRX: FCB $05,$00,$05,$00,$05 FCB $00,$00,$00,$05,$09 FCB $05,$00,$08,$19,$00 FCB $01,$0D,$80,$08,$80 ; OSARGS - Read info on open file ; =============================== ; On entry: A=action ; X=>data (little-endian) ; Y=handle ; On exit: A=returned value ; X preserved ; X=>any returned data ; Y preserved ; ; Tube Data: $0C handle block function -- result block ; ARGS: PSHS A,B,Y ; Save handle, B reg, and function LDA #$0C JSR SendCommand ; Send command $0C - OSARGS JSR SendByteY ; Send handle IF BIGENDIAN CLRB ; Point to first byte of word to send JSR SEND_WORDS ; Send four-byte control block ELSE LDB #4 ; Four bytes to send JSR SEND_BLOCK ; Send four-byte control block ENDIF PULS A ; Get action back JSR SendByte ; Send action JSR WaitByte ; Wait for returned result PSHS A ; Save result IF BIGENDIAN CLRB ; Point to first byte of word to read JSR WAIT_WORDS ; Wait for four-byte control block ELSE LDB #4 ; Four bytes to read JSR WAIT_BLOCK ; Wait for four-byte control block ENDIF PULS A,B,Y,PC ; Get result and original handle back, and return ; OSBGET - Get a byte from open file ; ================================== ; On entry: Y=handle ; On exit: A=byte Read ; Y=preserved ; Cy set if EOF ; ; Tube data: $0E handle -- Carry byte ; BGet: PSHS B LDA #$0E JSR SendCommand ; Send command $0E - OSBGET JSR SendByteY ; Send handle PULS B JMP WAIT_CHAR ; Wait for Carry, Byte ; OSBPUT - Put a byte to an open file ; =================================== ; On entry: A=byte to write ; Y=handle ; On exit: A=preserved ; Y=preserved ; ; Tube data: $10 handle byte -- $7F ; BPut: PSHS A,B ; Save byte and B LDA #$10 JSR SendCommand ; Send command $10 - OSBPUT JSR SendByteY ; Send handle PULS A,B ; Get A and B back PSHS A,B JSR SendByte ; Send byte to Tube JSR WaitByte ; Wait for acknowledgement PULS A,B,PC ; Restore A and B, and return ; OSFIND - Open or Close a file ; ============================= ; On entry: A=function ; Y=handle or X=>filename ; On exit: A=zero or handle ; X,Y preserved ; ; Tube data: $12 function string $0D -- handle ; $12 $00 handle -- $7F ; FIND: PSHS A,B ; Save function and B LDA #$12 JSR SendCommand ; Send command $12 - OSFIND PULS A,B ; Get A back JSR SendByte ; Send function TSTA ; Check function BEQ Close ; Jump to deal with Close JSR SEND_STR ; Send string at X JMP WaitByte ; Wait for returned handle Close: PSHS B JSR SendByteY ; Send handle JSR WaitByte ; Wait for acknowledgement CLRA ; Zero A PULS B,PC ; Restore B and return ; OSFILE - Operate on whole files ; =============================== ; On entry: A=function ; X=>control block ; On exit: A=result ; X preserved ; control block updated ; ; Note: Address of text string in control block is local big-endian address ; ; Tube data: $14 block string function -- result block ; FILE: PSHS A,B,X,Y ; Save B,X,Y registers and function in A LDA #$14 JSR SendCommand ; Send command $14 - OSFILE LEAX 2,X ; Point to control block contents IF BIGENDIAN LDB #12 ; Point to first byte to send JSR SEND_WORDS ; Send 16-byte control block ELSE LDB #16 ; 16 bytes to send JSR SEND_BLOCK ; Send 16-byte control block ENDIF LDX ,--X ; Get big-endian filename pointer to X JSR SEND_STR ; Send filename string PULS A ; Get function back JSR SendByte ; Send function JSR WaitByte ; Wait for returned result ; ; Data transfer via interupts may happen while waiting PULS B,X ; Restore B, get control block pointer back PSHS A,B ; Save result and B again LEAX 2,X ; Point to control block contents IF BIGENDIAN LDB #12 ; Point to first byte to read JSR WAIT_WORDS ; Wait for 16-byte control block ELSE LDB #16 ; 16 bytes to read JSR WAIT_BLOCK ; Wait for 16-byte control block ENDIF LEAX -2,X ; Restore X PULS A,B,Y,PC ; Get result, restore registers, and return ; OSGBPB - Multiple byte Read and write ; ===================================== ; On entry: A=function ; X=>control block ; On exit: A=returned value ; control block updated ; ; Tube data: $16 block function -- block Carry result ; GBPB: PSHS A,B,Y ; Save Y and function LDA #$16 JSR SendCommand ; Send command $16 - OSGBPB IF BIGENDIAN LEAX 1,X ; Step past handle LDB #8 ; Point to first byte to send JSR SEND_WORDS ; Send 12-byte control block LDA ,-X ; Get handle JSR SendByte ; Send 0th byte of control block ELSE LDB #13 JSR SEND_BLOCK ; Send 13-byte control block ENDIF PULS A ; Get function back JSR SendByte ; Send function IF BIGENDIAN LEAX 1,X ; Step past handle LDB #8 ; Point to first byte to read JSR WAIT_WORDS ; Read 12-byte control block JSR WaitByte ; Get 0th byte of control block STA ,-X ELSE LDB #13 JSR WAIT_BLOCK ; Wait for 13-byte control block ENDIF PULS B,Y ; Get B and Y back JMP WAIT_CHAR ; Get Carry and result byte ; ***************** ; Tube I/O routines ; ***************** ; Send cr-string at X to Tube ; =========================== SEND_STR: LDA ,X+ ; Get byte from X, increment X BSR SendByte ; Send byte via Tube CMPA #13 ; Test current character ; EORA #13 would preserve carry BNE SEND_STR ; Loop until CR sent RTS ; Send block at X to Tube Register 2, B=block length ; ================================================== ; Returns X=preserved, B=0 SEND_BLOCK: ABX ; X=X+B, X=>after last byte to send SEND_BLKLP: LDA ,-X ; Decrement X, get byte from X JSR SendByte ; Send byte via Tube DECB ; Decrement count of bytes to send BNE SEND_BLKLP ; Loop until all bytes sent RTS ; Wait for block at X from Tube Register 2, B=block length ; ======================================================== ; Returns X=preserved, B=0 WAIT_BLOCK: ABX ; X=X+B, X=>after last byte to receive WAIT_BLKLP: JSR WaitByte ; Get byte from Tube STA ,-X ; Decrement X, store byte at X DECB ; Decrement count of bytes BNE WAIT_BLKLP ; Loop until all bytes sent RTS IF BIGENDIAN ; Send big-endian block at X to Tube Register 2, B=>first word ; ============================================================= ; Returns X=preserved, B<0 SEND_WORDS: ABX ; X=X+B, X=>first byte to send SEND_WDLP: LDA ,X+ ; Get byte from X, increment X BSR SendByte ; Send byte via Tube R2 DECB ; Decrement byte count BITB #3 BNE SEND_WDLP ; Loop for four bytes LEAX -8,X ; Point to next word down TSTB BPL SEND_WDLP ; Another word to do LEAX 4,X ; Restore X RTS ; Wait for big-endian block at X from Tube Register 2, B=>first word ; ================================================================== ; Returns X=preserved, B<0 WAIT_WORDS: ABX ; X=X+B, X=>first byte to receive WAIT_WDLP: JSR WaitByte ; Get byte via Tube R2 STA ,X+ ; Store byte at X, increment X DECB ; Decrement count of bytes BITB #3 BNE WAIT_WDLP ; Loop for four bytes LEAX -8,X ; Point to next word down TSTB BPL WAIT_WDLP ; Another word to do LEAX 4,X ; Restore X RTS ENDIF ; Send byte in Y to Tube via B ; ============================ SendByteY: TFR Y,D ; ; Fall through into SendByteB ; Send byte in B to Tube ; ====================== SendByteB: TFR B,A ; ; Fall through into SendByte ; OSWRCH - Send character in A to Tube ; ==================================== ; On entry, A =character ; On exit, A =preserved WRCH: ; ; WRCH is simply SendByte ; ********************** ; Tube Core I/O Routines ; ********************** ; Characters and commands are sent over the same single port. ; Outward commands are escaped, and inward responses are escaped. ; ; Outward ; x VDU x ; esc,esc VDU esc ; esc,n MOS function, control block follows ; ; Inward ; x char/byte x ; esc,esc char/byte esc ; esc,&00 BRK, error number+text+null follows ; esc,<&80 read returned control block set length ; esc,&8n Escape change, b0=new state ; esc,&9x,Y,X,A Event ; esc,&Ax reserved for networking ; esc,&Bx end transfer ; esc,&Cx,addr set address ; esc,&Dx,addr execute address ; esc,&Ex,addr start load from address ; esc,&Fx,addr start save from address ; All commands are data inward, except esc,&Fx which is data outward ; Send byte in A, escaping it if needed ; ===================================== SendByte: CMPA #esc ; Escape character? BNE SEND_DATA ; No, send raw JSR SEND_DATA ; Double escape character SEND_DATA: PSHS A ; Save byte SEND_WAIT: LDA >TxSTATUS ANDA #TxRDY BEQ SEND_WAIT ; Loop until data can be sent PULS A ; Get byte back STA >TxDATA ; Send data RTS ; Send an escaped command ; ======================= SendCommand: PSHS A ; Save command byte ; Should flush input here LDA #esc JSR SEND_DATA ; Send esc prefix PULS A ORA >DMA_TYPE ; Add personality bits BRA SEND_DATA ; Send command byte ; Check if a byte is waiting, and read it if there ; ================================================ BYTE_READ: LDA >RxSTATUS ANDA #RxRDY BNE WAIT_EXIT ; Nothing pending, return ; ; Continue into WaitByte ; Wait for a byte, decoding escaped data ; ====================================== ; On exit, A =byte ; F =preserved ; WaitByte: PSHS CC ; Save flags WaitByteLP: JSR WAIT_DATA BNE WaitByteOK ; Not esc, return JSR WAIT_DATA ; Get another byte BEQ WaitByteOK ; esc,esc, return PSHS X,Y,B,U ; Push all registers JSR WAIT_COMMAND ; Decode escaped command PULS X,Y,B,U ; Pop all registers BRA WaitByteLP WaitByteOK: PULS CC ; Pop flags WAIT_EXIT: SendByte2: RTS ; Wait for data ; ============= ; On exit, A =byte ; F =Z byte=esc, NZ byte<>esc ; WAIT_DATA: LDA >RxSTATUS ANDA #RxRDY BEQ WAIT_DATA ; Loop until data present LDA >RxDATA ; Fetch data CMPA #esc ; Is it esc prefix? RTS ; Decode escaped command ; ====================== ; On entry, A=command ; All registers can be trashed ; WAIT_COMMAND: TSTA BEQ WAIT_ERROR ; esc,&00 - error BMI WAIT_TRANSFER ; esc,>&7F - data transfer ; esc,1..127 - read a control block ; ================================= ; This depends on MOS calls storing control block address, ; which none of them do for this CPU. ; Just read data and absorb it. ; May think of something else to do with this. ; TFR A,B ; Move count to low byte of AB WAIT_LEN: JSR WaitByte ; Wait for a byte DECB ; Decrement count of bytes read BNE WAIT_LEN ; Loop to read all bytes RTS ; Return to WaitByte ; esc,&00 - error ; =============== WAIT_ERROR: LDS #ERRSTK ; Collapse stack LDX #ERRBLK ; Point to error buffer LDA #$3F ; SWI opcode STA ,X+ ; Store SWI opcode JSR WaitByte ; Get error number STA ,X+ ; Store error number FIRQ_R4LP: JSR WaitByte ; Wait for byte of error string STA ,X+ ; Store in error buffer BNE FIRQ_R4LP ; Loop until terminating $00 received ;;;6502 Client pauses at this point ;;;ANFS generates error 0 for 0,"Remoted" when remote starts ;;;NFS generates error 0 for 0,"" when remote starts ;;;Check for 0,0,0 which is error 0,"" LDA ERRBLK+1 ORA ERRBLK+2 ; Check for error 0,"" LBEQ STARTUP ; Restart client LDX #ERRBLK+1 ; Point to error block after SWI opcode PSHS X ; Push error pointer onto stack JMP OSERROR ; Jump to generate error ; esc,&8n - Escape change ; ======================= WAIT_TRANSFER: CMPA #$C0 BCC WAIT_START CMPA #$A0 BCC WAIT_END CMPA #$90 BCC WAIT_EVENT ; %1000xxxE:1 LSRA ; %01000xxx:E RORA ; %E01000xx:x ; Move b0 into b7, b6=0 STA >ESCFLG ; Store Escape flag RTS ; esc,&9x - Event ; =============== WAIT_EVENT: JSR WaitByte ; Get event Y parameter TFR A,B CLRA TFR D,Y JSR WaitByte ; Get event X parameter TFR A,B CLRA TFR D,X JSR WaitByte ; Get event A parameter JMP [EVENTV] ; Dispatch event ; esc,&Ax - Reserved ; ================== WAIT_END: CMPA #$B0 BCS WAIT_EXIT ; &Ax - Return to WaitByte ; esc,&Bx - End transfer ; ====================== LEAS 12,S ; Drop data from stack to fall ; ; out of WaitSave/WaitLoad loop WAIT_EXIT2: RTS ; Return to WaitByte ; esc,&C0+ - Start transfer ; ========================= WAIT_START: PSHS A ; Save transfer type JSR WaitByte ; Get data address MSB JSR WaitByte ; Get data address JSR WaitByte ; Get data address EXG A,B JSR WaitByte ; Get data address LSB EXG A,B ; D=data address TFR D,X ; X=data address PULS A ; Get transfer type back CMPA #$D0 BCS WAIT_ADDR ; &Cx - set address CMPA #$E0 BCS WAIT_CODE ; &Dx - enter code CMPA #$F0 BCC WAIT_SAVE ; &Fx - save data WAIT_LOAD: JSR WaitByte ; &Ex - load data STA ,X+ ; Get byte, store it BRA WAIT_LOAD ; Loop until terminated WAIT_SAVE: LDA ,X+ ; Get byte JSR SendByte ; Send it JSR BYTE_READ ; Poll input for termination BRA WAIT_SAVE ; Loop until terminated WAIT_CODE: JMP ,X ; Jump directly to code WAIT_ADDR: STX ADDRESS ; Set entry address RTS ; Null interupt routines ; ====================== ; OS-9 and FLEX use a timer on IRQ to poll background processes ; NULL_RTI: RES_HANDLE: SWI2_HANDLE: SWI3_HANDLE: FIRQ_HANDLE: IRQ_HANDLE: NMI_HANDLE: RTI ; SWI - Generate an error ; ======================= M6309: EQU 1 ; Check for 6309 CPU SWI_HANDLE: IF M6309 CMPU 10,S ; Compare U with stacked U or stacked PC BNE SWI_6809 ; It's stacked PC, so is not 6309 native mode LEAS 2,S ; Step past stacked W SWI_6809: ENDIF LEAS 10,S ; Point to stacked PC ERR_HANDLE: PULS X ; Pop address of error block after SWI opcode STX FAULT ; Save pointer to last error ANDCC #$00 ; Clear all flags, enable interupts JMP [BRKV] ; Jump to current error handler with X=>error IF WORKSP < ROMSTART ; Default values for WORKSP ; ========================= ORG $FE40 ; Same location as SWTPC VECTORS: FDB RES_HANDLE ; RESV FDB SWI3_HANDLE ; SWI3V FDB SWI2_HANDLE ; SWI2V FDB FIRQ_HANDLE ; FIRQV FDB IRQ_HANDLE ; IRQV DEFSWI: FDB SWI_HANDLE ; SWIV FDB NMI_HANDLE ; NMIV FDB $0000 ; DMA_DONE, PENDA FDB NULL ; EVENTV DEFBRK: FDB COM_ERR ; BRKV FDB NULL ; xxx1V FDB NULL_RTI ; IRQ2V FDB CLI ; CLIV FDB BYTE ; BYTEV FDB WORD ; WORDV FDB WRCH ; WRCHV FDB RDCH ; RDCHV FDB FILE ; FILEV FDB ARGS ; ARGSV FDB BGet ; BGETV FDB BPut ; BPUTV FDB GBPB ; GBPBV FDB FIND ; FINDV ; FDB WARM ; PROGRAM - Program entry address ;DEFBOT:FDB RAMSTART ; MEMBOT - Bottom of user memory ;DEFTOP:FDB RAMEND ; MEMTOP - Top of user memory ; FDB $0000 ; ADDRESS - Tube exectution address ; FDB $0000 ; TRANSFER - Tube transfer address ; FDB BANNER ; LPTR - Command line tail ; FDB $0000 ; CTRL - Control block address ; FCB $00 ; DMA_TYPE - TempA/Client personality ; FDB BANNER ; FAULT - Last error message ; FCB $00 ; ESCFLG - Escape flag ; Vector dispatch ; =============== ORG $FE80 XRES: JMP [RESV] ; $FE80 via $DFC0 or $00C0 XSWI3: JMP [SWI3V] ; $FE84 via $DFC2 or $00C2 XSWI2: JMP [SWI2V] ; $FE88 via $DFC4 or $00C4 XFIRQ: JMP [FIRQV] ; $FE8C via $DFC6 or $00C6 XIRQ: JMP [IRQV] ; $FE90 via $DFC8 or $00C8 XSWI: JMP [SWIV] ; $FE94 via $DFCA or $00CA XNMI: JMP [NMIV] ; $FE98 via $DFCC or $00CC JMP [NMIV+2] ; $FE9C XEVENT: JMP [EVENTV] ; $FEA0 via $DFD0 or $00D0 XBRK: JMP [BRKV] ; $FEA4 via $DFD2 or $00D2 Xxxx1: JMP [xxx1V] ; $FEA8 via $DFD4 or $00D4 ; QUITV? XIRQ2: JMP [IRQ2V] ; $FEAC via $DFD6 or $00D6 XCLI: JMP [CLIV] ; $FEB0 via $DFD8 or $00D8 XBYTE: JMP [BYTEV] ; $FEB4 via $DFDA or $00DA XWORD: JMP [WORDV] ; $FEB8 via $DFDC or $00DC XWRCH: JMP [WRCHV] ; $FEBC via $DFDE or $00DE XRDCH: JMP [RDCHV] ; $FEC0 via $DFE0 or $00E0 XFILE: JMP [FILEV] ; $FEC4 via $DFE2 or $00E2 XARGS: JMP [ARGSV] ; $FEC8 via $DFE4 or $00E4 XBGet: JMP [BGETV] ; $FECC via $DFE6 or $00E6 XBPut: JMP [BPUTV] ; $FED0 via $DFE8 or $00E8 XGBPB: JMP [GBPBV] ; $FED4 via $DFEA or $00EA XFIND: JMP [FINDV] ; $FED8 via $DFEC or $00EC JMP [FINDV+2] ; $FEDC ENDIF ; I/O address space ; ================= ORG $FEE0 IOADDRS: FDB 0,0,0,0 FDB 0,0,0,0 ; Remapped hardware vectors ; ========================= ; BA/BS decoded to toggle address line A8 to map hardware vectors to $FEF0 ; ORG $FEF0 ROMHIGH: IF WORKSP < ROMSTART FDB XRES ; $FEF0 FDB XSWI3 ; $FEF2 FDB XSWI2 ; $FEF4 FDB XFIRQ ; $FEF6 FDB XIRQ ; $FEF8 FDB XSWI ; $FEFA FDB XNMI ; $FEFC FDB RESET ; $FEFE ELSE RESV: FDB RES_HANDLE ; $FEF0 SWI3V: FDB SWI3_HANDLE ; $FEF2 SWI2V: FDB SWI2_HANDLE ; $FEF4 FIRQV: FDB FIRQ_HANDLE ; $FEF6 IRQV: FDB IRQ_HANDLE ; $FEF8 SWIV: FDB SWI_HANDLE ; $FEFA NMIV: FDB NMI_HANDLE ; $FEFC FDB RESET ; $FEFE ENDIF ; Text buffers ; ============ ORG $FF00 IF WORKSP > ROMSTART ERRBLK: ; Buffer to store host error block CLIBUF: RMB 128-32 ; Space to enter command line from CLI prompt CLIEND: RMB 32 ENDIF BOTSTK: EQU ERRBLK ; Bottom of internal stack ERRSTK: EQU CLIEND ; Internal stack for host errors CLISTK: EQU CLIEND ; Internal stack for CLI commands ; as main memory may be overwritten ; Tube system workspace and MOS entry block ; ========================================= ORG $FF80 ; Use same addresses as Z80 FCB 0 ; $FF80 ; Escape flag FCB 0 ; $FF81 ; TempA/Client personality FDB BANNER ; $FF82 ; Last error message DEFERR: FDB COM_ERR ; $FF84 ; Default error handler FDB BANNER ; $FF86 ; Command line tail DEFBOT: FDB RAMSTART ; $FF88 ; Bottom of user memory DEFTOP: FDB RAMEND ; $FF8A ; Top of user memory FDB CLICOM ; $FF8C ; Tube execution address FDB CLICOM ; $FF8E ; Tube transfer address FDB CLICOM ; $FF90 ; Current program address FDB 0 ; $FF92 ; Memory address high word FCB 0 ; $FF94 ; Transfer completion flag ; BBC MOS entry points ; -------------------- ORG $FF95 JMP >NULL ; &FF95 ; Service OSCOLD: JMP >RESET ; &FF98 ; Cold PRSTRING: JMP >SEND_TXT ; &FF9B ; Print ASCIIZ text at X FCB $7E ; &FF9E ; Force a JMP opcode FDB OSWRCH SCANHEX: JMP >RD_HEX ; &FFA1 ; ReadHex DISKACC: JMP >NULL ; &FFA4 ; DiskAccess DISKCPP: JMP >NULL ; &FFA7 PRHEX: JMP >PR_HEX1 ; &FFAA ; Print A as 8-bit hex PR2HEX: JMP >PR_HEX2 ; &FFAD ; Print X as 16-bit hex USERINT: JMP >NULL_RTI ; &FFB0 ; Pass on FIRQs if not Tube FIRQ PRTEXT: JMP >PR_TEXT ; &FFB3 ; Print inline ASCIIZ text JMP >NULL ; &FFB6 ; VecDef/PrntC CLICOM: OSQUIT: JMP >CLILOOP ; &FFB9 ; Quit current process, enter Supervisor ERRJMP: OSERROR: JMP >ERR_HANDLE; &FFBC ; Generate an error INITERR: OSINIT: JMP >ERR_INIT ; &FFBF ; Initialise program environment DISKRST: JMP >NULL ; &FFC2 ; DiskReset JMP >NULL ; &FFC5 ; Deprecated JMP >NULL ; &FFC8 JMP >FSC ; &FFCB IF WORKSP > ROMSTART ; MOS file entry points ; --------------------- OSFIND: JMP >FIND ; $FFCE OSGBPB: JMP >GBPB ; $FFD1 OSBPUT: JMP >BPut ; $FFD4 OSBGET: JMP >BGet ; $FFD7 OSARGS: JMP >ARGS ; $FFDA OSFILE: JMP >FILE ; $FFDD ; MOS character entry points ; -------------------------- OSRDCH: JMP >RDCH ; $FFE0 OSASCI: CMPA #13 ; $FFE3 BNE OSWRCH OSNEWL: LDA #10 ; $FFE7 JSR OSWRCH OSWRCR: LDA #13 ; $FFEC OSWRCH: JMP >WRCH ; $FFEE ; MOS system entry points ; ----------------------- OSWORD: JMP >WORD ; $FFF1 OSBYTE: JMP >BYTE ; $FFF4 OS_CLI: JMP >CLI ; $FFF7 ; System vectors ; -------------- FDB COM_ERR ; $FFFA - BRKV - Error handler FDB NULL ; $FFFC - EVENTV - Event vector L_FFFE: FDB RESET ; $FFFE - Reset vector ; This has to be here otherwise as9 complains ; ------------------------------------------- BRKV: EQU $FFFA ; $FFFA EVENTV: EQU $FFFC ; $FFFC CLIV: EQU OS_CLI+1 ; $FFF8 BYTEV: EQU OSBYTE+1 ; $FFF5 WORDV: EQU OSWORD+1 ; $FFF2 WRCHV: EQU OSWRCH+1 ; $FFEF RDCHV: EQU OSRDCH+1 ; $FFE1 FILEV: EQU OSFILE+1 ; $FFDE ARGSV: EQU OSARGS+1 ; $FFDB BGETV: EQU OSBGET+1 ; $FFD8 BPUTV: EQU OSBPUT+1 ; $FFD5 GBPBV: EQU OSGBPB+1 ; $FFD2 FINDV: EQU OSFIND+1 ; $FFCF IRQ2V: EQU USERINT+1 ; $FFB1 ELSE ; File I/O entries ; ---------------- OSFIND: JMP >XFIND ; $FFCE OSGBPB: JMP >XGBPB ; $FFD1 OSBPUT: JMP >XBPut ; $FFD4 OSBGET: JMP >XBGet ; $FFD7 OSARGS: JMP >XARGS ; $FFDA OSFILE: JMP >XFILE ; $FFDD ; Character I/O entries ; --------------------- OSRDCH: JMP >XRDCH ; $FFE0 OSASCI: CMPA #13 ; $FFE3 BNE OSWRCH OSNEWL: LDA #10 ; $FFE7 JSR >OSWRCH OSWRCR: LDA #13 ; $FFEC OSWRCH: JMP >XWRCH ; $FFEE ; MOS entries ; ----------- OSWORD: JMP >XWORD ; $FFF1 OSBYTE: JMP >XBYTE ; $FFF4 OS_CLI: JMP >XCLI ; $FFF7 ; System vectors ; -------------- FDB COM_ERR ; $FFFA - BRKV - Error handler FDB NULL ; $FFFC - EVENTV - Event vector L_FFFE: FDB RESET ; $FFFE - Reset vector ENDIF