000000 ; PDP11 Tube Client Code 000000 ; ====================== 000000 ; Copyright (C)1989,2008,2014,2015,2017 J.G.Harston 000000 ; 000000 ; NB: *BUG* All routines that return a byte in R0 actually sign extend the returned value, so 000000 ; instead of &00-&FF being returned, they return &FF80-&007F. This affects OSRDCH, OSBGET, 000000 ; OSARGS, OSFILE, OSFIND, OSGBPB. As long as the caller does any checks with CMPB and any stores 000000 ; with MOVB this will be fine. BASIC returns from OSRDCH, OSBGET, OSFIND via "return8bit" so the 000000 ; bug is not noticed. 000000 ; The sign extension also affects OSWORD. If a A>&7F call is made with the send or receive length 000000 ; set to &80, instead fo transfering &80 bytes an attempt is made to transfer &FF80 bytes, which 000000 ; fails. This has been fixed for OSWORD &01-&14 which use a table look-up, but user constructed 000000 ; calls will fail if passed an explicit &80 length byte. 000000 ; 000000 ; NB *BUG* OSBYTE never returns Carry set due to SWAB clearing the carry. This means that calls 000000 ; to INKEY and buffer read/write/test do not return any information in the Carry flag. BASIC 000000 ; tests returned R2 value, so the bug is not noticed. 000000 ; 000000 ; v0.10 11-Mar-1989 JGH: Initial version, untested proof of concept. 000000 ; v0.11 09-Sep-2005 JGH: Altered some labels to assemble from BASIC 000000 ; v0.12 12-Jul-2008 JGH: Interupt handler data transfers. 000000 ; v0.13 15-Jul-2008 JGH: Added EMT handler. 000000 ; v0.14 17-Jul-2008 JGH: Checks executed code for ROM and Unix headers. 000000 ; v0.15 06-Dec-2008 JGH: Rearranged MOS interface code around, changed some labels. 000000 ; v0.16 25-Dec-2014 JGH: Optimised some common calls to SEND_BYTE. 000000 ; v0.17 01-Jan-2015 JGH: Addresses in control blocks allowed to be unaligned. 000000 ; v0.18 09-Aug-2015 DMB: Bugfix: CLICOM calling OSCLI with wrong register, RDLINE has (R1),R1 wrong way 000000 ; around, IRQHandler needs IRQs disabled. 000000 ; JGH: GBPB from channel 0 and EXECUTE uses FETCHBYTE. EXECUTE checks RESET/OSCLI flag on 000000 ; error, saves/restores PROG. IRQ_DATA stores ADDR with MOVBs. 000000 ; DMB: EMTVEC corrected. Indirect JMP/JSRs may be wrong, testing with @vec instead of @#vec 000000 ; and JSR PC,@(SP)+ in EMT dispatcher. 000000 ; JGH: Uses OSASCI for SEND_TXT, OSBYTE returns 16-bit results, MEMBOT and MEMTOP are 000000 ; variables. 000000 ; DMB: GetADDR in IRQ_DATA needs to use @#ADDR+n instead of @ADDR+n. MOVB used to read 000000 ; errors. 000000 ; v0.19 10-Aug-2015 DMB: Bugfix: EMT dispatcher index into vectors, unbalanced stack in Release. TST #n,R in 000000 ; EXECUTE should be BIT #n,R. A few ADD/SUB #n wrong way around. 000000 ; JGH: Code with a Unix header copied to MEMBOT instead of &0000 as no MMU in this 000000 ; implementation. 000000 ; Bugs: EXECUTE doesn't prepare valid stack frame for code with Unix header. 000000 ; 11-Aug-2015 DMB: Bugfix, OSWORD <>0 wasn't fetching block lengths correctly. EMT 14 wrote wrong 000000 ; 'address' if not passed 'handler'. ESCHAND should use @ESCADDR. HANDLERS equate had 000000 ; wrong arithmetic. 000000 ; JGH: INIT_ENV correctly initialises EMTs 0-15 and nulls 16-255 instead of setting 16-31 to 000000 ; garbage and missing 240-255 completely. EQUs to allow more handlers to be added easily. 000000 ; 11-Aug-2015 JGH: Crunched some blocks of common code. 000000 ; 11-Aug-2015 JGH: Crunched NMI data transfer code. 000000 ; 12-Aug-2015 JGH: STARTUP uses internal stack. MEMBOT and MEMTOP fetched from variables. All unused 000000 ; hardware vectors set to NULLIRQ. 000000 ; 13-Aug-2015 JGH: Tidied up and optimised EXECUTE, OSCLI saves all registers, uses internal stack. 000000 ; EMT14 optimised. 000000 ; 16-Aug-2015 JGH: TRAP jumps to error, EMT 15 used instead of MKERR. 000000 ; 16-Aug-2015 DMB: Bugfix: INIT_ENV was looping forever, wrong number of WORKSPACE words initialised 000000 ; starting from wrong workspace offset, TRAP vector was wrong. Wrong address mode in 000000 ; MOV @#COM_BLK,R0 in CLICOM. 000000 ; 16-Aug-2015 DMB: All NMIs were failing to update ADDR by returning via wrong exit. 000000 ; v0.20 17-Aug-2015 JGH: EXECUTE possibly not working when entering BASIC. 000000 ; 18-Aug-2015 DMB: Bugfix: EMT handler lost R0, EMT14 restored ERRV even after attempts to change it. 000000 ; Optimised EMT15 to free up space to fit EMT14 bugfix. 000000 ; 18-Aug-2015 DMB: Bugfix: EMT15 optimisation used EMTHANDLER ERRV instead of mainline ERRV. 000000 ; v0.21 19-Aug-2015 JGH: Incremented to version 0.21. Added conditional switch to remove BPUT/BGET/GBPB to 000000 ; channel 0, saves about 100 bytes if removed. 000000 ; v0.22 22-Aug-2015 JGH: Optimised checking b6 of status registers, space to implement EMT 13,3. 000000 ; 09-Sep-2015 JGH: Code is entered with default environment, R0 contents not lost. INIT_* routine. 000000 ; 28-Sep-2015 JGH: Bugfixes: EXECUTE doesn't make raw code the current program. EXECUTE no longer loses 000000 ; contents of R0 when calling FETCHWORD. Child program inherits parent environment 000000 ; until child requests new environment, so transient children pass errors/exits/etc to 000000 ; caller. 000000 ; v0.23 27-Oct-2015 JGH: MEMTOP moved if executed code is in high memory. LPTR set to command parameters, 000000 ; OSBYTE 142 sets LPTR=>. 000000 ; v0.25 01-Nov-2015 JGH: Rolled over to version 0.25. 000000 ; v0.26 17-Jul-2017 JGH: Bugfix: By reference with 6502 Client, data transfer 0/1 (1-byte transfers) should 000000 ; not change ADDR, as changing ADDR means peeking Client memory during startup causes 000000 ; a Soft Break to jump to the peeked memory instead of the last PROG. 000000 ; 17-Jul-2017 JGH: Once code with a Unix header has been moved into place and entered it no longer has 000000 ; a header, so on the next Soft Break is re-entered but not made the current program as 000000 ; it does not have a header, so on the next Soft Break after that it is not re-entered. 000000 ; EMT 13 to create a new environment makes the caller the current program. 000000 ; 24-Jul-2017 JGH: NMI data transfer address now a seperate variable so able to rationalise and tidy 000000 ; ADDRHI/ADDR/TRANS/PROG variables. 000000 ; 05-Aug-2017 JGH: EXECUTE sets TRANS to entered code even if it has no header - as when re-entering 000000 ; code with a Unix header after Soft-Reset when it will have been moved and the header 000000 ; removed. 000000 ; v0.27 06-Aug-2017 JGH: Rolled over to version 0.27. 000000 ; 11-Aug-2017 JGH: '*RUN filename params' and '*/ filename params' skips filename to set LPTR. 000000 ; Rolled over to version 0.28. 000000 ; v0.28 24-May-2022 JGH: Supervisor claims handlers when entered as default EXIT handler and from Soft Break. 000000 ; 06-Jun-2022 JGH: Updated INIT_ENV code 000000 ; 08-Jun-2022 JGH: STARTUP leaves PROG set to default if PROG=0 and MEMBOT>0. 000000 ; v0.30 12-Aug-2022 JGH: Rolled over to version 0.30. 000000 VERSION: EQU &0030 000000 BUILD: EQU 0 000000 ; 000000 ; *BUG* If OSWORD block length is &80 it declares &80 but tries to send &FF80 bytes due to MOVB sign extending. 000000 WORDFIX: EQU 1 ; Patch OSWORD table to avoid &80 - won't stop WORD>&7F with explicit len=&80. 000000 #if WORDFIX 000000 VERSION: EQU &0030 000000 BUILD: EQU 1 000000 #endif 000000 ; v0.30b 29-Apr-2024 JGH: OSWORD dispatch uses byte counters for block lengths. 000000 ; v0.30c 29-Apr-2024 JGH: WAITBYTE clamps to 8-bit value, associated changes to calling code. 000000 BUILD: EQU 3 000000 ; 000000 ; 000000 JOINERR: EQU 1 ; Combine EXECUTE error messages 000000 NOCHNZERO: EQU 1 ; Remove bput/bget#0 -> wrch/rdch, saves about 100 bytes 000000 ; 000000 ; 000000 ; This code may be freely reused. 000000 ; 000000 ; Acknowledgements to: 000000 ; David Banks: Plenty of testing on the Matchbox CoPro "real" hardware on the MatchBox CoPro. 000000 ; 000000 ; Notes: 000000 ; This code assumes a single memory space and assumes that code can access I/O devices, low memory 000000 ; vectors and user memory with ordinary MOV instructions. This will work with no seperate 000000 ; Kernal/User space, or with code never running in User mode. Running code can chose to switch to 000000 ; User mode. 000000 000000 ; Memory map: 000000 ; +-----------------------+ 0000 000000 ; | Hardware vectors, etc | 000000 ; MEMBOT +-----------------------+ 0100 000000 ; | | 000000 ; | | 000000 ; MEMTOP | ^^^User Stack^^^ | 000000 ; WORKSP +-----------------------+ F500 000000 ; | ERRBLK--> | 000000 ; | CLIBUF--> | 000000 ; +-----------------------+ F5B8 000000 ; | <--CLISTK | 000000 ; | <--ERRSTK | 000000 ; HANDLERS +-----------------------+ 000000 ; | EXITV EXITADDR | F5D8 000000 ; | ESCV ESCADDR | F5DC 000000 ; | ERRV ERRADDR | F5E0 000000 ; | EVENTV EVENTADDR | F5E4 000000 ; | IRQV IRQADDR | F5E8 000000 ; | EMTV EMTADDR | F5EC 000000 ; | LPTR ADDRHI | F5F0 000000 ; | MEMBOT MEMTOP | F5F4 000000 ; | ADDR TRANS | F5F8 000000 ; | PROG | F5FC 000000 ; | unused byte | F5FE 000000 ; | ESCFLG | F5FF 000000 ; EMTTABLE +-----------------------+ 000000 ; | EMT 0 code address | F600 000000 ; | EMT 1 code address | F602 000000 ; | EMT 2 code address | F604 000000 ; | EMT 3 code address | F606 000000 ; | EMT 4 code address | F608 000000 ; | ... | 000000 ; | EMT 255 code address | F7FE 000000 ; START +-----------------------+ F800 000000 ; | Tube client code | 000000 ; +-----------------------+ FFF0 000000 ; | Tube I/O registers | 000000 ; +-----------------------+ FFFF 000000 000000 ; Some naming conventions 000000 ; xxxxVEC - hardware vectors, eg EMTVEC, NMIVEC, etc 000000 ; xxxxV - software vectors, eg ESCV, ERRV, etc. 000000 ; xxxx or xxxHAND - handler for xxxx, eg ESCHAND, ERRHAND, etc. 000000 ; OSxxx or _xxx - routines handled by xxx, eg OSFILE handled by _FILE 000000 000000 ; Some register conventions 000000 ; R0 - usually holds bytes/characters 000000 ; R1 - usually points to strings or data blocks 000000 000000 000000 ; PDP11 hardware addresses 000000 ; ======================== 000000 STACKVEC: EQU &04 ; Stack too low 000000 EMTVEC: EQU &18 ; Vector called by EMT call 000000 TRAPVEC: EQU &1C ; Vector called by TRAP call DMB 000000 000000 000000 ; Tube client system configuration tweekables 000000 ; =========================================== 000000 START: EQU &F800 ; Start of code 000000 TUBEIO: EQU &FFF0 ; Base of Tube I/O registers 000000 NMIVEC: EQU &80 ; Vector called by Tube NMIs with priority 7 000000 IRQVEC: EQU &84 ; Vector called by Tube IRQs with priority 6 000000 000000 EMTMAX: EQU 256 ; Number of EMTs 000000 EMTTABLE: EQU START-EMTMAX*2 ; EMT dispatch table 000000 WORKSP: EQU EMTTABLE-256 ; 256 bytes for buffers, etc 000000 RAMSTART: EQU &0100 ; Lowest available user memory address 000000 RAMEND: EQU WORKSP ; Highest available user memory address 000000 000000 000000 ; Internal buffers, etc 000000 ; ===================== 000000 ERRBLK: EQU WORKSP ; Buffer to store host error block 000000 CLIBUF: EQU WORKSP ; Space to enter command line from CLI prompt 000000 CLIEND: EQU HANDLERS-32 000000 CLISTK: EQU HANDLERS ; Internal stack for CLI commands 000000 ; as main memory may be overwritten 000000 ERRSTK: EQU HANDLERS ; Internal stack for host errors 000000 000000 WORKSPMAX: EQU 16 ; 16 bytes of general workspace 000000 HANDLEMAX: EQU 6 ; 6 environment handlers 000000 HANDLERS: EQU WORKSP+256-WORKSPMAX-4*HANDLEMAX ; Address of environment handlers 000000 HANDLEWORDS: EQU HANDLEMAX*2 ; Size of handlers in words 000000 WORKSPWORDS: EQU WORKSPMAX/2 ; Size of workspace in words 000000 EXITV: EQU HANDLERS+&00 ; Address of exit handler 000000 EXITADDR: EQU HANDLERS+&02 ; unused - holds client version 000000 ESCV: EQU HANDLERS+&04 ; Address of escape handler 000000 ESCADDR: EQU HANDLERS+&06 ; Address of escape flag 000000 ERRV: EQU HANDLERS+&08 ; Address of error handler 000000 ERRADDR: EQU HANDLERS+&0A ; Address of error buffer 000000 EVENTV: EQU HANDLERS+&0C ; Address of event handler 000000 EVENTADDR: EQU HANDLERS+&0E ; unused 000000 IRQV: EQU HANDLERS+&10 ; Address of unknown IRQ handler 000000 IRQADDR: EQU HANDLERS+&12 ; Data transfer address within IRQ handler 000000 EMTV: EQU HANDLERS+&14 ; Old SP within EMT handler 000000 EMTADDR: EQU HANDLERS+&16 ; Address of EMT dispatch table 000000 000000 LPTR: EQU WORKSP+&F0 ; Pointer to command line 000000 ADDRHI: EQU WORKSP+&F2 ; Memory address high word - OSBYTE &82 000000 MEMBOT: EQU WORKSP+&F4 ; Lowest user memory address - OSBYTE &83 000000 MEMTOP: EQU WORKSP+&F6 ; Highest user memory address - OSBYTE &84 000000 ADDR: EQU WORKSP+&F8 ; Program transfer address 000000 TRANS: EQU WORKSP+&FA ; Execution transfer address 000000 PROG: EQU WORKSP+&FC ; Current program 000000 PROG2: EQU WORKSP+&FE ; Reserved for b16-b23 of address, b23-22=Kernel/User 000000 ESCFLG: EQU WORKSP+&FF ; Escape flag 000000 000000 000000 ; Tube I/O devices 000000 ; ================ 000000 TUBE1S: EQU TUBEIO+0 ; Tube Status 1 000000 TUBE1: EQU TUBEIO+2 ; Tube Data 1 - VDU 000000 TUBE2S: EQU TUBEIO+4 ; Tube Status 2 000000 TUBE2: EQU TUBEIO+6 ; Tube Data 2 - Command 000000 TUBE3S: EQU TUBEIO+8 ; Tube Status 3 000000 TUBE3: EQU TUBEIO+10 ; Tube Data 3 - Data 000000 TUBE4S: EQU TUBEIO+12 ; Tube Status 4 000000 TUBE4: EQU TUBEIO+14 ; Tube Data 4 - Interrupt 000000 000000 000000 ; Code Entry 000000 ; ========== 000000 ; Set up vectors, hardware, memory, etc. 000000 ; Must be entered in Kernel mode. 000000 ; Hardware RESET sets PSW=0, KERNAL+IRQsOn+NoTrap+flags clear 000000 ; Client code expects RESET to place Client code at location zero, first read from high 000000 ; memory pages RAM into memory. 174000 ORG START 174000 000167 000030 JMP STARTUP ; Jump to start up Tube code, paging out ROM from 174004 ; low memory 174004 174004 ; STARTUP 174004 ; ======= 174004 ; Tube data: via R1: string &00 -- via R2: &7F or &80 174004 ; 174004 BANNER: 174004 015 EQUB 13 174005 120 104 120 061 061 040 124 125 102 105 040 066 064 113 040 EQUS "PDP11 TUBE 64K " 174024 060 EQUB ((VERSION >> 8) AND 15)+48 174025 056 EQUS "." 174026 063 EQUB ((VERSION >> 4) AND 15)+48 174027 060 EQUB (VERSION AND 15)+48 174030 #ifdef BUILD 174030 143 EQUB ((96+BUILD)AND(BUILD>0)) OR (32AND(BUILD=0)) 174031 #else 174031 EQUS " " ; Fixed-length string so can be overwritten 174031 #endif 174031 015 EQUB 13 174032 015 EQUB 13 174033 000 EQUB 0 174034 ALIGN 174034 STARTUP: 174034 012706 172730 MOV #CLISTK,SP ; Use internal stack 174040 013746 172774 MOV @#PROG,-(SP) ; Save current program 174044 004767 002572 JSR PC,INIT_ENV ; Set up default handlers 174050 #if RAMSTART=0 174050 MOV (SP)+,@#ADDR ; Restore current program as default entry address 174050 #else 174050 012600 MOV (SP)+,R0 ; Get saved program start 174052 001402 BEQ NOPROG ; If no program, leave default setting 174054 010037 172770 MOV R0,@#ADDR ; Restore current program as default entry address 174060 NOPROG: 174060 #endif 174060 174060 ; explicity enable IRQs here? 174060 012701 174004 MOV #BANNER,R1 ; Point to startup banner 174064 004767 000210 JSR PC,SEND_TXT ; Print it via Tube WRCH protocol 174070 ;CLR R0 ; SEND_TXT returns R0=0 174070 004767 002226 JSR PC,_WRCH ; Send terminating zero byte 174074 000241 CLC ; Clear Carry Flag to indicate Reset 174076 004767 000450 JSR PC,CLI_WAIT ; Wait for and check result byte 174102 ; Fall through to CLICOM if nothing executed 174102 174102 ; Supervisor Command line prompt 174102 ; ============================== 174102 ; Allow user to enter *commands. 174102 ; We deliberately call _WORD and _CLI so if EMT vectors are trashed we can still access 174102 ; MOS and so that errors go via ERRV instead of us having to check returned V flag. 174102 ; We will normally be running in Kernal mode so we can see the system vectors and high 174102 ; memory. 174102 ; 174102 ; We can get to here from: 174102 ; * Startup, with no code executed - default environment and handlers 174102 ; * Startup, code to execute not PDP11 code - default environment and handlers 174102 ; * OSBYTE/OSCLI, error 'Not PDP11 code' - default environment and handlers 174102 ; * EMT OSEXIT - caller's environment and handlers 174102 ; 174102 ; IRQ/NMI will already be set up, assume any changes must remain. 174102 ; EMT dispatch table set up, don't change to allow *commands to change them. 174102 ; 174102 EXITHAND: 174102 CLICOM: 174102 004767 002640 JSR PC,INIT_MEMORY ; Initialise handlers and memory layout 174106 ; 174106 ; Re-enter here after an error 174106 ; If we implement '*GOS', have to think where entry should be 174106 CLISTART: 174106 013706 172766 MOV @#MEMTOP,SP ; Put stack at top of user memory 174112 012737 174102 172774 MOV #CLICOM,@#PROG ; Make Supervisor the current program 174120 ; 174120 ; Re-enter here after successful command 174120 CLILOOP: 174120 012701 174150 MOV #PROMPT,R1 174124 004767 000150 JSR PC,SEND_TXT ; Display prompt 174130 ;CLR R0 ; SEND_TXT returns R0=0 174130 ;MOV #COM_BLK,R1 ; Point to control block, SENT_TXT returns R1=COM_BLK 174130 004767 001200 JSR PC,_WORD ; Read a line of text 174134 103414 BCS COM_ESC ; Escape pressed 174136 013700 174160 MOV @#COM_BLK,R0 ; Get address of input buffer DMB 174142 004767 000166 JSR PC,_CLI ; Execute command 174146 000764 BR CLILOOP ; Loop back for another line 174150 PROMPT: 174150 120 104 120 061 061 076 052 EQUS "PDP11>*" ; Command prompt 174157 000 EQUB 0 174160 ; Run straight into control block so R1 already pointing to COM_BLK. 174160 174160 COM_BLK: 174160 172400 EQUW CLIBUF ; Input buffer 174162 270 EQUB CLIEND-CLIBUF ; Buffer length 174163 040 EQUB 32 ; Lowest acceptable char 174164 377 EQUB 255 ; Highest acceptable char 174165 000 ALIGN 174166 ; 174166 COM_ESC: 174166 012700 000176 MOV #126,R0 174172 004767 000744 JSR PC,_BYTE ; Acknowledge Escape 174176 104017 EMT 15 174200 021 105 163 143 141 160 145 000 EQUB 17,"Escape",0 174210 ALIGN 174210 174210 ; Default error handler 174210 ; --------------------- 174210 ; On entry, R0=>error block 174210 ERRHAND: 174210 013706 172766 MOV @#MEMTOP,SP ; Reset stack to top of user memory 174214 010001 MOV R0,R1 ; Point R1=>error block 174216 005201 INC R1 ; Step past error number 174220 004767 002062 JSR PC,_NEWL ; Print a newline 174224 004767 000050 JSR PC,SEND_TXT ; Print error message 174230 004767 002052 JSR PC,_NEWL ; Print another newline 174234 000724 BR CLISTART 174236 174236 ; Default escape handler 174236 ; ---------------------- 174236 ESCHAND: 174236 060000 ADD R0,R0 ; Move b6 into b7 174240 110077 176472 MOVB R0,@ESCADDR ; Store Escape flag 174244 000207 RTS PC 174246 174246 174246 ; Fetch word from unaligned R1 to R1 174246 ; ================================== 174246 ; Unaligned version of MOV (R1),R1, corrupts R0 174246 ; FETCHWORD2 - R1=>word+2 174246 ; FETCHWORD - R1=>word 174246 ; 174246 FETCHWORD2: 174246 162701 000002 SUB #2,R1 ; Step back to point to word 174252 FETCHWORD: 174252 112100 MOVB (R1)+,R0 ; Fetch low byte 174254 042700 177400 BIC #&FF00,R0 ; Ensure 8-bit value 174260 111101 MOVB (R1),R1 ; Fetch high byte 174262 042701 177400 BIC #&FF00,R1 ; Ensure 8-bit word 174266 000301 SWAB R1 ; Swap high byte to top of register 174270 050001 BIS R0,R1 ; Merge together 174272 000207 RTS PC 174274 174274 174274 ; Print zero-terminated text string at R1 174274 ; ======================================= 174274 SEND_TXTLP: 174274 004767 002000 JSR PC,_ASCII ; Send to WRCH via Tube R1 174300 SEND_TXT: 174300 112100 MOVB (R1)+,R0 ; Get byte from R1, increment R1 174302 001374 BNE SEND_TXTLP ; Loop until &00 byte 174304 000207 RTS PC 174306 174306 174306 ; Skip parameter word 174306 ; =================== 174306 ; On entry, R0=>command line 174306 ; On exit, R0=>first non-space character after parameter 174306 SKIPWORD: 174306 122027 000041 CMPB (R0)+,#ASC"!" 174312 103375 BCC SKIPWORD ; Skip past parameter 174314 005300 DEC R0 ; Point back to character 174316 ; Fall through into SKIPSPC 174316 ; Skip spaces 174316 ; =========== 174316 ; On entry, R0=>command line 174316 ; On exit, R0=>first non-space character 174316 SKIPSPC: 174316 122027 000040 CMPB (R0)+,#ASC" " 174322 001775 BEQ SKIPSPC ; Skip spaces 174324 005300 DEC R0 ; Point back to non-space character 174326 000207 RTS PC 174330 174330 CMDTABLE: 174330 162 165 156 000 EQUS "run",0 174334 174334 174334 ; ************* 174334 ; MOS INTERFACE 174334 ; ************* 174334 174334 ; OSCLI - Send command line to host 174334 ; ================================= 174334 ; On entry: R0=>command string 174334 ; On exit: R0=return value 174334 ; 174334 ; Tube data: &02 string &0D -- &7F or &80 174334 ; 174334 _CLI: 174334 010146 MOV R1,-(SP) ; Save registers 174336 010246 MOV R2,-(SP) 174340 010346 MOV R3,-(SP) 174342 010446 MOV R4,-(SP) 174344 010546 MOV R5,-(SP) 174346 174346 ; As a *command may result in data transfer, that data may end up overwriting stack in user memory, 174346 ; so use a temporary stack to do OSCLI. If OSCLI ends up jumping to a new process, a new stack will 174346 ; be set up by that new process. If we are already using the internal stack we continue using it 174346 ; so that transient OSCLIs can call more OSCLIs. 174346 174346 ; Should we also preserve the program environment? The 6502 client preserves the environment by 174346 ; preserving MEMTOP, the equivalent here would be preserving the various handlers. This is 40 174346 ; bytes of data! Concluded that preferable to document that if the program changes the environment 174346 ; the program must change it back before exiting, similar to ARM environment. 174346 174346 ; This saving could be done within EXECUTE to speed up OSCLI calls that don't cause a local execute 174346 ; and to avoid filling user's stack space when a local execute isn't done. 174346 174346 010605 MOV SP,R5 ; Copy stack pointer so we can stack it 174350 020527 172400 CMP R5,#WORKSP ; Check where the stack is 174354 103002 BCC CLI_SYSSTK ; We're already using internal stack 174356 012706 172730 MOV #CLISTK,SP ; Use internal stack 174362 CLI_SYSSTK: 174362 010546 MOV R5,-(SP) ; Save caller's stack pointer 174364 ; Could also save MEMBOT and adjust MEMBOT with code moved with a Unix header 174364 013746 172766 MOV @#MEMTOP,-(SP) ; Save current top of memory 174370 013746 172774 MOV @#PROG,-(SP) ; Save current program as top item on stack 174374 004767 000030 JSR PC,CLI_GO ; Do the OSCLI call 174400 012637 172774 MOV (SP)+,@#PROG ; Restore current program 174404 012637 172766 MOV (SP)+,@#MEMTOP ; Restore top of memory 174410 011606 MOV (SP),SP ; Restore caller's stack pointer 174412 012605 MOV (SP)+,R5 ; Restore registers 174414 012604 MOV (SP)+,R4 174416 012603 MOV (SP)+,R3 174420 012602 MOV (SP)+,R2 174422 012601 MOV (SP)+,R1 174424 005000 CLR R0 ; Return R0=0 from OSCLI 174426 CLI_DONE: 174426 000207 RTS PC 174430 174430 CLI_GO: 174430 CLI_SKIP1: 174430 004767 177662 JSR PC,SKIPSPC ; Skip leading spaces 174434 122027 000052 CMPB (R0)+,#ASC"*" 174440 001773 BEQ CLI_SKIP1 ; Skip leading stars 174442 005300 DEC R0 174444 010001 MOV R0,R1 ; R1=>start of command word 174446 010004 MOV R0,R4 ; R4=>start of command word to step past 174450 122027 000057 CMPB (R0)+,#ASC"/" 174454 001417 BEQ CLI_RUN ; Step past '*/filename' or '*/ filename' 174456 005300 DEC R0 174460 012702 174330 MOV #CMDTABLE,R2 ; R2=>command table 174464 CLI_LOOP: 174464 112003 MOVB (R0)+,R3 ; Get command character 174466 052703 000040 BIS #&20,R3 ; Force to lower case 174472 122203 CMPB (R2)+,R3 ; Compare with table 174474 001773 BEQ CLI_LOOP ; Loop while matching 174476 005300 DEC R0 ; Point to non-matching command character 174500 005302 DEC R2 ; Point to non-matching table character 174502 121027 000041 CMPB (R0),#ASC"!" 174506 103005 BCC CLI_NOTRUN ; Not end of command string, not 'RUN' 174510 105712 TSTB (R2) 174512 001003 BNE CLI_NOTRUN ; Not end of command table, not 'RUN' 174514 CLI_RUN: 174514 004767 177576 JSR PC,SKIPSPC ; Step to start of 'filename' 174520 010004 MOV R0,R4 ; R4=>filename, ready to step past 174522 CLI_NOTRUN: 174522 010400 MOV R4,R0 ; Point R0=>start of command word (or *RUN filename) 174524 004767 177556 JSR PC,SKIPWORD ; Skip command word (or *RUN filename) 174530 010067 176224 MOV R0,LPTR ; LPTR=>parameters or 174534 174534 ; Send command line to host 174534 ; ------------------------- 174534 ; R1=>command line, LPTR=>parameters 174534 ; 174534 CLI_TUBE: 174534 012700 000002 MOV #2,R0 174540 004767 002454 JSR PC,SEND_CMD ; Send command &02 - OSCLI 174544 004767 002370 JSR PC,SEND_STR ; Send command string at R1 174550 CLI_WAIT1: 174550 000261 SEC ; Set Carry to indicate OSCLI 174552 CLI_WAIT: 174552 004767 001452 JSR PC,WAIT_BYTE ; Wait for result via Tube R2 (preserves Cy) 174556 ; WAIT_BYTE returns flags set from R0 174556 ; *BUG* This appears to be the only place MI/PL is tested, so to fix the sign extension 174556 ; WAIT_BYTE could use BIC #&FF00 and this is the only place that needs to change the 174556 ; test on return to test bit 7, ensuring Carry not affected. 174556 #if VERSION*16+BUILD>=&303 174556 132700 000200 BITB #&80,R0 ; Test bit 7, must not affect Carry 174562 001721 BEQ CLI_DONE ; No code to be executed 174564 #else 174564 BPL CLI_DONE ; No code to be executed 174564 #endif 174564 ; Fall through into EXECUTE 174564 174564 174564 ; EXECUTE - Enter code at ADDR 174564 ; ============================ 174564 ; Checks for possible code header, sets TRANS to current code. 174564 ; On entry, ADDR=code entry address 174564 ; CC=entering from RESET 174564 ; CS=entering from OSCLI/OSBYTE 174564 ; 174564 ; Caller should preserve registers before calling here. 174564 ; 174564 EXECUTE: 174564 013701 172770 MOV @#ADDR,R1 ; Get transfer address 174570 012705 000000 MOV #0,R5 ; R5=0 - prepare for raw code 174574 006105 ROL R5 ; Save RESET/OSCLI flag in Carry in R5 174576 010146 MOV R1,-(SP) ; Save entry address 174600 116102 000007 MOVB 7(R1),R2 ; Get copyright offset 174604 042702 177400 BIC #&FF00,R2 ; Ensure 8-bit value 174610 060201 ADD R2,R1 ; R1=>copyright string 174612 105721 TSTB (R1)+ ; Check for copyright string 174614 001046 BNE EXEC_NOTROM 174616 122127 000050 CMPB (R1)+,#ASC"(" 174622 001043 BNE EXEC_NOTROM 174624 122127 000103 CMPB (R1)+,#ASC"C" 174630 001040 BNE EXEC_NOTROM 174632 122127 000051 CMPB (R1)+,#ASC")" 174636 001035 BNE EXEC_NOTROM 174640 011601 MOV (SP),R1 ; Get entry address back 174642 116102 000006 MOVB 6(R1),R2 ; Get ROM type 174646 #ifdef JOINERR 174646 ; Can use the following if combined code header errors: 174646 042702 177660 BIC #&FFB0,R2 ; Mask out all but language+CPU 174652 020227 000107 CMP R2,#&47 ; Is it language+PDP11? 174656 001106 BNE EXEC_NOTPDP 174660 #else 174660 BIT #&40,R2 ; Is language bit set? 174660 BEQ EXEC_NOTLANG 174660 BIC #&FFF0,R2 ; Mask out non-CPU bits 174660 CMP R2,#&07 ; Is CPU set to PDP11? 174660 BNE EXEC_NOTPDP 174660 #endif 174660 116102 000006 MOVB 6(R1),R2 ; Get ROM type again 174664 032702 000040 BIT #&20,R2 ; Does Tube transfer address exist? 174670 001416 BEQ EXEC_ROM ; No, use stacked entry address 174672 116102 000007 MOVB 7(R1),R2 ; Get copyright offset 174676 042702 177400 BIC #&FF00,R2 ; Ensure 8-bit value 174702 060201 ADD R2,R1 ; Point to copyright message 174704 005201 INC R1 ; Step past first zero byte 174706 EXEC_SKIP: 174706 105721 TSTB (R1)+ ; Find terminating zero byte 174710 001376 BNE EXEC_SKIP 174712 062701 000004 ADD #4,R1 ; Step past transfer address 174716 004767 177330 JSR PC,FETCHWORD ; R1=offset from start address *NOTE* corrupts R0 174722 062601 ADD (SP)+,R1 ; Add start entry to offset, R1 is now entry address 174724 010146 MOV R1,-(SP) ; Push it back 174726 EXEC_ROM: 174726 052705 000002 BIS #2,R5 ; R5.1=1 to indicate code with header (will become R0=1) 174732 ; Now see if a Unix header also exists 174732 ; 174732 EXEC_NOTROM: 174732 012601 MOV (SP)+,R1 ; Get entry address back 174734 042701 000001 BIC #1,R1 ; Ensure word aligned 174740 011102 MOV (R1),R2 ; Get magic number 174742 020227 000405 CMP R2,#&105 ; &o0405 - overlay 174746 103432 BCS EXEC_CODE ; &o0407 - normal 174750 020227 000411 CMP R2,#&109 ; &o0410 - read-only text 174754 103027 BCC EXEC_CODE ; &o0411 - seperated I&D 174756 005721 TST (R1)+ ; Step to next entry 174760 012103 MOV (R1)+,R3 ; Size of text 174762 012104 MOV (R1)+,R4 ; Size of initialised data 174764 060403 ADD R4,R3 ; Size of program 174766 012104 MOV (R1)+,R4 ; Size of uninitialised data 174770 013702 172764 MOV @#MEMBOT,R2 ; Destination address 174774 062701 000010 ADD #8,R1 ; Set past other fields to start of code 175000 175000 ; R1=source 175000 ; R2=dest 175000 ; R3=size of code+data 175000 ; R4=size to be zeroed 175000 175000 006203 ASR R3 ; Divide by two to get size in words 175002 EXEC_COPY: 175002 012122 MOV (R1)+,(R2)+ ; Copy program to MEMBOT 175004 005303 DEC R3 ; Decrement number of words to copy 175006 001375 BNE EXEC_COPY 175010 006204 ASR R4 ; Divide by two to get size in words 175012 001403 BEQ EXEC_ENTER ; No uninitialised data to clear 175014 EXEC_ZERO: 175014 005022 CLR (R2)+ ; Zero uninitialised data 175016 005304 DEC R4 ; Decrement number of words to clear 175020 001375 BNE EXEC_ZERO 175022 EXEC_ENTER: 175022 013701 172764 MOV @#MEMBOT,R1 ; Entry address 175026 ; Build an empty stack frame 175026 005046 CLR -(SP) ; argv[1]=0 175030 005046 CLR -(SP) ; argv[0]=0 175032 005046 CLR -(SP) ; argn=0 175034 175034 EXEC_CODE: 175034 ; r5=raw/rom+reset/oscli 175034 ; r1=entry address 175034 010146 MOV R1,-(SP) ; Stack entry address 175036 010500 MOV R5,R0 ; Get entry flags to R0 175040 012705 005674 MOV #&0BBC,R5 ; R5=&0BBC to indicate BBC EMTs available 175044 005004 CLR R4 ; Clear all other registers 175046 005003 CLR R3 175050 005002 CLR R2 175052 013701 172760 MOV @#LPTR,R1 ; R1=>command line 175056 011637 172772 MOV (SP),@#TRANS ; Set address of entered code 175062 100002 BPL EXEC_LOW ; Code in low memory, don't change MEMTOP 175064 011637 172766 MOV (SP),@#MEMTOP ; Code in high memory, put MEMTOP below code 175070 EXEC_LOW: 175070 006200 ASR R0 ; R0=0/1 for raw/header, Cy=RESET/OSCLI 175072 000207 RTS PC ; Jump to code via RTS 175074 175074 EXEC_NOTLANG: 175074 #ifndef JOINERR 175074 ; ASR R5 175074 ; BCC EXEC_CLICOM ; Entered from RESET, drop into CLICOM 175074 ; JSR PC,INIT_HANDLES ; Connect to default error handler, Cy=SEC 175074 ; EMT 15 175074 ; EQUB 249,"This is not a language",0 175074 ; ALIGN 175074 #endif 175074 EXEC_NOTPDP: 175074 006205 ASR R5 175076 103013 BCC EXEC_CLICOM ; Entered from RESET, drop into CLICOM 175100 004767 001626 JSR PC,INIT_HANDLES ; Connect to default error handler, Cy=SEC 175104 104017 EMT 15 175106 371 116 157 164 040 120 104 120 061 061 040 143 157 144 145 000 EQUB 249,"Not PDP11 code",0 175126 ALIGN 175126 175126 EXEC_CLICOM: 175126 000167 176750 JMP CLICOM ; Drop into Supervisor command prompt 175132 175132 175132 ; OSBYTE 175132 ; ====== 175132 ; On entry: R0,R1,R2=OSBYTE parameters 175132 ; On exit: R0 preserved 175132 ; If R0<&80, R1=returned value 175132 ; If R0>&7F, R1, R2, Carry=returned values 175132 ; 175132 ; Tube data: &04 X A -- X 175132 ; &06 X Y A -- Cy Y X 175132 ; 175132 BYTE_WAIT: 175132 012767 174004 175620 MOV #BANNER,LPTR ; Point LPTR=> 175140 000603 BR CLI_WAIT1 ; Jump to wait for ack. from OSCLI/OSBYTE 175142 _BYTE: 175142 010046 MOV R0,-(SP) ; Save R0 175144 105700 TSTB R0 175146 100413 BMI BYTE_HI ; Jump with high OSBYTEs 175150 012700 000004 MOV #4,R0 175154 004767 002032 JSR PC,SEND_CMD_R1 ; Send command and second parameter 175160 011600 MOV (SP),R0 ; Get first parameter from top of stack 175162 004767 002032 JSR PC,SEND_BYTE ; Send first parameter 175166 004767 001036 JSR PC,WAIT_BYTE ; Wait for response 175172 010001 MOV R0,R1 ; Pass to R1 175174 000455 BR BYTE_DONE 175176 175176 ; OSBYTE >&7F 175176 ; ----------- 175176 BYTE_HI: 175176 020027 000202 CMP R0,#&82 175202 103403 BCS BYTE_HI1 ; Not a memory OSBYTE 175204 #if ADDRHI<>MEMBOT-2 175204 BEQ MEM82 ; Fetch address high word 175204 #endif 175204 020027 000205 CMP R0,#&85 175210 103440 BCS MEM83 ; Fetch low/high memory limit 175212 BYTE_HI1: 175212 012700 000006 MOV #6,R0 175216 004767 001770 JSR PC,SEND_CMD_R1 ; Send command and second parameter 175222 010200 MOV R2,R0 175224 004767 001770 JSR PC,SEND_BYTE ; Send third parameter 175230 012600 MOV (SP)+,R0 ; Get first parameter from stack 175232 004767 001762 JSR PC,SEND_BYTE ; Send first parameter 175236 020027 000235 CMP R0,#&9D ; Was it Fast BPut? 175242 001433 BEQ BYTE_DONE1 ; Don't wait for response 175244 020027 000216 CMP R0,#&8E ; Was it language startup? 175250 001730 BEQ BYTE_WAIT ; Wait for program startup 175252 010046 MOV R0,-(SP) ; Save R0 again 175254 004767 000750 JSR PC,WAIT_BYTE ; Wait for response 175260 #if VERSION*16+BUILD<&303 175260 ADD #&FF80,R0 ; Copy b7 into Carry 175260 JSR PC,WAIT_BYTE ; Wait for 8-bit response 175260 BIC #&FF00,R0 ; Ensure 8-bit value (bugfix will remove this) 175260 MOV R0,R2 ; Pass to R2 175260 SWAB R0 ; *BUG* Clears carry 175260 MOV R0,R1 ; Pass to R1 as b8-b15 of result 175260 JSR PC,WAIT_BYTE ; Wait for 8-bit response 175260 BIC #&FF00,R0 ; Ensure 8-bit value (bugfix will remove this) 175260 BIS R0,R1 ; Merge with b8-b15 set from R2 175260 #else 175260 062700 177600 ADD #&FF80,R0 ; Copy b7 into Carry 175264 006116 ROL (SP) ; Save Carry on stack with saved R0 175266 004767 000736 JSR PC,WAIT_BYTE ; Wait for 8-bit response 175272 010002 MOV R0,R2 ; Pass to R2 175274 000300 SWAB R0 ; NB: Clears carry 175276 010001 MOV R0,R1 ; Pass to R1 as b8-b15 of result 175300 004767 000724 JSR PC,WAIT_BYTE ; Wait for 8-bit response 175304 050001 BIS R0,R1 ; Merge with b8-b15 set from R2 175306 006016 ROR (SP) ; Get Carry back, restore saved R0 175310 #endif 175310 000407 BR BYTE_DONE 175312 175312 ; Read memory locations 175312 ; --------------------- 175312 MEM82: 175312 MEM83: 175312 MEM84: 175312 006300 ASL R0 ; A=&0104,&0106,&0108 175314 016001 172356 MOV MEMBOT-&106(R0),R1 ; Fetch address value 175320 010102 MOV R1,R2 ; R2=R1 DIV 256 175322 000302 SWAB R2 175324 042702 177400 BIC #&FF00,R2 175330 BYTE_DONE: 175330 012600 MOV (SP)+,R0 ; Restore R0 175332 BYTE_DONE1: 175332 000207 RTS PC 175334 175334 175334 ; OSWORD 175334 ; ====== 175334 ; On entry: R0=OSWORD number 175334 ; R1=>control block 175334 ; 175334 _WORD: 175334 005700 TST R0 175336 001515 BEQ RDLINE ; OSWORD 0, jump to read line 175340 ; 175340 ; OSWORD <>&00 175340 ; ------------ 175340 ; Tube data: &08 function in_length block out_length -- block 175340 ; 175340 010346 MOV R3,-(SP) ; Save R3 175342 010246 MOV R2,-(SP) ; Save R2 175344 010046 MOV R0,-(SP) ; Save R0 175346 012700 000010 MOV #8,R0 175352 004767 001642 JSR PC,SEND_CMD ; Send command &08 - OSWORD 175356 011600 MOV (SP),R0 ; Get R0 back 175360 004767 001634 JSR PC,SEND_BYTE ; Send OSWORD number 175364 105700 TSTB R0 ; Check OSWORD number 175366 100003 BPL WORDLO ; <&80, calculate control block sizes 175370 112102 MOVB (R1)+,R2 ; Get transmit size from control block DMB: should be R1 175372 111103 MOVB (R1),R3 ; Get receive size from control block DMB: should be R1 175374 000414 BR WORDGO ; Jump to send OSWORD command 175376 WORDLO: 175376 012702 000020 MOV #&10,R2 ; OSWORD &15-&7F uses 16 bytes both ways 175402 012703 000020 MOV #&10,R3 175406 020027 000025 CMP R0,#&15 ; OSWORD &01-&7F use fixed control block sizes 175412 103005 BCC WORDGO ; OSWORD &15-&7F, jump to send OSWORD command 175414 060000 ADD R0,R0 ; Double R0 to index into table 175416 062700 175520 ADD #WORD_TABLE-2,R0 ; Point to table entry DMB: #WORD_TABLE 175422 112002 MOVB (R0)+,R2 ; Fetch send length - *NOTE* &80 becomes &FF80 175424 111003 MOVB (R0),R3 ; Fetch receive length - *NOTE* &80 becomes &FF80 175426 ; NB: &80 will be sign-extended to &FF80, must ensure manipulated as a byte 175426 WORDGO: 175426 010200 MOV R2,R0 ; Get transmit block length 175430 004767 001564 JSR PC,SEND_BYTE ; Send transmit block length 175434 060201 ADD R2,R1 ; Point to past end of control block 175436 #if VERSION*16+BUILD<&302 175436 DEC R2 ; Convert 0 to -1 175436 CMP R2,#&80 ; Check length of transmit block 175436 #else 175436 105302 DECB R2 ; Convert 0 to -1 175440 120227 000200 CMPB R2,#&80 ; Check length of transmit block 175444 #endif 175444 103005 BCC WORD_NOTX ; Transmit block=0 or >&80, send nothing 175446 WORD_TX: 175446 114100 MOVB -(R1),R0 ; Get byte from control block 175450 004767 001544 JSR PC,SEND_BYTE ; Send byte to Tube R2 175454 #if VERSION*16+BUILD<&302 175454 DEC R2 175454 #else 175454 105302 DECB R2 ; 8-bit decrement 175456 #endif 175456 100373 BPL WORD_TX ; Loop to send control block 175460 WORD_NOTX: 175460 010300 MOV R3,R0 ; Get recive block length 175462 004767 001532 JSR PC,SEND_BYTE ; Send receive block length 175466 060301 ADD R3,R1 ; Point past end of control block 175470 #if VERSION*16+BUILD<&302 175470 DEC R3 ; Convert 0 to -1 175470 CMP R3,#&80 ; Check length of received block 175470 #else 175470 105303 DECB R3 ; Convert 0 to -1 175472 120327 000200 CMPB R3,#&80 ; Check length of received block 175476 #endif 175476 103005 BCC WORD_NORX ; Receive block=0 or >&80, receive nothing 175500 WORD_RX: 175500 004767 000524 JSR PC,WAIT_BYTE ; Get byte from Tube R2 175504 110041 MOVB R0,-(R1) ; Store byte in control block 175506 #if VERSION*16+BUILD<&302 175506 DEC R3 175506 #else 175506 105303 DECB R3 ; 8-bit decrement 175510 #endif 175510 100373 BPL WORD_RX ; Loop to receive control block 175512 WORD_NORX: 175512 012600 MOV (SP)+,R0 ; Restore registers 175514 012602 MOV (SP)+,R2 175516 012603 MOV (SP)+,R3 175520 000207 RTS PC 175522 ; 175522 ; Table of OSWORD control block lengths for &01-&14 175522 ; ------------------------------------------------- 175522 ; low byte=send length, high byte=recive length 175522 WORD_TABLE: 175522 002400 EQUW &0500 ; &01 =TIME 175524 000005 EQUW &0005 ; &02 TIME= 175526 002400 EQUW &0500 ; &03 =TIMER 175530 000005 EQUW &0005 ; &04 TIMER= 175532 002404 EQUW &0504 ; &05 =MEM 175534 000005 EQUW &0005 ; &06 MEM= 175536 000010 EQUW &0008 ; &07 SOUND 175540 000016 EQUW &000E ; &08 ENVELOPE 175542 002404 EQUW &0504 ; &09 =POINT 175544 004401 EQUW &0901 ; &0A Read bitmap 175546 002401 EQUW &0501 ; &0B Read palette 175550 000005 EQUW &0005 ; &0C Write palette 175552 004000 EQUW &0800 ; &0D Read graphics coords 175554 014420 EQUW &1910 ; &0E =TIME$ 175556 000040 EQUW &0020 ; &0F TIME$= 175560 000420 EQUW &0110 ; &10 Net_Tx 175562 006415 EQUW &0D0D ; &11 Net_Rx 175564 #if WORDFIX AND VERSION*16+BUILD<&302 175564 EQUW &7F00 ; &12 Net_Params 175564 EQUW &0808 ; &13 Net_Info 175564 EQUW &7F7F ; &14 NetFS_Op 175564 #else 175564 100000 EQUW &8000 ; &12 Net_Params 175566 004010 EQUW &0808 ; &13 Net_Info 175570 100200 EQUW &8080 ; &14 NetFS_Op 175572 #endif 175572 175572 175572 ; Read a line of text 175572 ; ------------------- 175572 ; Tube data: &0A block -- &FF or &7F string &0D 175572 ; 175572 RDLINE: 175572 012700 000012 MOV #10,R0 175576 004767 001416 JSR PC,SEND_CMD ; Send command &0A - RDLINE 175602 062701 000002 ADD #2,R1 175606 012702 000003 MOV #3,R2 175612 004767 001340 JSR PC,SEND_BLK ; Send 3-byte control block 175616 012700 000007 MOV #7,R0 175622 004767 001372 JSR PC,SEND_BYTE ; Send &0700 175626 005000 CLR R0 175630 004767 001364 JSR PC,SEND_BYTE 175634 004767 000370 JSR PC,WAIT_BYTE ; Wait for response 175640 005002 CLR R2 ; Clear count of received bytes 175642 062700 177600 ADD #&FF80,R0 ; Copy b7 into Carry 175646 103412 BCS RD_DONE 175650 004767 176372 JSR PC,FETCHWORD2 ; Get address to store text, allowing for nonalignment 175654 RD_STR: 175654 004767 000350 JSR PC,WAIT_BYTE ; Wait for byte from Tube R2 175660 110021 MOVB R0,(R1)+ ; Store it 175662 005202 INC R2 ; Increment number of bytes 175664 020027 000015 CMP R0,#13 ; Check current byte 175670 001371 BNE RD_STR ; Loop until and Clear Carry 175672 005302 DEC R2 ; R2 is length of string 175674 RD_DONE: 175674 000207 RTS PC 175676 175676 175676 ; OSARGS - Read info on open file or filing system 175676 ; ================================================ 175676 ; On entry: R0=function 175676 ; R1=handle 175676 ; R2=>control block 175676 ; On exit: R0=returned value 175676 ; R1 preserved 175676 ; R2 preserved 175676 ; 175676 ; Tube Data: &0C handle block function -- result block 175676 ; 175676 _ARGS: 175676 010246 MOV R2,-(SP) ; Save control block pointer 175700 010146 MOV R1,-(SP) ; Save handle 175702 010046 MOV R0,-(SP) ; Save function 175704 012700 000014 MOV #&0C,R0 175710 004767 001276 JSR PC,SEND_CMD_R1 ; Send command and handle 175714 010201 MOV R2,R1 175716 012702 000004 MOV #4,R2 175722 004767 001230 JSR PC,SEND_BLK ; Send four-byte control block 175726 012600 MOV (SP)+,R0 175730 004767 001264 JSR PC,SEND_BYTE ; Send function 175734 004767 000270 JSR PC,WAIT_BYTE ; Wait for returned result *BUG* sign extended 175740 ; 8-bit filing systems are always <&80 anyway 175740 010046 MOV R0,-(SP) ; Save result 175742 012702 000004 MOV #4,R2 ; Prepare to wait for 4-byte control block 175746 000463 BR FILE_DONE ; Wait for control block, restore and return 175750 175750 175750 ; OSFIND - Open or Close a file 175750 ; ============================= 175750 ; On entry: R0=function 175750 ; R1=handle or =>filename 175750 ; On exit: R0=zero or handle 175750 ; 175750 ; Tube data: &12 function string &0D -- handle 175750 ; &12 &00 handle -- &7F 175750 ; 175750 _FIND: 175750 010046 MOV R0,-(SP) ; Save R0 175752 012700 000022 MOV #&12,R0 175756 004767 001236 JSR PC,SEND_CMD ; Send command &12 - OSFIND 175762 012600 MOV (SP)+,R0 ; Get R0 back 175764 004767 001230 JSR PC,SEND_BYTE ; Send function 175770 005700 TST R0 ; Check function 175772 001006 BNE OPEN ; Jump to deal with OPEN 175774 ; CLOSE 175774 004767 001216 JSR PC,SEND_BYTE_R1 ; Send handle to Tube 176000 004767 000224 JSR PC,WAIT_BYTE ; Wait for acknowledgement 176004 005000 CLR R0 ; Zero R0 176006 000207 RTS PC 176010 OPEN: 176010 010146 MOV R1,-(SP) ; Save R1 176012 004767 001122 JSR PC,SEND_STR ; Send string at R1 176016 004767 000206 JSR PC,WAIT_BYTE ; Wait for returned handle 176022 ; *BUG* sign extended 176022 012601 MOV (SP)+,R1 ; Restore R1 176024 000207 RTS PC 176026 176026 176026 ; OSFILE - Operate on whole files 176026 ; =============================== 176026 ; On entry: R0=function 176026 ; R1=>control block 176026 ; On exit: R0=result 176026 ; R1 preserved 176026 ; control block updated 176026 ; 176026 ; Tube data: &14 block string function -- result block 176026 ; 176026 _FILE: 176026 010246 MOV R2,-(SP) ; Save R2 176030 010146 MOV R1,-(SP) ; Save R1 176032 010046 MOV R0,-(SP) ; Save function 176034 012700 000024 MOV #&14,R0 176040 004767 001154 JSR PC,SEND_CMD ; Send command &14 - OSFILE 176044 062701 000002 ADD #2,R1 ; Point to control block contents 176050 012702 000020 MOV #16,R2 176054 004767 001076 JSR PC,SEND_BLK ; Send 16-byte control block 176060 004767 176162 JSR PC,FETCHWORD2 ; Get address of filename, allowing for nonalignment 176064 004767 001050 JSR PC,SEND_STR ; Send filename string 176070 012600 MOV (SP)+,R0 176072 004767 001122 JSR PC,SEND_BYTE ; Send function 176076 004767 000126 JSR PC,WAIT_BYTE ; Wait for returned result *BUG* sign extended 176102 011601 MOV (SP),R1 ; Get control block pointer back 176104 010046 MOV R0,-(SP) ; Save result 176106 062701 000002 ADD #2,R1 ; Point to control block contents 176112 012702 000020 MOV #16,R2 ; Prepate to wait for 16-byte control block 176116 FILE_DONE: 176116 004767 001052 JSR PC,WAIT_BLK ; Wait for control block 176122 012600 MOV (SP)+,R0 ; Get result back 176124 012601 MOV (SP)+,R1 ; Get control block pointer back 176126 012602 MOV (SP)+,R2 ; Get R2 back 176130 000207 RTS PC 176132 176132 176132 ; OS_GBPB - Multiple byte read and write 176132 ; ===================================== 176132 ; On entry: R0=function 176132 ; R1=>control block 176132 ; On exit: R0=returned value 176132 ; control block updated 176132 ; 176132 ; Tube data: &16 block function -- block Carry result 176132 ; 176132 _GBPB: 176132 #ifndef NOCHNZERO 176132 TSTB (R1) ; Check handle 176132 BNE GBPB1 ; Non-zero handle 176132 TST R0 ; Check function 176132 BEQ GBPB1 ; Pass OSGBPB 0 to Tube 176132 CMP R0,#5 176132 BCS GBPB_RDWR ; Channel 0 via OSRDCH/OSWRCH 176132 #endif 176132 GBPB1: 176132 010246 MOV R2,-(SP) ; Save R2 176134 010046 MOV R0,-(SP) ; Save function 176136 012700 000026 MOV #&16,R0 176142 004767 001052 JSR PC,SEND_CMD ; Send command &16 - OSGBPB 176146 012702 000015 MOV #13,R2 176152 004767 001000 JSR PC,SEND_BLK ; Send 13-byte control block 176156 012600 MOV (SP)+,R0 176160 004767 001034 JSR PC,SEND_BYTE ; Send function 176164 012702 000015 MOV #13,R2 176170 004767 001000 JSR PC,WAIT_BLK ; Wait for 13-byte control block 176174 012602 MOV (SP)+,R2 ; Get R2 back 176176 000410 BR WAIT_CHAR ; Get Carry and result byte 176200 ; *BUG* This will sign extend R0 176200 ; 176200 ; Read or write block of memory to/from OSWRCH/OSRDCH 176200 ; --------------------------------------------------- 176200 ; NB, only uses 16-bit address and count, b16-b31 ignored and not updated 176200 ; 176200 #ifndef NOCHNZERO 176200 GBPB_RDWR: 176200 MOV R2,-(SP) ; Save R2 176200 MOV R0,-(SP) ; Save function 176200 MOV R1,-(SP) ; Save pointer to control block 176200 INC R1 ; Point to Address 176200 JSR PC,FETCHWORD ; R1=Address, allowing for nonalignment 176200 MOV R1,R2 ; R2=Address 176200 MOV (SP)+,R1 ; Restore pointer to control block 176200 176200 GBPB_LP: 176200 CMP (SP),#3 176200 BCC GBPB_RD ; Function 3/4, read characters 176200 ; Function 1/2, write characters 176200 MOVB (R2)+,R0 ; Get character from memory 176200 JSR PC,_WRCH ; Write it 176200 BR GBPB_NEXT ; Jump to update and loop 176200 GBPB_RD: 176200 JSR PC,_RDCH ; Read character 176200 BCS GBPB_EXIT ; Carry set, exit 176200 MOVB R0,(R2)+ ; Store character 176200 GBPB_NEXT: 176200 TSTB 5(R1) ; Test byte low byte 176200 BNE GBPB_LO ; Count<>&xx00 176200 DECB 6(R1) ; Decrement count high byte 176200 GBPB_LO: 176200 DECB 5(R1) ; Decrement count low byte 176200 BNE GBPB_LP ; Loop until all done 176200 TSTB 6(R1) ; Test count high byte 176200 BNE GBPB_LP ; Loop until all done 176200 CLC ; Clear carry for OK 176200 GBPB_EXIT: 176200 MOVB R2,1(R1) 176200 SWAB R2 176200 MOVB R2,2(R1) ; Update address 176200 TST (SP)+ ; Drop function 176200 MOV (SP)+,R2 ; Restore R2 176200 MOV #0,R0 ; R0=0, function supported, don't affect Carry 176200 RTS PC 176200 #endif 176200 176200 176200 ; OSBGET - Get a byte from open file 176200 ; ================================== 176200 ; On entry: R1=handle 176200 ; On exit: R0=byte Read 176200 ; R1=preserved 176200 ; Cy set if EOF 176200 ; 176200 ; Tube data: &0E handle -- Carry byte 176200 ; 176200 _BGET: 176200 #ifndef NOCHNZERO 176200 TST R1 ; Check handle 176200 BEQ _RDCH ; BGET#0 calls OSRDCH 176200 #endif 176200 012700 000016 MOV #&0E,R0 176204 004767 001002 JSR PC,SEND_CMD_R1 ; Send command and handle 176210 000403 BR WAIT_CHAR ; Wait for Carry, Byte 176212 ; *BUG* This will sign extend R0 176212 176212 176212 ; OSRDCH - Wait for character from input stream 176212 ; ============================================= 176212 ; On exit: R0=char, Cy=carry 176212 ; 176212 ; Tube data: &00 -- Carry Char 176212 ; 176212 _RDCH: 176212 005000 CLR R0 176214 004767 001000 JSR PC,SEND_CMD ; Send command &00 - OSRDCH 176220 WAIT_CHAR: 176220 004767 000004 JSR PC,WAIT_BYTE ; Get returned byte 176224 062700 177600 ADD #&FF80,R0 ; Copy b7 into carry 176230 ; Continue to fetch byte from Tube R2 176230 ; *BUG* This will sign extend R0 176230 176230 176230 ; Wait for byte in Tube Register 1 to return in R0, preserving Carry 176230 ; ================================================================== 176230 WAIT_BYTE: 176230 113700 177764 MOVB @#TUBE2S,R0 ; Read Tube R2 status 176234 100375 BPL WAIT_BYTE ; Loop until b7 set 176236 113700 177766 MOVB @#TUBE2,R0 ; Get byte from Tube R2 176242 #if VERSION*16+BUILD>=&303 176242 042700 177400 BIC #&FF00,R0 ; Ensure 8-bit value (Carry unchanged) 176246 #endif 176246 000207 RTS PC 176250 176250 176250 ; OSBPUT - Put a byte to an open file 176250 ; =================================== 176250 ; On entry: R0=byte to write 176250 ; R1=handle 176250 ; On exit: R0=preserved 176250 ; R1=preserved 176250 ; 176250 ; Tube data: &10 handle byte -- &7F 176250 ; 176250 _BPUT: 176250 #ifndef NOCHNZERO 176250 TST R1 ; Check handle 176250 BEQ _WRCH ; BPUT#0 calls OSWRCH 176250 #endif 176250 010046 MOV R0,-(SP) ; Save R0 176252 012700 000020 MOV #&10,R0 176256 004767 000730 JSR PC,SEND_CMD_R1 ; Send command and handle 176262 011600 MOV (SP),R0 ; Get R0 back 176264 004767 000730 JSR PC,SEND_BYTE ; Send byte to Tube 176270 004767 177734 JSR PC,WAIT_BYTE ; Wait for acknowledgement 176274 012600 MOV (SP)+,R0 ; Restore R0 176276 000207 RTS PC 176300 176300 176300 ; OSASCI - Send ASCII character 176300 ; ============================= 176300 _ASCII: 176300 022700 000015 CMP #13,R0 ; If not , send raw character 176304 001006 BNE _WRCH ; If , fall through to send NEWL 176306 176306 176306 ; OSNEWL - Send LF/CR sequence 176306 ; ============================ 176306 _NEWL: 176306 012700 000012 MOV #10,R0 176312 004767 000004 JSR PC,_WRCH ; Output LF 176316 012700 000015 MOV #13,R0 ; Fall through into WRCH 176322 176322 176322 ; OSWRCH - Send character in R0 to Tube Register 1 176322 ; ================================================ 176322 _WRCH: 176322 SEND_R1LP: 176322 032737 000100 177760 BIT #&40,@#TUBE1S ; Check b6 of Tube R1 status 176330 001774 BEQ SEND_R1LP ; Loop until b6 set 176332 110037 177762 MOVB R0,@#TUBE1 ; Send byte to Tube R1 176336 000207 RTS PC 176340 176340 176340 ; TRAP handler 176340 ; ============ 176340 ; TRAP is used for Unix calls and is followed by a variable number of inline parameters, 176340 ; so it is impossible to simply do a null return. So, the safest option is to give the 176340 ; standard CoPro client 'unsupported' error. 176340 TRAP_HANDLER: 176340 104017 EMT 15 176342 377 102 141 144 000 EQUB 255,"Bad",0 176347 000 ALIGN 176350 176350 176350 ; EMT handler 176350 ; =========== 176350 ; On extry, R0-R5 contain any parameters 176350 ; PSW ignored 176350 ; On exit, R0-R5 contain any returned values 176350 ; C returns any returned value 176350 ; V set if error, R0=>error block 176350 ; 176350 EMT_HANDLER: 176350 042766 177760 000002 BIC #&FFF0,2(SP) ; Clear stacked flags 176356 013746 172740 MOV @#ERRV,-(SP) ; Save old ERR handler 176362 013746 172754 MOV @#EMTV,-(SP) ; Save old EMT SP 176366 010637 172754 MOV SP,@#EMTV ; Save current EMT SP 176372 012737 176436 172740 MOV #EMT_ERROR,@#ERRV ; Catch EMT errors 176400 005746 TST -(SP) ; Make space on stack 176402 010046 MOV R0,-(SP) ; Save R0 176404 016600 000010 MOV 8(SP),R0 ; Get return address 176410 014000 MOV -(R0),R0 ; Get EMT instruction 176412 042700 177400 BIC #&FF00,R0 ; Get EMT number 176416 #if EMTMAX<256 176416 CMP R0,#EMTMAX 176416 BCC EMT_TOOHIGH ; Out of range 176416 #endif 176416 060000 ADD R0,R0 ; Index into dispatch table 176420 063700 172756 ADD @#EMTADDR,R0 ; Index into dispatch table 176424 011066 000002 MOV (R0),2(SP) ; Copy address to stack 176430 012600 MOV (SP)+,R0 ; Restore R0 176432 004736 JSR PC,@(SP)+ ; Jump to routine 176434 102005 BVC EMT_NOERROR ; V clear, jump to check Carry 176436 EMT_ERROR: 176436 013706 172754 MOV @#EMTV,SP ; Get saved EMT SP 176442 052766 000002 000006 BIS #2,6(SP) ; Set stacked V flag 176450 EMT_NOERROR: 176450 103003 BCC EMT_EXIT ; C clear, jump to exit 176452 052766 000001 000006 BIS #1,6(SP) ; Set stacked C flag 176460 EMT_EXIT: 176460 012637 172754 MOV (SP)+,@#EMTV ; Restore old EMT SP 176464 012637 172740 MOV (SP)+,@#ERRV ; Restore old error handler 176470 000002 RTI ; Return from EMT 176472 #if EMTMAX<256 176472 EMT_TOOHIGH: 176472 MOV (SP)+,R0 ; Restore R0 176472 TST (SP)+ ; Balance stack 176472 BR EMT_EXIT ; Should go through a generic handler 176472 #endif 176472 176472 176472 ; EMT 15 - Generate an error 176472 ; -------------------------- 176472 EMT15: 176472 016600 000006 MOV 6(SP),R0 ; Get return address pointing to inline error block 176476 016666 000004 000006 MOV 4(SP),6(SP) ; Replace return address with mainline error handler 176504 000207 RTS PC ; Return to EMT handler, thence to error handler 176506 176506 176506 ; EMT 14 - Read/Write handlers, etc. 176506 ; ---------------------------------- 176506 ; On entry: R0=0..255 to claim EMTs 0..255 176506 ; R1=new routine address or 0 to read 176506 ; R0=&FFxx to set environment handlers 176506 ; R1=new handler address or 0 to read 176506 ; R2=new handler data address or 0 to read 176506 ; On exit: R0=preserved 176506 ; R1=old address 176506 ; R2=old handler address or preserved 176506 ; 176506 EMT14: 176506 ; So that EMT14 can change the ERRV we have to rewind out of the EMT handler and restore ERRV so 176506 ; that it can be changed. Otherwise, the EMT handler will just restore ERRV to whatever it was 176506 ; before. 176506 005726 TST (SP)+ ; Drop return to EMT handler 176510 012637 172754 MOV (SP)+,@#EMTV ; Restore old EMT SP 176514 012637 172740 MOV (SP)+,@#ERRV ; Restore mainline error handler 176520 010046 MOV R0,-(SP) ; Save R0 176522 100413 BMI EMT14_HANDLER ; Negative, set up handler 176524 020027 000400 CMP R0,#EMTMAX 176530 103033 BCC EMT14_QUIT ; Out of range 176532 060000 ADD R0,R0 ; Double R0 to offset into table 176534 063700 172756 ADD @#EMTADDR,R0 ; Index into EMT dispatch table 176540 011046 MOV (R0),-(SP) ; Get old address 176542 005701 TST R1 176544 001424 BEQ EMT14_READ ; Zero, just read 176546 010110 MOV R1,(R0) ; Store new address if non-zero 176550 000422 BR EMT14_READ 176552 176552 EMT14_HANDLER: 176552 005100 COM R0 176554 ; CMP R0,#HANDLEMAX 176554 020027 000012 CMP R0,#HANDLEMAX+WORKSPMAX/4 ; Allow access to workspace as well as handles 176560 103017 BCC EMT14_QUIT ; Out of range 176562 060000 ADD R0,R0 176564 060000 ADD R0,R0 ; Times four to offset into table 176566 062700 172730 ADD #HANDLERS,R0 ; Index into handlers 176572 011046 MOV (R0),-(SP) ; Save old handler address 176574 005701 TST R1 176576 001401 BEQ EMT14_HAND2 ; Just read old handler address 176600 010110 MOV R1,(R0) ; Store new handler address 176602 EMT14_HAND2: 176602 005720 TST (R0)+ ; DMB: Step to data address 176604 011046 MOV (R0),-(SP) ; Save old data address 176606 005702 TST R2 176610 001401 BEQ EMT14_HAND3 ; Just read old data address 176612 010210 MOV R2,(R0) ; Store new data address 176614 EMT14_HAND3: 176614 012602 MOV (SP)+,R2 ; Get old data 176616 EMT14_READ: 176616 012601 MOV (SP)+,R1 ; Get old address 176620 EMT14_QUIT: 176620 012600 MOV (SP)+,R0 ; Restore R0 176622 000002 RTI 176624 176624 176624 ; EMT 0 - Exit current program 176624 ; ---------------------------- 176624 EMT0: 176624 000177 174100 JMP @EXITV ; Jump via exit handler 176630 176630 176630 ; EMT 13 - Misc control functions 176630 ; ------------------------------- 176630 ; On entry: R0=0 - Load BBC BASIC 176630 ; 1 - Set up new program environment - default environment handlers only, 176630 ; sets this environment as the current program to re-enter on Soft Break 176630 ; 2 - Set up software environment - default environment handlers, memory limits and EMTs 176630 ; 3 - Set up hardware environment - default environment handlers, memory, EMTs, hardware vectors 176630 ; <0 - Emulator only - toggle emulated EMTs on/off 176630 ; 176630 ; NB: When resetting environment, *MUST* return VC, otherwise EMT handler will 'restore' SP 176630 ; to zero. 176630 ; 176630 EMT13: ; Entered with flags already set from R0 176630 001475 BEQ EMTNULL ; R0=0 - unsupported 176632 020027 000003 CMP R0,#3 ; R0=1 - set up environment handlers - create a new program environment 176636 103436 BCS INIT_HANDLES1 ; R0=2 - set up environment handlers, workspace 176640 001071 BNE EMTNULL 176642 176642 176642 ; Set up default system environment 176642 ; ================================= 176642 INIT_ENV: 176642 005000 CLR R0 ; Start at base of hardware vectors 176644 INIT_LP1: 176644 012720 177734 MOV #NULLIRQ,(R0)+ ; Set all hardware vectors to NULLIRQ 176650 005020 CLR (R0)+ ; Allow all interupts 176652 020027 000400 CMP R0,#&100 ; Hardware vectors at at &0000-&00FF 176656 001372 BNE INIT_LP1 176660 012737 176340 000034 MOV #TRAP_HANDLER,@#TRAPVEC ; Set up TRAP vector to give an error 176666 012737 176350 000030 MOV #EMT_HANDLER ,@#EMTVEC ; Set up EMT vector 176674 012737 177730 000200 MOV #NMI_ACK ,@#NMIVEC+0 ; Set up NMI vector 176702 012737 000340 000202 MOV #&00E0 ,@#NMIVEC+2 ; NMI processor status - bar all interupts 176710 012737 177266 000204 MOV #IRQ ,@#IRQVEC+0 ; Set up IRQ vector 176716 012737 000300 000206 MOV #&00C0 ,@#IRQVEC+2 ; IRQ processor status - bar all except NMIs 176724 ; We have CC from CMP/BNE above 176724 012700 000044 MOV #HANDLEWORDS+WORKSPWORDS+EMTCORE,R0 176730 000411 BR INIT_ENV6 ; R0=full system, CC=set EMTs 176732 176732 INIT_HANDLES: 176732 005000 CLR R0 176734 INIT_HANDLES1: 176734 022700 000001 CMP #1,R0 ; 0,1 - CC=handles only, 2 - CS=handles and memory 176740 012700 000014 MOV #HANDLEWORDS,R0 ; R0=word count of handlers 176744 103002 BCC INIT_ENV5 176746 INIT_MEMORY: 176746 INIT_ENV3: 176746 012700 000024 MOV #HANDLEWORDS+WORKSPWORDS,R0 176752 INIT_ENV5: 176752 000261 SEC ; CS=Not resetting EMTs 176754 INIT_ENV6: 176754 012701 177026 MOV #HANDDEFAULT,R1 ; R1=> Default handlers and EMTs 176760 012702 172730 MOV #HANDLERS,R2 ; R2=> Start of handlers DMB 176764 INIT_LP2: 176764 012122 MOV (R1)+,(R2)+ ; Set up initial settings 176766 005300 DEC R0 ; and EMT dispatch table, 176770 001375 BNE INIT_LP2 ; while preserving Carry 176772 103405 BCS INIT_DONE ; Carry was Set on entry, don't initialise EMTs 176774 012700 000360 MOV #EMTMAX-EMTCORE,R0 ; Number of unused EMTs 177000 INIT_CLR: 177000 011122 MOV (R1),(R2)+ ; EMTs 16-255 do nothing 177002 005300 DEC R0 177004 001375 BNE INIT_CLR 177006 INIT_DONE: 177006 013737 172772 172774 MOV @#TRANS,@#PROG ; Make last-entered code the current program 177014 ; Will be set from default with R0=2,3 177014 100403 BMI EMTNULL ; Code in high memory, use pre-initialised MEMTOP 177016 013737 177064 172766 MOV @#MEMDEFAULT,@#MEMTOP ; Code in low memory, use full memory 177024 EMTNULL: ; EMTs 16-255 177024 EVENT: ; Null event handler 177024 000207 RTS PC ; Return with R0=0, R1,R2 corrupted 177026 177026 177026 ; Default settings and EMT table 177026 ; ============================== 177026 HANDDEFAULT: 177026 174102 EQUW EXITHAND ; &D8 - Default exit handler 177030 000060 EQUW VERSION ; &DA - Unused - Client version 177032 174236 EQUW ESCHAND ; &DC - Default escape handler 177034 172777 EQUW ESCFLG ; &DE - Default escape flag 177036 174210 EQUW ERRHAND ; &E0 - Default error handler 177040 172400 EQUW ERRBLK ; &E2 - Default error buffer 177042 177024 EQUW EVENT ; &E4 - Default event handler 177044 000000 EQUW 0 ; &E6 - Unused 177046 177734 EQUW USERIRQ ; &E8 - Default unknown IRQ handler 177050 000000 EQUW 0 ; &EA - Data transfer address within IRQ handler 177052 000000 EQUW 0 ; &EC - Holds old SP within EMT handler 177054 173000 EQUW EMTTABLE ; &EE - Default EMT dispatch table 177056 174004 EQUW BANNER ; &F0 - LPTR - Line pointer 177060 000000 EQUW 0 ; &F2 - ADDRHI - Memory address high word - OSBYTE &82 177062 000400 EQUW RAMSTART ; &F4 - MEMBOT - Lowest user memory - OSBYTE &83 177064 MEMDEFAULT: 177064 172400 EQUW RAMEND ; &F6 - MEMTOP - Highest user memory - OSBYTE &84 177066 174102 EQUW CLICOM ; &F8 - ADDR - Execution address 177070 174102 EQUW CLICOM ; &FA - TRANS - Transfer address 177072 174102 EQUW CLICOM ; &FC - PROG - Current program 177074 000 EQUB 0 ; &FE - Spare byte 177075 000 EQUB 0 ; &FF - ESCFLG - Escape flag 177076 EMTDEFAULT: 177076 176624 EQUW EMT0 ; EMT 0 - QUIT 177100 174334 EQUW _CLI ; EMT 1 - OSCLI 177102 175142 EQUW _BYTE ; EMT 2 - OSBYTE 177104 175334 EQUW _WORD ; EMT 3 - OSWORD 177106 176322 EQUW _WRCH ; EMT 4 - OSWRCH 177110 176306 EQUW _NEWL ; EMT 5 - OSNEWL 177112 176212 EQUW _RDCH ; EMT 6 - OSRDCH 177114 176026 EQUW _FILE ; EMT 7 - OSFILE 177116 175676 EQUW _ARGS ; EMT 8 - OSARGS 177120 176200 EQUW _BGET ; EMT 9 - OSBGET 177122 176250 EQUW _BPUT ; EMT 10 - OSBPUT 177124 176132 EQUW _GBPB ; EMT 11 - OSGBPB 177126 175750 EQUW _FIND ; EMT 12 - OSFIND 177130 176630 EQUW EMT13 ; EMT 13 - System control 177132 176506 EQUW EMT14 ; EMT 14 - Set handlers 177134 176472 EQUW EMT15 ; EMT 15 - ERROR 177136 EMTCORE: EQU ($-EMTDEFAULT)/2 177136 177024 EQUW EMTNULL ; EMTs 16-255 - unused 177140 177140 177140 ; ***************** 177140 ; TUBE I/O ROUTINES 177140 ; ***************** 177140 177140 ; Send -string at R1 to Tube Register 2 177140 ; ========================================= 177140 SEND_STR: 177140 112100 MOVB (R1)+,R0 ; Get byte from R1, increment R1 177142 004767 000052 JSR PC,SEND_BYTE ; Send byte via Tube R2 177146 020027 000015 CMP R0,#13 ; Test current character 177152 001372 BNE SEND_STR ; Loop until sent 177154 000207 RTS PC 177156 177156 177156 ; Send block at R1 to Tube Register 2 177156 ; =================================== 177156 SEND_BLK: 177156 060201 ADD R2,R1 ; Add length of control block to R1 177160 SEND_BLKLP: 177160 114100 MOVB -(R1),R0 ; Decrement R1, Get byte from R1 177162 004767 000032 JSR PC,SEND_BYTE ; Send byte via Tube R2 177166 005302 DEC R2 ; Decrement count 177170 001373 BNE SEND_BLKLP ; Loop until all sent 177172 000207 RTS PC 177174 177174 177174 ; Wait for block at R1 from Tube Register 2 177174 ; ========================================= 177174 WAIT_BLK: 177174 060201 ADD R2,R1 ; Add length of control block to R1 177176 WAIT_BLKLP: 177176 004767 177026 JSR PC,WAIT_BYTE ; Wait for byte via Tube R2 177202 110041 MOVB R0,-(R1) ; Decrement R1, store byte to R1 177204 005302 DEC R2 ; Decrement count 177206 001373 BNE WAIT_BLKLP ; Loop until all received 177210 000207 RTS PC 177212 177212 177212 ; Send command in R0 followed by byte in R1 177212 ; ========================================= 177212 SEND_CMD_R1: 177212 004767 000002 JSR PC,SEND_CMD ; Send command 177216 177216 177216 ; Send byte in R1 to Tube Register 2 177216 ; ================================== 177216 SEND_BYTE_R1: 177216 010100 MOV R1,R0 ; Pass byte to R0 and fall through 177220 177220 177220 ; Send byte in R0 to Tube Register 2 177220 ; ================================== 177220 SEND_CMD: 177220 SEND_BYTE: 177220 032737 000100 177764 BIT #&40,@#TUBE2S ; Check b6 of Tube R2 status 177226 001774 BEQ SEND_BYTE ; Loop until b6 set 177230 110037 177766 MOVB R0,@#TUBE2 ; Send byte to Tube R2 177234 000207 RTS PC 177236 177236 177236 ; Host->Client communication via interupts 177236 ; ======================================== 177236 Get_R1: 177236 113700 177760 MOVB @#TUBE1S,R0 ; Read Tube R1 status 177242 100375 BPL Get_R1 ; Loop until b7 set 177244 113700 177762 MOVB @#TUBE1,R0 ; Get byte from Tube R1 177250 000207 RTS PC 177252 177252 Get_R4: 177252 113700 177774 MOVB @#TUBE4S,R0 ; Read Tube R4 status 177256 100375 BPL Get_R4 ; Loop until b7 set 177260 113700 177776 MOVB @#TUBE4,R0 ; Get byte from Tube R4 177264 000207 RTS PC 177266 177266 177266 ; Interrupt handler 177266 ; ================ 177266 ; When Host sends a byte to R1 or R4 it generates a Client IRQ. 177266 ; Within the interupt handler PSW has been saved on the stack 177266 ; and further interrupts are disabled. 177266 ; 177266 IRQ: 177266 010046 MOV R0,-(SP) ; Save R0 177270 113700 177774 MOVB @#TUBE4S,R0 ; Read Tube R4 status 177274 100434 BMI IRQ_R4 ; If b7 set, R4 generated the interrupt 177276 113700 177760 MOVB @#TUBE1S,R0 ; Read Tube R1 status 177302 100403 BMI IRQ_R1 ; If b7 set, R1 generated the interrupt 177304 012600 MOV (SP)+,R0 ; Get R0 back 177306 000177 173436 JMP @IRQV ; DMB Something else generated the interrupt 177312 177312 ; Data present in Tube R1 generated an interrupt 177312 ; 177312 IRQ_R1: 177312 113700 177762 MOVB @#TUBE1,R0 ; Get byte from Tube R1 177316 100420 BMI IRQ_ESCAPE ; b7 set, change Escape state 177320 ; 177320 ; R1<&80 - Host event being passed to client 177320 ; Tube data: via R1: &00 Y X A 177320 ; 177320 010146 MOV R1,-(SP) ; Save R1 177322 010246 MOV R2,-(SP) ; Save R2 177324 004767 177706 JSR PC,Get_R1 ; Wait for byte via Tube R1 177330 010002 MOV R0,R2 ; Pass to R2 177332 004767 177700 JSR PC,Get_R1 ; Wait for byte via Tube R1 177336 010001 MOV R0,R1 ; Pass to R1 177340 004767 177672 JSR PC,Get_R1 ; Wait for byte via Tube R1 177344 004777 173374 JSR PC,@EVENTV ; DMB Call event vector 177350 012602 MOV (SP)+,R2 ; Restore R2 177352 NMI_DONE2: 177352 012601 MOV (SP)+,R1 ; Restore registers 177354 NMI_DONE1: 177354 012600 MOV (SP)+,R0 177356 000002 RTI ; Return from interupt 177360 177360 ; R1>&7F - Host changing Escape state 177360 ; Tube data: via R1: flag, b7=1, b6=state 177360 ; 177360 IRQ_ESCAPE: 177360 004777 173350 JSR PC,@ESCV ; DMB Call Escape handler 177364 000773 BR NMI_DONE1 ; Restore and return from interrupt 177366 177366 ; Data present in Tube R4 generated an interupt to start a data transfer 177366 ; 177366 IRQ_R4: 177366 010146 MOV R1,-(SP) ; Save R1 177370 113700 177776 MOVB @#TUBE4,R0 ; Get byte from Tube R4 177374 100022 BPL IRQ_DATA ; b7=0, jump to do data transfer 177376 177376 ; R4>&7F - Error occured 177376 ; Tube data: via R4: &FF, via R2: &00 err string &00 177376 ; 177376 004767 176626 JSR PC,WAIT_BYTE ; Wait for an initial byte from R2 177402 013701 172742 MOV @#ERRADDR,R1 ; Point to error buffer 177406 004767 176616 JSR PC,WAIT_BYTE 177412 110021 MOVB R0,(R1)+ ; Store error number 177414 IRQ_R4LP: 177414 004767 176610 JSR PC,WAIT_BYTE ; Wait for byte of error string 177420 110021 MOVB R0,(R1)+ ; Store in error buffer 177422 001374 BNE IRQ_R4LP ; Loop until terminating &00 received 177424 012601 MOV (SP)+,R1 ; Restore R1 177426 012600 MOV (SP)+,R0 ; Balance stack 177430 013700 172742 MOV @#ERRADDR,R0 ; Point to error block 177434 013716 172740 MOV @#ERRV,(SP) ; Replace return address with error handler 177440 000002 RTI ; Restore PSW and jump to error handler 177442 177442 ; R4<&80 - Data transfer 177442 ; Tube data: via R4: action ID address sync, via R3: data 177442 ; 177442 IRQ_DATA: 177442 ; R0=transfer type, (sp)=mainline R0 177442 ; 177442 010001 MOV R0,R1 ; Save transfer type in R1 177444 004767 177602 JSR PC,Get_R4 ; Wait for caller ID 177450 020127 000005 CMP R1,#5 ; Is transfer 'release'? 177454 001736 BEQ NMI_DONE2 ; Exit if 'release', restoring two registers 177456 004767 177570 JSR PC,Get_R4 ; Get data address byte 4 177462 004767 177564 JSR PC,Get_R4 ; Get data address byte 3 177466 004767 177560 JSR PC,Get_R4 ; Get data address byte 2 177472 110037 172753 MOVB R0,@#IRQADDR+1 177476 004767 177550 JSR PC,Get_R4 ; Get data address byte 1 177502 110037 172752 MOVB R0,@#IRQADDR+0 177506 060101 ADD R1,R1 ; Index into NMI dispatch table 177510 016137 177736 000200 MOV NMIADDRS(R1),@#NMIVEC ; Set up NMI vector 177516 004767 177530 JSR PC,Get_R4 ; Get sync byte 177522 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177526 020127 000010 CMP R1,#4*2 ; Check transfer type 177532 001435 BEQ NMI4 ; Jump with Set Execute Address 177534 020127 000014 CMP R1,#6*2 ; Check transfer type 177540 103704 BCS NMI_DONE2 ; Jump to exit if not 256-byte transfers 177542 001412 BEQ NMI6 ; Jump with 256-byte write 177544 177544 ; Transfer 7 - Read 256 bytes from Host via R3 177544 ; -------------------------------------------- 177544 NMI7: 177544 012701 000400 MOV #256,R1 ; Prepare to transfer 256 bytes 177550 NMI7_LOOP: 177550 105737 177770 TSTB @#TUBE3S 177554 100375 BPL NMI7_LOOP ; Wait for Tube R3 ready 177556 113720 177772 MOVB @#TUBE3,(R0)+ ; Fetch byte from Tube R3 and store 177562 005301 DEC R1 ; Decrement count 177564 001371 BNE NMI7_LOOP ; Loop for 256 bytes 177566 000671 BR NMI_DONE2 ; Finished, pop two registers and return 177570 177570 ; Transfer 6 - Send 256 bytes to Host via R3 177570 ; ------------------------------------------ 177570 NMI6: 177570 012701 000400 MOV #256,R1 ; Prepare to transfer 256 bytes 177574 NMI6_LOOP: 177574 105737 177770 TSTB @#TUBE3S 177600 100375 BPL NMI6_LOOP ; Wait for Tube R3 ready 177602 112037 177772 MOVB (R0)+,@#TUBE3 ; Fetch byte and send to Tube R3 177606 005301 DEC R1 ; Decrement count 177610 001371 BNE NMI6_LOOP ; Loop for 256 bytes 177612 NMI6_DONE: 177612 105737 177770 TSTB @#TUBE3S 177616 100375 BPL NMI6_DONE ; Wait for Tube R3 ready again 177620 105037 177772 CLRB @#TUBE3 ; Send final sync byte 177624 000652 BR NMI_DONE2 ; Finished, pop two registers and return 177626 177626 ; Transfer 4 - Set Execute Address 177626 ; -------------------------------- 177626 NMI4: 177626 010037 172770 MOV R0,@#ADDR ; Set Execute address 177632 000647 BR NMI_DONE2 ; Restore two registers and return 177634 177634 ; Transfer 3 - Read double bytes from host 177634 ; ---------------------------------------- 177634 NMI3: 177634 010046 MOV R0,-(SP) 177636 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177642 113720 177772 MOVB @#TUBE3,(R0)+ ; Read two bytes 177646 113720 177772 MOVB @#TUBE3,(R0)+ 177652 000423 BR NMI_UPDATE ; Update transfer address and return 177654 177654 ; Transfer 2 - Send double bytes to host 177654 ; -------------------------------------- 177654 NMI2: 177654 010046 MOV R0,-(SP) 177656 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177662 112037 177772 MOVB (R0)+,@#TUBE3 ; Send two bytes 177666 112037 177772 MOVB (R0)+,@#TUBE3 177672 000413 BR NMI_UPDATE ; Update transfer address and return 177674 177674 ; Transfer 1 - Read single byte from host 177674 ; --------------------------------------- 177674 NMI1: 177674 010046 MOV R0,-(SP) 177676 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177702 113720 177772 MOVB @#TUBE3,(R0)+ ; Transfer byte from Tube 177706 000405 BR NMI_UPDATE ; Update transfer address and return 177710 177710 ; Transfer 0 - Send single byte to Host 177710 ; ------------------------------------- 177710 NMI0: 177710 010046 MOV R0,-(SP) 177712 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177716 112037 177772 MOVB (R0)+,@#TUBE3 ; Transfer byte to Tube 177722 177722 NMI_UPDATE: 177722 010037 172752 MOV R0,@#IRQADDR,R0 ; Update data address 177726 000612 BR NMI_DONE1 177730 177730 ; Transfers 4,5,6,7 - Just acknowledge NMI 177730 ; ---------------------------------------- 177730 NMI_ACK: 177730 105037 177772 CLRB @#TUBE3 ; Store to Tube R3 to acknowledge NMI 177734 USERIRQ: ; Default unknown IRQ handler 177734 NULLIRQ: ; Default unused hardware vector handler 177734 000002 RTI 177736 177736 ; NMI transfer dispatch table 177736 ; --------------------------- 177736 NMIADDRS: 177736 177710 EQUW NMI0 ; Single byte to host 177740 177674 EQUW NMI1 ; Single byte from host 177742 177654 EQUW NMI2 ; Double byte to host 177744 177634 EQUW NMI3 ; Double byte from host 177746 177730 EQUW NMI_ACK ; Execute 177750 177730 EQUW NMI_ACK ; Release 177752 177730 EQUW NMI_ACK ; 256 bytes to host 177754 177730 EQUW NMI_ACK ; 256 bytes from host 177756 177756 177756 ; Spare space 177756 ; =========== 177756 000 000 EQUM TUBEIO-$ ; Spare space 177760 177760 177760 ; Tube I/O registers 177760 ; ================== 177760 000000 000000 000000 000000 000000 000000 000000 000000 EQUW 0,0,0,0,0,0,0,0 ; Tube registers 200000 Errors: 0