10 REM > OswFA/src
   20 REM Source for OSWORD &FA in 80x86 Tube client
   30 :
   40 REM OSWORD &FA transfers blocks of memory between I/O and CoPro memory
   50 REM   XY?0  =&0D or &0E (send block length)
   60 REM   XY?1  =&10 (receive block length)
   70 REM   XY!2  =I/O address
   80 REM   XY!6  =CoPro address (segment:offset)
   90 REM   XY!10 =Number of bytes to transfer, &0000=nothing to do
  100 REM   XY?12 =Tube transfer function
  110 REM   XY?13 =memory access control if X%?0=&0E
  120 REM This performs almost the same actions as OSWORD &FF in Z80 Tube client
  130 REM I don't know why they didn't just make the call number also &FF.
  140 :
  150 REM Notes:
  160 REM Does not work on the Electron as the Tube I/O port on the Electron
  170 REM is at &FCE5 instead of at &FEE5.
  180 REM Does not work on the BBC/Electron due to Master-specific OSBYTE calls,
  190 REM and access to Master-specific ACCCON memory control register.
  200 :
  210 REM Master 512 Reference Manual reports:
  220 REM * 1-byte and 256-byte transfers fail if ACCCON altered
  230 REM * Two-byte C->H transfers consistantly occasionally fail
  240 REM
  250 REM This could a result of the slightly different code in transfer 2 and
  260 REM transfer 3.
  270 :
  280 OSBYTE=&FFF4
  290 ctrl=&70:rom=&72:shadow=&73:addr=&74:numHI=&76:numLO=&77
  300 :
  310 load%=&FFFF2500:exec%=load%:fname$="OswFA"
  320 DIM mcode% &1FF
  330 FOR P=0 TO 1
  340   P%=load%:O%=mcode%
  350   [OPT P*3+4
  360   .L2500
  370   CLC:BCC L2505              :\ New USER entry
  380   .OldUSERV
  390   EQUW &E310                 :\ Holds old USERV entry
  400   .L2505
  410   CMP #&FA:BEQ L250C         :\ If my OSWORD, jump to respond
  420   JMP (OldUSERV)             :\ Continue via OldUSERV
  430   :
  440   .L250C
  450   STX ctrl+0:STY ctrl+1:PHA  :\ Save OSWORD parameters, can't use &F0/&F1
  460   LDA #&FB:LDX #&00:LDY #&FF :\  as OSBYTE calls will overwrite them
  470   JSR OSBYTE:STX shadow      :\ Get shadow screen setting (Master only)
  480   LDA &FE34:PHA              :\ Save current ACCCON setting (Master only)
  490   .L2520
  500   LDA #&C0+7:JSR &0406             :\ Claim with ID=7
  510   BCC L2520                        :\ Loop until claimed
  520   LDY #&00:LDA (ctrl),Y            :\ Read number of parameters
  530   CMP #&0D:PHP                     :\ Save EQ/NE for memory access byte supplied
  540   LDA &F4:STA rom                  :\ Save current ROM
  550   LDY #&0D:LDA (ctrl),Y:TAX        :\ Get ROM access value
  560   LDY #&02:LDA (ctrl),Y:STA addr+0 :\ I/O address low byte
  570   INY:LDA (ctrl),Y:STA addr+1      :\ I/O address high byte
  580   PLP:BEQ L2585                    :\ Skip past if no memory access byte supplied
  590   TXA                              :\ Get memory access byte
  600   PHA:AND #&40:BNE L255E           :\ b6 set, use shadow memory
  610   TXA:AND #&20:BNE L2554           :\ b5 set, use current screen memory
  620   :
  630   \ Use I/O memory (equivalent to &FFFFxxxx)
  640   \ ----------------------------------------
  650   .L2550
  660   LDX #&00:BEQ L2556            :\ Jump to select I/O memory
  670   :
  680   \ Use current screen memory (equivlent to &FFFExxxx)
  690   \ --------------------------------------------------
  700   .L2554
  710   LDX #&01                      :\ X=1 to select screen memory
  720   .L2556
  730   LDA #&6C:JSR OSBYTE           :\ Select specified screen memory
  740   JMP L2577                     :\ Jump forward to test ROM number
  750   :
  760   \ Use shadow screen memory (&FFFDxxxx)
  770   \ ------------------------------------
  780   .L255E
  790   \ The following tests if a shadow screen is selected
  800   LDA #&84:JSR OSBYTE           :\ Read top of I/O memory
  810   CPY #&80:BNE L256F            :\ If Y<>&80, not shadow screen
  820   :
  830   \ Shadow screen selected
  840   \ ----------------------
  850   LDA #&01:CMP shadow:BNE L2554 :\ Jump to select shadow memory
  860   BEQ L2550                     :\ Jump to select I/O memory
  870   :
  880   \ Non-shadow screen selected
  890   \ --------------------------
  900   .L256F
  910   LDA #&02:CMP shadow:BNE L2550 :\ Jump to select I/O memory
  920   BEQ L2554                     :\ Jump to select screen memory
  930   :
  940   \ Test supplied ROM number
  950   \ ------------------------
  960   .L2577
  970   PLA:TAX:AND #&10:BNE L2585    :\ b4 set, use current ROM
  980   TXA:AND #&0F                  :\ Reduce to 0-15
  990   STA &F4:STA &FE30             :\ Set ROMCOPY and select ROM
 1000   :
 1010   \ Prepare to do the transfer
 1020   \ --------------------------
 1030   .L2585
 1040   LDY #&0A:LDA (ctrl),Y:STA numLO :\ Get count low byte
 1050   INY:LDA (ctrl),Y:STA numHI      :\ Get count high byte
 1060   ORA numLO:BNE L2596:BEQ L2604   :\ Jump to finish if count=zero
 1070   .L2596
 1080   LDA numLO:BEQ L259C:INC numHI   :\ Inc high byte to balance DECs later
 1090   .L259C
 1100   INY:LDA (ctrl),Y                :\ Get transfer function
 1110   :
 1120   \ Loop to do multiple transfers
 1130   \ -----------------------------
 1140   .L259F
 1150   PHA:LDA numLO:BEQ L25B5       :\ Count=&xx00, jump past
 1160   LDA numHI:CMP #&01:BNE L25B5  :\ More than 256 bytes to transfer, jump past
 1170   :
 1180   \ If <256 bytes to transfer, convert 256-byte function to 1-byte function
 1190   \ -----------------------------------------------------------------------
 1200   PLA:PHA:CMP #&06:BCC L25B5    :\ Jump with <256-byte transfers
 1210   PLA:SEC:SBC #&06:PHA          :\ Convert 256-byte transfer to 1-byte transfer
 1220   .L25B5
 1230   LDA ctrl+0:CLC:ADC #&06:TAX   :\ Point to Control+6
 1240   LDA #&00:ADC ctrl+1:TAY       :\ XY->CoPro address in control block
 1250   PLA:PHA:JSR &0406             :\ Initiate specified action
 1260   LDX numLO:PLA:LDY #&00
 1270   CMP #&00:BEQ L25EC            :\ Jump with 1-byte CoPro->I/O
 1280   CMP #&01:BEQ L2607            :\ Jump with 1-byte I/O->CoPro
 1290   CMP #&02:BEQ L261F            :\ Jump with 2-byte CoPro->I/O
 1300   CMP #&03:BEQ L264A            :\ Jump with 2-byte I/O->CoPro
 1310   CMP #&06:BEQ L25E6            :\ Jump with 256-byte CoPro->I/O
 1320   CMP #&07:BEQ L25E9            :\ Jump with 256-byte I/O->CoPro
 1330   LDA #&00:BEQ L2604            :\ Anything else, jump to release and finish
 1340   .L25E6:JMP L2675              :\ Jump ahead with 256-byte transfer
 1350   .L25E9:JMP L26A3              :\ Jump ahead with 256-byte transfer
 1360   :
 1370   \ 1-byte CoPro->I/O
 1380   \ -----------------
 1390   .L25EC
 1400   JSR L26F4                       :\ Delay before starting
 1410   .L25EF
 1420   LDA &FEE5:STA (addr),Y          :\ Transfer a byte to I/O memory
 1430   JSR L26F4                       :\ Delay between bytes
 1440   INC addr+0:BNE L25FD:INC addr+1 :\ Update I/O address
 1450   .L25FD
 1460   DEX:BNE L25EF                   :\ Loop for up to 256 bytes
 1470   DEC numHI:BNE L25EF             :\ Loop for each 256-byte chunck
 1480   .L2604
 1490   JMP L26DA                       :\ Jump to exit when finished
 1500   :
 1510   \ 1-byte I/O->CoPro
 1520   \ -----------------
 1530   .L2607
 1540   LDA (addr),Y:STA &FEE5          :\ Transfer a byte from I/O memory
 1550   JSR L26F4                       :\ Delay between bytes
 1560   INC addr+0:BNE L2615:INC addr+1 :\ Update I/O address
 1570   .L2615
 1580   DEX:BNE L2607                   :\ Loop for up to 256 bytes
 1590   DEC numHI:BNE L2607             :\ Loop for each 256-byte chunk
 1600   JMP L26DA                       :\ Jump to exit when finished
 1610   :
 1620   \ 2-byte CoPro->I/O
 1630   \ -----------------
 1640   .L261F
 1650   JSR L26F4                       :\ Delay before starting
 1660   .L2622
 1670   LDA &FEE5:STA (addr),Y          :\ Transfer a byte to I/O memory
 1680   INC addr+0:BNE L262D:INC addr+1 :\ Update I/O address
 1690   .L262D
 1700   NOP:NOP                         :\ Delay between bytes
 1710   LDA &FEE5:STA (addr),Y          :\ Transfer a byte to I/O memory
 1720   INC addr+0:BNE L263A:INC addr+1 :\ Update I/O address
 1730   .L263A
 1740   JSR L26F3:NOP:NOP               :\ Delay
 1750   DEX:DEX:BNE L2622               :\ Loop for up to 256 bytes
 1760   DEC numHI:BNE L2622             :\ Loop for each 256-byte chunk
 1770   JMP L26DA                       :\ Jump to exit when finished
 1780   :
 1790   \ 2-byte I/O->CoPro
 1800   \ -----------------
 1810   .L264A
 1820   LDA (addr),Y:STA &FEE5     :\ Transfer a byte from I/O memory
 1830   INC addr+0                 :\ Update I/O address low byte
 1840   BEQ L2656:NOP:BNE L2658    :\ Delay between bytes
 1850   .L2656
 1860   INC addr+1                 :\ Update I/O address high byte
 1870   .L2658
 1880   LDA shadow                 :\ Delay by loading a byte
 1890   LDA (addr),Y:STA &FEE5     :\ Transfer a byte from I/O memory
 1900   INC addr                   :\ Update I/O address low byte
 1910   BEQ L2666:NOP:BNE L2668    :\ Another delay added
 1920   .L2666
 1930   INC addr+1
 1940   .L2668
 1950   JSR L26F3                  :\ Delay
 1960   DEX:DEX:BNE L264A          :\ Loop for up to 256 bytes
 1970   DEC numHI:BNE L264A        :\ Loop for each 256-byte chunk
 1980   BEQ L26DA                  :\ Jump to exit when finished
 1990   :
 2000   \ 256-byte CoPro->I/O
 2010   \ -------------------
 2020   .L2675
 2030   JSR L26F4                  :\ Delay before starting
 2040   .L2678
 2050   LDA &FEE5:STA (addr),Y     :\ Transfer a byte to I/O
 2060   NOP:NOP:NOP:INY:BNE L2678  :\ Delay and loop for 256 bytes
 2070   CPX #&00:BNE L2693         :\
 2080   DEC numHI:BEQ L26DA        :\ Loop for each 256-byte chunk
 2090   .L268B
 2100   JSR L26CE                  :\ Add 256 to I/O address in control block
 2110   LDA #&06:JMP L259F         :\ Jump back to do another 256-byte transfer
 2120   :
 2130   .L2693
 2140   DEC numHI:LDA numHI        :\ Decrement 256-byte counter
 2150   CMP #&01:BNE L268B         :\ If 256 bytes or more left, loop back
 2160   JSR L26CE                  :\ Update I/O transfer address
 2170   LDA #&00:JMP L259F         :\ Do final bytes with 1-byte transfer
 2180   :
 2190   \ 256-byte I/O->CoPro
 2200   \ -------------------
 2210   .L26A3
 2220   LDA (addr),Y:STA &FEE5     :\ Transfer a byte from I/O
 2230   NOP:NOP:NOP:INY:BNE L26A3  :\ Delay and loop for 256 bytes
 2240   CPX #&00:BNE L26BE
 2250   DEC numHI:BEQ L26DA        :\ Loop for each 256-byte chunk
 2260   .L26B6
 2270   JSR L26CE                  :\ Add 256 to I/O address in control block
 2280   LDA #&07:JMP L259F         :\ Jump back to do another 256-byte transfer
 2290   :
 2300   .L26BE
 2310   DEC numHI:LDA numHI        :\ Decrement 256-byte counter
 2320   CMP #&01:BNE L26B6         :\ If 256 bytes or more left, loop back
 2330   JSR L26CE                  :\ Update I/O transfer address
 2340   LDA #&01:JMP L259F         :\ Do final bytes with 1-byte transfer
 2350   :
 2360   \ Add 256 to I/O address in control block
 2370   \ ---------------------------------------
 2380   .L26CE
 2390   INC addr+1                 :\ Update I/O address
 2400   LDY #&07:LDA (ctrl),Y      :\ Get I/O address from control block
 2410   CLC:ADC #&01:STA (ctrl),Y  :\ Add 256 to it, store back
 2420   RTS
 2430   :
 2440   \ Finished, release Tube and return
 2450   \ ---------------------------------
 2460   .L26DA
 2470   LDA #&80+7:JSR &0406       :\ Release with ID=7
 2480   LDA rom:CMP &F4:BEQ L26EA  :\ Skip if previous ROM = current ROM
 2490   STA &F4:STA &FE30          :\ Restore previous ROM
 2500   .L26EA
 2510   PLA:STA &FE34              :\ Restore ACCON
 2520   LDX ctrl+0:LDY ctrl+1:PLA  :\ Restore registers
 2530   .L26F3
 2540   RTS
 2550   :
 2560   \ Delay for 36 clock cycles
 2570   \ -------------------------
 2580   .L26F4
 2590   JSR L26F3:JSR L26F3:RTS
 2600   :
 2610 ]NEXT
 2620 A$=fname$+" "+STR$~mcode%+" "+STR$~O%+" "+STR$~(exec%OR&FFFF0000)+" "+STR$~load%
 2630 PRINT"Saving ";A$:OSCLI "SAVE "+A$:PRINT