REM > TubeHost/src REM Tube Host code for BBC/Electron/Master REM Relocatable module, loadable with *SMLoad and *SRLoad : ON ERROR REPORT:PRINT" at line ";ERL:END IF PAGE>&8000:SYS "OS_GetEnv"TOA$:IFLEFT$(A$,5)<>"B6502":OSCLI"B6502"+MID$(A$,INSTR(A$," ")) : TUBE%=&FCE0:REM Electon TUBE%=&FEE0:REM BBC/Master : name$="TUBE":title$="":IF TUBE%=&FCE0:name$="TUBEelk":title$="ELECTRON " DIM mcode% 2100,L%-1:REM Need enough space for two copies OS_CLI=&FFF7:OSBYTE=&FFF4:OSWORD=&FFF1:OSWRCH=&FFEE OSWRCR=&FFEC:OSNEWL=&FFE7:OSASCI=&FFE3:OSRDCH=&FFE0 OSFILE=&FFDD:OSARGS=&FFDA:OSBGET=&FFD7:OSBPUT=&FFD4 OSGBPB=&FFD1:OSFIND=&FFCE:GSINIT=&FFC2:GSREAD=&FFC5 : TUBES1=TUBE%+0:TUBES2=TUBE%+2:TUBES3=TUBE%+4:TUBES4=TUBE%+6 TUBER1=TUBE%+1:TUBER2=TUBE%+3:TUBER3=TUBE%+5:TUBER4=TUBE%+7 : FOR pass%=0 TO 3 opt%=FNsm_pass(pass%) [OPT opt% .RomStart BRK:EQUW RelocTable JMP Service EQUB &82:EQUB Copyright-RomStart .RomTitle EQUB &00:EQUS title$+"TUBE HOST" EQUB &00:EQUS "1.10 (01 Jan 1990)" .Copyright EQUB &00:EQUS "(C)JGH & Acorn":EQUB 0 : .Service CMP #9:BNE NotServ9 :\ Not *Help LDA (&F2),Y CMP #13:BNE Serv9Skip :\ Not *Help LDA &27A:BPL Serv9Skip :\ No Tube present JSR OSNEWL:LDX #0 .Serv9Lp LDA RomTitle+1,X :\ Print ROM title BNE P%+4:LDA #ASC" " :\ Convert &00 to CMP #ASC"(":BEQ Serv9Done :\ End at '(' JSR OSWRCH:INX:BNE Serv9Lp .Serv9Done JSR OSNEWL .Serv9Skip LDA #9:RTS : .NotServ9 CMP #&FE:BCC ServExit :\ Not &FE/&FF, exit BNE ServFF :\ Jump with service call &FF \ ServFE - Tube PostInit \ ---------------------- CPY #0:BEQ ServExit :\ Y=0, exit with no Tube system present LDA #20:LDX #6:JSR OSBYTE :\ Explode character set .ServFElp BIT TUBES1:BPL ServFElp :\ Loop until data present in TubeR0 LDA TUBER1:BEQ ServClaim :\ Fetch byte, if &00 jump to finish JSR OSWRCH:JMP ServFElp :\ Print startup banner character \ ServFF - Tube PreInit \ --------------------- .ServFF LDA #L06AD AND 255:STA &0220 :\ Point EVNTV to Tube event handler LDA #L06AD DIV 256:STA &0221 LDA #L0016 AND 255:STA &0202 :\ Point BRKV to main loop at &0016 LDA #L0016 DIV 256:STA &0203 LDA #&8E:STA TUBES1 :\ Enable NMI on R1, IRQ on R4, IRQ on R1 LDY #&00 :\ Initialise code at &0400-&05FF .ServFFlp1 LDA TubeCode+&000,Y:STA &0400,Y LDA TubeCode+&100,Y:STA &0500,Y LDA TubeCode+&200,Y:STA &0600,Y INY:BNE ServFFlp1 LDX #&41 :\ Initialise main loop at &0016-&0057 .ServFFlp2 LDA TubeZero,X:STA &16,X DEX:BPL ServFFlp2 JSR L0421 :\ Set Tube 'free' and no owner .ServClaim LDA #&00 :\ Claim call .ServExit RTS :\ And return \ Tube idle loop code copied to &0016-&0057 \ ========================================= .TubeZero ]:P%=&16:[OPT opt% \ Tube host workspace \ ------------------- \ &0000 - Control block \ &0012/3 - Control block pointer \ &0014 - b7=Tube free \ &0015 - Claimant ID \ BRK handler \ ----------- .L0016 LDA #&FF:JSR L069E :\ Send &FF to R4 to interupt CoPro LDA TUBER2 :\ Get ACK byte from CoPro via R2 LDA #&00:JSR L0695 :\ Send &00 to R2 to specify ERROR TAY:LDA (&FD),Y:JSR L0695 :\ Send error number via R2 .L0029 INY:LDA (&FD),Y:JSR L0695 :\ Send error character via R2 TAX:BNE L0029 :\ Loop until terminating &00 sent : \ Idle startup \ ------------ .L0032 LDX #&FF:TXS:CLI :\ Clear stack, enable IRQs : \ Tube idle loop \ -------------- .L0036 BIT TUBES1:BPL L0041 :\ No character in R1, check for command in R2 .L003B LDA TUBER1:JSR OSWRCH :\ Get character from R1 and send to OSWRCH .L0041 BIT TUBES2:BPL L0036 :\ No command in R2, loop back BIT TUBES1:BMI L003B :\ Deal with pending character first LDX TUBER2:STX P%+3 :\ Get command from R2 and index into &0500 JMP (&0500) :\ Jump to command routine .L0053 EQUD &00800000 :\ Tube transfer address \ Main Tube code copied to &0400-&06FF \ ==================================== ]:A%=P%-L0016:P%=TubeZero+A%:[OPT opt% .TubeCode ]:P%=&400:[OPT opt% .L0400:JMP L0484 :\ Copy language across the Tube .L0403:JMP L06A7 :\ Copy Escape state across the Tube .L0406 :\ Tube Transfer/Claim/Release CMP #&80:BCC L0435 :\ If <&80, data transfer action CMP #&C0:BCS L0428 :\ &C0-&FF - jump to claim Tube : \ Release Tube \ ------------ ORA #&40 :\ Ensure release ID same as claim ID CMP &15:BNE L0434 :\ Not same as the claim ID, exit .L0414 PHP:SEI :\ Disable IRQs LDA #05:JSR L069E :\ Send &05 via R4 to CoPro JSR L069C :\ Send Tube ID to notify a Tube release PLP :\ Restore IRQs : \ Clear Tube status and owner \ --------------------------- .L0421 LDA #&80 STA &14 :\ Set Tube status to 'free' and ID to 'unclaimed' .L0432 STA &15 :\ Store Tube ID .L0434 RTS \ Claim Tube \ ---------- .L0428 ASL &14:BCS L0432 :\ If Tube free, jump to claim it, exit with CS CMP &15:BEQ L0434 :\ Tube ID same as claimer, we already own it, exit with CS CLC:RTS :\ Exit with CC='can't claim Tube' \ Tube data transfer \ ================== \ On entry, XY=>data transfer address \ A=data transfer action .L0435 PHP:SEI :\ Disable IRQs STX &12:STY &13 :\ Store pointer to control block JSR L069E :\ Send action code via R4 to interrupt CoPro TAX :\ Save action code in X LDY #&03 :\ Prepare to send 4 byte control block JSR L069C :\ Send Tube ID via R4 .L0446 LDA (&12),Y:JSR L069E :\ Send control block across Tube via R4 DEY:BPL L0446 LDY #&18:STY TUBES1 :\ Disable FIFO on R3, and NMI on R3 by default LDA L0518,X:STA TUBES1 :\ Set Tube I/O setting according to action code LSR A:LSR A :\ Move b1 to Carry (b1 set = Copro->I/O) BCC L0463 :\ I/O->CoPro, no pre-delay needed BIT TUBER3:BIT TUBER3 :\ Read R3 twice to delay & empty FIFO .L0463 JSR L069E :\ Send flag via R4 to synchronise .L0466 BIT TUBES4:BVC L0466 :\ Loop until data has left R4 BCS L047A :\ Carry still indicates direction, jump with CoPro->I/O CPX #&04:BNE L0482 :\ If not 'execute code' jump to finish .L0471 JSR L0414 :\ Release Tube JSR L0695 :\ Send &80 via R2 JMP L0032 :\ Jump to Tube Idle loop .L047A LSR A:BCC L0482 :\ If Tube I/O flag in b2 was clear, no NMI required, jump to exit LDY #&88:STY TUBES1 :\ Set Tube I/O to NMI on R3 .L0482 PLP:RTS :\ Restore IRQs and exit \ Copy language across Tube \ ========================= \ On entry, A=1 - enter language, CLC=Break, SEC=OSBYTE 142 \ A=0 - no language found at Break \ .L0484 CLI :\ Enable IRQs BCS L0491 :\ Branch if selected with *fx142 BNE L048C :\ A<>0, jump to enter language JMP L059C :\ A=0, send &7F ack, enter Tube Idle loop \ Language entered at BREAK \ ------------------------- .L048C LDX &028D:BEQ L0471 :\ If Soft Break, just release Tube, send &80 and jump to idle \ The current language is not copied across the Tube on Soft Break, only on \ Power-On Break and Hard Break, or when entered explicitly with OSBYTE 142. \ Language entered with OSBYTE 142, or on Hard Break \ -------------------------------------------------- .L0491 LDA #&FF:JSR L0406 :\ Claim Tube with ID=&3F BCC L0491 :\ Loop until Tube available JSR L04CE :\ Find address to copy language to \ Send language ROM via Tube 256 bytes at a time \ ---------------------------------------------- .L049B PHP:SEI :\ Disable IRQs LDA #&07:JSR L04C7 :\ Start I/O->CoPro 256-byte transfer from (&53-&56) : LDY #&00:STY &00 :\ Start copying from &8000 .L04A6 LDA (&00),Y:STA TUBER3 :\ Get byte from ROM, send to CoPro via R3 NOP:NOP:NOP :\ Delay INY:BNE L04A6 :\ Loop for 256 bytes : PLP :\ Restore IRQs INC L0053+1:BNE L04BC :\ Update transfer address INC L0053+2:BNE L04BC INC L0053+3 .L04BC INC &01 :\ Update source address BIT &01:BVC L049B :\ Check b6 of source high byte, loop until source=&C000 : JSR L04CE :\ Find start address language copied to LDA #&04 :\ Drop through to execute code in CoPro : :\ Finished by sending &80 to Copro via R2 : \ Start a Tube transfer with address block at &0053 \ ------------------------------------------------- .L04C7 LDX #L0053:LDY #&00 :\ Point to Tube control block JMP &0406 :\ Jump to do a data transfer \ Set Tube address to destination to copy language to \ --------------------------------------------------- \ Also sets source address at &00/&01 to &80xx \ .L04CE LDA #&80 STA L0053+1 :\ Set transfer address to &xxxx80xx STA &01 :\ Set source address to &80xx LDA &8006:AND #&20 :\ Check relocation bit in ROM type byte TAY:STY L0053 :\ If no relocation, A=0, Y=0, set address to &xxxx8000 BEQ L04F7 :\ Jump forward with no relocation : LDX &8007 :\ Get offset to ROM copyright .L04E1 INX:LDA &8000,X :\ Skip past copyright message BNE L04E1 :\ Loop until terminating zero byte LDA &8001,X:STA L0053+0 :\ Get relocation address from after copyright message LDA &8002,X:STA L0053+1 LDY &8003,X :\ Get two high bytes to Y and A LDA &8004,X \ Set Tube address high bytes \ --------------------------- .L04F7 STY L0053+2 :\ Set Tube address high bytes STA L0053+3 RTS \ OSRDCH \ ====== .L0537 JSR OSRDCH :\ Wait for a character .L053A ROR A:JSR L0695 :\ Move carry to b7 and send via R2 ROL A:JMP L059E :\ Move A back, send via R2 and return to idle loop \ Spare bytes \ ----------- EQUS STRING$(&500-P%,CHR$0) \ Tube R2 command entry block \ =========================== \ Has to be fixed at &0500 for jump dispatch \ EQUW L0537 :\ &00 OSRDCH EQUW L0596 :\ &02 OSCLI EQUW L05F2 :\ &04 OSBYTELO EQUW L0607 :\ &06 OSBYTEHI EQUW L0627 :\ &08 OSWORD EQUW L0668 :\ &0A RDLINE EQUW L055E :\ &0C OSARGS EQUW L052D :\ &0E OSBGET EQUW L0520 :\ &10 OSBPUT EQUW L0542 :\ &12 OSFIND EQUW L05A9 :\ &14 OSFILE EQUW L05D1 :\ &16 OSGBPB \ Tube data transfer flags \ ------------------------ .L0518 EQUB &86 :\ CoPro->I/O bytes EQUB &88 :\ I/O->CoPro bytes EQUB &96 :\ CoPro->I/O words EQUB &98 :\ I/O->CoPro words EQUB &18 :\ Execute in CoPro EQUB &18 :\ Reserved EQUB &82 :\ CoPro->I/O 256 bytes EQUB &18 :\ I/O->CoPro 256 bytes \ OSBPUT \ ====== .L0520 JSR L06C5:TAY :\ Wait for a handle via R2 JSR L06C5 :\ Wait for a byte via R2 JSR OSBPUT :\ Write byte to file JMP L059C :\ Send &7F ack byte via R2 and return to idle loop \ OSBGET \ ====== .L052D JSR L06C5:TAY :\ Wait for a handle via R2 JSR OSBGET :\ Fetch a byte from file JMP L053A :\ Jump to send Carry and A \ OSFIND \ ====== .L0542 JSR L06C5:BEQ L0552 :\ Wait for a byte in R2, if zero jump to do CLOSE PHA:JSR L0582:PLA :\ Get a string via R2 JSR OSFIND :\ Do the OPEN JMP L059E :\ Send handle via R2 and go to idle loop \ CLOSE \ ----- .L0552 JSR L06C5:TAY :\ Wait for a handle via R2 LDA #&00:JSR OSFIND :\ Do the CLOSE JMP L059C :\ Send &7F ack and jump to idle loop \ OSARGS \ ====== .L055E JSR L06C5:TAY :\ Wait for a handle via R2 LDX #&04:JSR L06BA :\ Fetch four bytes for control block, returns X=&00 JSR L06C5 :\ Wait for ARGS function via R2 JSR OSARGS :\ Do the OSARGS action JSR L0695 :\ Send A back via R2 LDX #&03:JSR L06B5 :\ Send four bytes from control block JMP L0036 :\ Jump to Tube idle loop \ Read a string via R2 into string buffer at &0700 \ ================================================ .L0582 LDY #&00 :\ Y=index into string .L0586 JSR L06C5:STA &0700,Y :\ Wait for a byte via R2, store in string buffer INY:BEQ L0593 :\ Buffer full, end loop CMP #&0D:BNE L0586 :\ Loop until received .L0593 LDX #&00:LDY #&07 :\ Return XY pointing to string at &0700 RTS \ OSCLI \ ===== .L0596 JSR L0582 :\ Read string to &0700 JSR OS_CLI :\ Execute the command \ If the command returns here, the CoPro will get &7F as an acknowledgement. \ The CoPro also gets sent a &7F byte if there is no language available on \ Break. If calling OSCLI results in code being run in the CoPro or a language \ being copied over and entered, the CoPro will get an &80 acknowledgement \ elsewhere. \ Send &7F acknowledgement byte via R2 and return to idle loop \ ------------------------------------------------------------ .L059C LDA #&7F :\ Send &7F to CoPro \ Send byte in A via R2 and return to Tube idle loop \ -------------------------------------------------- .L059E BIT TUBES2:BVC L059E :\ Loop until port free STA TUBER2 :\ Send byte in A .L05A6 JMP L0036 :\ Jump to Tube idle loop \ OSFILE \ ====== .L05A9 LDX #&10 :\ Loop for 16-byte control block .L05AB JSR L06C5:STA &01,X :\ Get byte via R2, store in control block DEX:BNE L05AB JSR L0582 :\ Read string to &0700, returns YX=&0700 STX &00:STY &01 :\ Point control block to string JSR L06C3 :\ Wait for action byte via R2, returns Y=&00 JSR OSFILE :\ Do the OSFILE call \ During the OSFILE call the Tube system may be called to do a data transfer JSR L0695 :\ Send result back via R2 LDX #&10 :\ Send 16-byte control block back .L05C7 LDA &01,X:JSR L0695 :\ Get byte from control block, send via R2 DEX:BNE L05C7 BEQ L05A6 :\ Jump to Tube idle loop \ OSGBPB \ ====== .L05D1 LDX #&0D:JSR L06BA :\ Fetch 13-byte control block, returns X=&00 JSR L06C3 :\ Wait for action byte via R2, returns Y=&00 JSR OSGBPB :\ Do the OSGBPB call \ During the OSGBPB call the Tube system may be called to do a data transfer PHA :\ Save result LDX #&0C:JSR L06B5 :\ Save the result, send 13-byte control block PLA:JMP L053A :\ Get result byte, jump to send Carry and A, :\ then return to Tube idle loop \ OSBYTELO - OSBYTEs &00-&7F \ ========================== .L05F2 JSR L06C5:TAX :\ Wait for X parameter via R2 JSR L06C5 :\ Wait for A parameter via R2 JSR OSBYTE :\ Do the OSBYTE call \ Send byte in X via R2 and jump to Tube idle loop \ ------------------------------------------------ .L05FC BIT TUBES2:BVC L05FC :\ Loop until port free STX TUBER2 :\ Send X via R2 .L0604 JMP L0036 :\ Return to Tube idle loop \ OSBYTEHI - OSBYTE &80-&FF \ ========================= .L0607 JSR L06C5:TAX :\ Wait for X parameter via R2 JSR L06C5:TAY :\ Wait for Y parameter via R2 JSR L06C5 :\ Wait for A parameter via R2 JSR OSBYTE :\ Do the OSBYTE call \ If the OSBYTE results in code being executed - ie, OSBYTE 142, then the \ call will not be returned here, and the CoPro will be sent an &80 ack \ byte. EOR #&9D:BEQ L0604 :\ If OSBYTE &9D, Fast BPUT, jump stright back to idle loop ROR A:JSR L0695 :\ Move Carry into b7 of A, send via R2 \ Send bytes in Y, X via R2, then jump to Tube idle loop \ ------------------------------------------------------ .L061D BIT TUBES2:BVC L061D :\ Loop until port free STY TUBER2 :\ Send byte in Y BVS L05FC :\ Jump to send X and jump to idle loop \ OSWORD \ ====== .L0627 JSR L06C5:TAY :\ Wait for action byte in R2, save in Y .L062B BIT TUBES2:BPL L062B :\ Loop until data present in R2 LDX TUBER2 :\ Get X from R2 - number of bytes inward DEX:BMI L0645 :\ Jump if no bytes to read :\ Note, it is the Client that declares how :\ many bytes to transfer, and the protocol :\ treats &81-&FF as zero as well .L0636 BIT TUBES2:BPL L0636 :\ Loop until data present in R2 LDA TUBER2:STA &128,X :\ Get byte via R2, store in control block DEX:BPL L0636 TYA :\ Get function back to A \ This builds the OSWORD control block in &0128 up towards &01FF, allowing a \ maximum control block of almost 216 bytes before it crashes into the stack. \ However, the protocol treats a control block length of &81-&FF as zero, so \ the maximum size control block will be &0128-&01A7. .L0645 LDX #&28:LDY #&01 :\ Point XY to control block at &0128 JSR OSWORD :\ Do the OSWORD call .L064C BIT TUBES2:BPL L064C :\ Loop until data present in R2 LDX TUBER2 :\ Get output control block size to X DEX:BMI L0665 :\ Jump if no bytes to return, again the Client :\ declares how many bytes to transfer, and the :\ protocol treats &81-&FF as zero. .L0657 LDY &128,X :\ Get byte from control block .L065A BIT TUBES2:BVC L065A :\ Loop until R2 free STY TUBER2 :\ Send byte via R2 DEX:BPL L0657 .L0665 JMP L0036 :\ Return to Tube idle loop \ RDLINE - Read a line \ ==================== .L0668 LDX #&05:JSR L06BA :\ Fetch five bytes into control block, returns X=&00 LDA #&07:STA &01 :\ Point to string buffer at &0700 STX &00:TXA:TAY :\ Point XY to control block at &0000 JSR OSWORD :\ Call OSWORD A=0 to read the line BCC L0680 :\ Jump if no Escape LDA #&FF:JMP L059E :\ Send &FF via R2 for Escape, return to Tube idle loop .L0680 LDX #&00 :\ Point to start of string buffer LDA #&7F:JSR L0695 :\ Send &7F via R2 to indicate no Escape .L0687 LDA &0700,X:JSR L0695 :\ Get byte from string buffer, send via R2 INX :\ Move to next byte CMP #&0D:BNE L0687 :\ Loop until sent BEQ L0665 :\ Return to Tube idle loop \ Copy Escape state across Tube \ ============================= .L06A7 LDA &FF :\ Get Escape state SEC:ROR A :\ Rotate escape to b6 and set b7 BMI L06BC :\ Interupt client with byte send via R1 \ Send event across Tube \ ====================== .L06AD PHA :\ Save A LDA #&00:JSR L06BC :\ Send &00 via R1, generating client IRQ \ After being interupted with the first byte, above, the client usually \ disables IRQs and reads the following bytes directly from the Tube registers. TYA:JSR L06BC :\ Send Y via R1 TXA:JSR L06BC :\ Send X via R1 PLA :\ Get A back and send via R1 : : \ Send byte in A via R1 \ ===================== .L06BC BIT TUBES1:BVC L06BC :\ Loop until R1 free STA TUBER1:RTS :\ Send byte \ Send control block via R2 \ ========================= .L06B5 LDA &00,X:JSR L0695 :\ Send from control block at &0000 via R2 DEX:BPL L06B5:RTS \ Send byte in A via R2 \ ===================== .L0695 BIT TUBES2:BVC L0695 :\ Loop until R2 free STA TUBER2:RTS :\ Send byte \ Send Tube ID via R4 \ =================== .L069C LDA &15 :\ Get Tube ID : \ Send byte in A via R4 \ ===================== .L069E BIT TUBES4:BVC L069E :\ Loop until R4 free STA TUBER4:RTS :\ Send byte \ Wait for control block via R2 \ ============================= .L06BA JSR L06C5:STA &FF,X :\ Fill control block at &0000 via R2 DEX:BNE L06BA:RTS :\ Returns X=&00 to point to control block at &xx00 \ Wait for data via R2 \ ==================== .L06C3 LDY #&00 :\ Point XY to control block at &00xx .L06C5 BIT TUBES2:BPL L06C5 :\ Loop until data present LDA TUBER2:RTS :\ Get byte ]:A%=P%-L0400:P%=TubeCode+A%:[OPT opt% \ Relocation table to make *SMLoad-able \ ===================================== .RelocTable ]NEXT:PROCsm_table A$="*Save "+name$+" "+STR$~(mcode%+mclen%)+" "+STR$~O%+" FFFF0000 FFFBBC00" P.A$;:OS.A$:P. END : DEFFNsm_pass(pass%) IFpass%=0:mclen%=0 IFpass%=1:mclen%=O%-mcode% P%=&8100-128*(pass%AND2) O%=mcode%+mclen%*(pass%AND2)DIV2 IFpass%=1:IF O%+mclen%*2.125>L%:PRINT"Code overrun":END =VALMID$("4647",pass%+1,1) : DEFPROCsm_table base80%=mcode%+mclen%:base81%=mcode%:byte%=0:count%=0:off%=0:REPEAT byte80%=base80%?off%:byte81%=base81%?off%:IF off%>=mclen%:byte80%=&80:byte81%=&80 IF ((byte81%-byte80%) AND &FE)<>0 THEN PRINT "ERROR: Offset by more than one page at &";~&8000+off% IF (byte80% AND &C0)=&80:byte%=byte%DIV2+128*(byte81%-byte80%):count%=count%+1 IF count%=8:?O%=byte%:O%=O%+1:byte%=0:count%=0 off%=off%+1:UNTILoff%>=mclen% AND count%=0 ENDPROC