 lib environment
 lib bfhdr
 lib dtab
 lib macdefs
 lib blktab
 data
 ifc &c,"NO-5"
 sttl GIMIX 8" Floppy Disk Drivers
 else
 sttl GIMIX 8"/5" Floppy Disk Drivers
 endif
 pag
 name gfddrvr
 global dmfopn,dmfcls,dmfint,dmfio
 ifnc &c,"NO-5"
 global dmfTO
 endif
 global fchop,fchcl,fchsp,fchio,fchrd,fchwr
 global fchgb,fchcn2,frechbf

*
* Print control flags
*
P_REQ equ 0 -- Set to 0 to disable print of requests
P_ERR equ 0 -- Set to 0 to disable print on errors
P_TRC equ 0 -- Set to 0 to disable trace printing

*
* This file contains the drivers for the
* GIMIX dual density floppy disk controller.
*

*
* driver constants
*

SECLEN equ 512 sector length
RWOK equ %11011000 read write status mask
RNFD equ %00010000 record not found error
MAXTRK equ 76 maximum track number allowed
 ifnc &c,"NO-5"
MAXTK540 equ 39 max tracks on 5" disk
MAXTK580 equ 79 Max tracks on 5"/80 disk
 endif
RDCMND equ $88 read block command
WTCMND equ $A8 write block command
DWTCMD equ $AC write block with head settle
UNLDHD equ $10 unload head command
SKCMND equ $18 seek command
RSCMND equ $08 restore command
WTTCMD equ $F4 write track command
FICMND equ $D0 force interrupt command
RDMSCM equ $98 read multiple command (swap in)
WTMSCM equ $B8 write multiple command (swap out)
DWTMSC equ $BC write multiple with head settle
SIDE1 equ $02 Second side bit for read/write sector

 pag

*
* driver variable storage
*

*
* Devices are structured as follows:
*   Minor Device   Device Type    Allowable Media Type
*   ============   ===========    ====================
*      0 - 3          8"             8"
*      4 - 7          5"/40          5"/40 Tracks
*      8 - 11         5"/80          5"/40 or 5"/77
*

dmfdrv fcb 0 current drive selected
dmfhdr fcb 1 hardware compat drive number
dmftrk fcb 0,0,0,0,0,0,0,0,0,0,0,0 track table
dmfsts fcb 0,0,0,0,0,0,0,0,0,0,0,0 side table
dmfstd fcb 0,0,0,0,0,0,0,0,0,0,0,0 density table
dmfmtp fcb 0,0,0,0,0,0,0,0,0,0,0,0 media type table
dmfopt fcb 0,0,0,0,0,0,0,0,0,0,0,0 dmf open table
dmascb fcb 0 side compare bit
dmfden fcb 0 density flag
dmfsid fcb 0 side flag
dmftyp fcb 0 current 40/80 media indication
sidebit fcb 0 side bit valid in header on disk
dmasd fcb 0 current side select
dmftmp fdb 0 temp space for open
dmfcds fcb 0 controller flag (1=cds)
dmswop fcb 0 swap operation (read or write)
dmswdr fcb 0 swap direction for controller
dmswpt fdb 0 swap table pointer
dmfskf fcb 0 seek flag
dmbcnt fcb 0 swap block counter (gimix only)
dmdidw fcb 0 DMA just did a write operation
new_track fcb 0 Actual track for 5"/40 media on 5"/80 drive
BDtable fdb 0 Block Device Table pointer
wd_cnt fcb 0 Wait disk time-out count
WD_MAX equ 10 10*.5 second wait for drive ready
gfd_trk fcb 0
gfd_dat fcb 0
gfd_sec fcb 0
gfd_com fcb 0
gfd_ccr fcb 0
gfd_adr fcb 0
gfd_drv fcb 0

 pag

*
* dmfopn
*
* Open dmaf2 device.  This guy reads in the sir
* block (1) and sets up the density and side info.
*

dmfopn pshs d save device number
 subb #DMF2minor compute relative drive #
 ldx #dmfopt point to open table
 lda b,x get this guys entry
 beq dmfop2 jump if not currently open
 inc b,x bump open count
 bra dmfop3 all done
dmfop2 ldd 0,s reset device number
 jsr fchop do char open stuff
 ldd 0,s get dev number
 bsr dmfrbo read block one (sir)
 beq dmfop4 error?
 clr uerror reset error status
 jsr freebf free the buffer
 ldx #dmfstd point to density table
 ldd 0,s get device number
 subb #DMF2minor compute relative drive #
 inc b,x set to double density
 ldd 0,s restore device number
 bsr dmfrbo read sir again
 beq dmfop4 error?
dmfop25 lda #EIO set io error
 sta uerror
 lbsr freebf free the buffer
 ldx #dmfopt point to open table
 ldd 0,s get device number
 subb #DMF2minor compute relative drive #
 clr b,x clear status
