; 6809 Tube Client Code ; ===================== ; Copyright (C)1989, 2008, 2012, 2014, 2017 J.G.Harston ; This can be assembled with: ; DOS as9: as9 Client09.src -l now bin > errors.txt ; Kingswood as09: as09-dos -i -lTube09.lst -oTube09.bin Client09.src ; 6809.org.uk asm6809: (not yet checked) ; ; v0.10 1989 JGH: Initial unfinished version. ; v0.20 11-Mar-2008 JGH: Inspired by Johan Heuseveldt to complete ; 12-Mar-2008 JGH: Fixed where A/B was wrong way around in D ; OSBYTE &8E no longer ends with unbalanced stack ; BYTE_HI sends X,Y correct way around ; Internal variables at same address as Z80 ; v0.21 07-Jan-2012 JGH: Added client error handler ; v0.22 20-Nov-2014 DMB: Bugfix in FIRQ code from David Banks ; JGH: Added PRHEX and HR2HEX, execute enters at PROGRAM ; 25-Nov-2014 JGH: Added OSWORD <>0 handler, preparing to merge source ; with 6809 Serial Tube client. Crunched some JSRs to BSRs. ; Shorter FIRQ dispatch code. Moved text buffers to $FF00. ; 28-Nov-2014 JGH: Replaced LDA #0 with CLRA, use returned flags from WAIT_BYTE. ; STARTUP and OSCLI use internal stack. ; MEMTOP moved below code if code is in high memory. ; v0.23 30-Nov-2014 DMB: JSR [,X] should be JSR ,X in EXEC_ENTER. ; JGH: Fixed off-by-one in PR_HEX and preserves A, added PR_STRING. ; PULS reg followed by RTS merged into PULS reg,PC ; 02-Dec-2014 JGH: Implemented KBDTST for 6809 BIOS calls. 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. ; Bugfix: data transfer FIRQs corrupted foreground X and B. ; DMB: Bugfix: EXECUTE should use ADDRESS+2 big-endian address. ; JGH: 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. ; DMB: Bug: Hangs on Ctrl-Break, but not on Soft-Break ; v0.25 20-Dec-2014 JGH: Bugfix: Fixed hang on Ctrl-Break, FIRQ6/7 was wrong way around. ; 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 21-Jun-2017 DMB: Bugfix: Event handler status register was read wrong way. ; 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 25-Jun-2017 JGH: Rolled over to version 1.02. ; 28-Jun-2017 JGH: Added test for 6309 to SWI code, optimised PRHEX routines. ; 16-Jul-2017 JGH: Checking with 6502 Client, only Transfer Type 4 updates ADDRESS. ; Byte/Word transfers check DMADONE before doing transfers. ; 25-Jul-2017 DMB: Bugfix: FIRQ6/7 were using signed counter offset. ; JGH: FIRQ execute/transfer addresses now a seperate variable so able ; to rationalise and tidy ADDRHI/ADDR/TRANS/PROG variables. ; 30-Jul-2017 DMB: Swap Sync/EnableIRQ in data transfer. ; 31-Jul-2017 JGH: Slight optimisation to BYTE_HI. ; v1.03 06-Aug-2017 JGH: Rolled over to version 1.03. ; 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 ; 02-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: plenty of testing 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 this on Matchbox emulated hardware and shown it to work. ; ; ; Memory layout: ; +-------------+--------------------------------------------------------+ ; | 0000- | vvv User memory vvv | ; | 00C0-00FF | Internal variables if in low RAM | ; | 0100-01FF | Text buffers if in low RAM | ; | | | ; | -F6FF | ^^^ User stack ^^^ | ; | F700-F7BF | Text buffers if in high RAM | ; | F7C0-F7FF | Internal variables if in high RAM | ; | -F7FF | ^^^ User stack if Client in RAM ^^^ | ; +-------------+--------------------------------------------------------+ ; | F800-FFFF | Client ROM, copied to RAM on startup | ; | F800-F815 | 6809 BIOS entry vectors | ; | | | ; | FEE0-FEEF | Tube registers | ; | 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. ; ; START OF ROM CODE ; ================= ORG $F800 ROMSTART: RAMSTART: EQU $0000 RAMEND: EQU 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 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: ORCC #$50 ; Ensure interupts disabled LDS #RAMEND ; Put stack at top of RAM IF RAMEND = 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 #DMADONE RESETLP3: CMPX #0 BNE RESETLP1 ; Copy top part of ROM to RAM LDA >TUBE1S ; Access I/O registers to page ROM out ; Once ROM is paged out we can do subroutine calls ; as we can now read from stack in RAM ; STARTUP ; ======= ; Tube data: via R1: string $00 -- via R2: $7F or $80 ; STARTUP: ORCC #$50 ; Disable interupts LDS #CLISTK ; Use internal stack 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 JSR PR_HELP ; Print banner via Tube WRCH protocol JSR OSNEWL ; SEND_TXT will use 7 bytes on stack CLRA ; Set A to zero ; STA >ESCFLG ; Clear Escape flag ; ECSFLG will have been cleared when copying ROM/initialising workspace JSR OSWRCH ; Send terminating zero byte ; This will use 3 bytes on stack CLRA ; Carry Clear to indicate Reset JSR CLI_WAIT ; Wait for result byte and enter code ; This will use 14 bytes on stack including data transfer ; Data transfer may happen while waiting ; Fall through to CLICOM if nothing executed ; Supervisor command line prompt ; ============================== ; Allow user to enter *command ; 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 ; OSWORD &00 - 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: COM_INIT: ; This has already been done by copying ROM to RAM. But, when entering ; Supervisor we want to ensure full memory available. LDD #RAMSTART STD MEMBOT ; Initialise bottom of user memory LDD #RAMEND STD MEMTOP ; Initialise top of user memory 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: LDD #SWI_HANDLE STD XSWIV ; Point SWI vector to error BRK 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 _PENDA: LDA #0 ; Get pending character PENDA: EQU _PENDA+1 ; Pending character is parameter of LDA RTS ; LDA PENDA ; Get pending character ; 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 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 sent ; ********************** ; 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 ; CMPS #ROMSTART ; Check where the stack is CMPS #BOTSTK ; Check where the stack is BCC CLI_SYSSTK1 ; We're already using internal stack 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 ; If we get back, 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_LOOP1: 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_LOOP1 ; 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 to Y 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 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 SEND_CMD ; Send command $02 = OSCLI JSR SEND_STR ; Send string at X CLI_WAIT1: ORCC #$01 ; Set Carry to indicate OSCLI CLI_WAIT: JSR WAIT_BYTE ; Wait for result via Tube R2 (preserves Cy) ; Data transfer may happen while waiting ; TSTA ; Check return code ; WAIT_BYTE returns flags set from A 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 execution 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 ; ; Tube data: $00 -- Carry Char ; RDCH: CLRA JSR SEND_CMD ; Send command $00 - OSRDCH WAIT_CHAR: BSR WAIT_BYTE ; Get returned byte ASLA ; Move b7 into Carry ; Fall through to fetch byte from Tube R2 ; Wait for a byte from Tube Register 2 ; ==================================== ; On exit: A=byte read ; PL/MI, EQ/NE set from A ; Other flags preserved ; WAIT_BYTE: LDA >TUBE2S ; Read Tube R2 Status BPL WAIT_BYTE ; Wait for b7 set LDA >TUBE2 ; Get byte from Tube R2 BYTE_DONE: RTS ; 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 SEND_CMD ; Send command $04 - short BYTE TFR X,D ; B=X JSR SEND_BYTEB ; Send second parameter from B PULS A,B PSHS A,B JSR SEND_BYTE ; Send first parameter JSR WAIT_BYTE ; 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 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 JSR SEND_CMD ; Send command $06 - long byte TFR X,D ; B=X - second parameter JSR SEND_BYTEB ; Send second parameter from B JSR SEND_BYTEY ; Send third parameter from Y PULS A,B JSR SEND_BYTE ; 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 WAIT_BYTE ; Wait for response ASLA ; Move b7 into Carry PSHS CC ; Save flags JSR WAIT_BYTE ; 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 WAIT_BYTE ; 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 as returned Y*256+X value PULS CC,A,B,PC ; Restore flags/A/B 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 SEND_CMD ; Send command $08 - OSWORD JSR SEND_BYTEB ; Send OSWORD function in 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 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 SEND_BYTEB ; 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 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 SEND_BYTEB ; 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 (Y=0 if escape) ; ; 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 SEND_CMD ; 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 SEND_BYTE ; Send $0700 JSR SEND_BYTEB ; Send $00 as B=0 from SEND_BLK JSR WAIT_BYTE ; Wait for response ASLA ; Move bit 7 into Carry BCS RD_DONE LDX ,--X ; Get text pointer from control block ; LDB #0 ; B=number received (B already zero from SEND_BLK) RD_STR: JSR WAIT_BYTE ; Wait for bytes from Tube R2 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 and function LDA #$0C JSR SEND_CMD ; Send command $0C - OSARGS JSR SEND_BYTEY ; 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 SEND_BYTE ; Send action JSR WAIT_BYTE ; 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 SEND_CMD ; Send command $0E - OSBGet JSR SEND_BYTEY ; 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 SEND_CMD ; Send command $10 - OSBPut JSR SEND_BYTEY ; Send handle PULS A,B ; Get A and B back PSHS A,B JSR SEND_BYTE ; Send byte to Tube JSR WAIT_BYTE ; Wait for acknowledgement PULS A,B,PC ; Restore A/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 A LDA #$12 JSR SEND_CMD ; Send command $12 - OSFIND PULS A,B ; Get function back JSR SEND_BYTE ; Send function TSTA ; Check function BEQ Close ; Jump to deal with Close JSR SEND_STR ; Send string at X JMP WAIT_BYTE ; Wait for returned handle Close: PSHS B JSR SEND_BYTEY ; Send handle to Tube JSR WAIT_BYTE ; 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 SEND_CMD ; 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 SEND_BYTE ; Send function JSR WAIT_BYTE ; 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 SEND_CMD ; 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 SEND_BYTE ; Send 0th byte of control block ELSE LDB #13 JSR SEND_BLOCK ; Send 13-byte control block ENDIF PULS A JSR SEND_BYTE ; 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 WAIT_BYTE ; 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 Register 2 ; ========================================= SEND_STR: LDA ,X+ ; Get byte from X, increment X BSR SEND_BYTE ; Send byte via Tube R2 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 BSR SEND_BYTE ; Send byte via Tube R2 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 WAIT_BYTE ; Get byte via Tube R2 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 SEND_BYTE ; 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 WAIT_BYTE ; 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 ; OSWRCH - Send character in A to Tube Register 1 ; =============================================== ; Preserves A, corrupts flags ; WRCH: PSHS A ; Save A SEND_R1LP: LDA >TUBE1S ; Read Tube R1 status ASLA ; Move TxRDY from b6 to b7 BPL SEND_R1LP ; Loop until TxRDY set PULS A ; Get A back STA >TUBE1 ; Send byte to Tube R1 RTS ; Send byte in Y to Tube Register 2 via B ; ======================================= SEND_BYTEY: TFR Y,D ; ; Fall through into SEND_BYTEB ; Send byte in B to Tube Register 2 ; ================================== SEND_BYTEB: TFR B,A ; ; Fall through into SendByte ; Send byte in A to Tube Register 2 ; ================================== ; Preserves A, corrupts flags ; SEND_CMD: SEND_BYTE: PSHS A ; Save A SEND_BYTELP: LDA >TUBE2S ; Read Tube R2 status ASLA ; Move TxRDY from b6 to b7 ; ANDA #$40 would preserve carry BPL SEND_BYTELP ; Loop until TxRDY set PULS A ; Get A back STA >TUBE2 ; Send byte to Tube R2 RTS ; **************************************** ; Host->Client communication via interupts ; **************************************** ; When Host sends a byte to R1 or R4 it generates a Client IRQ. ; Data transfers are syncronised with client NMIs. Graham Toal's ; design sends Tube IRQs to 6809 FIRQs and Tube NMIs to 6809 IRQs. ; Transfers are synchronised with SYNC instruction waiting for IRQs. ; ; This code manually translated from Graham Toal's Skimp code ; with half an eye on the PDP-11, Z80 and 6502 code. ; Most code before this point translated from JGH PDP-11 client. ; Interupt handler ; ================ ; Stack holds: CC with E=0, PC ; CC has I and F set to disable further IRQs and FIRQs ; FIRQ_HANDLE: PSHS A LDA >TUBE4S ; Read Tube R4 status BMI FIRQ_R4 ; If data in Tube R4, jump to process errors and transfers LDA >TUBE1S ; Read Tube R1 status BMI FIRQ_R1 ; If data in Tube R1, jump to process Escape and Events PULS A ; Get A back JMP [IRQ2V] ; Pass interupt onwards ; Interupt generated by data in Tube R1 ; ------------------------------------- FIRQ_R1: LDA >TUBE1 BMI FIRQ_Escape ; b7=1, jump to set Escape state ; ; A<$80 - Host event being passed to client ; Tube data: via R1: $00 Y X A ; PULS A ; Pop A to restack all registers PSHS A,B,X,Y,U ; Save everything BSR Get_R1 ; Get event Y parameter TFR A,B CLRA ; Ensure AB is 8-bit value TFR D,Y BSR Get_R1 ; Get event X parameter TFR A,B CLRA ; Ensure AB is 8-bit value TFR D,X BSR Get_R1 ; Get event A parameter JSR [EVENTV] ; Dispatch event PULS U,Y,X,B,A ; Restore registers RTI ; Return from interupt ; A>$7F - Host changing Escape state ; Tube data: via R1: flag, b7=1, b6=state ; FIRQ_Escape: ASLA ; Move b6 into b7 STA >ESCFLG ; Store Escape flag PULS A ; Restore A RTI ; Return from interupt ; Read byte from Tube R1, allowing Tube R4 transfers to take place ; ---------------------------------------------------------------- Get_R1: LDA >TUBE4S ; Read Tube R4 status BPL NotFIRQ_R4 ; Pending R4 transfer higher priority than R1 transfer BSR FIRQ_R4_CC ; Deal with R4 interupt NotFIRQ_R4: LDA >TUBE1S ; Read Tube R1 status BPL Get_R1 ; Loop until b7 set LDA >TUBE1 ; Get byte from Tube R1 RTS ; Read byte from Tube R4 ; ---------------------- Get_R4: LDA >TUBE4S ; Read Tube R4 status BPL Get_R4 ; Loop until b7 set LDA >TUBE4 ; Get byte from Tube R4 RTS ; Interrupt generated by data in Tube R4 ; -------------------------------------- FIRQ_R4_CC: ANDCC #$7F ; Clear 'all registers' bit PSHS CC ; Push CC to let this be a subroutine PSHS A ; Push A to balance following Pull FIRQ_R4: PULS A ; Pop A to restack registers PSHS A,B,X ; Rearrange stack contents LDA >TUBE4 BPL FIRQ_Data ; b7=0, jump for data transfer ; A>$7F - Error occured ; Tube data: via R2: $00 err string $00 ; LDS #ERRSTK ; Collapse stack LDX #ERRBLK ; Point to error buffer JSR WAIT_BYTE ; Wait for BRK error code LDA #$3F ; SWI opcode STA ,X+ ; Store SWI opcode JSR WAIT_BYTE ; Get error number STA ,X+ ; Store error number FIRQ_R4LP: JSR WAIT_BYTE ; Wait for byte of error string STA ,X+ ; Store in error buffer ; WAIT_BYTE returns flags set from A BNE FIRQ_R4LP ; Loop until terminating $00 received LDX #ERRBLK+1 ; Point to error block after SWI opcode PSHS X ; Push error pointer onto stack JMP OSERROR ; Jump to generate error ; FIRQ and IRQ will be enabled by error handler ; Data transfer via FIRQs ; ----------------------- ; R4<$80 - Data transfer ; Tube data: via R4: action ID block sync, via R3: data ; ; Cribbed from Graham Toal's Skimp code ; FIRQ_Data: ; A=transfer type ; PSHS A ; Save transfer type BSR Get_R4 ; Wait for caller ID PULS A ; Get transfer type back CMPA #5 ; Is it 'release'? BNE FIRQ_NotDone; No, jump to do transfer DMB: fixed BEQ/BNE error CLR DMADONE ; Signal 'transfer done' PULS A,B,X ; Restore registers RTI ; and return from interupt ; FIRQ_NotDone: PSHS A ; Save transfer type again BSR Get_R4 ; Get data address MSB BSR Get_R4 ; Get data address BSR Get_R4 ; Get data address EXG A,B BSR Get_R4 ; Get data address LSB EXG A,B ; D=data address TFR D,X ; X=data address ; LDA >TUBE3S ; Read from Tube R3 twice to clear FIFO ; LDA >TUBE3S ; Z80, 32016 and 80x86 client don't actually do this LDA #$FF STA DMADONE ; Signal 'transfer in progress' BSR Get_R4 ; Get sync byte ANDCC #$BF ; DMB: re-enable FIRQ interrupts to allow Release FIRQ LDA ,S+ ; Pop A from stack and set Z flag BEQ FIRQ0 ; Dispatch to FIRQ subhandler CMPA #2 BCS FIRQ1 BEQ FIRQ2 CMPA #4 BCS FIRQ3 BEQ FIRQ4 CLRB ; B=counter for 256-byte transfers CMPA #7 ; We will never get a FIRQ5 here as filtered out earlier BCS FIRQ6 BNE FIRQ_DONE ; 1-byte and 2-byte Tube R3 interrupts done via IRQ/SYNC mechanism ; 256-byte transfers done by polling transfer register ; Transfer 7 - Multiple byte host -> parasite ; ------------------------------------------- FIRQ7: FIRQ7lp: LDA >TUBE3S ; Wait for Tube R3 ready BPL FIRQ7lp LDA >TUBE3 ; Get byte from Tube host via R3 STA ,X+ ; Store in parasite memory at X DECB ; Decrement counter BNE FIRQ7lp ; Loop 256 times BRA FIRQ_DONE ; Transfer 6 - Multiple byte parasite -> host ; ------------------------------------------- FIRQ6: FIRQ6lp: LDA >TUBE3S ; Wait for Tube R3 ready BPL FIRQ6lp LDA ,X+ ; Get byte from parasite memory at X STA >TUBE3 ; Send to Tube host via R3 DECB ; Decrement counter BNE FIRQ6lp ; Loop 256 times FIRQ6lp2: LDA >TUBE3S ; Wait for Tube R3 ready BPL FIRQ6lp2 STA >TUBE3 ; Send 257th byte to flush FIFO BRA FIRQ_DONE ; Transfer 3 - Double byte host -> parasite ; ----------------------------------------- FIRQ3: FIRQ3lp: SYNC ; Wait for IRQ LDA DMADONE ; Has flag changed? BEQ FIRQ_DONE ; FIRQ5 has cleared flag LDA >TUBE3 ; Get two bytes from Tube host LDB >TUBE3 STD ,X++ ; Store them in parasite memory BRA FIRQ3lp ; Loop until FIRQ5 clears flag ; Transfer 2 - Double byte parasite -> host ; ----------------------------------------- FIRQ2: FIRQ2lp: SYNC ; Wait for IRQ LDA DMADONE ; Has flag changed? BEQ FIRQ_DONE ; FIRQ5 has cleared flag LDD ,X++ ; Fetch two bytes from parasite memory STA >TUBE3 ; Send them to Tube host STB >TUBE3 BRA FIRQ2lp ; Loop until FIRQ5 clears flag ; Transfer 1 - Single bytes host -> parasite ; ------------------------------------------ FIRQ1: FIRQ1lp: SYNC ; Wait for IRQ LDA DMADONE ; Has flag changed? BEQ FIRQ_DONE ; FIRQ5 has cleared flag LDA >TUBE3 ; Get byte from Tube host STA ,X+ ; Store in parasite memory BRA FIRQ1lp ; Loop until FIRQ5 clears flag ; Transfer 0 - Single bytes parasite -> host ; ------------------------------------------ FIRQ0: FIRQ0lp: SYNC ; Wait for IRQ LDA DMADONE ; Has flag changed? BEQ FIRQ_DONE ; FIRQ5 has cleared flag LDA ,X+ ; Get byte from parasite memory STA >TUBE3 ; Send to Tube host BRA FIRQ0lp ; Loop until FIRQ5 clears flag ; Transfer 4 - Set program execute address ; ---------------------------------------- FIRQ4: STX ADDRESS ; Update Execution Address FIRQ_DONE: FIRQ_EXIT: PULS A,B,X ; After a FIRQ4 the Host will then send a FIRQ5 to release the Tube and ; clear DNA_DONE. OSCLI handler will pick up execution address and pass ; it to EXECUTE. ; Null interupt routines ; ====================== ; OS-9 and FLEX use a timer on IRQ to poll background processes ; NULL_RTI: RES_HANDLE: SWI2_HANDLE: SWI3_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 ; I/O address space ; ================= ORG $FEC0 IOADDRS: ; Add any extra I/O devices here ; For example: ; $FECx VIA ; $FEDx MMU ; $FEEx TUBE ORG $FEE0 TUBE1S: FCB 0 ; $FEE0 ; Tube registers TUBE1: FCB 0 ; $FEE1 TUBE2S: FCB 0 ; $FEE2 TUBE2: FCB 0 ; $FEE3 TUBE3S: FCB 0 ; $FEE4 TUBE3: FCB 0 ; $FEE5 TUBE4S: FCB 0 ; $FEE6 TUBE4: FCB 0 ; $FEE7 ; If Tube registers fully decoded, space for 8 I/O addresses here ; For example: ; $FEE8/9 ACIA ; Remapped hardware vectors ; ========================= ; BA/BS decoded to toggle address line A8 to map hardware vectors to $FEF0 ; ORG $FEF0 ROMHIGH: XTRAPV: FDB RES_HANDLE ; $FEF0 ; Hardware vectors, paged in to $FFFx XSWI3V: FDB SWI3_HANDLE ; $FEF2 XSWI2V: FDB SWI2_HANDLE ; $FEF4 XFIRQV: FDB FIRQ_HANDLE ; $FEF6 XIRQV: FDB IRQ_HANDLE ; $FEF8 XSWIV: FDB SWI_HANDLE ; $FEFA XNMIV: FDB NMI_HANDLE ; $FEFC XRESETV: FDB RESET ; $FEFE ; Text buffers ; ============ ORG $FF00 ERRBLK: ; Buffer to store host error block BOTSTK: ; Bottom of internal stack CLIBUF: RMB 128-32 ; Space to enter command line from CLI prompt CLIEND: RMB 32 ERRSTK: ; Internal stack for host errors CLISTK: ; Internal stack for CLI commands ; as main memory may be overwritten ; Tube system workspace and MOS entry block ; ========================================= ORG $FF80 WORKSP: ; Use same addresses as Z80 ESCFLG: FCB 0 ; $FF80 ; Escape and pending input flag TEMPA: FCB 0 ; $FF81 ; TempA/Personality FAULT: FDB BANNER ; $FF82 ; Last error message DEFERR: FDB COM_ERR ; $FF84 ; Default error handler LPTR: FDB BANNER ; $FF86 ; Command line tail MEMBOT: FDB RAMSTART ; $FF88 ; Bottom of user memory MEMTOP: FDB RAMEND ; $FF8A ; Top of user memory ADDRESS: FDB CLICOM ; $FF8C ; Execution address TRANSFER: FDB CLICOM ; $FF8E ; Transfer address PROGRAM: FDB CLICOM ; $FF90 ; Current program address ADDRHI: FDB 0 ; $FF92 ; Memory address high word DMADONE: 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 DISKCCP: 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 IRQ2V: EQU USERINT+1 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 >NULL ; &FFCB ; BBC 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 ; BBC 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 ; BBC MOS system entry points ; --------------------------- OSWORD: JMP >WORD ; $FFF1 OSBYTE: JMP >BYTE ; $FFF4 OS_CLI: JMP >CLI ; $FFF7 ; System vectors ; -------------- BRKV: FDB COM_ERR ; $FFFA ; Error handler EVENTV: FDB NULL ; $FFFC ; Event vector L_FFFE: FDB RESET ; $FFFE ; Reset vector ; Memory Mapping Unit/Dynamic Address Translation ; =============================================== ; SWTPC clones have write-only MMU/DAT at $FFF0