10
20
30
40
50 :
60
70
80
90
100
110
120
130 :
140
150
160
170
180
190
200 :
210 OS_CLI=&FFF7:OSBYTE=&FFF4:OSWORD=&FFF1:OSWRCH=&FFEE
220 OSWRCR=&FFEC:OSNEWL=&FFE7:OSASCI=&FFE3:OSRDCH=&FFE0
230 USERV=&200
240 :
250 ctrl=&70:saveA=&72:unused=&73:addr=&74:count=&76
260 :
270 run%=&FFFF2500:load%=&FFFF0900
280 DIM mcode% &180
290 FOR P=0 TO 1
300 P%=run%:O%=mcode%
310 [OPT P*3+4
320 .L2500
330 JMP L2505 :\ New USER entry
340 .OldUSERV
350 EQUW L2500 :\ Swapped with USERV to claim
360 .L2505
370 CMP #&FF:BEQ L250C :\ If my OSWORD, jump to respond
380 JMP (OldUSERV) :\ Continue via OldUSERV
390 :
400 .L250C
410 STX ctrl+0:STY ctrl+1:PHA :\ Save OSWORD parameters
420 LDA &F4:PHA :\ Save current ROM
430 LDA &FFB2:EOR #&BC :\ Check for Electron, has to be done
440 CMP #&FC:BNE GetAddress :\ inline as no initialisation call
450 STA TUBEIO1+2:STA TUBEIO2+2 :\ Change to Electron TUBEIO
460 LDA #5:STA ROMSEL+1 :\ Change to Electron ROMSEL
470 :
480 .GetAddress
490 LDY #&02:LDA (ctrl),Y:STA addr+0 :\ I/O address low byte
500 INY:LDA (ctrl),Y:STA addr+1 :\ I/O address high byte
510 INY:LDA (ctrl),Y:TAX :\ I/O address ROM/screen byte
520 CLC:ADC #&40 :\ Allow &0x and &Fx for ROMs
530 JSR ROMSelect :\ Page in ROM
540 INX:BEQ GetIOMem :\ &FFFFxxxx - I/O memory
550 INX:BEQ GetDisplay :\ &FFFExxxx - current screen
560 INX:BEQ GetShadow :\ &FFFDxxxx - shadow memory
570 BNE GetIOMem
580 :
590 .GetDisplay
600 LDA #&84:JSR OSBYTE
610 TYA:BPL GetIOMem :\ Not shadow screen displayed
620 .GetShadow
630 LDA #1:JSR vramSelect
640 :
650 .GetIOMem
660 .TubeClaim
670 LDA #&C0+7:JSR &0406 :\ Claim the Tube with ID=7
680 BCC TubeClaim :\ Loop until claimed
690 LDY #&0C:LDA (ctrl),Y:PHA :\ Get read/write command
700 LDA ctrl+0:CLC:ADC #&06:TAX :\ Point to Control+6
710 LDA #&00:ADC ctrl+1:TAY :\ XY->CoPro address in control block
720 PLA:PHA:JSR &406 :\ Initiate specified action
730 LDY #&0A:LDA (ctrl),Y:TAX :\ Get X=count low byte
740 INY:LDA (ctrl),Y:STA count :\ Get count high byte
750 BNE L2544 :\ Jump forward if >255 bytes to do
760 TXA:BEQ L2592 :\ Jump to exit if no bytes to do
770 .L2544
780 TXA:BEQ L2549 :\ Jump forward if multiple of 256 bytes
790 INC count :\ Inc high byte to balance DECs later
800 .L2549
810 LDY #&00 :\ Prepare zero offset for (zp),Y
820 PLA:ROR A:BCS L2577 :\ Get command back, jump if H->C
830 :
840 \ Copy from Client to Host
850 \ ------------------------
860 .L2558
870 JSR TubeDelay :\ Delay before start and between bytes
880 .TUBEIO1
890 LDA &FEE5:STA (addr),Y :\ Transfer a byte C->H
900 INC addr+0:BNE L256C:INC addr+1 :\ Update I/O address
910 .L256C
920 DEX:BNE L2558 :\ Loop for sub-256 byte count
930 DEC count:BNE L2558 :\ Loop for each 256-byte chunk
940 BEQ L2592 :\ Jump to exit when finished
950 :
960 \ Copy from Host to Client
970 \ ------------------------
980 .L2577
990 LDA (addr),Y
1000 .TUBEIO2
1010 STA &FEE5 :\ Transfer byte H->C
1020 JSR TubeDelay :\ Delay between bytes
1030 INC addr+0:BNE L258B:INC addr+1 :\ Update I/O address
1040 .L258B
1050 DEX:BNE L2577 :\ Loop for sub-256 byte count
1060 DEC count:BNE L2577 :\ Loop for each 256-byte chunk
1070 :
1080 \ Release and return
1090 \ ------------------
1100 .L2592
1110 LDA #&80+7:JSR &0406 :\ Release Tube with ID=7
1120 LDA #0:JSR vramSelect :\ Page main memory back in
1130 PLA:JSR ROMSelect :\ Restore ROM
1140 LDX ctrl+0:LDY ctrl+1:PLA :\ Restore entry registers
1150 RTS :\ And return
1160 :
1170 .TubeDelay :JSR TubeDelay2 :\ 24us between Tube accesses
1180 .TubeDelay2:JSR TubeDelay3
1190 .TubeDelay3:RTS
1200 :
1210 .ROMSelect
1220 PHA:LDA #12:JSR ROMSelect2:PLA :\ Double select for Electron
1230 .ROMSelect2
1240 STA &F4
1250 .ROMSEL
1260 STA &FE30
1270 .vramOk
1280 RTS
1290 :
1300 .vramSelect
1310 PHA:TAX :\ A=0 main RAM, A=1 video RAM
1320 LDA #108:JSR OSBYTE :\ Attempt to select Master video RAM
1330 PLA:INX:BNE vramOk :\ X<>255, successful
1340 EOR #1:TAX :\ A=1 main RAM, A=0 video RAM
1350 LDA #111:JMP OSBYTE :\ Attempt to select Aries/Watford RAM
1360 .end%
1370
1380 ]:P%=P%-run%+load%:off%=run%-load%:[OPT P*3+4
1390 :
1400 \ This code allows OSWORD &FF to live in a *runnable program
1410 \ Can be *RUN on top of itself or on top of other code at &2500
1420 :
1430 .exec%
1440 PHP:SEI :\ Prevent IRQs while changing vectors
1450 BIT &27A:BPL SetupExit :\ No Tube present
1460 LDA USERV+1:CMP OldUSERV-off%+1:BNE Setup :\ Not linked to me, claim it
1470 LDA USERV+0:CMP OldUSERV-off%+0:BNE Setup :\ Not linked to me, claim it
1480 LDA OldUSERV+1:STA USERV+1 :\ Already linked, restore USERV
1490 LDA OldUSERV+0:STA USERV+0 :\ And then reclaim it
1500 .Setup
1510 LDY #0
1520 .SetupLp
1530 LDA load%,Y:STA run%,Y:INY:BNE SetupLp :\ Copy code to main memory
1540 LDA USERV+1:LDX OldUSERV+1 :\ Claim USERV
1550 STX USERV+1:STA OldUSERV+1
1560 LDA USERV+0:LDX OldUSERV+0
1570 STX USERV+0:STA OldUSERV+0
1580 .SetupExit
1590 PLP:RTS :\ Exit
1600 :
1610 ]NEXT
1620 PRINT "*SAVE OswFFv2 ";~mcode%;" ";~O%;" ";~exec%OR&FFFF0000;" ";~load%