dmfop3 puls d,pc return
dmfop4 pshs y save buffer
 ldx 0,s get buffer
 ldy #dmftmp point to data block
 ldu #sdenf set density offset in sir
 ldd #2 set byte count to 2
 jsr cpybts get density & side info
 ldd 2,s get device number
 subb #DMF2minor compute relative drive #
 ldx #dmfsts point to side table
 lda dmftmp+1 get side data
 anda #$7F Strip 40/80 indicator
 sta b,x set in table
 ldx #dmfstd density table
 lda dmftmp get density info
 anda #$7F Strip 40/80 indicator
 sta b,x set in table
 ldx #dmfmtp media type table
 lda dmftmp get 40/80 indicator
 anda #$80
 sta b,x
 puls y get buffer
 beq 00f 40 track media allowed on any drive
 cmpb #4 8" drives don't care about 40/80 indicator
 blo 00f
 cmpb #8 80 track media only allowed on 5"/80
 blo dmfop25 error
00 jsr freebf free the buffer
 puls d,pc return

*
* read sir
*

dmfrbo ldx #1 set block 1
 ldy #0
 lda #DMF2major set Major Device Number
 jsr rdbuf read in the block
 lda bfflag,y get flags
 pshs a save original flags
 ora #BFERR invalidate buffer in cache
 sta bfflag,y
 puls a restore original flags
 bita #BFERR error?
 rts return

*
* dmfcls
*
* Close dmaf2 device.  Nothing to do right now.
*

dmfcls subb #DMF2minor compute relative drive #
 ldx #dmfopt point to open table
 dec b,x dec count of opens
 bpl dmfcl2
 clr b,x
dmfcl2 bne dmfcl3 jump if still open
 clr DMFDRV otherwise - de-select drive
dmfcl3 rts return

*
* dmfio
*
* Start dmaf executing a requested operation.
*   (X) - Block Device Table Address
*   (Y) - Buffer Header
*

dmfio stx BDtable save Block Device Table address
 if P_REQ
 jsr prt_env
 endif
dmfio2 inc dmfdt+dtbusy set busy status
 clr dmfcds clear controller flag
 lda bfdvn+1,y get device number
 suba #DMF2minor compute relative drive #
 clrb
 cmpa dmfdrv is it the current drive?
 bne dmfcd if not - change drives
 ldx #dmfsts point to side table
 leax a,x point to entry
 lda 0,x get side
 anda #$0F get side only flag
 sta dmfsid set flag
 lda 0,x get side field again
 anda #$10 get "side bit in headers" flag
 sta sidebit save flag
 lda 12,x get density
 sta dmfden set flag
 lda 12*2,x get media type
 sta dmftyp
 lbra dmfsk go seek

 pag

*
* dmfcd
*
* Change drives.  Select the drive specified in
* the A accumulator.  This routine is entered by
* falling through from above.
*

dmfcd bsr dmfftr find the track number
 sta dmfdrv save the drive number
 ldb DMFTRK get track from wd
 stb 0,x save in table
 bsr dmfftr find entry
 ldb 0,x get old track number
 stb gfd_trk
 stb DMFTRK set into wd
 stb DMFDAT set data register for seek
 ldb 12,x get side info
 stb dmfsid
 andb #$10 get "side bit in headers" flag
 stb sidebit save flag
 ldb 12*2,x get density info
 stb dmfden
 ldb 12*3,x get media type indicator
 stb dmftyp
 anda #$3 only 2 significant bits!
 ldb #1 calculate hardware drive spec
 tsta
 beq dmfcd4
dmfcd2 aslb raise dr to power
 deca
 bne dmfcd2
dmfcd4 stb dmfhdr save hardware drive number
 lda dmfdrv check drive type
 cmpa #4 5" disk?
 bhs dmfcd5 yes
 orb #$40 no - select cable
dmfcd5 orb #$30 set for controller
 stb gfd_drv
 stb DMFDRV select drive
 ldb #UNLDHD set unload command
 stb gfd_com
 stb DMFCOM send command
 pshs d delay some
 puls d
dmfcd6 ldb DMFINT wait for command to finish
 aslb check int bit
 bpl dmfcd6
 bra dmfsk go do seek

*
* dmfftr
*
* Find old value of the track register in wd.
* Also set the new value since changing drives.
*

dmfftr ldx #dmftrk point to track table
 ldb dmfdrv get drive number
 abx point to entry
 rts return

 pag

*
* dmfsk
*
* Seek the head to the desired track.  Calculate
* the track and sector numbers from the block
* number passed in the transaction record.  The
* track is blkno>>3, and the sector is (blkno&3)+1.
* This is step one of the drivers and is indicated
* by the dtbusy flag set to 1.
*

