; Boilerplate definitions to build with MGTEK 68x12 MiniIDE INCLUDE 'derivative.inc' ; 9S12 Serial Tube Client Code ; ============================ ; Copyright (C)2010 J.G.Harston - mdfs.net/tube ; Copyright (C)2011 J.S.Flynn ; ; v0.10 01-Jan-2012 JSF: Converted from 6809 client ; v0.11 01-Jul-2012 JGH: Added full OSWORD handler ; v0.12 15-Nov-2013 JGH: Added software vectors, hardware vectors in RAM ; 21-Nov-2013 JGH: JMP [addr] fixed, replaced with JMP [addr,PC] ; TRAP $Fn -> MOS API call ; v0.24 08-Dec-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. ; Added PR_STRING. Implemented KBDTST for 6812 BIOS calls. ; STARTUP and OSCLI use internal stack. Execute checks ; code header. WARM resets MEMBOT & MEMTOP. ; 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 code is not made the current program. ; Supervisor prompt makes itself current program. Full ; header (C) check. ; 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. 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: Optimised PRHEX routines. Only Transfer Type &Cx updates ; ADDRESS, uses 16-bit address. Slightly optimised 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. ; 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 0 ; KBDTST/KBDIN pending input ; v1.06 25-Jun-2017 JGH: Rolled over to version 1.06. VERSION: EQU $0105 BUILD: EQU 2 ; ; BIGENDIAN: EQU 1 ; Big-endian control block contents for OSFILE/OSGBPB/OSARGS ; ; ; Based on 6809 Serial Tube Client ; This code may be freely reused. ; NOTE! Only tested in CodeWarrior simulator ; Interupt Vector Base Register is used to remap hardware vectors ; +-------------+--------------------------------------------------+ ; | 0000-0FFF | Registers, I/O, EEPROM, system RAM | ; | 0800-09FF | Workspace if at bottom of very low memory | ; | 0E00-0FFF | Workspace if at top of very low memory | ; | 1000- | Onboard RAM | ; | | On 9S12E series RAM extends upwards from 1000, | ; | | extra RAM moving RAMTOP upwards. | ; | | On 9S12D series RAM extends downwards from 7FFF, | ; | | extra RAM moving RAMBOT downwards. | ; | BUT! 1000-1FFF can be paged out! swapping out workspace! | ; | | | ; | 1000-103F | Internal variables if in low user memory | ; | 1040-10FF | Text buffers if in low user memory | ; | 1100-11FF | Hardware vectors if mapped to low user memory | ; | 1100- | Start of user memory if vectors in ROM | ; | 1200- | Start of user memory if vectors in RAM | ; | -3FFF | If 12K RAM present | ; | -7FFF | If 12K+16K RAM present | ; | | Client should really count memory | ; | | | ; | -xxFF | ^^^ User stack ^^^ | ; | xE00-xEFF | Hardware vectors if mapped to high memory | ; | xF00-xFBF | Text buffers if in high memory | ; | xFC0-xFFF | Internal variables if in high memory | ; +-------------+--------------------------------------------------+ ; | F800-FFFF | Client ROM | ; | F800-F815 | Monitor ROM entry vectors | ; | FE00-FEFF | Hardware vectors if mapped to ROM | ; | FF80- | Internal variables if running from RAM | ; | -FFF9 | BBC MOS entry block | ; +-------------+--------------------------------------------------+ ; Client error implemented with SWI (equivalent of 6502 BRK, Z80 RST &38, etc.) ; TRAP nn>$3F used for system calls (equivalent of 6809's SWI2, SWI3, etc.) ; Note to self: CMP sets Carry Z80-style, CS=lower, CC=same+higher ; Tube client system configuration tweekables ; =========================================== ; Workspace in low memory ; ----------------------- WORKSP: EQU $1000 ; Workspace at bottom of RAM ;WORKSP: EQU $0800 ; Workspace at bottom of low RAM VECBASE: EQU WORKSP+$100 ; Hardware vectors redirected to RAM RAMSTART: EQU WORKSP+$200 ; Start of user RAM RAMEND: EQU $4000 ; End of user RAM+1 with 12K RAM ;RAMEND: EQU $8000 ; End of user RAM+1 with 12K+16K RAM ROMSTART: EQU $F800 ; Start of code ESCOFF: EQU $00 ; Workspace goes upwards in memory STACK: EQU RAMEND ; System stack at top of user RAM LOCAL_GO: EQU 1 ; Include local *GO command LOCAL_HELP: EQU 1 ; Include local *HELP command LOCAL_RUN: EQU 1 ; Include local *RUN command ; Note: if VECBASE is in ROM there is not enough space for both *GO and *HELP ; ; Workspace in high memory ; ; ------------------------ ; RAMSTART: EQU $1000 ; Start of user RAM ; ;RAMSTART: EQU $0800 ; Start of user RAM ; WORKSP: EQU $3FC0 ; Workspace at top of RAM ; VECBASE: EQU WORKSP-$1C0 ; Hardware vectors redirected to RAM ; RAMEND: EQU VECBASE ; End of user RAM+1 ; ROMSTART: EQU $F800 ; Start of code ; ESCOFF: EQU $FF ; Workspace goes downwards in memory ; STACK: EQU RAMEND ; System stack at top of user RAM ; LOCAL_GO EQU 1 ; Include local *GO command ; LOCAL_HELP EQU 1 ; Include local *HELP command ; LOCAL_RUN: EQU 1 ; Include local *RUN command ; ; Note: if VECBASE is in ROM there is not enough space for both *GO and *HELP ; ToDo: Workspace use can be crushed down to 16 bytes to fit on the S912A/B/C which ; only have RAM at $0800-$0BFF or -$0FFF. This needs conditional assembly adding to ; remove vectors and text buffers, program space then $0810-$0BFF or $0800-$0BEF. ; Serial I/O Addresses ; ==================== TxSTATUS EQU SCI0SR1 TxCTRL EQU SCI0CR2 TxDATA EQU SCI0DRL RxSTATUS EQU SCI0SR1 RxCTRL EQU SCI0CR2 RxDATA EQU SCI0DRL TxRDY EQU mSCI0SR1_TDRE RxRDY EQU mSCI0SR1_RDRF RxINIT EQU mSCI0CR2_RE TxINIT EQU mSCI0CR2_TE ; 68HC12 I/O and Registers ; ======================== IO_IVBR: EQU $0121 ; Interupt vector base high byte of address ; Serial Tube system values ; ========================= esc EQU $9B ; Client MOS Workspace ; ==================== IF ESCOFF = $00 ; Workspace runs from low memory upwards ; -------------------------------------- ESCFLG: EQU WORKSP+$00 ; $1000 - Escape flag DMA_TYPE: EQU WORKSP+$01 ; $1001 - Tube personality FAULT: EQU WORKSP+$02 ; $1002 - Last error message WORK04: EQU WORKSP+$04 ; $1004 - Not used LPTR: EQU WORKSP+$06 ; $1006 - Command line tail MEMBOT: EQU WORKSP+$08 ; $1008 - Bottom of user memory MEMTOP: EQU WORKSP+$0A ; $100A - Top of user memory ADDRESS: EQU WORKSP+$0C ; $100C - Tube execution address TRANSFER: EQU WORKSP+$0E ; $100E - Tube transfer address PROGRAM: EQU WORKSP+$10 ; $1010 - Program entry address CTRL: EQU WORKSP+$12 ; $1012 - Control address ADDRHI: EQU WORKSP+$12 ; $1012 - Address high word DMADONE: EQU WORKSP+$1E ; $101E - Transfer completion flag PENDA: EQU WORKSP+$1F ; $101F - Pending keypress ; ; 6809 Hardware vectors ; ; --------------------- ; SWI3V: EQU WORKSP+$14 ; $1014 ; SWI2V: EQU WORKSP+$16 ; $1016 ; FIRQV: EQU WORKSP+$18 ; $1018 ; IRQV: EQU WORKSP+$1A ; $101A ; SWIV: EQU WORKSP+$1C ; $101C ; Software vectors ; ---------------- EVENTV: EQU WORKSP+$20 ; $1020 BRKV: EQU WORKSP+$22 ; $1022 xxx2V: EQU WORKSP+$24 ; $1024 QUITV: EQU WORKSP+$26 ; $1026 CLIV: EQU WORKSP+$28 ; $1028 BYTEV: EQU WORKSP+$2A ; $102A WORDV: EQU WORKSP+$2C ; $102C WRCHV: EQU WORKSP+$2E ; $102E RDCHV: EQU WORKSP+$30 ; $1030 FILEV: EQU WORKSP+$32 ; $1032 ARGSV: EQU WORKSP+$34 ; $1034 BGETV: EQU WORKSP+$36 ; $1036 BPUTV: EQU WORKSP+$38 ; $1038 GBPBV: EQU WORKSP+$3A ; $103A FINDV: EQU WORKSP+$3C ; $103C ; Text buffers ; ------------ ERRBLK: EQU WORKSP+$40 ; $1040 - Buffer to store host error block, ; allowed to overflow into CLIBUF CLIBUF: EQU WORKSP+$60 ; $1060 - Buffer to enter command line from CLI prompt CLIEND: EQU WORKSP+$100 ; $1100 - 160 bytes for command line ; $1100 - Redirected hardware vectors if in RAM ELSE ; Workspace runs from high memory downwards ; ----------------------------------------- ERRBLK: EQU WORKSP-$C0 ; $7F00 - Buffer to store host error block, allowed to overflow into CLIBUF CLIBUF: EQU WORKSP-$90 ; $7F30 - Space to enter command line from CLI prompt CLIEND: EQU WORKSP-$00 ; $7FC0 - 144 bytes for command line ; ; 6809 Hardware vectors ; ; --------------------- ; RESV: EQU WORKSP+$00 ; $7FC0 - Same location as SWTPC ; SWI3V: EQU WORKSP+$02 ; $7FC2 ; SWI2V: EQU WORKSP+$04 ; $7FC4 ; FIRQV: EQU WORKSP+$06 ; $7FC6 ; IRQV: EQU WORKSP+$08 ; $7FC8 ; SWIV: EQU WORKSP+$0A ; $7FCA ; NMIV: EQU WORKSP+$0C ; $7FCC DMADONE: EQU WORKSP+$0E ; $7FCE - Transfer completion flag PENDA: EQU WORKSP+$0F ; $7FCF - Pending keypress ; ; Software vectors ; ---------------- EVENTV: EQU WORKSP+$10 ; $7FD0 BRKV: EQU WORKSP+$12 ; $7FD2 xxx2V: EQU WORKSP+$14 ; $7FD4 QUITV: EQU WORKSP+$16 ; $7FD6 CLIV: EQU WORKSP+$18 ; $7FD8 BYTEV: EQU WORKSP+$1A ; $7FDA WORDV: EQU WORKSP+$1C ; $7FDC WRCHV: EQU WORKSP+$1E ; $7FDE RDCHV: EQU WORKSP+$20 ; $7FE0 FILEV: EQU WORKSP+$22 ; $7FE2 ARGSV: EQU WORKSP+$24 ; $7FE4 BGETV: EQU WORKSP+$26 ; $7FE6 BPUTV: EQU WORKSP+$28 ; $7FE8 GBPBV: EQU WORKSP+$2A ; $7FEA FINDV: EQU WORKSP+$2C ; $7FEC PROGRAM: EQU WORKSP+$2E ; $7FEE - Program entry address MEMBOT: EQU WORKSP+$30 ; $7FF0 - Bottom of user memory MEMTOP: EQU WORKSP+$32 ; $7FF2 - Top of user memory ADDRESS: EQU WORKSP+$34 ; $7FF4 - Tube execution address TRANSFER: EQU WORKSP+$36 ; $7FF6 - Tube transfer address LPTR: EQU WORKSP+$38 ; $7FF8 - Command line tail CTRL: EQU WORKSP+$3A ; $7FFA - Control address ADDRHI: EQU WORKSP+$3A ; $7FFA - Address high word DMA_TYPE: EQU WORKSP+$3C ; $7FFC - TempA/Client personality FAULT: EQU WORKSP+$3D ; $7FFD - Last error message ESCFLG: EQU WORKSP+$3F ; $7FFF - Escape flag ENDIF ERRSTK: EQU CLIEND CLISTK: EQU CLIBUF LOCAL_CMDS: EQU LOCAL_GO + LOCAL_HELP + LOCAL_RUN ; START OF ROM CODE ; ================= ORG ROMSTART ; 68xx BIOS entry block always at F800 ; ==================================== ORG $F800 COLD: FDB RESET ; $F800 - cold start WARM: FDB WARMS ; $F802 - warm start INCH: FDB OSRDCH ; $F804 - char input 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 "MC9S12XE SERIAL TUBE" IF RAMEND = $4000 FCC " 12K" ENDIF IF RAMEND = $8000 FCC " 28K" ; Client should really count available memory ENDIF FCC " " FCB ((VERSION >> 8) & 15) + $30 FCB "." FCB ((VERSION >> 4) & 15) +$30 FCB (VERSION & 15) + $30 IF BUILD FCB BUILD+96 ELSE FCB 32 ENDIF FCB 13,0 WARMS: JMP CLILOOP ; RESET ; ===== ; Ensure code is executable, then continue into STARTUP ; Normally, all HC12 platforms have the client in ROM ; RESET: ; SEI ; Ensure interupts disabled ; IF WORKSP > ROMSTART ; LDS #STACK ; Set up system stack at top of user memory ; LDX #ROMSTART ; Start at start of ROM ;RESETLP1: ; LDAA ,X ; Get a byte from ROM ; STAA 1,X+ ; Store to RAM and increment X ; CPX #IOADDRS ; BNE RESETLP1 ; Loop until hit I/O space ; LDX #ROMHIGH ; Point to ROM after I/O space ;RESETLP2: ; LDAA ,X ; STAA 1,X+ ; CPX #0 ; BNE RESETLP2 ; Copy top part of ROM to RAM ; ENDIF ; STARTUP ; ======= ; Reset all vectors, initialise Client state, tell Host we've restarted ; STARTUP: SEI ; Disable interupts LDS #CLISTK ; Put stack in temporary stack area LDX #VECBASE ; Set up hardware vectors TFR X,D ; Copy VECBASE to AB STA >IO_IVBR ; Set high byte of CPU's Interupt Vector Base IF VECBASE < ROMSTART ; If hardware vectors in RAM, initialise them to null ISRs ; -------------------------------------------------------- LDY #RTI ; Point all vectors to null routine LDB #$80 ; 128 vectors to set up VECLOOP1: STY 2,X+ ; Store vector, increment X by 2 DECB ; Decrement number to do BNE VECLOOP1 ; Loop until add done ENDIF ; Initialise software vectors and workspace ; ----------------------------------------- LDX #VECTORS ; Copy default vectors and workspace LDY #EVENTV LDB #$20 VECLOOP2: LDA 1,X+ ; Get byte from default vectors STA 1,Y+ ; Store in vectors DECB ; Decrement number of bytes BNE VECLOOP2 ; Loop until add done ; Count available memory ; ---------------------- ; This doesn't work when inaccessible memory accesses mirrors of accessible memory ; ; LDY DEFBOT ; TFR Y,X ; LDAA #0 ; 0K found ; RAMLOOP: ; LDAB ,X ; Get byte ; EORB #$55 ; Toggle it ; STAB ,X ; Store it back ; CMPB ,X ; Did store work? ; BNE ROMFOUND ; No, no more RAM ; EORB #$55 ; Toggle byte back ; STAB ,X ; And restore it ; ADDA #4 ; Add 4K ; DAA ; Update BCD count ; LEAX $1000,X ; X=X+4K ; CMPX DEFTOP ; ; BCC RAMLOOP ; Check next 4K ; ROMFOUND: ; STY MEMBOT ; Initialise bottom of memory ; STX MEMTOP ; Initialise top of memory ; TFR X,S ; Put stack at top of memory ; ; A=amount of RAM in BCD LDAA TxCTRL ORAA #TxINIT STAA >TxCTRL ; Initialise data port and page ROM out LDAA RxCTRL ORAA #RxINIT STAA >RxCTRL ; 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 ; Set up SWIV, TRAPV, MEMBOT, MEMTOP, BRKV LDX PROGRAM ; Copy current PROGRAM to ADDRESS STX ADDRESS ; so will re-enter on Soft Break CLI ; 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 PSHB ; Save Soft Reset result, will be &00 if no response JSR PR_HELP ; Print startup banner via Tube wrch protocol JSR OSNEWL ; If we share a hardware Reset, we wait for an Ack here ; LDAA #0 ; JSR OSWRCH ; Send terminating zero byte ; CLC ; Clear Carry Flag to indicate Reset ; JSR CLI_WAIT ; Wait for result byte PULA ; We sent a Software Reset, so use the Ack from that CLC ; Clear Carry Flag to indicate Reset JSR CLI_CHECK ; Check result byte ; ; Fall through to CLICOM if nothing executed ; Supervisor command line prompt ; ============================== ; Allow user to enter *commands ; 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 CLICOM the current program to re-enter STX PROGRAM ; on soft reset CLI ; Enable interupts CLILOOP2: LDX #PROMPT JSR SEND_TXT ; Display prompt ; LDAA #0 ; A=0 to read text line (SEND_TXT returns A=0) ; LDX #COM_BLK ; Point to control block (SEND_TXT returns 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 "9S12>*" ; 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: LDAA #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 LDAA 1,X+ ; Step X past error number JSR SEND_TXT ; Print text at X JSR OSNEWL BRA CLILOOP2 ; Return to command prompt MEM_INIT: LDAA #0 STAA ESCFLG LDAA $FF81 STAA DMA_TYPE COM_INIT: LDD DEFBOT ; Initialise bottom of user memory STD MEMBOT LDD DEFTOP ; Initialise top of user memory STD MEMTOP 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 VECBASE < ROMSTART LDX #TRAP_HANDLE STX VECBASE+$F8 LDD #SWI_HANDLE ; Get default SWI address = error handler STD VECBASE+$F6 ; Point SWI vector to error handler ENDIF LDX #BRKV LDY #ESCFLG ; Return X=>BRKV, Y=>ESCFLG, NE=little-endian FS calls IF BIGENDIAN CLRB CLRA ; Return EQ=big-endian filing system calls ENDIF PREND: LREAL: RTS ; FLEX/OS-9 BIOS code ; =================== INECHO: JSR OSRDCH JMP OSWRCH PRTST: JSR OSNEWL PRDAT: LDAA 1,X+ ; Get character CMPA #4 ; EOT character? BEQ PREND ; End printing JSR OSWRCH ; Print character BRA PRDAT ; Loop to next KBDTST: PSHA PSHX PSHY LDA #$80 LDX #$FFFF ; Should check ADVAL(-2) if Serial is current input stream TFR X,Y JSR OSBYTE ; ADVAL(-1) - keyboard input buffer CPX #0 ; Set Z/NZ from X PULY PULX PULA RTS ; ***************** ; Printout Routines ; ***************** ; Print X as 4-digit hex ; ====================== ; API allows A to be corrupted ; All other registers preserved (v1.04 including B) PR_2HEX: PSHB TFR X,D ; Copy X to D, so copy b8-b15 to A BSR PR_HEX ; Print it TFR B,A ; Copy other b0-b7 to A PULB ; Restore B ; Fall through into PR_HEX ; Print A as 2-digit hex ; ====================== ; API allows A to be corrupted ; All other registers preserved PR_HEX: PSHA ; Standard hex-print code LSRA LSRA LSRA LSRA BSR PR_NYBBLE PULA ; 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 ; PR_TEXT: IF VERSION*16+BUILD>$1050 PSHX ; Save X LDX [2,SP] ; Get stacked PC to X BSR SEND_TXT ; Print text STX [2,SP] ; Update stacked PC PULX ; Restore X ELSE PULX ; Pop PC to X BSR SEND_TXT ; Print text PSHX ; Push updated X ENDIF SEND_END: RTS ; And return to code ; 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 SEND_TXT: LDA 1,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: IF LOCAL_GO LDY #0 ; Clear hex accumulator RD_HEXLP: LDA 1,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 ELSE RTS ENDIF IF LOCAL_CMDS ; 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 1,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 1,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 RTS ENDIF ; ********************** ; 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: PSHB PSHX PSHY ; Save everything on caller's stack, except A and CC ; IF WORKSP > ROMSTART ; CMPS #ROMSTART ; Check where the stack is ; BCC CLI_SYSSTK ; We're already using internal stack ; STS STKSAVE-2 ; As a *command may result in data transfer, that ; LDS #CLISTK ; data may end up overwriting stack in user memory, ;CLI_SYSSTK: ; so use a temporary stack to do OS_CLI. If OS_CLI ; ENDIF BSR CLI_GO ; ends up jumping to a new process, new stack will ; LDS #0 ; be set up by that new process. ;STKSAVE: PULY PULX PULB ; Restore everything and return contents of A. RTS ; Local *commands ; --------------- CLI_TABLE: IF LOCAL_GO FCC "GO" FCB $80 ENDIF IF LOCAL_HELP FCC "HELP" FCB $81 ENDIF IF LOCAL_RUN FCC "RUN" FCB $82 ENDIF IF LOCAL_CMDS FCB 0 ; OSCLI - Check for local commands ; -------------------------------- ; On entry: X=>command string ; CLI_GO: CLI_LP1: BSR SKIPSPC ; Skip leading spaces LDA 1,X+ CMPA #'*' BEQ CLI_LP1 ; Skip leading '*'s LEAX -1,X PSHX ; Save start of command text IF LOCAL_RUN CMPA #'/' ; Skip to parameters for '*/name' and '*/ name' BEQ CMD_SLASH ENDIF BSR SKIPWORD ; Step past command STX LPTR ; Point LPTR to command parameters LDY #CLI_TABLE ; Point to command table CLI_LOOP0: LDX ,SP ; 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 1,X+ ; Get character from command line ANDA #$DF ; Force to upper case CMPA 1,Y+ ; Compare with table entry BEQ CLI_LOOP2 ; Characters match, check next LDA 1,-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 1,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 1,Y+ ; Search for command token BPL CLI_DOT ; Step to end of table entry BRA CLI_MATCH2 CLI_MATCH: LDB 1,-X ; Get current character CMPB #'!' BCC CLI_TUBE ; Command line longer than table entry CLI_MATCH2: CMPA #$81 BEQ CMD_HELP BCC CMD_RUN ENDIF ; *GO ; parameters ; ----------------------- IF LOCAL_GO 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 JSR 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 PULX ; Drop old line pointer TFR Y,X ; X=entry address SEC ; Set Carry to indicate OSCLI BRA EXECUTE2 ENDIF ; *HELP () ; --------------- IF LOCAL_HELP CMD_HELP: ; JSR SKIPSPC ; BCS CMD_HELP2 ; *HELP - display help ; CMPA #'.' ; *HELP . - display help ; BNE CLI_TUBE ; Otherwise, pass stright to host CMD_HELP2: JSR PR_HELP ; Print banner as help message ; Fall through to CLI_TUBE, harmlessly updating LPTR ENDIF ; *RUN - updates LPTR to point to correct parameters ; -------------------------------------------------------------------------- IF LOCAL_RUN CMD_SLASH: LEAX 1,X ; Step past '/' CMD_RUN: JSR SKIPSPC JSR SKIPWORD ; Step past *RUN filename STX LPTR ; Update LPTR to parameters ENDIF ; OSCLI - Send command line to host ; ================================= ; On entry: X=>command string ; On exit: All registers possibly corrupted ; Registers should be preserved by calling code ; ; Tube data: $02 string $0D -- $7F or $80 ; IF LOCAL_CMDS CLI_TUBE: PULX ; Get command string back ELSE CLI_GO: ENDIF LDAA #2 JSR SendCommand ; Send command $02 = OSCLI JSR SEND_STR ; Send string at X CLI_WAIT1: SEC ; Clear Carry to indicate OSCLI (SEND_STR returns CC) CLI_WAIT: JSR WaitByte ; Wait for result via Tube R2 (preserves Cy) 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=enteriung 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 6812 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: PSHC ; Save RESET/OSCLI flag PSHX ; Save address on stack LDB 7,X ; Get (C) offset ABX ; X=>(C) string LDY #EXEC_CHK+4 ; Y=>check string LDB #4 ; 4 bytes to check EXEC_LP: LDA 1,X+ ; Get byte from header CMPA -1,-Y ; Compare with check string BNE EXEC_JUMP ; No match, enter as raw code DECB BNE EXEC_LP ; Loop to check all four bytes PULY ; Get entry address back 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 ; NB: 3=6809, need a further check for 6812 BNE ERR_NOT6812 IF 0 LDA ,Y ; Get entry opcode. Check if 6812 BRA or JMP to ANDA #$D9 ; filter out 68000 code with ROMTYPE=3 BNE ERR_NOT6812; Error if not 6812 BRA or JMP ENDIF LEAX -4,X STX FAULT ; Point FAULT to (C) string (should be version string) PULA ; Get RESET/OSCLI flag to A LDX PROGRAM PSHX ; Save current PROGRAM LDX MEMTOP PSHX ; Save current MEMTOP CPY #$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 RORA ; Move RESET/OSCLI flag back into Carry LDA #1 ; A=1 for code with a header JSR ,Y ; Call program execution address PULY STY MEMTOP ; Restore previous MEMTOP if code returns PULY STY PROGRAM ; Restore previous PROGRAM NULL: ; Null routines CLI_DONE: RTS ; Return EXEC_JUMP: LDX LPTR ; X=>command line PULY ; Get entry address back CLRA ; A=0 for raw code PULC ; Get RESET/OSCLI flag JMP ,Y ; Enter raw code EXEC_CHK: FCC ")C(" ; Deliberately backwards so doesn't match itself FCB 0 ERR_NOTCODE: ; PULC ; 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_NOT6812: PULC ; 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 6812 code" FCB 0 ; OSRDCH - Wait for character from input stream ; ============================================= ; On exit: A=char, Cy=carry ; RDCH: RDCH_RAW: JSR WaitByte ; Wait for character PSHA ; Save character LDAA >ESCFLG ; Get Escape flag ASLA ; Copy b7 of ESCFLG to Carry PULA ; 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: LDAA #0 JSR SendCommand ; Send command $00 - OSRDCH WAIT_CHAR: JSR WaitByte ; Get returned byte ASLA ; Move b7 into 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: PSHD TSTA BMI BYTE_HI LDAA #4 JSR SendCommand ; Send command $04 - short BYTE TFR X,D ; B=X JSR SendByteB ; Send second parameter from B PULD PSHD JSR SendByte ; Send first parameter JSR WaitByte ; Wait for response TFR A,B ; Move result to low byte LDAA #0 ; Ensure AB is only 8-bit value TFR D,X PULD RTS ; 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: PSHD LDAA #$18 ; Set command $18 - FSC BRA BYTE_CMD ; Jump to send command BYTE_WAIT: LDX #BANNER ; Point LPTR to STX LPTR LBRA CLI_WAIT1 ; Wait for program start ; OSBYTE >$7F ; ----------- BYTE_HI: CMPA #$82 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: LDAA #6 ; Set command $06 - long BYTE 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 PULD 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 PSHD JSR WaitByte ; Wait for response ASLA ; Move b7 into carry PSHC ; Save flags JSR WaitByte ; Wait for response TFR A,B ; Move result to low byte LDAA #0 ; 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 Y*256+X PULC ; Get flags back PULD RTS 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 PULD RTS ; 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 ; PSHD ; Save AB PSHY ; Save Y PSHX ; Save X=>control block TFR A,B ; B=function LDAA #$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 WORD_TXLO: LDAA #$10 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: LDAA ,X ; Get send block length from control block or table WORD_SEND1: TFR A,B ; B=control block length LDX [0,SP] ; 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: LDAB [2,SP] ; Get OSWORD function BMI WORD_RECV ; Jump to send control block with function>&7F WORD_RXLO: LDAA #$10 CMPB #$15 ; Check OSWORD function BCC WORD_RECV1 ; Use 16 bytes for OSWORD &15 to &7F LDX #WORDRX-2 ; X=>receive length table for OSWORD 1 to &14 ABX ; X=X+B, X=>receive length WORD_RECV: LDAA 1,X ; Get receive length from table or control block WORD_RECV1: TFR A,B ; B=send block length PULX ; 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: PULY ; Restore Y PULD ; Restore AB RTS ; 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 PSHB 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 ; Sent $00 as B=0 from SEND_BLK JSR WaitByte ; Wait for response ASLA ; Move b7 into Carry BCS RD_DONE LDX [-2,X] ; Get text pointer from control block ; CLRB ; B=number received - B already zero from SEND_BLK RD_STR: JSR WaitByte ; Wait for byte from Tube STA 1,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 ; CLC ; Clear Carry (already CC from CMPA #13) RD_DONE: LDA #0 ; Clear A without clearing Carry TFR D,Y ; Y=length PULB ; 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: PSHY ; Save handle PSHD ; Save function and B LDAA #$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 PULA ; Get action back JSR SendByte ; Send action JSR WaitByte ; Wait for returned result PSHA ; 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 PULD ; Get result and B back PULY ; Get original handle back RTS ; 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: PSHB LDAA #$0E JSR SendCommand ; Send command $0E - OSBGET JSR SendByteY ; Send handle PULB 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: PSHD ; Save byte LDAA #$10 JSR SendCommand ; Send command $10 - OSBPUT JSR SendByteY ; Send handle PULD ; Get A and B back PSHD JSR SendByte ; Send byte to Tube JSR WaitByte ; Wait for acknowledgement PULD ; Restore A RTS ; 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: PSHD ; Save A LDAA #$12 JSR SendCommand ; Send command $12 - OSFIND PULD ; 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: PSHB JSR SendByteY ; Send handle JSR WaitByte ; Wait for acknowledgement PULB LDAA #0 ; Zero A RTS ; 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 ; All other MOS calls use little-endian addresses to match Host ; ; Tube data: $14 block string function -- result block ; FILE: PSHY ; Save Y PSHX ; Save X PSHD ; Save function and B LDAA #$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 [-2,X] ; Get big-endian filename pointer JSR SEND_STR ; Send filename string PULA ; Get function back JSR SendByte ; Send function JSR WaitByte ; Wait for returned result ; ; Data transfer via interupts may happen while waiting PULB ; Get saved B from stack PULX ; Get control block pointer back PSHD ; Save result and B 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 PULD ; Get result and B back PULY ; Get Y back RTS ; 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: PSHY ; Save Y PSHD ; Save function and B LDAA #$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 LDAA 1,-X ; Decrement X, get handle JSR SendByte ; Send 0th byte of control block ELSE LDB #13 JSR SEND_BLOCK ; Send 13-byte control block ENDIF PULA ; 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 STAA 1,-X ; Decrement X, store cycle ELSE LDB #13 JSR WAIT_BLOCK ; Wait for 13-byte control block ENDIF PULB ; Get B back PULY ; Get Y back JMP WAIT_CHAR ; Get Carry and result byte ; ***************** ; Tube I/O routines ; ***************** ; Send cr-string at X to Tube ; =========================== SEND_STR: LDAA 1,X+ ; Get byte from X, increment X JSR SendByte ; Send byte CMPA #13 ; Test current character BNE SEND_STR ; Loop until sent RTS ; Send block at X to Tube, B=block length ; ======================================= ; Returns X=preserved, B=0 SEND_BLOCK: ABX ; X=X+B, X points to end of block+1 SEND_BLKLP: LDAA 1,-X ; Decrement X, get byte from X JSR SendByte ; Send byte DECB ; Decrement count of bytes to send BNE SEND_BLKLP ; Loop until all bytes sent RTS ; Wait for block at X from Tube, B=block length ; ============================================= ; Returns X=preserved, B=0 WAIT_BLOCK: ABX ; X=X+B, X points to end of block+1 WAIT_BLKLP: JSR WaitByte ; Get byte STAA 1,-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: LDAA 1,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 1,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: PSHA ; Save byte SEND_WAIT: LDAA >TxSTATUS ANDA #TxRDY ; Flag is 1 when TXDR is empty BEQ SEND_WAIT ; Loop until data can be sent PULA ; Get byte back STAA >TxDATA ; Send data RTS ; Send an escaped command ; ======================= SendCommand: PSHA ; Save command byte ; Should flush input here LDAA #esc JSR SEND_DATA ; Send esc prefix PULA ORAA >DMA_TYPE ; Add personality bits BRA SEND_DATA ; Send command byte ; Check if a byte is waiting, and read it if there ; ================================================ READ_BYTE: LDAA >RxSTATUS ANDA #RxRDY ; Flag is 1 when RXDR is full BEQ WAIT_EXIT ; Nothing pending, return ; ; Continue into WaitByte ; Wait for a byte, decoding escaped data ; ====================================== ; On exit, A =byte ; F =preserved ; WaitByte: PSHC ; Save flags WaitByteLP: JSR WAIT_DATA BNE WaitByteOK ; Not esc, return JSR WAIT_DATA ; Get another byte BEQ WaitByteOK ; esc,esc, return PSHX PSHY PSHB ; Push all registers JSR WAIT_COMMAND ; Decode escaped command PULB PULY PULX ; Pop all registers BRA WaitByteLP WaitByteOK: PULC ; Pop flags WAIT_EXIT: RTS ; Wait for data ; ============= ; On exit, A =byte ; F =Z byte=esc, NZ byte<>esc ; WAIT_DATA: LDAA >RxSTATUS ANDA #RxRDY BEQ WAIT_DATA ; Loop until data present LDAA >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. ; 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 STAA 1,X+ ; Store BRK JSR WaitByte ; Get error number STAA 1,X+ ; Store error number FIRQ_R4LP: JSR WaitByte ; Wait for byte of error string STAA 1,X+ ; Store in error buffer BNE FIRQ_R4LP ; Loop until terminating $00 received ;;;6502 Client pauses at this point LDAA ERRBLK+1 ORAA ERRBLK+2 ; Check for error 0,"" LBEQ STARTUP ; Reset all vectors and restart client LDX #ERRBLK+1 ; Point to error block after SWI opcode PSHX ; Push error pointer onto stack JMP ERRJMP ; 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 LSRA RORA ; Move b0 into b7, b6=0 STAA >ESCFLG ; Store Escape flag RTS ; esc,&9x - Event ; =============== WAIT_EVENT: JSR WaitByte ; Get event Y parameter TFR A,B LDAA #0 TFR D,Y JSR WaitByte ; Get event X parameter TFR A,B LDAA #0 TFR D,X JSR WaitByte ; Get event A parameter JMP [EVENTV,PC] ; Dispatch event ; esc,&Ax - Reserved ; ================== WAIT_END: CMPA #$B0 BCS WAIT_EXIT ; &Ax - Return to WaitByte ; esc,&Bx - End transfer ; ====================== LEAS 10,SP ; Drop data from stack to fall ; ; out of WaitSave/WaitLoad loop ; WAIT_EXIT2: RTS ; Return to WaitByte ; esc,&C0+ - Start transfer ; ========================= WAIT_START: PSHA ; Save transfer type JSR WaitByte ; Get data address MSB JSR WaitByte 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 PULA ; Get transfer type back CMPA #$D0 BLT WAIT_ADDR ; &Cx - set address CMPA #$E0 BLT WAIT_CODE ; &Dx - enter code CMPA #$F0 BCC WAIT_SAVE ; &Fx - save data WAIT_LOAD: JSR WaitByte ; &Ex - load data STAA 1,X+ ; Get byte, store it BRA WAIT_LOAD ; Loop until terminated WAIT_SAVE: LDAA 1,X+ ; Get byte JSR SendByte ; Send it JSR READ_BYTE ; 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 ; SWI - Generate an error ; ======================= SWI_HANDLE: LEAS 7,SP ; Step past stack contents ERR_HANDLE: PULX ; Pop PC to X STX FAULT ; Save pointer to last error ANDCC #$00 ; Clear all flags, enable interupts JMP [BRKV,PC] ; Jump to current error handler ; TRAP - Trap handler ; =================== ; TRAP $FF -> OSQUIT -> $FF0C ; TRAP $FE -> OSCLI -> $FF10 ; TRAP $FD -> OSBYTE -> $FF14 ; TRAP $FC -> OSWORD -> $FF18 ; TRAP $FB -> OSWRCH -> $FF1C ; TRAP $FA -> OSRDCH -> $FF20 ; TRAP $F9 -> OSFILE -> $FF24 ; TRAP $F8 -> OSARGS -> $FF28 ; TRAP $F7 -> OSBGET -> $FF2C ; TRAP $F6 -> OSBPUT -> $FF30 ; TRAP $F5 -> OSGBPB -> $FF34 ; TRAP $F4 -> OSFIND -> $FF38 ; TRAP $F3 -> MISC ; TRAP $F2 -> SYS ; TRAP $F1 -> RDINF ; TRAP $F0 -> WRINF ; SP -> CCR, AB, X, Y, RET TRAP_HANDLE: LDX [7,SP] ; Get return address LDAB [-1,X] ; Get TRAP number CMPB #$F4 BCS RTI ; <$F4, just return ; Should pass on to a further vector EORB #$FF ASLB ASLB ; Multiply TRAP number by 4 ADDB #$0C ; A is now low byte of dispatch jump LDAA #$FF ; AB is now dispatch address JSR TRAP_GO ; Call the dispatch address STD [1,SP] ; Update stacked registers with return values STX [3,SP] STY [5,SP] PULA ; Drop stacked CCR PSHC ; Replace it with current CCR ; todo: TRAP should call MOS entry and catch any error, returning V=0 if ok, ; returning 1, D=>error if error occured. cf PDP-11, 32016 and ARM. ; Null interupt routines ; ====================== RTI: RTI TRAP_GO: ; SP -> RET, CCR, AB, X, Y, RET PSHD ; SP -> ADDR, RET, CCR, AB, X, Y, RET LDD [5,SP] ; Fetch entry AB from stack RTS ; Jump to dispatch block via stack ; END of movable ROM part ; ======================= IF VECBASE > ROMSTART ; Hardware vectors in ROM ; ======================= ORG $FE00 FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,RTI,RTI,RTI,RTI,RTI FDB RTI,RTI,RTI,SWI_HANDLE,TRAP_HANDLE,RTI,RTI,RTI ENDIF ; Hardware vectors: ; VECBASE+$F2 IRQ ; VECBASE+$F4 XIRQ ; VECBASE+$F6 SWI ; VECBASE+$F8 TRAP ; VECBASE+$FA COP ; VECBASE+$FC CLOCK ; VECBASE+$FE RESET ; Vector dispatch ; =============== ORG $FF00 XEVENT: JMP [EVENTV,PC] ; $FF00 via $1020 XBRK: JMP [BRKV,PC] ; $FF04 via $1022 Xxx2: JMP [xxx2V,PC] ; $FF08 via $1024 XQUIT: JMP [QUITV,PC] ; $FF0C via $1026 TRAP $FF OS_QUIT XCLI: JMP [CLIV,PC] ; $FF10 via $1028 TRAP $FE OS_CLI XBYTE: JMP [BYTEV,PC] ; $FF14 via $102A TRAP $FD OS_BYTE XWORD: JMP [WORDV,PC] ; $FF18 via $102C TRAP $FC OS_WORD XWRCH: JMP [WRCHV,PC] ; $FF1C via $102E TRAP $FB OS_WRCH XRDCH: JMP [RDCHV,PC] ; $FF20 via $1030 TRAP $FA OS_RDCH XFILE: JMP [FILEV,PC] ; $FF24 via $1032 TRAP $F9 OS_FILE XARGS: JMP [ARGSV,PC] ; $FF28 via $1034 TRAP $F8 OS_ARGS XBGet: JMP [BGETV,PC] ; $FF2C via $1036 TRAP $F7 OS_BGET XBPut: JMP [BPUTV,PC] ; $FF30 via $1038 TRAP $F6 OS_BPUT XGBPB: JMP [GBPBV,PC] ; $FF34 via $103A TRAP $F5 OS_GBPB XFIND: JMP [FINDV,PC] ; $FF38 via $103C TRAP $F4 OS_FIND JMP [FINDV+2,PC] ; $FF3C via $103E ; Default software vectors ; ------------------------ ORG $FF40 VECTORS: FDB NULL ; $FF40 -> EVENTV FDB COM_ERR ; $FF42 -> BRKV FDB NULL ; $FF44 -> xxx2V FDB CLICOM ; $FF46 -> QUITV FDB CLI ; $FF48 -> CLIV FDB BYTE ; $FF4A -> BYTEV FDB WORD ; $FF4C -> WORDV FDB WRCH ; $FF4E -> WRCHV FDB RDCH ; $FF50 -> RDCHV FDB FILE ; $FF52 -> FILEV FDB ARGS ; $FF54 -> ARGSV FDB BGet ; $FF56 -> BGETV FDB BPut ; $FF58 -> BPUTV FDB GBPB ; $FF5A -> GBPBV FDB FIND ; $FF5C -> FINDV FDB WARMS ; $FF5E -> PROGRAM ; Tube system defaults 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 0 ; $FF8C ; Tube execution address FDB 0 ; $FF8E ; Tube transfer address FDB WARMS ; $FF90 ; Program entry address FDB 0 ; $FF92 ; 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 $06 ; &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_HEX ; &FFAA ; Print A as 8-bit hex PR2HEX: JMP >PR_2HEX ; &FFAD ; Print X as 16-bit hex USERINT: JMP >NULL ; &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 default error handler DISKRST: JMP >NULL ; &FFC2 ; DiskReset JMP >NULL ; &FFC5 ; Deprecated JMP >NULL ; &FFC8 JMP >FSC ; &FFCB ; MOS file entry points ; --------------------- OSFIND: JMP >XFIND ; $FFCE OSGBPB: JMP >XGBPB ; $FFD1 OSBPUT: JMP >XBPut ; $FFD4 OSBGET: JMP >XBGet ; $FFD7 OSARGS: JMP >XARGS ; $FFDA OSFILE: JMP >XFILE ; $FFDD ; MOS character entry points ; -------------------------- 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 system entry points ; ----------------------- OSWORD: JMP >XWORD ; $FFF1 OSBYTE: JMP >XBYTE ; $FFF4 OS_CLI: JMP >XCLI ; $FFF7 ; Standard RESET locations (These don't remap even if Vector Base changed) ; ======================================================================== L_FFFA: FDB RTI ; $FFFA - COP failure reset L_FFFC: FDB RTI ; $FFFC - COP monitor fail reset L_FFFE: FDB RESET ; $FFFE - Reset vector