10 REM > OswFFv136/src
   20 REM Source for OSWORD &FF in Z80 Tube client 1.36
   30 :
   40 REM OSWORD &FF transfers blocks of memory between I/O and CoPro memory
   50 REM   XY?0 =&0D
   60 REM   XY?1 =&01
   70 REM   XY!2 =I/O address
   80 REM   XY!6 =CoPro address
   90 REM   XY!10=Number of bytes to transfer
  100 REM   XY?12=0 for C->H, =1 for H->C
  110 :
  120 REM Note: the default code does not work on the Electron as the Tube I/O
  130 REM port on the Electron is at &FCE5 instead of at &FEE5.
  140 :
  150 USERV=&200
  160 ctrl=&70:saveA=&72:unused=&73:addr=&74:count=&76:countLO=&77
  170 :
  180 run%=&FFFF2500:load%=&FFFF0900:fname$="OswFFv136"
  190 DIM mcode% &180
  200 FOR P=0 TO 1
  210   P%=run%:O%=mcode%
  220   [OPT P*3+4:\                      \v1.20     :\ v1.36
  230   .L2500
  240   CLC:BCC L2505                     \JMP L2505 :\ New USER entry
  250   .OldUSERV
  260   EQUW &E310                        \EQUW L2500:\ Swapped with USERV to claim
  270   .L2505
  280   CMP #&FF:BEQ L250C                           :\ If my OSWORD, jump to respond
  290   JMP (OldUSERV)                               :\ Continue via OldUSERV
  300   :
  310   .L250C
  320   STX ctrl+0:STY ctrl+1:PHA         \STA saveA :\ Save OSWORD parameters
  330   .L2511
  340   LDA #&C0+7:JSR &0406              \new       :\ Claim with ID=7
  350   BCC L2511                         \new       :\ Loop until claimed
  360   LDY #&02:LDA (ctrl),Y:STA addr+0             :\ I/O address low byte
  370   INY:LDA (ctrl),Y:STA addr+1                  :\ I/O address high byte
  380   :                                 \JSR L259C :\ Claim the Tube
  390   LDY #&0A:LDA (ctrl),Y:STA countLO \TAX       :\ Get count low byte
  400   INY:LDA (ctrl),Y:STA count        \new       :\ Get count high byte
  410   ORA countLO:BNE L2534:BEQ L257F   \new       :\ Nothing to do
  420   .L2534
  430   LDA countLO                       \TXA       :\ Jump forward if multiple of 256
  440   BEQ L253A:INC count               \          :\ Inc high byte to balance DECs later
  450   .L253A
  460   INY                               \LDY #&0C
  470   LDA (ctrl),Y:PHA                             :\ Get read/write command
  480   LDA ctrl+0:CLC:ADC #&06:TAX                  :\ Point to Control+6
  490   LDA #&00:ADC ctrl+1:TAY                      :\ XY->CoPro address in control block
  500   PLA:PHA:JSR &406                             :\ Initiate specified action
  510   LDX countLO                       \new       :\ Get vount low into X
  520   PLA                                          :\ Get command back
  530   LDY #0                            \new       :\ Zero offset for (zp),Y
  540   CMP #0:BEQ L2567                  \new       :\ Transfer bytes C->H
  550   CMP #1:BEQ L2581                  \new       :\ Transfer bytes H->C
  560   CMP #2:BEQ L2598                  \new       :\ Transfer pairs C->H
  570   CMP #3:BEQ L25C2                  \new       :\ Transfer pairs H->C
  580   LDA #0:BEQ L257F                  \new       :\ Ignore
  590   :
  600   \ 1-byte CoPro->Host
  610   \ ------------------
  620   .L2567
  630   JSR L25F6                                    :\ Delay before starting
  640   :                                 \LDY #&00  :\ Zero offset for (zp),Y
  650   .L256A
  660   LDA &FEE5:STA (addr),Y                       :\ Transfer a byte C->H
  670   JSR L25F6                                    :\ Delay between bytes
  680   :                                 \JSR L259B
  690   :                                 \JSR L259B
  700   INC addr+0:BNE L2578:INC addr+1              :\ Update I/O address
  710   .L2578
  720   DEX:BNE L256A                                :\ Loop for up to 256 bytes
  730   DEC count:BNE L256A                          :\ Loop for each 256-byte chunk
  740   .L257F
  750   BEQ L25EB                                    :\ Jump to exit when finished
  760   :
  770   \ 1-byte Host->CoPro
  780   \ ------------------
  790   :                                 \LDY #&00  :\ Zero offset for (zp),Y
  800   .L2581
  810   LDA (addr),Y:STA &FEE5                       :\ Transfer byte H->C
  820   JSR L25F6                                    :\ Delay between bytes
  830   :                                 \JSR L259B
  840   :                                 \JSR L259B
  850   INC addr+0:BNE L258F:INC addr+1              :\ Update I/O address
  860   .L258F
  870   DEX:BNE L2581                                :\ Loop for up to 256 bytes
  880   DEC count:BNE L2581                          :\ Loop for each 256-byte chunk
  890   BEQ L25EB                                    :\ Jump to exit when finished
  900   :
  910   \ 2-byte CoPro->Host - must be an even number of bytes
  920   \ ----------------------------------------------------
  930   .L2598
  940   JSR L25F6                         \new       :\ Delay before starting
  950   .L259B                            \new
  960   LDA &FEE5:STA (addr),Y            \new       :\ Transfer a byte C->H
  970   INC addr+0:BNE L25A6:INC addr+1   \new       :\ Update I/O address
  980   .L25A6                            \new
  990   NOP:NOP                           \new       :\ Sort delay between bytes
 1000   LDA &FEE5:STA (addr),Y            \new       :\ Transfer a byte C->H
 1010   INC addr+0:BNE L25B3:INC addr+1   \new       :\ Update I/O address
 1020   .L25B3                            \new
 1030   JSR L25F5                         \new       :\ Delay between bytes
 1040   NOP:NOP                           \new       :\ Sort delay between bytes
 1050   DEX:DEX:BNE L259B                 \new       :\ Loop for up to 256 bytes
 1060   DEC count:BNE L259B               \new       :\ Loop for each 256-byte chunk
 1070   BEQ L25EB                         \new       :\ Jump to exit when finished
 1080   :
 1090   \ 2-byte Host->CoPro - must be an even number of bytes
 1100   \ ----------------------------------------------------
 1110   .L25C2
 1120   LDA (addr),Y:STA &FEE5            \new       :\ Transfer byte H->C
 1130   INC addr+0:BEQ L25CE              \new       :\ Update I/O address
 1140   NOP:BNE L25D0                     \new       :\ Short delay between bytes
 1150   .L25CE                            \new
 1160   INC addr+1                        \new
 1170   .L25D0                            \new
 1180   LDA unused                        \new       :\ Another delay between bytes
 1190   LDA (addr),Y:STA &FEE5            \new       :\ Transfer byte H->C
 1200   INC addr+0:BEQ L25DE              \new       :\ Update I/O address
 1210   NOP:BNE L25E0                     \new       :\ Short delay between bytes
 1220   .L25DE                            \new
 1230   INC addr+1                        \new
 1240   .L25E0                            \new
 1250   JSR L25F5                         \new       :\ Delay between bytes
 1260   DEX:DEX:BNE L25C2                 \new       :\ Loop for up to 256 bytes
 1270   DEC count:BNE L25C2               \new       :\ Loop for each 256-byte chunk
 1280   :
 1290   .L25EB
 1300   LDA #&80+7:JSR &0406              \JSR L25A4 :\ Release with ID=7
 1310   LDX ctrl+0:LDY ctrl+1:PLA         \LDA saveA :\ Restore entry registers
 1320   .L25F5                                       :\ Call here to delay 6us
 1330   RTS                                          :\ And return
 1340   :
 1350   .L25F6
 1360   JSR L25F5:JSR L25F5:RTS           \new       :\ Delay 18us
 1370   :
 1380   :                                 \.L259C
 1390   :                                 \LDA #&C0+7
 1400   :                                 \JSR &0406 :\ Claim with ID=7
 1410   :                                 \BCC L259C :\ Loop until claimed
 1420   :                                 \RTS
 1430   :
 1440   :                                 \.L25A4
 1450   :                                 \LDA #&80+7
 1460   :                                 \JSR &0406 :\ Release with ID=7
 1470   :                                 \RTS
 1480   .end%
 1490   :
 1500   ]:P%=P%-run%+load%:off%=run%-load%:[OPT P*3+4
 1510   :
 1520   \ This code allows OSWORD &FF to live in a *runnable program
 1530   :
 1540   .exec%
 1550   BIT &27A:BPL SetupExit                        :\ No Tube present
 1560   LDA USERV+0:CMP OldUSERV-off%+0:BNE Setup     :\ Vector diff, claim it
 1570   LDA USERV+1:CMP OldUSERV-off%+1:BEQ SetupExit :\ Same, already claimed
 1580   .Setup
 1590   PHP:SEI:LDY #0                                :\ Prevent IRQs while changing vectors
 1600   .SetupLp
 1610   LDA load%,Y:STA run%,Y:INY                    :\ Copy code to main memory
 1620   CPY #(end%-run%)AND&FF:BNE SetupLp
 1630   LDA USERV+0:LDX OldUSERV+0                    :\ Claim USERV
 1640   STX USERV+0:STA OldUSERV+0
 1650   LDA USERV+1:LDX OldUSERV+1
 1660   STX USERV+1:STA OldUSERV+1
 1670   PLP                               :\ Restore IRQs and exit
 1680   .SetupExit
 1690   RTS
 1700   :
 1710 ]NEXT
 1720 A$=fname$+" "+STR$~mcode%+" "+STR$~O%+" "+STR$~(exec%OR&FFFF0000)+" "+STR$~load%
 1730 PRINT"Saving ";A$:OSCLI "SAVE "+A$:PRINT