dmfsk clrb set status
 tst bfblch,y check high block number
 lbne dmfer2 show error if set
 ldd bfxfc,y get xfr count
 cmpd #512 doing block transfer?
 beq dmfsk2
 cmpd #16 is it a swap?
 lbhi dmcsk if not - do character
dmfsk2 lda bfdvn+1,y get drive #
 ifnc &c,"NO-5"
 cmpa #4 5" drive?
 lbhs dm5sk yes - set up for 5"
 endif
 ldd bfblck,y get low block number
 tst dmfden double density?
 lbne dmfdd if so - go handle it
 pshs b
 lsra divide by 16
 rorb which is sectors per track
 lsra
 rorb
 lsra
 rorb all done now
 tsta check track too big
 beq dmfsk3
 puls b clean up stack
 lbra dmfsk8 go report error
dmfsk3 tfr b,a put track number in a
 puls b get original
 andb #7 mask sector number
 incb adjust (no sector 0)
 clr dmasd clear side flag
 tst dmfsid is this drive double sided?
 beq dmfsk4
 lsra adjust track number
 bcc dmfsk4 switch sides?
 addb #8 adjust sector for 2nd side
 inc dmasd set side 2 flag
dmfsk4
 ifnc &c,"NO-5"
 pshs a save track #
 lda dmfdrv 8" or 5" drive
 cmpa #4
 puls a
 blo 10f jump for 8"
 tst dmftyp check media type
 bne 05f
 cmpa #MAXTK540 legal value?
00 bhi dmfsk8
 bra dmfsk5 common seek handling
05 cmpa #MAXTK580
 bra 00b
 endif
10 cmpa #MAXTRK is it in range?
 bhi dmfsk8 if not - error
dmfsk5 clr dmfskf clear seek flag
 lbsr dmfsd do side and density setting
 tst DMFCOM check for 'ready'
 lbmi dmfwd if not, go wait
 cmpa DMFTRK same track as before?
 lbeq dmfrw if so, skip seek
 sta new_track save new track value
 ldb dmfdrv is this a 5"/80 drive
 cmpb #8
 blo 00f no - blunder on
 tst dmftyp is the media 5"/40?
 bne 00f no - just continue
 ldb DMFTRK yes - confuse the drive
 lslb
 stb gfd_trk
 stb DMFTRK
 lsla must seek to desired track * 2
00 sta DMFDAT set track number in wd
 sta gfd_dat
 mul delay some
 mul delay some more
 tst dmdidw DMA just do write?
 beq dmfsk7 no - continue
 lda #80 yes - delay some more
dmfsk6 deca
 bne dmfsk6
dmfsk7 clr dmdidw reset flag
 lda #$80 enable ints
 sta DMFCCR
 sta gfd_ccr
 lda #SKCMND set up seek command
 ora FD_SRT
 sta DMFCOM send to wd command register
 sta gfd_com
 inc dmfskf set seek flag
 lda #FD5time 5" Timeout value
 sta FD5cnt
 if P_TRC
 ldx #00f
 jsr Pdata
 endif
 rts return
dmfsk8 clrb set status
 lbra dmfer2 report error
 if P_TRC
00 fcc $d,'Floppy Seek',0
 endif

 pag

*
* dmfdd
*
* Do seek calculations for double density disk.
* Same as above, except 15 sectors per track.
*

dmfdd pshs b save low byte block number
 lsra divide block number by 16
 rorb by shifting 4 times
 lsra
 rorb
 lsra
 rorb
 lsra
 rorb
 tsta in range?
 beq dmfdd2
 puls b clean stack
 bra dmfsk8 report error
dmfdd2 tfr b,a put track in a
 puls b
 andb #$f get remainder
 incb adjust sector by 1 (no sec 0)
 clr dmasd reset side status
 tst dmfsid double sided disk?
 lbeq dmfsk4
 lsra adjust track count
 lbcc dmfsk4 on to side 2?
 pshs a
 lda dmfden TRS80 format?
 bita #$02
 puls a
 bne 00f yes - don't adjust sector #
 addb #16 adjust sector for 2nd side
00 inc dmasd set side 2 flag
 lbra dmfsk4 go finish

 ifnc &c,"NO-5"
 pag

*
* dm5sk - set up for 5" seek
*
dm5sk ldd bfblck,y get block #
 std r0 set up for divide
 lda #5 assume single density
 ldb dmfden double density?
 beq 00f yes
 lda #10 assume 10 sectors/track
 cmpb #2 special 9 sectors/track (Momentum 68000)?
 bne 00f no
 lda #9 yes - set up special value
00 cmpb #3 special 8 sectors/track (Tek 68000)?
 bne 00f
 lda #8
00 sta r1 set up for divide
 bsr div compute track/sector
 lda r0+1 get track #
 ldb rmndr get sector #
 incb adjust (no sector 0)
 clr dmasd set up side info
 tst dmfsid double sided disk?
 beq 10f no
 lsra adjust track #
 bcc 10f second side?
 inc dmasd
