000000 ; > Tube11.src - PDP11 Tube Client Code 000000 ; ===================================== 000000 ; Copyright (C)1989,2008,2014,2015,2017,2024 J.G.Harston 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 000000 ; wrong way around, IRQHandler needs IRQs disabled. 000000 ; JGH: GBPB from channel 0 and EXECUTE uses FETCHBYTE. EXECUTE checks 000000 ; RESET/OSCLI flag on error, saves/restores PROG. IRQ_DATA stores ADDR 000000 ; with MOVBs. 000000 ; DMB: EMTVEC corrected. Indirect JMP/JSRs may be wrong, testing with @vec 000000 ; instead of @#vec and JSR PC,@(SP)+ in EMT dispatcher. 000000 ; JGH: Uses OSASCI for SEND_TXT, OSBYTE returns 16-bit results, MEMBOT and 000000 ; MEMTOP are variables. 000000 ; DMB: GetADDR in IRQ_DATA needs to use @#ADDR+n instead of @ADDR+n. MOVB used 000000 ; to read errors. 000000 ; v0.19 10-Aug-2015 DMB: Bugfix: EMT dispatcher index into vectors, unbalanced stack in Release. 000000 ; TST #n,R in EXECUTE should be BIT #n,R. A few ADD/SUB #n wrong way 000000 ; around. 000000 ; JGH: Code with a Unix header copied to MEMBOT instead of &0000 as no MMU 000000 ; in this implementation. 000000 ; Bugs: EXECUTE doesn't prepare valid stack frame for code with Unix 000000 ; header. 000000 ; 11-Aug-2015 DMB: Bugfix, OSWORD <>0 wasn't fetching block lengths correctly. EMT 14 wrote 000000 ; wrong 'address' if not passed 'handler'. ESCHAND should use @ESCADDR. 000000 ; HANDLERS equate had wrong arithmetic. 000000 ; JGH: INIT_ENV correctly initialises EMTs 0-15 and nulls 16-255 instead of 000000 ; setting 16-31 to garbage and missing 240-255 completely. EQUs to allow 000000 ; 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. 000000 ; All unused hardware vectors set to NULLIRQ. 000000 ; 13-Aug-2015 JGH: Tidied up and optimised EXECUTE, OSCLI saves all registers, uses 000000 ; internal stack. 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 000000 ; initialised starting from wrong workspace offset, TRAP vector was wrong. 000000 ; Wrong address mode in 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 000000 ; to change it. Optimised EMT15 to free up space to fit EMT14 bugfix. 000000 ; 18-Aug-2015 DMB: Bugfix: EMT15 optimisation was EMTHANDLER ERRV instead of mainline ERRV. 000000 ; v0.21 19-Aug-2015 JGH: Incremented to version 0.21. Added conditional switch to remove 000000 ; BPUT/BGET/GBPB to 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_* 000000 ; routine. 000000 ; 28-Sep-2015 JGH: Bugfixes: EXECUTE doesn't make raw code the current program. EXECUTE no 000000 ; longer loses contents of R0 when calling FETCHWORD. Child program 000000 ; inherits parent environment until child requests new environment, so 000000 ; transient children pass errors/exits/etc to caller. 000000 ; v0.23 27-Oct-2015 JGH: MEMTOP moved if executed code is in high memory. LPTR set to command 000000 ; parameters, 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 000000 ; transfers) should not change ADDR, as changing ADDR means peeking 000000 ; Client memory during startup causes a Soft Break to jump to the peeked 000000 ; 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 000000 ; no longer has a header, so on the next Soft Break is re-entered but not 000000 ; made the current program as it does not have a header, so on the next 000000 ; Soft Break after that it is not re-entered. EMT 13 to create a new 000000 ; environment makes the caller the current program. 000000 ; 24-Jul-2017 JGH: NMI data transfer address now a seperate variable so able to rationalise 000000 ; and tidy ADDRHI/ADDR/TRANS/PROG variables. 000000 ; 05-Aug-2017 JGH: EXECUTE sets TRANS to entered code even if it has no header - as when 000000 ; re-entering code with a Unix header after Soft-Reset when it will have 000000 ; been moved and the header 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 000000 ; LPTR. Rolled over to version 0.28. 000000 ; v0.28 24-May-2022 JGH: Supervisor claims handlers when entered as default EXIT handler and 000000 ; 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 ; *BUG* All routines that return a byte in R0 actually sign extend the 000000 ; returned value, so instead of &00-&FF being returned, they return 000000 ; &FF80-&007F. This affects OSRDCH, OSBGET, OSARGS, OSFILE, OSFIND, 000000 ; OSGBPB. This causes OSWORD to fail if the control block length is 000000 ; &80 as it tries to transfer &FF80 bytes. 000000 ; *BUG* OSBYTE never returns Carry set due to SWAB clearing the carry. 000000 ; This means that calls to INKEY and buffer read/write/test do not return 000000 ; any information in the Carry flag. 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 ; v0.30d 09-May-2024 JGH OSWORD dispatch rewritten and optimised to correctly use 8-bit lengths. 000000 ; v0.40 10-May-2024 JGH Rolled over to v0.40. 000000 VERSION: EQU &0040 000000 BUILD: EQU 0 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 ; Addresses are offset by two, as when PDP11 reads a byte, a word is read, so MOVB TUBEIO+0 000000 ; will also read from TUBEIO+1, so we don't want that to erroneously do anything. Writing a 000000 ; byte always only ever writes a single byte. 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 064 EQUB ((VERSION >> 4) AND 15)+48 174027 060 EQUB (VERSION AND 15)+48 174030 #ifdef BUILD 174030 040 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 002514 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 002150 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 002562 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 002004 JSR PC,_NEWL ; Print a newline 174224 004767 000050 JSR PC,SEND_TXT ; Print error message 174230 004767 001774 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 001722 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 002440 JSR PC,SEND_CMD ; Send command &02 - OSCLI 174544 004767 002312 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 001374 JSR PC,WAIT_BYTE ; Wait for result via Tube R2 (preserves Cy) 174556 132700 000200 BITB #&80,R0 ; Test bit 7, must not affect Carry 174562 001721 BEQ CLI_DONE ; No code to be executed 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 001550 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 002016 JSR PC,SEND_CMD_R1 ; Send command and second parameter 175160 011600 MOV (SP),R0 ; Get first parameter from top of stack 175162 004767 002016 JSR PC,SEND_BYTE ; Send first parameter 175166 004767 000760 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 001754 JSR PC,SEND_CMD_R1 ; Send command and second parameter 175222 010200 MOV R2,R0 175224 004767 001754 JSR PC,SEND_BYTE ; Send third parameter 175230 012600 MOV (SP)+,R0 ; Get first parameter from stack 175232 004767 001746 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 000672 JSR PC,WAIT_BYTE ; Wait for response 175260 062700 177600 ADD #&FF80,R0 ; Copy b7 into Carry 175264 006116 ROL (SP) ; Save Carry on stack with saved R0 175266 004767 000660 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 000646 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 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 001466 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 001626 JSR PC,SEND_CMD ; Send command &08 - OSWORD 175356 011600 MOV (SP),R0 ; Get R0 back 175360 010146 MOV R1,-(SP) ; Save R1=>control block 175362 004767 001616 JSR PC,SEND_BYTE ; Send OSWORD number 175366 105700 TSTB R0 ; Check OSWORD number 175370 100412 BMI WORDHI ; >&7F, get lengths from control block 175372 012702 000020 MOV #&10,R2 ; OSWORD &15-&7F uses 16 bytes both ways 175376 010203 MOV R2,R3 175400 020027 000025 CMP R0,#&15 ; OSWORD &01-&7F use fixed control block sizes 175404 103006 BCC WORDGO ; OSWORD &15-&7F, jump to send OSWORD command 175406 060000 ADD R0,R0 ; Double R0 to index into table 175410 062700 175442 ADD #WORD_TABLE-2,R0 ; Point to table entry DMB: #WORD_TABLE 175414 010001 MOV R0,R1 ; R1=>lookup table entry 175416 WORDHI: 175416 112102 MOVB (R1)+,R2 ; Fetch send length - *NOTE* &80 becomes &FF80 175420 111103 MOVB (R1),R3 ; Fetch receive length - *NOTE* &80 becomes &FF80 175422 WORDGO: 175422 012601 MOV (SP)+,R1 ; Get R1=>control block back 175424 004767 001476 JSR PC,SEND_BLK80 ; Send control block with 8-bit length 175430 004767 001516 JSR PC,WAIT_BLK80 ; Read returned control block with 8-bit length 175434 012600 MOV (SP)+,R0 ; Restore registers 175436 012602 MOV (SP)+,R2 175440 012603 MOV (SP)+,R3 175442 000207 RTS PC 175444 ; 175444 ; Table of OSWORD control block lengths for &01-&14 175444 ; ------------------------------------------------- 175444 ; low byte=send length, high byte=recive length 175444 WORD_TABLE: 175444 002400 EQUW &0500 ; &01 =TIME 175446 000005 EQUW &0005 ; &02 TIME= 175450 002400 EQUW &0500 ; &03 =TIMER 175452 000005 EQUW &0005 ; &04 TIMER= 175454 002404 EQUW &0504 ; &05 =MEM 175456 000005 EQUW &0005 ; &06 MEM= 175460 000010 EQUW &0008 ; &07 SOUND 175462 000016 EQUW &000E ; &08 ENVELOPE 175464 002404 EQUW &0504 ; &09 =POINT 175466 004401 EQUW &0901 ; &0A Read bitmap 175470 002401 EQUW &0501 ; &0B Read palette 175472 000005 EQUW &0005 ; &0C Write palette 175474 004000 EQUW &0800 ; &0D Read graphics coords 175476 014420 EQUW &1910 ; &0E =TIME$ 175500 000040 EQUW &0020 ; &0F TIME$= 175502 000420 EQUW &0110 ; &10 Net_Tx 175504 006415 EQUW &0D0D ; &11 Net_Rx 175506 100000 EQUW &8000 ; &12 Net_Params 175510 004010 EQUW &0808 ; &13 Net_Info 175512 100200 EQUW &8080 ; &14 NetFS_Op 175514 175514 175514 ; Read a line of text 175514 ; ------------------- 175514 ; Tube data: &0A block -- &FF or &7F string &0D 175514 ; 175514 RDLINE: 175514 012700 000012 MOV #10,R0 175520 004767 001460 JSR PC,SEND_CMD ; Send command &0A - RDLINE 175524 062701 000002 ADD #2,R1 175530 012702 000003 MOV #3,R2 175534 004767 001374 JSR PC,SEND_BLK ; Send 3-byte control block 175540 012700 000007 MOV #7,R0 175544 004767 001434 JSR PC,SEND_BYTE ; Send &0700 175550 005000 CLR R0 175552 004767 001426 JSR PC,SEND_BYTE 175556 004767 000370 JSR PC,WAIT_BYTE ; Wait for response 175562 005002 CLR R2 ; Clear count of received bytes 175564 062700 177600 ADD #&FF80,R0 ; Copy b7 into Carry 175570 103412 BCS RD_DONE 175572 004767 176450 JSR PC,FETCHWORD2 ; Get address to store text, allowing for nonalignment 175576 RD_STR: 175576 004767 000350 JSR PC,WAIT_BYTE ; Wait for byte from Tube R2 175602 110021 MOVB R0,(R1)+ ; Store it 175604 005202 INC R2 ; Increment number of bytes 175606 020027 000015 CMP R0,#13 ; Check current byte 175612 001371 BNE RD_STR ; Loop until and Clear Carry 175614 005302 DEC R2 ; R2 is length of string 175616 RD_DONE: 175616 000207 RTS PC 175620 175620 175620 ; OSARGS - Read info on open file or filing system 175620 ; ================================================ 175620 ; On entry: R0=function 175620 ; R1=handle 175620 ; R2=>control block 175620 ; On exit: R0=returned value 175620 ; R1 preserved 175620 ; R2 preserved 175620 ; 175620 ; Tube Data: &0C handle block function -- result block 175620 ; 175620 _ARGS: 175620 010246 MOV R2,-(SP) ; Save control block pointer 175622 010146 MOV R1,-(SP) ; Save handle 175624 010046 MOV R0,-(SP) ; Save function 175626 012700 000014 MOV #&0C,R0 175632 004767 001340 JSR PC,SEND_CMD_R1 ; Send command and handle 175636 010201 MOV R2,R1 175640 012702 000004 MOV #4,R2 175644 004767 001264 JSR PC,SEND_BLK ; Send four-byte control block 175650 012600 MOV (SP)+,R0 175652 004767 001326 JSR PC,SEND_BYTE ; Send function 175656 004767 000270 JSR PC,WAIT_BYTE ; Wait for returned result 175662 010046 MOV R0,-(SP) ; Save result 175664 012702 000004 MOV #4,R2 ; Prepare to wait for 4-byte control block 175670 000463 BR FILE_DONE ; Wait for control block, restore and return 175672 175672 175672 ; OSFIND - Open or Close a file 175672 ; ============================= 175672 ; On entry: R0=function 175672 ; R1=handle or =>filename 175672 ; On exit: R0=zero or handle 175672 ; 175672 ; Tube data: &12 function string &0D -- handle 175672 ; &12 &00 handle -- &7F 175672 ; 175672 _FIND: 175672 010046 MOV R0,-(SP) ; Save R0 175674 012700 000022 MOV #&12,R0 175700 004767 001300 JSR PC,SEND_CMD ; Send command &12 - OSFIND 175704 012600 MOV (SP)+,R0 ; Get R0 back 175706 004767 001272 JSR PC,SEND_BYTE ; Send function 175712 005700 TST R0 ; Check function 175714 001006 BNE OPEN ; Jump to deal with OPEN 175716 ; CLOSE 175716 004767 001260 JSR PC,SEND_BYTE_R1 ; Send handle to Tube 175722 004767 000224 JSR PC,WAIT_BYTE ; Wait for acknowledgement 175726 005000 CLR R0 ; Zero R0 175730 000207 RTS PC 175732 OPEN: 175732 010146 MOV R1,-(SP) ; Save R1 175734 004767 001122 JSR PC,SEND_STR ; Send string at R1 175740 004767 000206 JSR PC,WAIT_BYTE ; Wait for returned handle 175744 012601 MOV (SP)+,R1 ; Restore R1 175746 000207 RTS PC 175750 175750 175750 ; OSFILE - Operate on whole files 175750 ; =============================== 175750 ; On entry: R0=function 175750 ; R1=>control block 175750 ; On exit: R0=result 175750 ; R1 preserved 175750 ; control block updated 175750 ; 175750 ; Tube data: &14 block string function -- result block 175750 ; 175750 _FILE: 175750 010246 MOV R2,-(SP) ; Save R2 175752 010146 MOV R1,-(SP) ; Save R1 175754 010046 MOV R0,-(SP) ; Save function 175756 012700 000024 MOV #&14,R0 175762 004767 001216 JSR PC,SEND_CMD ; Send command &14 - OSFILE 175766 062701 000002 ADD #2,R1 ; Point to control block contents 175772 012702 000020 MOV #16,R2 175776 004767 001132 JSR PC,SEND_BLK ; Send 16-byte control block 176002 004767 176240 JSR PC,FETCHWORD2 ; Get address of filename, allowing for nonalignment 176006 004767 001050 JSR PC,SEND_STR ; Send filename string 176012 012600 MOV (SP)+,R0 176014 004767 001164 JSR PC,SEND_BYTE ; Send function 176020 004767 000126 JSR PC,WAIT_BYTE ; Wait for returned result 176024 011601 MOV (SP),R1 ; Get control block pointer back 176026 010046 MOV R0,-(SP) ; Save result 176030 062701 000002 ADD #2,R1 ; Point to control block contents 176034 012702 000020 MOV #16,R2 ; Prepate to wait for 16-byte control block 176040 FILE_DONE: 176040 004767 001114 JSR PC,WAIT_BLK ; Wait for control block 176044 012600 MOV (SP)+,R0 ; Get result back 176046 012601 MOV (SP)+,R1 ; Get control block pointer back 176050 012602 MOV (SP)+,R2 ; Get R2 back 176052 000207 RTS PC 176054 176054 176054 ; OS_GBPB - Multiple byte read and write 176054 ; ===================================== 176054 ; On entry: R0=function 176054 ; R1=>control block 176054 ; On exit: R0=returned value 176054 ; control block updated 176054 ; 176054 ; Tube data: &16 block function -- block Carry result 176054 ; 176054 _GBPB: 176054 #ifndef NOCHNZERO 176054 TSTB (R1) ; Check handle 176054 BNE GBPB1 ; Non-zero handle 176054 TST R0 ; Check function 176054 BEQ GBPB1 ; Pass OSGBPB 0 to Tube 176054 CMP R0,#5 176054 BCS GBPB_RDWR ; Channel 0 via OSRDCH/OSWRCH 176054 #endif 176054 GBPB1: 176054 010246 MOV R2,-(SP) ; Save R2 176056 010046 MOV R0,-(SP) ; Save function 176060 012700 000026 MOV #&16,R0 176064 004767 001114 JSR PC,SEND_CMD ; Send command &16 - OSGBPB 176070 012702 000015 MOV #13,R2 176074 004767 001034 JSR PC,SEND_BLK ; Send 13-byte control block 176100 012600 MOV (SP)+,R0 176102 004767 001076 JSR PC,SEND_BYTE ; Send function 176106 012702 000015 MOV #13,R2 176112 004767 001042 JSR PC,WAIT_BLK ; Wait for 13-byte control block 176116 012602 MOV (SP)+,R2 ; Get R2 back 176120 000410 BR WAIT_CHAR ; Get Carry and result byte 176122 ; 176122 ; Read or write block of memory to/from OSWRCH/OSRDCH 176122 ; --------------------------------------------------- 176122 ; NB, only uses 16-bit address and count, b16-b31 ignored and not updated 176122 ; 176122 #ifndef NOCHNZERO 176122 GBPB_RDWR: 176122 MOV R2,-(SP) ; Save R2 176122 MOV R0,-(SP) ; Save function 176122 MOV R1,-(SP) ; Save pointer to control block 176122 INC R1 ; Point to Address 176122 JSR PC,FETCHWORD ; R1=Address, allowing for nonalignment 176122 MOV R1,R2 ; R2=Address 176122 MOV (SP)+,R1 ; Restore pointer to control block 176122 176122 GBPB_LP: 176122 CMP (SP),#3 176122 BCC GBPB_RD ; Function 3/4, read characters 176122 ; Function 1/2, write characters 176122 MOVB (R2)+,R0 ; Get character from memory 176122 JSR PC,_WRCH ; Write it 176122 BR GBPB_NEXT ; Jump to update and loop 176122 GBPB_RD: 176122 JSR PC,_RDCH ; Read character 176122 BCS GBPB_EXIT ; Carry set, exit 176122 MOVB R0,(R2)+ ; Store character 176122 GBPB_NEXT: 176122 TSTB 5(R1) ; Test byte low byte 176122 BNE GBPB_LO ; Count<>&xx00 176122 DECB 6(R1) ; Decrement count high byte 176122 GBPB_LO: 176122 DECB 5(R1) ; Decrement count low byte 176122 BNE GBPB_LP ; Loop until all done 176122 TSTB 6(R1) ; Test count high byte 176122 BNE GBPB_LP ; Loop until all done 176122 CLC ; Clear carry for OK 176122 GBPB_EXIT: 176122 MOVB R2,1(R1) 176122 SWAB R2 176122 MOVB R2,2(R1) ; Update address 176122 TST (SP)+ ; Drop function 176122 MOV (SP)+,R2 ; Restore R2 176122 MOV #0,R0 ; R0=0, function supported, don't affect Carry 176122 RTS PC 176122 #endif 176122 176122 176122 ; OSBGET - Get a byte from open file 176122 ; ================================== 176122 ; On entry: R1=handle 176122 ; On exit: R0=byte Read 176122 ; R1=preserved 176122 ; Cy set if EOF 176122 ; 176122 ; Tube data: &0E handle -- Carry byte 176122 ; 176122 _BGET: 176122 #ifndef NOCHNZERO 176122 TST R1 ; Check handle 176122 BEQ _RDCH ; BGET#0 calls OSRDCH 176122 #endif 176122 012700 000016 MOV #&0E,R0 176126 004767 001044 JSR PC,SEND_CMD_R1 ; Send command and handle 176132 000403 BR WAIT_CHAR ; Wait for Carry, Byte 176134 176134 176134 ; OSRDCH - Wait for character from input stream 176134 ; ============================================= 176134 ; On exit: R0=char, Cy=carry 176134 ; 176134 ; Tube data: &00 -- Carry Char 176134 ; 176134 _RDCH: 176134 005000 CLR R0 176136 004767 001042 JSR PC,SEND_CMD ; Send command &00 - OSRDCH 176142 WAIT_CHAR: 176142 004767 000004 JSR PC,WAIT_BYTE ; Get returned byte 176146 062700 177600 ADD #&FF80,R0 ; Copy b7 into carry 176152 ; Continue to fetch byte from Tube R2 176152 176152 176152 ; Wait for byte in Tube Register 1 to return in R0, preserving Carry 176152 ; ================================================================== 176152 WAIT_BYTE: 176152 113700 177764 MOVB @#TUBE2S,R0 ; Read Tube R2 status 176156 100375 BPL WAIT_BYTE ; Loop until b7 set 176160 113700 177766 MOVB @#TUBE2,R0 ; Get byte from Tube R2 176164 042700 177400 BIC #&FF00,R0 ; Ensure 8-bit value (Carry unchanged) 176170 000207 RTS PC 176172 176172 176172 ; OSBPUT - Put a byte to an open file 176172 ; =================================== 176172 ; On entry: R0=byte to write 176172 ; R1=handle 176172 ; On exit: R0=preserved 176172 ; R1=preserved 176172 ; 176172 ; Tube data: &10 handle byte -- &7F 176172 ; 176172 _BPUT: 176172 #ifndef NOCHNZERO 176172 TST R1 ; Check handle 176172 BEQ _WRCH ; BPUT#0 calls OSWRCH 176172 #endif 176172 010046 MOV R0,-(SP) ; Save R0 176174 012700 000020 MOV #&10,R0 176200 004767 000772 JSR PC,SEND_CMD_R1 ; Send command and handle 176204 011600 MOV (SP),R0 ; Get R0 back 176206 004767 000772 JSR PC,SEND_BYTE ; Send byte to Tube 176212 004767 177734 JSR PC,WAIT_BYTE ; Wait for acknowledgement 176216 012600 MOV (SP)+,R0 ; Restore R0 176220 000207 RTS PC 176222 176222 176222 ; OSASCI - Send ASCII character 176222 ; ============================= 176222 _ASCII: 176222 022700 000015 CMP #13,R0 ; If not , send raw character 176226 001006 BNE _WRCH ; If , fall through to send NEWL 176230 176230 176230 ; OSNEWL - Send LF/CR sequence 176230 ; ============================ 176230 _NEWL: 176230 012700 000012 MOV #10,R0 176234 004767 000004 JSR PC,_WRCH ; Output LF 176240 012700 000015 MOV #13,R0 ; Fall through into WRCH 176244 176244 176244 ; OSWRCH - Send character in R0 to Tube Register 1 176244 ; ================================================ 176244 _WRCH: 176244 SEND_R1LP: 176244 032737 000100 177760 BIT #&40,@#TUBE1S ; Check b6 of Tube R1 status 176252 001774 BEQ SEND_R1LP ; Loop until b6 set 176254 110037 177762 MOVB R0,@#TUBE1 ; Send byte to Tube R1 176260 000207 RTS PC 176262 176262 176262 ; TRAP handler 176262 ; ============ 176262 ; TRAP is used for Unix calls and is followed by a variable number of inline parameters, 176262 ; so it is impossible to simply do a null return. So, the safest option is to give the 176262 ; standard CoPro client 'unsupported' error. 176262 TRAP_HANDLER: 176262 104017 EMT 15 176264 377 102 141 144 000 EQUB 255,"Bad",0 176271 000 ALIGN 176272 176272 176272 ; EMT handler 176272 ; =========== 176272 ; On extry, R0-R5 contain any parameters 176272 ; PSW ignored 176272 ; On exit, R0-R5 contain any returned values 176272 ; C returns any returned value 176272 ; V set if error, R0=>error block 176272 ; 176272 EMT_HANDLER: 176272 042766 000017 000002 BIC #&000F,2(SP) ; Clear stacked flags 176300 013746 172740 MOV @#ERRV,-(SP) ; Save old ERR handler 176304 013746 172754 MOV @#EMTV,-(SP) ; Save old EMT SP 176310 010637 172754 MOV SP,@#EMTV ; Save current EMT SP 176314 012737 176360 172740 MOV #EMT_ERROR,@#ERRV ; Catch EMT errors 176322 005746 TST -(SP) ; Make space on stack 176324 010046 MOV R0,-(SP) ; Save R0 176326 016600 000010 MOV 8(SP),R0 ; Get return address 176332 014000 MOV -(R0),R0 ; Get EMT instruction 176334 042700 177400 BIC #&FF00,R0 ; Get EMT number 176340 #if EMTMAX<256 176340 CMP R0,#EMTMAX 176340 BCC EMT_TOOHIGH ; Out of range 176340 #endif 176340 060000 ADD R0,R0 ; Index into dispatch table 176342 063700 172756 ADD @#EMTADDR,R0 ; Index into dispatch table 176346 011066 000002 MOV (R0),2(SP) ; Copy address to stack 176352 012600 MOV (SP)+,R0 ; Restore R0 176354 004736 JSR PC,@(SP)+ ; Jump to routine 176356 102005 BVC EMT_NOERROR ; V clear, jump to check Carry 176360 EMT_ERROR: 176360 013706 172754 MOV @#EMTV,SP ; Get saved EMT SP 176364 052766 000002 000006 BIS #2,6(SP) ; Set stacked V flag 176372 EMT_NOERROR: 176372 103003 BCC EMT_EXIT ; C clear, jump to exit 176374 052766 000001 000006 BIS #1,6(SP) ; Set stacked C flag 176402 EMT_EXIT: 176402 012637 172754 MOV (SP)+,@#EMTV ; Restore old EMT SP 176406 012637 172740 MOV (SP)+,@#ERRV ; Restore old error handler 176412 000002 RTI ; Return from EMT 176414 #if EMTMAX<256 176414 EMT_TOOHIGH: 176414 MOV (SP)+,R0 ; Restore R0 176414 TST (SP)+ ; Balance stack 176414 BR EMT_EXIT ; Should go through a generic handler 176414 #endif 176414 176414 176414 ; EMT 15 - Generate an error 176414 ; -------------------------- 176414 EMT15: 176414 016600 000006 MOV 6(SP),R0 ; Get return address pointing to inline error block 176420 016666 000004 000006 MOV 4(SP),6(SP) ; Replace return address with mainline error handler 176426 000207 RTS PC ; Return to EMT handler, thence to error handler 176430 176430 176430 ; EMT 14 - Read/Write handlers, etc. 176430 ; ---------------------------------- 176430 ; On entry: R0=0..255 to claim EMTs 0..255 176430 ; R1=new routine address or 0 to read 176430 ; R0=&FFxx to set environment handlers 176430 ; R1=new handler address or 0 to read 176430 ; R2=new handler data address or 0 to read 176430 ; On exit: R0=preserved 176430 ; R1=old address 176430 ; R2=old handler address or preserved 176430 ; 176430 EMT14: 176430 ; So that EMT14 can change the ERRV we have to rewind out of the EMT handler and restore ERRV so 176430 ; that it can be changed. Otherwise, the EMT handler will just restore ERRV to whatever it was 176430 ; before. 176430 005726 TST (SP)+ ; Drop return to EMT handler 176432 012637 172754 MOV (SP)+,@#EMTV ; Restore old EMT SP 176436 012637 172740 MOV (SP)+,@#ERRV ; Restore mainline error handler 176442 010046 MOV R0,-(SP) ; Save R0 176444 100413 BMI EMT14_HANDLER ; Negative, set up handler 176446 020027 000400 CMP R0,#EMTMAX 176452 103033 BCC EMT14_QUIT ; Out of range 176454 060000 ADD R0,R0 ; Double R0 to offset into table 176456 063700 172756 ADD @#EMTADDR,R0 ; Index into EMT dispatch table 176462 011046 MOV (R0),-(SP) ; Get old address 176464 005701 TST R1 176466 001424 BEQ EMT14_READ ; Zero, just read 176470 010110 MOV R1,(R0) ; Store new address if non-zero 176472 000422 BR EMT14_READ 176474 176474 EMT14_HANDLER: 176474 005100 COM R0 176476 ; CMP R0,#HANDLEMAX 176476 020027 000012 CMP R0,#HANDLEMAX+WORKSPMAX/4 ; Allow access to workspace as well as handles 176502 103017 BCC EMT14_QUIT ; Out of range 176504 060000 ADD R0,R0 176506 060000 ADD R0,R0 ; Times four to offset into table 176510 062700 172730 ADD #HANDLERS,R0 ; Index into handlers 176514 011046 MOV (R0),-(SP) ; Save old handler address 176516 005701 TST R1 176520 001401 BEQ EMT14_HAND2 ; Just read old handler address 176522 010110 MOV R1,(R0) ; Store new handler address 176524 EMT14_HAND2: 176524 005720 TST (R0)+ ; DMB: Step to data address 176526 011046 MOV (R0),-(SP) ; Save old data address 176530 005702 TST R2 176532 001401 BEQ EMT14_HAND3 ; Just read old data address 176534 010210 MOV R2,(R0) ; Store new data address 176536 EMT14_HAND3: 176536 012602 MOV (SP)+,R2 ; Get old data 176540 EMT14_READ: 176540 012601 MOV (SP)+,R1 ; Get old address 176542 EMT14_QUIT: 176542 012600 MOV (SP)+,R0 ; Restore R0 176544 000002 RTI 176546 176546 176546 ; EMT 0 - Exit current program 176546 ; ---------------------------- 176546 EMT0: 176546 000177 174156 JMP @EXITV ; Jump via exit handler 176552 176552 176552 ; EMT 13 - Misc control functions 176552 ; ------------------------------- 176552 ; On entry: R0=0 - Load BBC BASIC 176552 ; 1 - Set up new program environment - default environment handlers only, 176552 ; sets this environment as the current program to re-enter on Soft Break 176552 ; 2 - Set up software environment - default environment handlers, memory limits and EMTs 176552 ; 3 - Set up hardware environment - default environment handlers, memory, EMTs, hardware vectors 176552 ; <0 - Emulator only - toggle emulated EMTs on/off 176552 ; 176552 ; NB: When resetting environment, *MUST* return VC, otherwise EMT handler will 'restore' SP 176552 ; to zero. 176552 ; 176552 EMT13: ; Entered with flags already set from R0 176552 001475 BEQ EMTNULL ; R0=0 - unsupported 176554 020027 000003 CMP R0,#3 ; R0=1 - set up environment handlers - create a new program environment 176560 103436 BCS INIT_HANDLES1 ; R0=2 - set up environment handlers, workspace 176562 001071 BNE EMTNULL 176564 176564 176564 ; Set up default system environment 176564 ; ================================= 176564 INIT_ENV: 176564 005000 CLR R0 ; Start at base of hardware vectors 176566 INIT_LP1: 176566 012720 177720 MOV #NULLIRQ,(R0)+ ; Set all hardware vectors to NULLIRQ 176572 005020 CLR (R0)+ ; Allow all interupts 176574 020027 000400 CMP R0,#&100 ; Hardware vectors at at &0000-&00FF 176600 001372 BNE INIT_LP1 176602 012737 176262 000034 MOV #TRAP_HANDLER,@#TRAPVEC ; Set up TRAP vector to give an error 176610 012737 176272 000030 MOV #EMT_HANDLER ,@#EMTVEC ; Set up EMT vector 176616 012737 177714 000200 MOV #NMI_ACK ,@#NMIVEC+0 ; Set up NMI vector 176624 012737 000340 000202 MOV #&00E0 ,@#NMIVEC+2 ; NMI processor status - bar all interupts 176632 012737 177252 000204 MOV #IRQ ,@#IRQVEC+0 ; Set up IRQ vector 176640 012737 000300 000206 MOV #&00C0 ,@#IRQVEC+2 ; IRQ processor status - bar all except NMIs 176646 ; We have CC from CMP/BNE above 176646 012700 000044 MOV #HANDLEWORDS+WORKSPWORDS+EMTCORE,R0 176652 000411 BR INIT_ENV6 ; R0=full system, CC=set EMTs 176654 176654 INIT_HANDLES: 176654 005000 CLR R0 176656 INIT_HANDLES1: 176656 022700 000001 CMP #1,R0 ; 0,1 - CC=handles only, 2 - CS=handles and memory 176662 012700 000014 MOV #HANDLEWORDS,R0 ; R0=word count of handlers 176666 103002 BCC INIT_ENV5 176670 INIT_MEMORY: 176670 INIT_ENV3: 176670 012700 000024 MOV #HANDLEWORDS+WORKSPWORDS,R0 176674 INIT_ENV5: 176674 000261 SEC ; CS=Not resetting EMTs 176676 INIT_ENV6: 176676 012701 176750 MOV #HANDDEFAULT,R1 ; R1=> Default handlers and EMTs 176702 012702 172730 MOV #HANDLERS,R2 ; R2=> Start of handlers DMB 176706 INIT_LP2: 176706 012122 MOV (R1)+,(R2)+ ; Set up initial settings 176710 005300 DEC R0 ; and EMT dispatch table, 176712 001375 BNE INIT_LP2 ; while preserving Carry 176714 103405 BCS INIT_DONE ; Carry was Set on entry, don't initialise EMTs 176716 012700 000360 MOV #EMTMAX-EMTCORE,R0 ; Number of unused EMTs 176722 INIT_CLR: 176722 011122 MOV (R1),(R2)+ ; EMTs 16-255 do nothing 176724 005300 DEC R0 176726 001375 BNE INIT_CLR 176730 INIT_DONE: 176730 013737 172772 172774 MOV @#TRANS,@#PROG ; Make last-entered code the current program 176736 ; Will be set from default with R0=2,3 176736 100403 BMI EMTNULL ; Code in high memory, use pre-initialised MEMTOP 176740 013737 177006 172766 MOV @#MEMDEFAULT,@#MEMTOP ; Code in low memory, use full memory 176746 EMTNULL: ; EMTs 16-255 176746 EVENT: ; Null event handler 176746 000207 RTS PC ; Return with R0=0, R1,R2 corrupted 176750 176750 176750 ; Default settings and EMT table 176750 ; ============================== 176750 HANDDEFAULT: 176750 174102 EQUW EXITHAND ; &D8 - Default exit handler 176752 000100 EQUW VERSION ; &DA - Unused - Client version 176754 174236 EQUW ESCHAND ; &DC - Default escape handler 176756 172777 EQUW ESCFLG ; &DE - Default escape flag 176760 174210 EQUW ERRHAND ; &E0 - Default error handler 176762 172400 EQUW ERRBLK ; &E2 - Default error buffer 176764 176746 EQUW EVENT ; &E4 - Default event handler 176766 000000 EQUW 0 ; &E6 - Unused 176770 177720 EQUW USERIRQ ; &E8 - Default unknown IRQ handler 176772 000000 EQUW 0 ; &EA - Data transfer address within IRQ handler 176774 000000 EQUW 0 ; &EC - Holds old SP within EMT handler 176776 173000 EQUW EMTTABLE ; &EE - Default EMT dispatch table 177000 174004 EQUW BANNER ; &F0 - LPTR - Line pointer 177002 000000 EQUW 0 ; &F2 - ADDRHI - Memory address high word - OSBYTE &82 177004 000400 EQUW RAMSTART ; &F4 - MEMBOT - Lowest user memory - OSBYTE &83 177006 MEMDEFAULT: 177006 172400 EQUW RAMEND ; &F6 - MEMTOP - Highest user memory - OSBYTE &84 177010 174102 EQUW CLICOM ; &F8 - ADDR - Execution address 177012 174102 EQUW CLICOM ; &FA - TRANS - Transfer address 177014 174102 EQUW CLICOM ; &FC - PROG - Current program 177016 000 EQUB 0 ; &FE - Spare byte 177017 000 EQUB 0 ; &FF - ESCFLG - Escape flag 177020 EMTDEFAULT: 177020 176546 EQUW EMT0 ; EMT 0 - QUIT 177022 174334 EQUW _CLI ; EMT 1 - OSCLI 177024 175142 EQUW _BYTE ; EMT 2 - OSBYTE 177026 175334 EQUW _WORD ; EMT 3 - OSWORD 177030 176244 EQUW _WRCH ; EMT 4 - OSWRCH 177032 176230 EQUW _NEWL ; EMT 5 - OSNEWL 177034 176134 EQUW _RDCH ; EMT 6 - OSRDCH 177036 175750 EQUW _FILE ; EMT 7 - OSFILE 177040 175620 EQUW _ARGS ; EMT 8 - OSARGS 177042 176122 EQUW _BGET ; EMT 9 - OSBGET 177044 176172 EQUW _BPUT ; EMT 10 - OSBPUT 177046 176054 EQUW _GBPB ; EMT 11 - OSGBPB 177050 175672 EQUW _FIND ; EMT 12 - OSFIND 177052 176552 EQUW EMT13 ; EMT 13 - System control 177054 176430 EQUW EMT14 ; EMT 14 - Set handlers 177056 176414 EQUW EMT15 ; EMT 15 - ERROR 177060 EMTCORE: EQU ($-EMTDEFAULT)/2 177060 176746 EQUW EMTNULL ; EMTs 16-255 - unused 177062 177062 177062 ; ***************** 177062 ; TUBE I/O ROUTINES 177062 ; ***************** 177062 177062 ; Send -string at R1 to Tube Register 2 177062 ; ========================================= 177062 SEND_STR: 177062 112100 MOVB (R1)+,R0 ; Get byte from R1, increment R1 177064 004767 000114 JSR PC,SEND_BYTE ; Send byte via Tube R2 177070 020027 000015 CMP R0,#13 ; Test current character 177074 001372 BNE SEND_STR ; Loop until sent 177076 000207 RTS PC 177100 177100 BLK80_PREFIX: 177100 004767 000100 JSR PC,SEND_BYTE ; Send block length 177104 010002 MOV R0,R2 ; R1=block length 177106 105302 DECB R2 177110 100404 BMI BLK80_QUIT ; size=0 or size>&80, skip 177112 105202 INCB R2 177114 042702 177400 BIC #&FF00,R2 ; Ensure R2 is 8-bit value 177120 005746 TST -(SP) ; Balance next pop 177122 BLK80_QUIT: 177122 005726 TST (SP)+ ; Quit pops return address 177124 000207 RTS PC 177126 177126 ; Send OSWORD block at R1, length &00-&80 in R2 177126 ; ============================================= 177126 SEND_BLK80: 177126 010200 MOV R2,R0 ; Get send block length 177130 004767 177744 JSR PC,BLK80_PREFIX ; Check for &00-&80 177134 177134 ; Send block at R1, length in R2, to Tube Register 2 177134 ; ================================================== 177134 SEND_BLK: 177134 060201 ADD R2,R1 ; Add length of control block to R1 177136 SEND_BLKLP: 177136 114100 MOVB -(R1),R0 ; Decrement R1, Get byte from R1 177140 004767 000040 JSR PC,SEND_BYTE ; Send byte via Tube R2 177144 005302 DEC R2 ; Decrement count 177146 001373 BNE SEND_BLKLP ; Loop until all sent 177150 SEND_BLKOK: 177150 000207 RTS PC 177152 177152 ; Send OSWORD block at R1, length &00-&80 in R3 177152 ; ============================================= 177152 WAIT_BLK80: 177152 010300 MOV R3,R0 ; Get recive block length 177154 004767 177720 JSR PC,BLK80_PREFIX ; Check for &00-&80 177160 177160 ; Wait for block at R1, length in R2, from Tube Register 2 177160 ; ======================================================== 177160 WAIT_BLK: 177160 060201 ADD R2,R1 ; Add length of control block to R1 177162 WAIT_BLKLP: 177162 004767 176764 JSR PC,WAIT_BYTE ; Wait for byte via Tube R2 177166 110041 MOVB R0,-(R1) ; Decrement R1, store byte to R1 177170 005302 DEC R2 ; Decrement count 177172 001373 BNE WAIT_BLKLP ; Loop until all received 177174 WAIT_BLKOK: 177174 000207 RTS PC 177176 177176 177176 ; Send command in R0 followed by byte in R1 177176 ; ========================================= 177176 SEND_CMD_R1: 177176 004767 000002 JSR PC,SEND_CMD ; Send command 177202 177202 177202 ; Send byte in R1 to Tube Register 2 177202 ; ================================== 177202 SEND_BYTE_R1: 177202 010100 MOV R1,R0 ; Pass byte to R0 and fall through 177204 177204 177204 ; Send byte in R0 to Tube Register 2 177204 ; ================================== 177204 SEND_CMD: 177204 SEND_BYTE: 177204 032737 000100 177764 BIT #&40,@#TUBE2S ; Check b6 of Tube R2 status 177212 001774 BEQ SEND_BYTE ; Loop until b6 set 177214 110037 177766 MOVB R0,@#TUBE2 ; Send byte to Tube R2 177220 000207 RTS PC 177222 177222 177222 ; Host->Client communication via interupts 177222 ; ======================================== 177222 Get_R1: 177222 113700 177760 MOVB @#TUBE1S,R0 ; Read Tube R1 status 177226 100375 BPL Get_R1 ; Loop until b7 set 177230 113700 177762 MOVB @#TUBE1,R0 ; Get byte from Tube R1 177234 000207 RTS PC 177236 177236 Get_R4: 177236 113700 177774 MOVB @#TUBE4S,R0 ; Read Tube R4 status 177242 100375 BPL Get_R4 ; Loop until b7 set 177244 113700 177776 MOVB @#TUBE4,R0 ; Get byte from Tube R4 177250 000207 RTS PC 177252 177252 177252 ; Interrupt handler 177252 ; ================ 177252 ; When Host sends a byte to R1 or R4 it generates a Client IRQ. 177252 ; Within the interupt handler PSW has been saved on the stack 177252 ; and further interrupts are disabled. 177252 ; 177252 IRQ: 177252 010046 MOV R0,-(SP) ; Save R0 177254 113700 177774 MOVB @#TUBE4S,R0 ; Read Tube R4 status 177260 100434 BMI IRQ_R4 ; If b7 set, R4 generated the interrupt 177262 113700 177760 MOVB @#TUBE1S,R0 ; Read Tube R1 status 177266 100403 BMI IRQ_R1 ; If b7 set, R1 generated the interrupt 177270 012600 MOV (SP)+,R0 ; Get R0 back 177272 000177 173452 JMP @IRQV ; DMB Something else generated the interrupt 177276 177276 ; Data present in Tube R1 generated an interrupt 177276 ; 177276 IRQ_R1: 177276 113700 177762 MOVB @#TUBE1,R0 ; Get byte from Tube R1 177302 100420 BMI IRQ_ESCAPE ; b7 set, change Escape state 177304 ; 177304 ; R1<&80 - Host event being passed to client 177304 ; Tube data: via R1: &00 Y X A 177304 ; 177304 010146 MOV R1,-(SP) ; Save R1 177306 010246 MOV R2,-(SP) ; Save R2 177310 004767 177706 JSR PC,Get_R1 ; Wait for byte via Tube R1 177314 010002 MOV R0,R2 ; Pass to R2 177316 004767 177700 JSR PC,Get_R1 ; Wait for byte via Tube R1 177322 010001 MOV R0,R1 ; Pass to R1 177324 004767 177672 JSR PC,Get_R1 ; Wait for byte via Tube R1 177330 004777 173410 JSR PC,@EVENTV ; DMB Call event vector 177334 012602 MOV (SP)+,R2 ; Restore R2 177336 NMI_DONE2: 177336 012601 MOV (SP)+,R1 ; Restore registers 177340 NMI_DONE1: 177340 012600 MOV (SP)+,R0 177342 000002 RTI ; Return from interupt 177344 177344 ; R1>&7F - Host changing Escape state 177344 ; Tube data: via R1: flag, b7=1, b6=state 177344 ; 177344 IRQ_ESCAPE: 177344 004777 173364 JSR PC,@ESCV ; DMB Call Escape handler 177350 000773 BR NMI_DONE1 ; Restore and return from interrupt 177352 177352 ; Data present in Tube R4 generated an interupt to start a data transfer 177352 ; 177352 IRQ_R4: 177352 010146 MOV R1,-(SP) ; Save R1 177354 113700 177776 MOVB @#TUBE4,R0 ; Get byte from Tube R4 177360 100022 BPL IRQ_DATA ; b7=0, jump to do data transfer 177362 177362 ; R4>&7F - Error occured 177362 ; Tube data: via R4: &FF, via R2: &00 err string &00 177362 ; 177362 004767 176564 JSR PC,WAIT_BYTE ; Wait for an initial byte from R2 177366 013701 172742 MOV @#ERRADDR,R1 ; Point to error buffer 177372 004767 176554 JSR PC,WAIT_BYTE 177376 110021 MOVB R0,(R1)+ ; Store error number 177400 IRQ_R4LP: 177400 004767 176546 JSR PC,WAIT_BYTE ; Wait for byte of error string 177404 110021 MOVB R0,(R1)+ ; Store in error buffer 177406 001374 BNE IRQ_R4LP ; Loop until terminating &00 received 177410 012601 MOV (SP)+,R1 ; Restore R1 177412 012600 MOV (SP)+,R0 ; Balance stack 177414 013700 172742 MOV @#ERRADDR,R0 ; Point to error block 177420 013716 172740 MOV @#ERRV,(SP) ; Replace return address with error handler 177424 000002 RTI ; Restore PSW and jump to error handler 177426 177426 ; R4<&80 - Data transfer 177426 ; Tube data: via R4: action ID address sync, via R3: data 177426 ; 177426 IRQ_DATA: 177426 ; R0=transfer type, (sp)=mainline R0 177426 ; 177426 010001 MOV R0,R1 ; Save transfer type in R1 177430 004767 177602 JSR PC,Get_R4 ; Wait for caller ID 177434 020127 000005 CMP R1,#5 ; Is transfer 'release'? 177440 001736 BEQ NMI_DONE2 ; Exit if 'release', restoring two registers 177442 004767 177570 JSR PC,Get_R4 ; Get data address byte 4 177446 004767 177564 JSR PC,Get_R4 ; Get data address byte 3 177452 004767 177560 JSR PC,Get_R4 ; Get data address byte 2 177456 110037 172753 MOVB R0,@#IRQADDR+1 177462 004767 177550 JSR PC,Get_R4 ; Get data address byte 1 177466 110037 172752 MOVB R0,@#IRQADDR+0 177472 060101 ADD R1,R1 ; Index into NMI dispatch table 177474 016137 177722 000200 MOV NMIADDRS(R1),@#NMIVEC ; Set up NMI vector 177502 004767 177530 JSR PC,Get_R4 ; Get sync byte 177506 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177512 020127 000010 CMP R1,#4*2 ; Check transfer type 177516 001435 BEQ NMI4 ; Jump with Set Execute Address 177520 020127 000014 CMP R1,#6*2 ; Check transfer type 177524 103704 BCS NMI_DONE2 ; Jump to exit if not 256-byte transfers 177526 001412 BEQ NMI6 ; Jump with 256-byte write 177530 177530 ; Transfer 7 - Read 256 bytes from Host via R3 177530 ; -------------------------------------------- 177530 NMI7: 177530 012701 000400 MOV #256,R1 ; Prepare to transfer 256 bytes 177534 NMI7_LOOP: 177534 105737 177770 TSTB @#TUBE3S 177540 100375 BPL NMI7_LOOP ; Wait for Tube R3 ready 177542 113720 177772 MOVB @#TUBE3,(R0)+ ; Fetch byte from Tube R3 and store 177546 005301 DEC R1 ; Decrement count 177550 001371 BNE NMI7_LOOP ; Loop for 256 bytes 177552 000671 BR NMI_DONE2 ; Finished, pop two registers and return 177554 177554 ; Transfer 6 - Send 256 bytes to Host via R3 177554 ; ------------------------------------------ 177554 NMI6: 177554 012701 000400 MOV #256,R1 ; Prepare to transfer 256 bytes 177560 NMI6_LOOP: 177560 105737 177770 TSTB @#TUBE3S 177564 100375 BPL NMI6_LOOP ; Wait for Tube R3 ready 177566 112037 177772 MOVB (R0)+,@#TUBE3 ; Fetch byte and send to Tube R3 177572 005301 DEC R1 ; Decrement count 177574 001371 BNE NMI6_LOOP ; Loop for 256 bytes 177576 NMI6_DONE: 177576 105737 177770 TSTB @#TUBE3S 177602 100375 BPL NMI6_DONE ; Wait for Tube R3 ready again 177604 105037 177772 CLRB @#TUBE3 ; Send final sync byte 177610 000652 BR NMI_DONE2 ; Finished, pop two registers and return 177612 177612 ; Transfer 4 - Set Execute Address 177612 ; -------------------------------- 177612 NMI4: 177612 010037 172770 MOV R0,@#ADDR ; Set Execute address 177616 000647 BR NMI_DONE2 ; Restore two registers and return 177620 177620 ; Transfer 3 - Read double bytes from host 177620 ; ---------------------------------------- 177620 NMI3: 177620 010046 MOV R0,-(SP) 177622 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177626 113720 177772 MOVB @#TUBE3,(R0)+ ; Read two bytes 177632 113720 177772 MOVB @#TUBE3,(R0)+ 177636 000423 BR NMI_UPDATE ; Update transfer address and return 177640 177640 ; Transfer 2 - Send double bytes to host 177640 ; -------------------------------------- 177640 NMI2: 177640 010046 MOV R0,-(SP) 177642 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177646 112037 177772 MOVB (R0)+,@#TUBE3 ; Send two bytes 177652 112037 177772 MOVB (R0)+,@#TUBE3 177656 000413 BR NMI_UPDATE ; Update transfer address and return 177660 177660 ; Transfer 1 - Read single byte from host 177660 ; --------------------------------------- 177660 NMI1: 177660 010046 MOV R0,-(SP) 177662 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177666 113720 177772 MOVB @#TUBE3,(R0)+ ; Transfer byte from Tube 177672 000405 BR NMI_UPDATE ; Update transfer address and return 177674 177674 ; Transfer 0 - Send single byte to Host 177674 ; ------------------------------------- 177674 NMI0: 177674 010046 MOV R0,-(SP) 177676 013700 172752 MOV @#IRQADDR,R0 ; Get data address 177702 112037 177772 MOVB (R0)+,@#TUBE3 ; Transfer byte to Tube 177706 177706 NMI_UPDATE: 177706 010037 172752 MOV R0,@#IRQADDR,R0 ; Update data address 177712 000612 BR NMI_DONE1 177714 177714 ; Transfers 4,5,6,7 - Just acknowledge NMI 177714 ; ---------------------------------------- 177714 NMI_ACK: 177714 105037 177772 CLRB @#TUBE3 ; Store to Tube R3 to acknowledge NMI 177720 USERIRQ: ; Default unknown IRQ handler 177720 NULLIRQ: ; Default unused hardware vector handler 177720 000002 RTI 177722 177722 ; NMI transfer dispatch table 177722 ; --------------------------- 177722 NMIADDRS: 177722 177674 EQUW NMI0 ; Single byte to host 177724 177660 EQUW NMI1 ; Single byte from host 177726 177640 EQUW NMI2 ; Double byte to host 177730 177620 EQUW NMI3 ; Double byte from host 177732 177714 EQUW NMI_ACK ; Execute 177734 177714 EQUW NMI_ACK ; Release 177736 177714 EQUW NMI_ACK ; 256 bytes to host 177740 177714 EQUW NMI_ACK ; 256 bytes from host 177742 177742 177742 ; Spare space 177742 ; =========== 177742 000 000 000 000 000 000 000 000 000 000 000 000 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