10 REM > OswFB-FF/src
   20 REM Source for OSWORD &FB-&FF in 80x86 6502.SYS support file
   30 REM Commentary based on the disassembly in Master 512 Reference Manual
   40 :
   50 REM ZERO PAGE MOS VARIABLES
   60 REM -----------------------
   70 key1 = &EC :REM new key press in zero page
   80 key2 = &ED :REM old key press in zero page
   90 :
  100 REM MOS CALLS & VECTORS USED BY THE CODE
  110 REM ------------------------------------
  120 keyvec = &0228    :REM Keyboard status and control
  130 oswrch = &FFEE    :REM Character output
  140 osbyte = &FFF4    :REM General MOS call entry
  150 :
  160 REM ADDRESS OF INTERCEPTED VECTORS
  170 REM ------------------------------
  180 userv    = &200   :REM user vector
  190 irqv1    = &204   :REM Interrupt request 1
  200 eventvec = &220   :REM The event vector
  210 :
  220 REM ENTRY TO TUBE SUPPORT CODE
  230 REM --------------------------
  240 tube_entry = &406 :REM Tube claim/release/transfer
  250 :
  260 REM SHEILA MEMORY MAPPED ADDRESSES
  270 REM ------------------------------
  280 userport  = &FE60 :REM user-port data
  290 :
  300 REM 1770 FDC OFFSETS FROM &FE00
  310 REM ---------------------------
  320 Master_fdc_base=&24
  330 Master_fdn_stat=&28
  340 Master_fdc_data=&2B
  350 BBC_fdc_base   =&80
  360 BBC_fdc_stat   =&84
  370 BBC_fdc_data   =&87
  380 :
  390 REM TUBE STATUS AND DATA REGISTERS
  400 REM ------------------------------
  410 tubeR1stat = &FEE0 :REM Register 1 status
  420 tubeR1data = &FEE1 :REM Register 1 data
  430 tubeR2stat = &FEE2 :REM Register 2 status
  440 tubeR2data = &FEE3 :REM Register 2 data
  450 tubeR3stat = &FEE4 :REM Register 3 status
  460 tubeR3data = &FEE5 :REM Register 3 data
  470 tubeR4stat = &FEE6 :REM Register 4 status
  480 tubeR4data = &FEE7 :REM Register 4 data
  490 :
  500 REM OSWORD TYPE DECLERATIONS
  510 REM ------------------------
  520 OSW_GRAPH = &FF :REM Fast graphics update
  530 OSW_HDD   = &FE :REM Hard disk read/write (unimplemented)
  540 OSW_TEXT  = &FD :REM Text control (unimplemented)
  550 OSW_CRTC  = &FC :REM Cursor, soft scroll, mouse etc
  560 OSW_FDC   = &FB :REM MFM floppy disk access
  570 OSW_FAD   = &FA :REM Block data transfer
  580 :
  590 load%=&FFFF2800:exec%=load%:fname$="OswFB-FF"
  600 DIM mcode% &57F
  610 FOR P=0 TO 1
  620   P%=load%:O%=mcode%
  630   [OPT P*3+4
  640   .L2800
  650   JMP &2800        :\ Jumps to OSWORD &FA code, loader changes to &2500
  660   :
  670   \ Main entry point, USERV directed to here
  680   \ ========================================
  690   .L2803
  700   CMP #OSW_GRAPH:BEQ oswFFjmp :\ OSWORD &FF graphics update
  710   CMP #OSW_CRTC :BEQ oswFCjmp :\ OSWORD &FC CRTC control
  720   CMP #OSW_HDD  :BEQ oswFEjmp :\ OSWORD &FE hard disk
  730   CMP #OSW_FDC  :BEQ oswFBjmp :\ OSWORD &FB floppy disk controller
  740   JMP L2800                   :\ Else default code to go to OSWORD &FA
  750   NOTE - Doesn't test for OSWORD &FD
  760   :
  770   \ Memory used as transient storage
  780   \ --------------------------------
  790   \ Storage for FDC operations
  800   .fdc_dma     :EQUD 0        :\ FDC data transfer address
  810   .fdc_status  :EQUB 0        :\ FDC status when completed
  820   .tube_cmd    :EQUB 0        :\ Tube transfer command
  830   .sector_count:EQUB 0        :\ Number of sectors
  840   .error_mask  :EQUB 0        :\ Bitmap to test FDC status
  850   .fdc_cmd     :EQUB 0        :\ FDC command
  860   .watchdog1   :EQUB 0        :\ Timer to time out FDC commands
  870   .watchdog2   :EQUB 0
  880   :
  890   \ Mouse coordinates
  900   .mouse_y_hi  :EQUB 0
  910   .mouse_y_lo  :EQUB 0
  920   .mouse_x_hi  :EQUB 0
  930   .mouse_x_lo  :EQUB 0
  940   :
  950   .oswFEjmp:JMP osword_fe    :\ OSWORD &FE - Jump to RTS
  960   .oswFCjmp:JMP osword_crtc  :\ OSWORD &FC - CRTC control
  970   .oswFFjmp:JMP osword_graph :\ OSWORD &FF - Graphics output
  980   .oswFBjmp:JMP osword_fdc   :\ OSWORD &FB - Floppy disk access
  990   .newevent:JMP eventcode    :\ Jump to event handler
 1000   .newirq  :JMP newirq1      :\ Jump to IRQ1V handler
 1010   :
 1020   :
 1030   \ General Tube access routines
 1040   \ ============================
 1050   :
 1060   \ Get one byte from Tube register 2
 1070   \ ---------------------------------
 1080   .getR2data
 1090   LDA tubeR2stat:BPL tube_oswrch :\ Wait for data present in TubeR2
 1100   LDA tubeR2data:RTS             :\ Read data from TubeR2
 1110   :
 1120   \ Check if TubeR1 data present, if not wait for TubeR2 data
 1130   .tube_oswrch
 1140   LDA tubeR1stat:BPL getR2data  :\ If nothing in TubeR1, jump to check TubeR2
 1150   LDA tubeR1data:JSR oswrch     :\ Read R1 data and send to OSWRCH
 1160   JMP tube_oswrch               :\ Loop back to wait
 1170   :
 1180   \ Write one byte to register 2
 1190   \ ----------------------------
 1200   \ Not called from anywhere
 1210   .write_R2
 1220   BIT tubeR2stat:BVC write_R2   :\ Wait until TubeR2 available
 1230   STA tubeR2data:RTS            :\ Write data to TubeR2
 1240   :
 1250   \ Get one byte from register 4
 1260   \ ----------------------------
 1270   .read_R4
 1280   BIT tubeR4stat:BPL read_R4    :\ Wait for data present in TubeR4
 1290   LDA tubeR4data:RTS            :\ Read data from TubeR4
 1300   :
 1310   \ Write one byte to register 4
 1320   \ ----------------------------
 1330   \ Not called from anywhere
 1340   .write_R4
 1350   BIT tubeR4stat:BVC write_R4   :\ Wait until TubeR4 available
 1360   STA tubeR4data:RTS            :\ Write data to TubeR4
 1370   :
 1380   \ Write one byte to register 1
 1390   \ ----------------------------
 1400   .write_R1
 1410   BIT tubeR1stat:BVC write_R1  :\ Wait until TubeR1 available
 1420   STA tubeR1data:RTS           :\ Write data to TubeR1
 1430   :
 1440   :
 1450   \ OSWORD &FB for WD1770 floppy controller
 1460   \ =======================================
 1470   \ The floppy controller uses five registers, pointed to by (&A0)
 1480   \  0 drive control latch
 1490   \  4 command/status
 1500   \  5 track register
 1510   \  6 sector register
 1520   \  7 data register, track for SEEK
 1530   :
 1540   \ WARNING! - POSITION-DEPENDENT CODE FOLLOWS
 1550   \ ------------------------------------------
 1560   .fdc_exec
 1570   LDA #&00:STA fdc_status        :\ Set result=Ok
 1580   LDY #&07:LDA (&A0),Y           :\ Read FDC data reg to clear any pending DRQ
 1590   JSR getR2data:STA error_mask   :\ Get FDC status mask
 1600   JSR getR2data:STA sector_count :\ Get sector count
 1610   JSR getR2data:STA fdc_cmd      :\ Get FDC command
 1620   JSR tube_program               :\ Initiate Tube transfer
 1630   LDY #&04:LDA fdc_cmd           :\ Get FDC commend
 1640   STA (&A0),Y                    :\ Send command to FDC, start operation
 1650   AND #&C0                       :\ Check top two bits of FDC command
 1660   CMP #&C0:BNE fdc_e7            :\ Not Group 3 or 4 command, leave to interupts
 1670   \ This could be just CMP #&C0, BCC fdc_e7
 1680   :
 1690   LDA #&3C:STA watchdog1         :\ Set watchdog counters for
 1700   LDA #&00:STA watchdog2         :\  non-interupt-driven actions
 1710   TAX:LDY #&04                   :\ Prepare Y=>FDC status register
 1720   .fdc_e1
 1730   DEX:BNE fdc_e1                 :\ Wait 256 loops to allow status to go busy
 1740   .fdc_e2
 1750   LDA #&01:AND (&A0),Y           :\ Test FDC status BUSY flag
 1760   \
 1770   \ WARNING! - the following instruction must not lie
 1780   \ on an address where the bottom 3 bits are set
 1790   \ -------------------------------------------------
 1800   \ (why? I haven't found anything position-dependent in this code)
 1810   :
 1820   BEQ fdc_e4                     :\ No longer busy...
 1830   DEC watchdog2:BNE fdc_e1       :\ Loop back to keep waiting
 1840   DEC watchdog1:BNE fdc_e1       :\ Loop back to keep waiting
 1850   LDA #&FF:BNE fdc_e5            :\ Return &FF=timeout
 1860   :
 1870   .fdc_e4
 1880   LDY #&04:LDA (&A0),Y           :\ Get FDC status
 1890   .fdc_e5
 1900   STA fdc_status                 :\ Set the returned status
 1910   .fdc_e6
 1920   SEI:JSR fdc_event:CLI          :\ Generate FDC event
 1930   RTS
 1940   :
 1950   \ Not Group 3 or Group 4 command
 1960   \ ------------------------------
 1970   .fdc_e7
 1980   BPL fdc_e9                     :\ Group 1 command, return
 1990   LDY #&00:LDX #&00              :\ Group 2 command, wait for interupts
 2000   .fdc_e8
 2010   LDA status_pending:BNE fdc_e6  :\ NMI finished, return status
 2020   DEX:BNE fdc_e8                 :\ Loop and keep testing
 2030   DEY:BNE fdc_e8                 :\ Loop 256*256 times and keep testing
 2040   .fdc_e9
 2050   RTS                            :\ Return &00/<>&00 from status_pending
 2060   :
 2070   \ OSWORD &FB - FDC and I/O control
 2080   \ ================================
 2090   \ Commands are transfered via TubeR2 within the OSWORD transaction:
 2100   \  &00 no more commands
 2110   \  &01 FDC action on Master
 2120   \  &02 FDC action on Master
 2130   \      Command block for &01 and &02:
 2140   \      addr0,addr1,addr2,addr3,tube_cmd,rdwr,errmask,count,fdc_cmd
 2150   \      Drive, track and sector must be set beforehand by subcode &05+
 2160   \  &03 claim NMI and Tube
 2170   \  &04 release NMI and Tube
 2180   \ otherwise
 2190   \  &nn,&mm write &mm to I/O at &FE00+&nn
 2200   \
 2210   .osword_fdc
 2220   CLI:CLD          :\ Enable IRQs, ensure binary
 2230   JSR getR2data    :\ Read tube R2 data - get a command
 2240   BNE tube_call_1  :\ If non-zero, jump to test for tube_call_1
 2250   .osword_fdc_ret
 2260   RTS              :\ Done
 2270   :
 2280   \ These routines claim or release the tube, set up the l770 addresses
 2290   \ for the host machine, or write data to memory mapped I/O in SHEILA.
 2300   \ ===================================================================
 2310   .tube_call_1
 2320   CMP #&01          :\ Master FDC command?
 2330   BNE tube_call_2   :\ No, try tube_call_2 (B/B+)
 2340   JSR tube_setup    :\ Get FDC parameters, store at tube_cmd
 2350   JSR copy_NMI_data :\ Copy and adapt NMI routine
 2360   JSR Master_setup  :\ Set up FDC addresses for Master
 2370   JSR fdc_exec      :\ Get and execute command
 2380   JMP osword_fdc    :\ Loop back for another command
 2390   :
 2400   .tube_call_2
 2410   CMP #&02          :\ BBC (B/B+) FDC command?
 2420   BNE tube_call_3   :\ No, try tube_call_3
 2430   JSR tube_setup    :\ Get FDC parameters, store at tube_cmd
 2440   JSR copy_NMI_data :\ Copy and adapt NMI routine
 2450   JSR BBC_setup     :\ Set up FDC addresses for BBC
 2460   JSR fdc_exec      :\ Get and execute command
 2470   JMP osword_fdc    :\ Loop back for another command
 2480   :
 2490   .tube_call_3
 2500   CMP #&03          :\ Claim tube and NMI?
 2510   BNE tube_call_4   :\ No, try tube_call_4
 2520   JSR nmi_claim     :\ Claim NMIs
 2530   JSR tube_claim    :\ Claim the Tube
 2540   JMP osword_fdc    :\ Loop back for another command
 2550   :
 2560   .tube_call_4
 2570   CMP #&04          :\ Release tube and NMI?
 2580   BNE other_output  :\ No, branch to do generic I/O
 2590   JSR tube_release  :\ Release tube
 2600   JSR nmi_release   :\ Release NMIs
 2610   JMP osword_fdc    :\ Loop back for another command
 2620   :
 2630   .other_output
 2640   TAX               :\ Offset into SHEILA in X
 2650   JSR getR2data     :\ Read tube R2 data - get data byte
 2660   STA &FE00,X       :\ Store in SHEILA,X - write to I/O
 2670   JMP osword_fdc    :\ Loop back for another command
 2680   :
 2690   \ Set up offsets into SHEILA to FDC hardware
 2700   \ ------------------------------------------
 2710   .BBC_setup
 2720   LDX #&84          :\ BBC_fdc_stat at &FE84
 2730   LDY #&87          :\ BBC_fdc_data at &FE87
 2740   LDA #&80          :\ BBC_fdc_base at &FE80
 2750   BNE setup1
 2760   .Master_setup
 2770   LDX #&28          :\ Master_fdc_stat at &FE28
 2780   LDY #&2B          :\ Master_fdc_data at &FE2B
 2790   LDA #&24          :\ Master_fdc_base at &FE24
 2800   :
 2810   .setup1
 2820   STA &A0           :\ Offset to FDC hardware
 2830   LDA #&FE:STA &A1  :\ (&A0),Y=>FDC registers
 2840   STX &0D02         :\ Patch NMI status register address
 2850   JSR getR2data     :\ Get read/write setting
 2860   BEQ setup2        :\ &00 - set up for reading
 2870   STY &0D0E:RTS     :\ <>&00 - patch NMI code for writing
 2880   .setup2
 2890   STY &0D0B:RTS     :\ Patch NMI code for writing
 2900   :
 2910   .tube_claim
 2920   LDA #&C0+1:JSR tube_entry  :\ Claim Tube with ID=1
 2930   BCC tube_claim:RTS         :\ Loop until claimed
 2940   :
 2950   \ Fill in data transfer address
 2960   \ -----------------------------
 2970   .tube_setup
 2980   LDX #&00
 2990   .fdc_dma1
 3000   JSR getR2data:STA fdc_dma,X :\ Read address from R2data
 3010   INX:CPX #&04:BNE fdc_dma1   :\ Four bytes to read
 3020   JSR getR2data:STA tube_cmd  :\ Read R2data, store Tube action
 3030   RTS
 3040   :
 3050   .tube_program
 3060   LDA tube_cmd         :\ A=Tube transfer command
 3070   LDX #fdc_dma AND 255 :\ XY=>transfer address
 3080   LDY #fdc_dma DIV 256
 3090   JSR tube_entry       :\ Initiate Tube transfer
 3100   RTS
 3110   :
 3120   .tube_release
 3130   LDA #&80+1:JSR tube_entry   :\ Release Tube with ID=1
 3140   RTS
 3150   :
 3160   \ These routines claim or release NMI ownership according to whether
 3170   \ tube data transfer is to take place or disc access is required. In
 3180   \ practical terms only one of three facilities can be the current NMI
 3190   \ owner, the disc system, the network handler (econet), or the tube.
 3200   \ A copy of this routine's NMI code is kept at nmi_image (&29DB) and
 3210   \ transferred to page &D when NMI ownership is required. The routines
 3220   \ are called by the code in tube_call_1 to tube_call_4.
 3230   \ ===================================================================
 3240   .nmi_claim
 3250   LDA #&8F:LDX #&0C   :\ Service call &0C
 3260   LDY #&FF:JSR osbyte :\ Claim NMIs
 3270   STY nmi_owner:RTS   :\ Store previous NMI owner
 3280   :
 3290   .nmi_release
 3300   LDY nmi_owner       :\ Get previous NMI owner
 3310   LDA #&8F:LDX #&0B   :\ Service call &0B
 3320   JMP osbyte
 3330   :
 3340   .nmi_owner:EQUB 0   :\ Previous NMI owner
 3350   :
 3360   .copy_NMI_data
 3370   LDX #&1F                    :\ Copy 32 bytes (more than needed)
 3380   .nmi_lp1
 3390   LDA nmi_image,X:STA &0D00,X :\ Copy NMI code to NMi area
 3400   DEX:BPL nmi_lp1:RTS         :\ Loop until all done
 3410   :
 3420   \ This routine is called from the NMI code in the NMI area.
 3430   \ NMI occured with no data transfer, will be either an error or a
 3440   \ transfer completion. Decide to abort or step to next sector.
 3450   \ ---------------------------------------------------------------
 3460   .nmi_next_sect
 3470   LDY #&04:LDA (&A0),Y  :\ Get FDC status from FDC+4
 3480   STA fdc_status        :\ Save for returned status
 3490   AND error_mask        :\ Mask for any error bits on
 3500   BNE nmi_next_exit     :\ Error occured, abort operation
 3510   LDA fdc_cmd           :\ Check FDC command
 3520   BPL nmi_next_exit     :\ b7=0, Restore/Seek, so finished
 3530   LDA sector_count      :\ Get current sector count
 3540   CMP #&01              :\ Is this the last one?
 3550   BEQ nmi_next_exit     :\ Final sector done, finished
 3560   DEC sector_count      :\ Decre. sector count
 3570   LDY #&06:LDA (&A0),Y  :\ Get FDC sector register from FDC+6
 3580   CLC:ADC #&01          :\ Increment sector number
 3590   STA (&A0),Y           :\ Set FDC sector register
 3600   LDA fdc_cmd:AND #&F3  :\ Get FDC command without settle delay
 3610   LDY #&04:STA (&A0),Y  :\ Write to FDC command register to start again
 3620   RTS
 3630   .nmi_next_exit
 3640   LDA #&FF:STA status_pending  :\ Set that NMI has completed
 3650   RTS
 3660   :
 3670   \ FDC NMI routine, copied to NMI area at &0D00
 3680   \ --------------------------------------------
 3690   .nmi_image
 3700   PHA:LDA &FE00         :\ &0D00 Get FDC status, address set up earlier
 3710   AND #&1F              :\ &0D04 Keep error bits
 3720   CMP #&03:BNE nmi_exit :\ &0D06 If not DRQ+BSY, final NMI, exit
 3730   LDA tubeR3data        :\ &0D0A Either this or next instruction
 3740   STA tubeR3data        :\ &0D0D patched to point to FDC data register
 3750   PLA:RTI               :\ &0D11 Restore and return
 3760   .nmi_exit
 3770   TYA:PHA               :\ &0D12 Also save Y
 3780   JSR nmi_next_sect     :\ &0D14 Check for next sector or finish
 3790   PLA:TAY               :\ &0D17 Restore Y
 3800   PLA:RTI               :\ &0D19 Restore and return
 3810   .nmi_end
 3820   :
 3830   .status_pending:EQUB 0 :\ &00=NMI in progress, <>&00=NMI finished
 3840   :
 3850   :
 3860   \ OSWORD &FF - Write graphics data
 3870   \ =========================================================
 3880   \ Bit patterns are sent to write to screen memory
 3890   \ Commands are sent via TubeR2 within the OSWORD transaction:
 3900   \   &00 - no more commands
 3910   \   Otherwise high,low - address to start transfer
 3920   \ Then data is sent via TubeR1 with subcommand in TubeR2:
 3930   \   &00   - read 8 bytes from TubeR1 for next 8 addresses
 3940   \ &01-&FE - read 1 byte from TubeR1 for next 8 addresses
 3950   \   &FF   - go back to wait for another address or exit
 3960   \ The data address is updated and wraps from &7FFF to &4000
 3970   \ =========================================================
 3980   :
 3990   .osword_ff_exit
 4000   RTS
 4010   .osword_graph
 4020   CLI:CLD          :\ Enable IRQs, ensure binary
 4030   .next_addr_hi
 4040   JSR getR2data:BEQ osword_ff_exit  :\ &00, finish
 4050   STA &71                           :\ Store as high byte of data address
 4060   .next_addr_lo
 4070   LDA tubeR2stat:BPL next_addr_lo   :\ Wait for data in TubeR2
 4080   LDA tubeR2data:TAY                :\ Get as offset to data address
 4090   LDA #&00:STA &70                  :\ (&70)=>data
 4100   :
 4110   \ Writing to the screen is optimised for maximum possible
 4120   \ speed, hence these routines are written 'longhand' and
 4130   \ so avoid the use of counters, instructions to decrement
 4140   \ them and the tests to check when the loop is complete.
 4150   \ Spaces are further optimised because they are the most
 4160   \ frequently written character and only need a single
 4170   \ fill byte. They are therefore the easiest to write as
 4180   \ no reading of tubeR1data is required between bytes. Also,
 4190   \ the data address is assumed to always be a multiple of 8.
 4200   :
 4210   .transfer_loop1
 4220   LDA tubeR2stat:BPL transfer_loop1 :\ Wait for data in TubeR2
 4230   LDA tubeR2data:BEQ transfer_loop2 :\ Get data, if &00 jump to read 8 bytes
 4240   CMP #&FF:BEQ next_addr_hi         :\ &FF, go back for next command
 4250   :
 4260   \ Write single byte to 8 locations
 4270   \ --------------------------------
 4280   LDA tubeR1data                  :\ Get byte from TubeR1
 4290   STA (&70),Y:INY                 :\ Write to screen RAM eight times
 4300   STA (&70),Y:INY                 :\ as fast as possible
 4310   STA (&70),Y:INY
 4320   STA (&70),Y:INY
 4330   STA (&70),Y:INY
 4340   STA (&70),Y:INY
 4350   STA (&70),Y:INY
 4360   STA (&70),Y:INY
 4370   BNE transfer_loop1              :\ Not next page, go back for more
 4380   INC &71                         :\ Incr. address high byte
 4390   BPL transfer_loop1              :\ If not wrapped round, go back for more
 4400   LDA #&40:STA &71                :\ Reset to start of screen at &4000
 4410   BNE transfer_loop1              :\ And back for more
 4420   :
 4430   \ Write 8 bytes to 8 locations
 4440   \ ----------------------------
 4450   .transfer_loop2
 4460   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4470   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4480   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4490   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4500   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4510   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4520   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4530   LDA tubeR1data:STA (&70),Y:INY  :\ Copy byte to memory
 4540   BNE transfer_loop1              :\ Not next page, go back for more
 4550   INC &71                         :\ Incr. address high byte
 4560   BPL transfer_loop1              :\ If not wrapped round, go back for more
 4570   LDA #&40:STA &71                :\ Reset to start of screen at &4000
 4580   BNE transfer_loop1              :\ And back for more
 4590   :
 4600   :
 4610   \ OSWORD &FC - program CRTC controller, initialise mouse, etc.
 4620   \ ============================================================
 4630   \ Commands are sent via TubeR2 within the OSWORD transaction:
 4640   \  <&80,&mm - Write to CRTC register
 4650   \  &FF - No more commands
 4660   \  &FE - Initialise mouse handler
 4670   \  &FD - Initialise event code
 4680   \  &FC - Write to mouse port
 4690   \  Otherwise, exit
 4700   \
 4710   .osword_crtc
 4720   JSR getR2data:BMI &2A90   :\ Set up or exit if >&7F received
 4730   STA &FE00                 :\ Write 6845 CRTC address register
 4740   JSR getR2data:STA &FE01   :\ Write 6845 CRTC data register
 4750   JMP osword_crtc           :\ Loop for more
 4760   :
 4770   .osword_fc_1
 4780   TAX:INX:BEQ osword_fc_exit      :\ &FF - finished
 4790   INX:BNE osword_fc_2             :\ Not &FE, try osword_fc_2
 4800   :
 4810   \ Command &FE - initialise Mouse code
 4820   \ -----------------------------------
 4830   SEI                             :\ Disable IQRs while changing vector
 4840   LDA irqv1+0:STA oldirq1+1       :\ Store old IQR1V
 4850   LDA irqv1+1:STA oldirq1+2
 4860   LDA #newirq MOD 256:STA irqv1+0 :\ Set new IRQ1V
 4870   LDA #newirq DIV 256:STA irqv1+1 :\ Store in IRQ1 vector high byte
 4880   CLI                             :\ re-enable interrupts
 4890   LDA #0:STA &FE62                :\ Set UserData as inputs
 4900   LDA #&98:STA &FE6E              :\ Enable CB1+CB2 interupts
 4910   LDA &FE6B:AND #1:STA &FE6B      :\ Enable PA latching, disable everything else
 4920   LDA &FE6C:AND #&0F:STA &FE6C    :\ Clear CB1 and CB2
 4930   JMP osword_crtc                 :\ Loop for more commands
 4940   :
 4950   \ Command &FD - initialise Event code
 4960   \ -----------------------------------
 4970   .osword_fc_2
 4980   INX:BNE osword_fc_3             :\ Not &FD, try osword_fc_3
 4990   LDA eventvec+0:STA oldevent+1   :\ Store old EventV
 5000   LDA eventvec+1:STA oldevent+2
 5010   LDA #newevent MOD 256:STA eventvec+0  :\ Set new EventV
 5020   LDA #newevent DIV 256:STA eventvec+1
 5030   JMP osword_crtc                 :\ Loop for more commands
 5040   :
 5050   \ Command &FC - write to mouse port
 5060   \ -----------------------------------
 5070   \ Not implemented
 5080   .osword_fc_3
 5090   INX:BNE osword_fc_exit          :\ Not &FC, exit
 5100   JMP osword_crtc                 :\ Loop for more commands
 5110   :
 5120   .osword_fc_exit
 5130   RTS
 5140   :
 5150   :
 5160   \ EVENT PROCESSING
 5170   \ ================
 5180   \ On VSync event every 20cs, do some background processing
 5190   \
 5200   .fdc_event
 5210   LDX fdc_status               :\ Get the FDC status
 5220   LDY #0:STY status_pending    :\ Clear the FDC pending flag
 5230   LDA #&0A                     :\ Pass on as Event 10 - FDC result waiting
 5240   .oldevent
 5250   JMP &0000                    :\ Pass on to old EVENTV
 5260   :
 5270   \ Two-key rollover processing done on VSync event
 5280   \ -----------------------------------------------
 5290   .eventcode
 5300   CMP #4:BNE oldevent               :\ Not Event 4 (VSync), pass on
 5310   LDA status_pending:BEQ eventcode1 :\ Skip if no FDC event pending
 5320   JSR fdc_event                     :\ Do the FDC event
 5330   .eventcode1
 5340   JSR keyscan                       :\ Test SHIFT/CTRL keys
 5350   PHP:LDA key1:AND #&7F:PLP:PHP     :\ Get current key with bit 7 clear
 5360   BPL no_ctrl:ORA #&80              :\ Set bit 7 is CTRL pressed
 5370   .no_ctrl
 5380   TAX:LDA key2:AND #&7F:PLP         :\ Get previous key with bit 7 clear
 5390   BVC no_shift:ORA #&80             :\ Set bit 7 if SHIFT pressed
 5400   .no_shift
 5410   TAY                               :\ X=CTRL+current key, Y=SHIFT+last key
 5420   LDA #4:JSR oldevent               :\ Call old EVENTV claimant
 5430   :
 5440   \ The current key (if any in now in X - The top bit is set
 5450   \ if CONTROL was pressed, even when no other key was pressed.
 5460   \ The previous key, (if any) is now in Y - The top bit is set
 5470   \ if SHIFT was pressed, even when there is no previous key press.
 5480   \ This results in VSync Event handler being passed keypresses in
 5490   \ X and Y and thence passed over the Tube to the coprocessor.
 5500   :
 5510   .async_command
 5520   JSR read_R4:BEQ async_exit        :\ Read from TubeR4, exit if zero
 5530   TAX:DEX:BNE async_mouse           :\ Not &01, skip to try async_mouse
 5540   :
 5550   \ Update 6845 CRTC registers
 5560   \ --------------------------
 5570   JSR read_R4:STA &FE00             :\ Get data from TubeR4, write as CRTC address
 5580   JSR read_R4:STA &FE01             :\ Get data from TubeR4, write as CRTC data
 5590   JMP async_command                 :\ Go back for any more commands
 5600   :
 5610   \ Read mouse button state and position
 5620   \ ------------------------------------
 5630   .async_mouse
 5640   DEX:BNE async_leds :\ Not &02, skip to try async_leds
 5650   LDA amx:PHP        :\ Set flags from mouse type
 5660   LDA userport       :\ Read mouse data lines
 5670   PLP:BEQ &2B4F      :\ Not AMX, skip adjustment
 5680   ROL A:ROL A:ROL A  :\ AMX buttons are top bits, so move them
 5690   ROL A              :\ Buttons are now in bits 0, 1, 2
 5700   .async_mouse_1
 5710   AND #7:EOR #7      :\ Mask off and invert the buttons
 5720   JSR write_R1       :\ Send the button state to TubeR1
 5730   LDX #3             :\ Prepare to send four bytes of mouse coords
 5740   .async_mouse_2
 5750   LDA mouse_y_hi,X:JSR write_R1  :\ Send mouse coords to TubeR1
 5760   DEX:BPL async_mouse_2
 5770   JMP async_command              :\ Go back for any more commands
 5780   :
 5790   \ Set keyboard status and update LEDs
 5800   \ -----------------------------------
 5810   .async_leds
 5820   DEX:BNE async_command :\ Not &03, go back for more commands
 5830   JSR read_R4:TAX       :\ Read data from TubeR4
 5840   LDA #&CA              :\ *FX 202 - keyboard status
 5850   LDY #0:JSR osbyte     :\ Set keyboard status
 5860   LDA #&76:JSR osbyte   :\ Update LEDs to match status
 5870   JMP async_command     :\ Go back for any more commands
 5880   :
 5890   .async_exit
 5900   RTS
 5910   :
 5920   \ Test SHIFT and CTRL keys
 5930   \ ------------------------
 5940   .keyscan
 5950   CLC:CLV:JMP (keyvec)       :\ CC+CV=Test SHIFT+CTRL
 5960   :
 5970   :
 5980   \ WARNING! MASKABLE INTERRUPT CODE
 5990   \ ===========================================================
 6000   \ X and Y must be preserved here. The original contents of A
 6010   \ are preserved by the MOS, in zero page location &FC. This
 6020   \ must be reloaded before exiting - with an RTI if we process
 6030   \ the interrupt, or if we jump to the original vector because
 6040   \ we're not interested.
 6050   :
 6060   .newirq1
 6070   LDA &FE6D                  :\ Get Interupt Flag register
 6080   AND #&18:BNE new_irq_code  :\ Branch if CB1 or CB2 active edge
 6090   LDA &FC                    :\ Not CB1 or CB2, restore A and continue
 6100   .oldirq1
 6110   JMP &0000                  :\ Pass on to old IRQ1V
 6120   :
 6130   .new_irq_code
 6140   STA ifr_copy               :\ Save CB1/CB2 interupt flags
 6150   LDA userport:STA orb_copy  :\ Get PortB data
 6160   AND #&18                   :\ Check for AMX mouse
 6170   CMP #&18:BEQ new_irq_code2 :\ Will be %xxx11xxx for AMX mouse
 6180   LDA #0:STA amx             :\ Set amx=0 for tracker ball
 6190   LDA #&08:STA mouse_y_quad  :\ Set data bit for tracker ball y_quad signal
 6200   LDA #&10:STA mouse_x_quad  :\ Set data bit for tracker ball x_quad signal
 6210   :
 6220   \ Check for X movement
 6230   \ --------------------
 6240   .new_irq_code2
 6250   LDA ifr_copy               :\ Get interupt register back
 6260   AND #&10:BEQ new_irq_code5 :\ CB2 interupt, X moved
 6270   :
 6280   \ Update X movement
 6290   \ -----------------
 6300   LDA pcr_copy               :\ Get peripheral control copy
 6310   EOR #&10:STA pcr_copy      :\ Invert pos/neg edge bit
 6320   LDA x_edge                 :\ Get get edge triggering mode
 6330   EOR #&FF:STA x_edge        :\ Invert it
 6340   EOR orb_copy               :\ Invert with edge trigger
 6350   AND mouse_y_quad           :\ Test X direction bit
 6360   BNE new_irq_code3          :\ Xdir set, decrease X coord
 6370   INC mouse_x_lo             :\ Xdir clear, increase X coord
 6380   BNE new_irq_code5          :\ No rollover, jump to check Ymovement
 6390   INC mouse_x_hi             :\ Increase X coordinate
 6400   JMP new_irq_code5          :\ And jump to check Ymovement
 6410   .new_irq_code3
 6420   LDA mouse_x_lo             :\ Decrement Xcoord
 6430   BNE new_irq_code4          :\ Do a 16-bit decrement
 6440   DEC mouse_x_hi             :\ Decrement mouse_x_hi
 6450   .new_irq_code4
 6460   DEC mouse_x_lo             :\ Decrement mouse_x_lo
 6470   :
 6480   \ Check for Y movement
 6490   \ --------------------
 6500   .new_irq_code5
 6510   LDA ifr_copy               :\ Get interupt register back
 6520   AND #&08:BEQ new_irq_code8 :\ CB1 interupt, Y moved
 6530   :
 6540   \ Update Y movement
 6550   \ -----------------
 6560   LDA pcr_copy               :\ Get peripheral control copy
 6570   EOR #&40:STA pcr_copy      :\ Invert pos/neg edge bit
 6580   LDA y_edge                 :\ Get edge triggering mode
 6590   EOR #&FF:STA y_edge        :\ Invert it
 6600   EOR orb_copy               :\ Invert with edge trigger
 6610   AND mouse_x_quad           :\ Test Y direction bit
 6620   BNE new_irq_code6          :\ Ydir set, decrease Y coord
 6630   INC mouse_y_lo             :\ Ydir cler, increment Y coord
 6640   BNE new_irq_code8          :\ No rollover, exit
 6650   INC mouse_y_hi             :\ Increment Y coord
 6660   JMP new_irq_code8          :\ And jump to finish
 6670   .new_irq_code6
 6680   LDA mouse_y_lo             :\ Decrement Y coord
 6690   BNE new_irq_code7          :\ Do a 16-bit decrement
 6700   DEC mouse_y_hi             :\ Decrement mouse_y_hi
 6710   .new_irq_code7
 6720   DEC mouse_y_lo             :\ Decrement mouse_y_lo
 6730   :
 6740   .new_irq_code8
 6750   LDA &FE6C:AND #&0F         :\ Get peripheral control register CA bits
 6760   ORA pcr_copy:STA &FE6C     :\ Update with stored CB control bits
 6770   LDA #&18:STA &FE6D         :\ Clear CB1 and CB2 interupts
 6780   LDA &FC:RTI                :\ Recover A and return from interupt
 6790   :
 6800   .amx         :EQUB &FF     :\ default is AMX, 0 = Tracker
 6810   .mouse_y_quad:EQUB 1       :\ 01 = AMX, 08 = Tracker
 6820   .mouse_x_quad:EQUB 4       :\ 04 = AMX, 10 = Tracker
 6830   .ifr_copy    :BRK          :\ RAM copy of IFR state
 6840   .orb_copy    :BRK          :\ RAM copy of user port B
 6850   .x_edge      :BRK          :\ defines pos/neg edge triggering
 6860   .y_edge      :BRK          :\ defines pos/neg edge triggering
 6870   .pcr_copy    :BRK          :\ Local copy of PCR
 6880   :
 6890   NOTE A local copy of the peripheral control register is
 6900   \ maintained at pcr_copy because someone is reprogramming
 6910   \ the VIA when they shouldn't be, leading to mouse reversal
 6920   \ - suspect a Master 128 hardware problem (??)
 6930   :
 6940   \ OSWORD &FE - HARD DISK ACCESS
 6950   \ =============================
 6960   .osword_fe
 6970   RTS           :\ not implemented
 6980   :
 6990   :
 7000   .pad          :\ Pad to page boundary
 7010   EQUS STRING$(((P%+256)AND&FF00)-P%,CHR$0)
 7020 ]NEXT
 7030 A$=fname$+" "+STR$~mcode%+" "+STR$~O%+" "+STR$~(exec%OR&FFFF0000)+" "+STR$~load%
 7040 PRINT"Saving ";A$:OSCLI "SAVE "+A$:PRINT