10 lbra dmfsk4

*
* 'div' divides a 16 bit number (r0 -> r0+1) by a 8 bit
* number r1 and produces a 16 bit result
* in r0 and a 8 bit remainder in 'rmndr'.
*

div ldb #17 set loop counter
 lda #0 do init
 sta rmndr
 bra div2
div1 lda rmndr do subtraction
 suba r1
 bcs div2 rmndr > r1 ?
 sta rmndr set new work value
div2 rol r0+1 do shifting
 rol r0
 rol rmndr
 decb
 bne div1
 com r0 compliment the result
 com r0+1
 lsr rmndr adjust the remainder
 puls pc return
*
* div work area
*
r0 rzb 2
r1 rzb 1
rmndr rzb 1
 endif
 pag

*
* dmfsd
*
* Do density and drive select.  Also set sector in B.
*

dmfsd stb DMFSEC set in wd
 stb gfd_sec
 ldb dmfhdr get drive number
 ifnc &c,"NO-5"
 pshs a
 lda dmfdrv check drive type
 cmpa #4 5" drive?
 puls a
 bhs dmfsd1 yes
 endif
 orb #$40 set for 8" cable
dmfsd1 orb #$10 set write enable
dmfsd2 tst dmfden double density?
 bne dmfsd4
 orb #$20 set single density
dmfsd4 stb DMFDRV set in drive register
 stb gfd_drv
 rts return

*
* dmcsk
*
* Do character device type seek.  If sector
* oriented (xfrc=128 or 256), then bfblck has
* track and sector.  If track oriented then
* bfblck has track<<1 with low bit being the
* side select bit.
*

dmcsk cmpd #512 check count
 bhi dmcsk4 >512 xfr count
 clr dmfden clear density
 clr dmasd clear side flag
 cmpd #256 check sector side
 blo dmcsk2 only 2 sides for 256 bytes
 ldd bfblck,y get location
 cmpb #15 sector on side 2?
 bls dmcsk2
 inc dmasd set side 2
dmcsk2 ldd bfblck,y get track & sector
 jmp dmfsk4
dmcsk4 clr dmfden clear density
 clr dmasd clear side flag
 cmpd #10200 double density count?
 ifnc &c,"NO-5"
 beq dmcsk5
 cmpd #6250 double density 5"
 endif
 bne dmcsk6
dmcsk5 inc dmfden set double density flag
dmcsk6 lda bfblck+1,y get track number
 lsra strip off side bit
 bcc dmcsk7 side 2?
 inc dmasd set side 2
dmcsk7 ldb #1 set bogus sector
 jmp dmfsk4 go do it

 pag

*
* dmfrw
*
* Read write routine for dmaf2.  By the time
* control has gotten here, a successful seek
* has been performed.
*

dmfrw inc dmfdt+dtbusy set to next mode of busy (r/w)
dmfrw2
 lda dmfdrv is this 5"/80 drive?
 cmpa #8
 blo 00f no
 tst dmftyp is this 5"/40 media?
 bne 00f no
 lda bfflag,y can't write 40 track media on 80 track drive!
 bita #BFRWF
 lbeq dmfer2
 lda new_track fake out WD controller
 sta DMFTRK
 sta gfd_trk
00 ldd bfxfc,y get transfer count
 cmpd #16 is it a swap?
 lbls dmswp if so - go do it
 lda bfflag,y get buffer flags
 bita #BFSPC special i/o?
 bne dmfwtt
 bita #BFRWF are we reading?
 beq dmfwt go do write

*
* dmfrd
*
* Do dmaf read.  Read 512 bytes bytes into the buffer
* specified by y.
*

dmfrd ldb #$00 set status
 clr dmdidw no writing
 bsr dmfcn configure controller
 clr dmfskf reset seek flag
 lda #RDCMND set up read command
 ora dmascb set side compare bit
 sta DMFCOM set command
 sta gfd_com
 ifnc &c,"NO-5"
 lda #FD5time 5" Timeout value
 sta FD5cnt
 endif
 if P_TRC
 ldx #00f
 jsr Pdata
 endif
 rts return
 if P_TRC
00 fcc $d,'Floppy Read',0
 endif

*
* dmfwt
*
* Do dmaf write.  Write 512 bytes to disk from the
* buffer pointed to by y.
*

dmfwt ldb #$20 set status
 stb dmdidw DMA currently writing
 bsr dmfcn configure controller
 lda #WTCMND set up command
 tst dmfskf did we seek?
 beq dmfwt2
 clr dmfskf reset flag
 lda #DWTCMD use head settle write
dmfwt2 ora dmascb set side compare bit
 sta DMFCOM set command
 sta gfd_com
 ifnc &c,"NO-5"
 lda #FD5time 5" Timeout value
 sta FD5cnt
 endif
 if P_TRC
 ldx #00f
 jsr Pdata
 endif
 rts return
 if P_TRC
00 fcc $d,'Floppy Write',0
 endif

 pag
*
* dmfwtt
*
* Do dma write track.
*

dmfwtt ldb #$20 set status
 stb dmdidw DMA currently writing
 bsr dmfcn configure dma controller
 clr dmfskf reset seek flag
 lda #WTTCMD set up command
 bra dmfwt2

*
* dmfcn
*
* Configure the dmaf controller.  Essentially all
* that is done is setting up the dma count and
* address registers.
*

dmfcn pshs b save status
 ldd bfadr,y get low 16 bits of address
 std DMFADR set address register
 std gfd_adr
 lda bfxadr,y get extended address
 anda #$0f mask low 4 bits
 ora 0,s
 ora #$90 set up for controller
 clr dmascb compare for side 0
 tst dmasd second side?
 beq dmfcn2
 ora #$40 set side 2
 tst dmfdt+dtrtry doing a retry?
 bne dmfcn2 yes - don't set side bit
 ifnc &c,"NO-5"
 ldb dmfdrv check for 5" drive
 cmpb #4
 bhs dmfcn1
 endif
 tst sidebit side bit valid in header
 beq dmfcn2 no - don't try it
dmfcn1 ldb #SIDE1 set side compare bit
 stb dmascb
dmfcn2 sta DMFCCR tell controller
 sta gfd_ccr
 puls b,pc return


*
* dmfrs
*
* Restore drive to track zero.  This is done every-
* time a record not found error is detected.  The
* busy status is set to 3 which the interrupt handler
* recognizes as a reseek attempt and will re-issue
* the requested operation.
*

dmfrs ldb dmfdt+dtbusy get status
 cmpb #4 swapping?
 beq dmfrs2
dmfrs1 ldb #3 set busy status to 3
 stb dmfdt+dtbusy
 lda #$80 enable ints
 sta DMFCCR
 lda #RSCMND get restore command
 ora FD_SRT
 sta gfd_com
 sta DMFCOM send to controller
 rts return
dmfrs2 ldx dmswpt get swap pointer
 leax -1,x back it up
 stx dmswpt save new
 stx bfadr,y
 bra dmfrs1 go restore
 pag

*
* dmfint
*
* Interrupt entry for disk drivers.  There are two
* possible entry times.  If 'dtbusy' is 1, then
* the controller has just finished a seek operation.
* If it is 2, we have just finished a read or write.
*

 if P_REQ
00 fcc $d,'FD I/O Interrupt, State = ',0
 endif
dmfint
 if P_REQ
 ldx #00b
 jsr Pdata
 lda dmfdt+dtbusy
 jsr Phex
 endif
 ldy dmfdt+dtqfl get current transaction
 beq dmfint0 nothing going on - exit
 lda bfdvn+1,y make sure this is mine
 cmpa #DMF2minor
 blo dmfint0 out of range
 cmpa #DMF2minor+11
 bhi dmfint0
 lda dmfdt+dtbusy get busy status
 bne dmfin2
dmfint0 lda DMFCOM clear interrupt
dmfin1 rts not busy, so return
dmfin2 cmpa #4 were we swapping?
 bne dmfin3
 ldb DMFCOM get status
 rorb get 'busy' bit
 bcc dmfi25
 ldb #FICMND force interrupt to WD
 stb DMFCOM
dmfi25 bra dmfi35
dmfin3 ldb DMFCOM check if busy
 rorb get busy into carry
 lbcs dmfin1
 ldb DMFCOM clear interrupt status
dmfi35 tst dmfcds is this the guy running?
 lbne dmfin1 if not - ignore int
 cmpa #1 were we seeking?
 lbeq dmfrw if so, now go read/write
 cmpa #3 were we reseeking?
 bne dmfin4
 clr dmfdt+dtbusy clear out for retry
 jmp dmfio2 start all over
dmfin4 lda #$00 clear dma
 sta DMFCCR
 ldb DMFCOM get status
 bitb #RWOK any errors?
 bne dmfer
 ldb dmfdt+dtbusy get status
 cmpb #4 are we swapping
 lbeq dswpdo go do swap
dmfin6 clr dmfdt+dtbusy clear busy status
 clr dmfdt+dtrtry clear error count
 clr FD5cnt not waiting for 5" interrupt
 ldx BDtable restore block device table address
 ldy dmfdt+dtqfl pick up buffer header address
 jmp BDioend end of operation
 ifnc &c,"NO-5"
*
* 5" Timeout entry
*
dmfTO ldy dmfdt+dtqfl get transaction
 beq 99f exit if bogus
 lda bfdvn+1,y
 cmpa #DMF2minor
 blo 99f
 cmpa #DMF2minor+11
 bhi 99f
 lda dmfdt+dtbusy check busy flag
 bne dmfer2 good interrupt -- send error
99 rts ** Who knows why we're here?
 endif


 pag

*
* dmfer
*
* Handle an error from the controller.  If the
* error count is not max value, bump the count
* and try again.
*
dmfer
 if P_ERR
 bsr rpt_er report error
 endif
 lda dmfdt+dtrtry get error count
 cmpa #8 is it max?
 bhs dmfer2
 bitb #RNFD is it 'record not found'?
 bne dmfer1
 inc dmfdt+dtrtry bump the error count
 cmpa #4 should we reseek?
 lbeq dmfrs if so, call restore
 lda dmfdt+dtbusy get status
 cmpa #4 are we swapping?
 lbne dmfrw2 if not - repeat r/w op
 lbra dswpd7 go retry swap
dmfer1 adda #4 bump retry by 4
 sta dmfdt+dtrtry so do this max of 4 times!
 lbra dmfrs go restore
dmfer2 stb bfstat,y save status
 lda #FICMND do force int command
 sta DMFCOM send do wd chip
 lda #0 clear out drive select
 sta DMFCCR
 sta DMFDRV
dmfer9 lda bfflag,y get flags
 ora #BFERR set error status
 sta bfflag,y
 bra dmfin6

 if P_REQ|P_ERR
*
* rpt_er - Report error
*
rpt_er pshs d,x,y,u
 ldx #00f
 jsr Pdata
 lda 1,s
 jsr Phex
 bsr prt_env
 puls d,x,y,u,pc
*
* prt_env - print environment
*
prt_env pshs d,x,y,u
 ldx #10f
 jsr Pdata
 lda bfblch,y
 jsr Phex
 lda bfblck,y
 jsr Phex
 lda bfblck+1,y
 jsr Phex
 ldx #11f
 jsr Pdata
 lda bfflag,y
 jsr Phex
 lda bfflg2,y
 jsr Phex
 ldx #12f
 jsr Pdata
 lda bfxadr,y
 jsr Phex
 lda bfadr,y
 jsr Phex
 lda bfadr+1,y
 jsr Phex
 ldx #20f
 jsr Pdata
 lda gfd_com
 jsr Phex
 ldx #21f
 jsr Pdata
 lda gfd_trk
 jsr Phex
 ldx #22f
 jsr Pdata
 lda gfd_sec
 jsr Phex
 ldx #23f
 jsr Pdata
 lda gfd_ccr
 jsr Phex
 ldx #24f
 jsr Pdata
 lda gfd_dat
 jsr Phex
 ldx #25f
 jsr Pdata
 lda gfd_drv
 jsr Phex
 puls d,x,y,u,pc return
00 fcc $d,'Floppy Disk I/O Error - WD CSR = ',0
10 fcc $d,'Block = ',0
11 fcc ', Flags = ',0
12 fcc ', Buf = ',0
20 fcc $d,'COM = ',0
21 fcc ', TRK = ',0
22 fcc ', SEC = ',0
23 fcc ', CCR = ',0
24 fcc ', DAT = ',0
25 fcc ', DRV = ',0
 endif

*
* dmfwd
*
* Wait for drive ready.
*

dmfwd lda #WD_MAX set up # retries
 sta wd_cnt
dmfwd00 tfr y,x save parameter
 ldb #5 set delay (0.5 seconds)
 ldy #dmfch set routine address
 lbsr timout do a timeout
 rts return

*
* dmfch
*
* Check if drive is really not ready.
*

dmfch tfr x,y get pointer
 clr dmfdt+dtbusy clear status
 ldb DMFCOM get status
 lbpl dmfio2 if not busy, go start
 dec wd_cnt tried max times?
 bne dmfwd00 no - try again (total of 5 seconds wait)
 lbra dmfer2 report error

 pag

*
* dmswp
*
* Do swap to dmaf2 (UGH!)
*

dmswp
 if 0
 ifnc &c,"NO-5"
 lda dmfdrv 5" Drive?
 cmpa #4
 lbhs dmfer9 yes - no swapping!
 endif
 endif
 lda #4 set status to 4
 sta dmfdt+dtbusy
 lda #8 set swap block counter
 sta dmbcnt (8*512 = 4096)
 ldd bfadr,y get swap table pointer
 std dmswpt save it
 lda bfflag,y check for read or write
 bita #BFRWF
 beq dmswp2 ahead if writing
 ldb #$00 set controller direction
 clr dmdidw DMA not writing
 stb dmswdr
 ldb #RDCMND setup command
 tst dmfskf did we seek?
 beq dmswp1
 orb #$04 set head settle bit
dmswp1 stb dmswop
 bra dswpd1 go do swap
dmswp2 ldb #$20 set controller direction
 stb dmdidw DMA currently writing
 stb dmswdr
 ldb #WTCMND setup command
 tst dmfskf did we seek?
 beq dmswp4
 clr dmfskf reset seek flag
 orb #$04 set head settle bit
dmswp4 stb dmswop
 bra dswpd1 go do swap

*
* dswpdo
*
* Perform a swap operation.
*

dswpdo ldd bfblck,y get block number
 addd #1 advance by 8 blocks (4K a crack)
 std bfblck,y save it
 bcc dswpd0 hit hi byte?
 inc bfblch,y prop cary
dswpd0 clr dmfdt+dtrtry
 dec dmbcnt dec swap block counter
 bne dswpd6 finished with 4K segment?
 clr dmfdt+dtbusy
 lda [dmswpt] get next segment
 cmpa DSKTRM end of list?
 lbeq dmfin6
 jmp dmfio2
dswpd1 ldx dmswpt get swap pointer
 lda 0,x+ get a segment
 stx dmswpt save pointer
 stx bfadr,y
 cmpa DSKTRM end of list?
 lbeq dmfin6 if so - exit
dswpd2 pshs a save segment
 lsra calc latch value
 lsra
 lsra
 lsra
 ora dmswdr
 sta dmswdr
 puls a
 eora DATsense
 asla
 asla
 asla
 asla
 clrb make 16 bits
 std DMFADR save as address in dma
 clr dmascb compare for side 0
dswpd3 ldb dmswdr get direction
 orb #$90 set for controller
 tst dmasd side 2?
 beq dswpd4
 orb #$40 set for 2nd side
 ifnc &c,"NO-5"
 lda dmfdrv check drive type
 cmpa #4
 bhs 10f 5" disks always have side bits
 endif
 tst sidebit side bit valid in header
 beq dswpd4 no - don't try it
10 lda #SIDE1 set side compare bit
 sta dmascb
dswpd4 stb DMFCCR
 lda dmswop get operation (r/w)
 ora dmascb set side compare bit
 sta DMFCOM issue command
 clr dmfskf reset seek flag
 rts return
dswpd6 inc DMFSEC bump sector count
dswpd7 lda dmswop get swap operation
 anda #%11111011 clear head settle bit
 sta dmswop save command
 clr DMFADR+1 reset fault counter
 bra dswpd3
 sttl Floppy Disk Character Drivers
 pag

*
* The routines in this file make up the character
* device drivers for the floppy disks.  The driver
* code for the drives is shared by the block device
* drivers.
*

*
* fchop
*
* Character disk open routine.  Device number in D.
*

fchop ldx #dmfopt point to open table
 subb #DMF2minor compute relative drive #
 cmpb #11 check for valid drive number
 bhi fchop4
 tst b,x already open?
 bne fchop5
 inc b,x set open status
 ldx #dmftrk point to track table
 abx point to drives entry
 clr 12,x clear out side info
 clr 12*2,x clear out density
 clr 12*3,x also media indication
 rts return
fchop4 lda #EBARG set error
 sta uerror
fchop5 rts return


*
* fchcl
*
* Character disk close routine
*

fchcl jmp dmfcls do dmf close

 pag

*
* fchrd
*
* Character read routine for floopy disks.
*

fchrd pshs d save device number
 lbsr fchgb get device buffer
 puls d reset device number
 jsr fchcn configure buffer
 tst uerror test for errors
 beq fchrd4
 bsr frechbf release buffer
 rts error return
fchrd4 pshs a save task modes byte
 orb #BFRWF set read status
 stb bfflag,y save in buffer hdr
 bra fchio go do io


*
* fchwr
*
* Character write routine for floppy disks.
*

fchwr pshs d save device number
 lbsr fchgb get device buffer
 puls d reset device number
 jsr fchcn configure buffer
 tst uerror check for errors
 beq fchwr4
 bsr frechbf free character buffer from use
 rts error return
fchwr4 pshs a save task mode byte
 bra fchio go do io

*
* frechbf - free Floppy Diskette Character buffer
*
frechbf pshs d,x,y,u save registers
 ldy #fchbuf point to header
 lda bfflag,y get flags
 anda #!(BFALOC|BFREQ|BFSPC)&$ff clear out busy bits
 sta bfflag,y save new flags
 jsr wakbuf awake buffer sleepers
 puls d,x,y,u,pc return

*
* fchsp
*
* Special routine. Returns side as byte 0, density as 1.
*

fchsp tfr x,y save pointer
 ldx #dmfsts point to side table
 subb #DMF2minor compute relative drive #
 abx point to entry
 cmpy #0 doing set or get?
 bne fchsp4
fchsp2 cmpb dmfdrv current drive?
 beq fchsp6
 ldd usarg0 get arg
 anda #$7F strip 40/80 indicator
 sta 0,x set side
 andb #$7F strip 40/80 indicator
 stb 12,x set density
 lda usarg0 get 40/80 indicator
 anda #$80
 sta 12*2,x set in table
 rts return
fchsp4 lda 0,x get side
 ora 12*2,x set 40/80 indicator
 ldb 12,x get density
 orb 12*2,x
 std 0,y save in buffer
 rts return
fchsp6 ldd usarg0 get arg
 anda #$7F strip 40/80 indication
 sta 0,x set side
 andb #$7F strip 40/80 indication
 stb 12,x set density
 sta dmfsid set current side
 stb dmfden set current density
 lda usarg0 get 40/80 indication
 anda #$80
 sta 12*2,x
 sta dmftyp
 rts return

 pag

*
* fchio
*
* Perform the io specified by the buffer header
* pointed at by Y.  ** This routine assumes the
* floppy disks are major device 0 for the block
* type drivers **
*

fchio pshs y save buffer
 ldx #blktab
 if 0
 pshs u
 ldu blktpt,x
 bsr prt_dtq
 puls u
 endif
 jsr [blkio,x] call block io routine
 ldy 0,s reset buffer
 jsr fnshio finish io
 jsr wakbuf awake buffer sleepers
 puls y reset ptr
 lda bfflag,y get flags
 anda #!(BFALOC|BFREQ|BFSPC)&$ff clear out busy bits
 sta bfflag,y save new flags
 puls a get task modes
 ldx utask get task entry location
 sta tsmode,x save task modes
 ldd #0 reset data count to 0
 std uicnt
fchio6 rts return

 if 0
* Print device transaction queue for device (U)
prt_dtq pshs d,x,y,u save registers
 ldx #00f
 jsr Pdata
 lda dtbusy,u
 jsr Phex
0 ldy dtqfl,u get buffer header
 beq 99f
 bsr prt_bh print buffer header
 leau 0,y
 bra 0b
99 puls d,x,y,u,pc return
00 fcc $d,'Character Device queue: - Busy = ',0

prt_bh pshs d,x,y,u
 ldx #00f
 jsr Pdata
 ldd bfdvn,y
 bsr phex2
 ldx #01f
 jsr Pdata
 lda bfflag,y
 jsr Phex
 ldx #02f
 jsr Pdata
 lda bfblch,y
 jsr Phex
 ldd bfblck,y
 bsr phex2
 puls d,x,y,u,pc
*
phex2 pshs d
 jsr Phex
 lda 1,s
 jsr Phex
 puls d,pc
*
00 fcc $d,'Block I/O, Device = ',0
01 fcc ', Flags = ',0
02 fcc ', Block = ',0
 endif

*
* fchgb
*
* Get the character buffer header.  If it is busy,
* sleep on it.
*

fchgb ldy #fchbuf point to header
 pshs cc save status
 seti
 lda bfflag,y get buffer flags
 bita #BFALOC is buffer busy?
 beq fchgb2
 ora #BFREQ set request buffer bit
 sta bfflag,y
 puls cc reset status
 ldb #BUFPR set priority
 jsr sleep go sleep for buffer
 bra fchgb repeat
fchgb2 lda #BFALOC set busy status
 sta bfflag,y
 puls cc,pc return

 pag

*
* fchcn
*
* Configure the buffer header pointed at by Y.
* This routine sets up the character device info
* from the user block and puts it in the buffer
* header such that the device drivers can use
* the information for the data transfer.
*

fchcn std bfdvn,y save device number
 ldd uicnt get xfr count
 std bfxfc,y save in header
 cmpd #128 check for valid number
 beq fchcn4
 cmpd #256 is it a sector operation?
 beq fchcn4
 cmpd #512 is it 512 byte op?
 beq fchcn4
 ifnc &c,"NO-5"
** special code for gimix 5" mini-floppy
 cmpd #3125 look for 5" write track
 beq fchcn2
 cmpd #6250 5" dd write track
 beq fchcn2
**
 endif
 cmpd #5100 is it a write track?
 beq fchcn2
 cmpd #10200 is it dd write track?
 bne fchcn8 if not - error
fchcn2 lda bfflag,y get flags
 ora #BFSPC set special bit for drivers
 sta bfflag,y save new flags
fchcn4 ldd uipos2 get file position
 std bfblck,y save as block number
 lda uipos+1 store upper part
 sta bfblch,y
 ldd uistrt get start address of xfr
 std bfadr,y save in header
 jsr mapupg find user page
 std bfxadr,y save in header
 ldx utask point to task entry
 lda tsmode,x get mode bits
 pshs a save
 ora #TLOCK set lock bit (keep in mem)
 sta tsmode,x save new mode
 ldb bfflag,y get flags
 puls a,pc return
fchcn8 lda #EBARG set error
 sta uerror
 rts return
