 lib environment
 lib bfhdr
 lib dtab
 lib macdefs
 sttl Floppy Disk Drivers (DMAF2)
 pag
 data
 name dmf2drvr
 global dmf2opn,dmf2cls,dmf2io,dmf2int
 global fchop,fchcl,fchsp,fchio,fchrd,fchwr,fchgb,fchcn2,frechbf

PRT_TO equ 0 Print time-out messages
WAIT equ 0 Wait after time-out

*
* This file contains the drivers for the SWTPC
* DMAF2 dual density floppy disk controller.
* It should be used to model others after.
*

*
* driver constants
*

SECLEN equ $ffff-512 compliment of sector length
RWOK equ %11011000 read write status mask
RNFD equ %00010000 record not found error
MAXTRK equ 76 maximum track number allowed
RDCMND equ $88 read block command
WTCMND equ $a8 write block command
DWTCMD equ $ac write block with head settle
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 wtite multiple with head settle

 pag

*
* driver variable storage
*

dmfdrv fcb 0 current drive selected
dmfhdr fcb 1 hardware compat drive number
dmftrk fdb 0,0 track table
dmfsts fdb 0,0 side table
dmfstd fdb 0,0 density table
dmfden fcb 0 density flag
dmfsid fcb 0 side flag
dmasd fcb 0 current side select
dmftmp fdb 0 temp space for open
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
dmfopt fdb 0,0 dmf open table
dmdidw fcb 0 dmf did write last time flag
BDtable fdb 0 Block Device Table address
wd_cnt fcb 0 disk time-out count
WD_MAX equ 10 10*.5 seconds wait for drive ready
 global FD_TO
FD_TO fcb 0
FD_TOC equ 100 10 second timer
int_state fcb 0
old_csr fcb 0

 pag

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

dmf2opn pshs d save device number
 ldx #dmfopt point to open table
 subb #DMF2minor compute relative device #
 lda b,x get this guys entry
 beq dmfop2 if = 0 then open
 inc b,x show new guy
 bra dmfop3
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 device #
 inc b,x set to double density
 ldd 0,s restore device number
 bsr dmfrbo read sir again
 beq dmfop4 error?
 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 device #
 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 device #
 ldx #dmfsts point to side table
 lda dmftmp+1 get side data
 sta b,x set in table
 leax 4,x bump to density table
 lda dmftmp get density info
 sta b,x set in table
 puls y get buffer
 jsr freebf free the buffer
 puls d,pc return

*
* read sir
*

dmfrbo ldx #1 set block 1
 ldy #0
 lda #DMF2major set up device #
 jsr rdbuf read in the block
 lda bfflag,y get flags
 bita #BFERR error?
 rts return

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

dmf2cls ldx #dmfopt point to open table
 subb #DMF2minor compute relative device #
*dec b,x get rid of this guy
*bpl dmfcl2 check for neg
 clr b,x clear open status
dmfcl2 rts return

 pag

*
* dmf2io
*
* Start dmaf executing a requested operation.
*

dmf2io stx BDtable save Block Device Table address
dmf2io2 inc dmf2dt+dtbusy set busy status
 lda bfdvn+1,y get device number
 clrb
 suba #DMF2minor compute relative device #
 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
 sta dmfsid set flag
 lda 4,x get density
 sta dmfden set flag
 bra 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 DMF2TRK get track from wd
 stb 0,x save in table
 bsr dmfftr find entry
 ldb 0,x get old track number
 stb DMF2TRK set into wd
 ldb 4,x get side info
 stb dmfsid
 ldb 8,x get density info
 stb dmfden
 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
 ldd #20 delay some here
dmfcd5 subd #1 before drive change
 bne dmfcd5
 ldb dmfhdr reset drive number
 comb comp it
 andb #$7f mask for controller
 lda #$7f
 sta DMF2DRV strobe the hlr line
 lda #$ff
 sta DMF2DRV
 stb DMF2DRV set drive in controller
 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 ldd bfblck,y get low block number
 tst dmfden double density?
 bne 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
 bra 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 cmpa #MAXTRK is it in range?
 bhi dmfsk8 if not - error
 clr dmfskf clear seek flag
 bsr dmfsd do side and density setting
 tst DMF2COM check for 'ready'
 lbmi dmfwd if not, go wait
 cmpa DMF2TRK same track as before?
 lbeq dmfrw if so, skip seek
 sta DMF2DAT set track number in wd
 mul delay some
 mul delay some more
 tst dmdidw did we do write last time?
 beq dmfsk6
 lda #100 do delay
dmfsk5 deca
 bne dmfsk5
dmfsk6 clr dmdidw clear write flag
 lda #0 long way for delay!
 sta DMF2LAT enable interrupts
 lda #SKCMND set up seek command
 ora FD_SRT Floppy disk seek rate (in configuration table)
 sta DMF2COM send to wd command register
 inc dmfskf set seek flag
 lda #FD_TOC time-out count
 sta FD_TO
 clr int_state
 rts return
dmfsk8 clrb set status
 lbra dmfer2 report error

 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?
 beq dmfsk4
 lsra adjust track count
 bcc dmfsk4 on to side 2?
 addb #16 adjust sector for 2nd side
 inc dmasd set side 2 flag
 bra dmfsk4 go finish

 pag

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

dmfsd stb DMF2SEC set in wd
 ldb dmfhdr get drive number
 orb #$80 set for controller
 tst dmasd set 2nd side?
 beq dmfsd2
 orb #$10 set 2nd side
dmfsd2 tst dmfden double density?
 bne dmfsd4
 orb #$20 set single density
dmfsd4 comb
 stb DMF2DRV set in drive register
 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?
 bne dmcsk5
 inc dmfden set double density flag
dmcsk5 lda bfblck+1,y get track number
 lsra strip off side bit
 bcc dmcsk6 side 2?
 inc dmasd set side 2
dmcsk6 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 dmf2dt+dtbusy set to next mode of busy (r/w)
dmfrw2 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 #$ff set status
 clr dmdidw clear write flag
 bsr dmfcn configure controller
 clr dmfskf reset seek flag
 lda #RDCMND set up read command
 stb DMF2PRI set priority register
 sta DMF2COM set command
 lda #FD_TOC time-out count
 sta FD_TO
 clr int_state
 rts return

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

dmfwt ldb #$fe set status
 stb dmdidw set write flag
 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 stb DMF2PRI set priority
 sta DMF2COM set command
 lda #FD_TOC time-out count
 sta FD_TO
 clr int_state
 rts return

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

dmfwtt ldb #$fe set status
 stb dmdidw set write flag
 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
 lda bfxadr,y get extended part of address
 anda #$0f mask bottom half
 sta DMF2LAT set address latch
 ldd bfadr,y get low 16 bits of address
 std DMF2ADR set address register
 com DMF2ADR comp the address
 com DMF2ADR+1
 ldd #$ffff get byte count
 subd bfxfc,y
 std DMF2CNT set counter register
 puls b restore status
 stb DMF2CCR
 ldb #$fe set status
 stb DMF2INT enable ints
 rts 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 dmf2dt+dtbusy get status
 cmpb #4 swapping?
 beq dmfrs2
dmfrs1 ldb #3 set busy status to 3
 stb dmf2dt+dtbusy
 lda #RSCMND get restore command
 ora FD_SRT set up floppy disk seek rate
 sta DMF2COM 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

*
* dmf2int
*
* 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.
*

dmf2int ldy dmf2dt+dtqfl get buffer header
 beq 00f jump if not active
 lda bfdvn+1,y make sure right device
 cmpa #DMF2minor
 blo 00f jump if out of range
 cmpa #DMF2minor+3
 bhi 00f
 lda dmf2dt+dtbusy get busy status
 bne dmfin2
 if 0
00 lbsr prt_dq
 fdb 01f,dmf2dt
 bra 00f skip over message
01 fcc $d,'Unexpected DMF2 Interrupt!',0
 endif
00 tst DMF2COM
dmfin1 rts not busy, so return
dmfin2 inc int_state 1 ==> found interrupt/waiting for interrupt
 cmpa #4 were we swapping?
 bne dmfin3
 ldb DMF2COM get status
 rorb get 'busy' bit
 bcc dmfi25
 ldb #FICMND force interrupt to WD
 stb DMF2COM
dmfi25 bra dmfi35
dmfin3 ldb DMF2COM check if busy
 stb old_csr
 rorb get busy into carry
 bcs dmfin1
 inc int_state 2 ==> device not busy (command complete)
 ldb DMF2COM clear interrupt status
 stb old_csr
dmfi35 cmpa #1 were we seeking?
 lbeq dmfrw if so, now go read/write
 inc int_state 3 ==> interrupt after read/write/reseek
 cmpa #3 were we reseeking?
 bne dmfin4
 inc int_state 4 ==> interrupt after reseek
 clr dmf2dt+dtbusy clear out for retry
 lbra dmf2io2 start all over
dmfin4 ldd #$ffff clear dma
 std DMF2PRI
 ldb DMF2COM get status
 stb old_csr
 bitb #RWOK any errors?
 lbne dmfer
 ldb dmf2dt+dtbusy get status
 cmpb #4 are we swapping
 lbeq dswpdo go do swap
dmfin6 clr dmf2dt+dtbusy clear busy status
 clr dmf2dt+dtrtry clear error count
 clr FD_TO
 ldx BDtable restore Block Device Table address
 jmp BDioend end of I/O operation

*
* Floppy disk time-out entry
*
 global dmf2to
dmf2to
 ldy dmf2dt+dtqfl get buffer header
 if PRT_TO
 ldx #00f
 jsr Pdata
 lda bfdvn,y
 jsr Phex
 lda bfdvn+1,y
 jsr Phex
 ldx #01f
 jsr Pdata
 lda bfblch,y
 jsr Phex
 lda bfblck,y
 jsr Phex
 lda bfblck+1,y
 jsr Phex
 ldx #02f
 jsr Pdata
 lda bfflag,y
 jsr Phex
 ldx #10f
 jsr Pdata
 lda dmf2dt+dtbusy
 jsr Phex
 ldx #03f
 jsr Pdata
 lda DMF2COM
 jsr Phex
 ldx #04f
 jsr Pdata
 lda DMF2CCR
 jsr Phex
 ldx #05f
 jsr Pdata
 lda DMF2PRI
 jsr Phex
 lda DMF2PRI+1
 jsr Phex
 ldx #06f
 jsr Pdata
 lda DMF2CNT
 jsr Phex
 lda DMF2CNT+1
 jsr Phex
 ldx #07f
 jsr Pdata
 lda int_state
 jsr Phex
 ldx #08f
 jsr Pdata
 lda old_csr
 jsr Phex
 if WAIT
 jsr wait
 endif
 endif
 ldb #FICMND force interrupt to WD
 stb DMF2COM
 mul
 mul
dmf2to2 ldb DMF2COM get status
 rorb get 'busy' bit
 bcs dmf2to2 wait for not busy
 ldb #RNFD pretend a "record not found" error occurred
 jmp dmfer2 abort I/O
 if PRT_TO
00 fcc $d,'Floppy Disk Timeout! - Device = ',0
01 fcc ', Block = ',0
02 fcc ', Flags = ',0
10 fcc ', Busy = ',0
03 fcc $d,'WD CSR = ',0
04 fcc ', DMA CCR = ',0
05 fcc ', PRI = ',0
06 fcc ', COUNT = ',0
07 fcc ', STATE = ',0
08 fcc ', OLD CSR = ',0
 endif

 if 0
*
* Print Device Queue
*  jsr prt_dq
*  fdb Header message
*  fdb Device queue address
*
prt_dq pshs d,x,y,u save all registers
 ldy 8,s
 ldx ,y++ get header message
 ldu ,y++ get device queue address
 sty 8,s replace return address
 jsr Pdata print header
 bsr prt_dtq print device queue
 lbsr prt_idev print all interrupt device statuses
 jsr wait
99 puls d,x,y,u,pc return

* 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,'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

 lib inttab
 data
* prt_idev - Print all interrupt devices & status
prt_idev
 ldx #00f
 jsr Pdata
 ldy #inttab
 lda ,y+ get number of interrupt devices
10 pshs a save count
 ldx #01f
 jsr Pdata
 lda instat,y print device
 jsr Phex
 lda instat+1,y
 jsr Phex
 ldx #02f
 jsr Pdata
 ldu instat,y
 lda ,u
 jsr Phex
 leay INTSIZ,y bump pointer
 puls a
 deca
 bne 10b
 rts
00 fcc $d,'Interrupt Devices:',0
01 fcc $d,'Device Address = ',0
02 fcc ', Status = ',0
 endif

 if WAIT
*
* wait - wait for a control-D on the terminal
*
wait pshs d,x,y,u
 ldx #00f
 jsr Pdata
0 jsr sysgetc get a character
 cmpa #$04 is it control-D?
 bne 0b
 puls d,x,y,u,pc yes - exit
00 fcc $d,'!!!!! Please call Gary or Dave!!!!! -',0
 endif

 pag

*
* dmfer
*
* Handle an error from the controller.  If the
* error count is not max value, bump the count
* and try again.
*

dmfer lda dmf2dt+dtrtry get error count
 cmpa #8 is it max?
 bhs dmfer2
 bitb #RNFD is it 'record not found'?
 bne dmfer1
 inc dmf2dt+dtrtry bump the error count
 cmpa #4 should we reseek?
 lbeq dmfrs if so, call restore
 lda dmf2dt+dtbusy get status
 cmpa #4 are we swapping?
 lbne dmfrw2 if not - repeat r/w op
 ldx dmswpt get swap table ptr
 lda -1,x get last segment tried
 lbra dswpd2 go retry swap
dmfer1 adda #4 bump retry by 4
 sta dmf2dt+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 DMF2COM send do wd chip
 lda #!$20 clear out drive select
 sta DMF2DRV
 lda bfflag,y get flags
 ora #BFERR set error status
 sta bfflag,y
 lbra dmfin6

*
* dmfwd
*
* Wait for drive ready.
*

dmfwd lda #WD_MAX set up wait count
 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 dmf2dt+dtbusy clear status
 ldb DMF2COM get status
 lbpl dmf2io2 if not busy, go start
 dec wd_cnt tried max times?
 bne dmfwd00 no - try again
 bra dmfer2 report error
 pag

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

dmswp lda #4 set status to 4
 sta dmf2dt+dtbusy
 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 #$ff set controller direction
 clr dmdidw clear write flag
 stb dmswdr
 ldb #RDMSCM 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 #$fe set controller direction
 stb dmdidw set write flag
 stb dmswdr
 ldb #WTMSCM setup command
 tst dmfskf did we seek?
 beq dmswp4
 clr dmfskf reset seek flag
 ldb #DWTMSC set head settle command
dmswp4 stb dmswop
 bra dswpd1 go do swap

*
* dswpdo
*
* Perform a swap operation.
*

dswpdo ldd bfblck,y get block number
 addd #8 advance by 8 blocks (4K a crack)
 std bfblck,y save it
 bcc dswpd0 hit hi byte?
 inc bfblch,y prop cary
dswpd0 clr dmf2dt+dtrtry
 clr dmf2dt+dtbusy
 lda [dmswpt] get next segment
 cmpa DSKTRM end of list?
 lbne dmf2io2 if not - repeat
 jmp dmfin6 else - exit
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
 sta DMF2LAT set latch
 puls a
 eora DATsense
 asla
 asla
 asla
 asla
 clrb make 16 bits
 std DMF2ADR save as address in dma
 com DMF2ADR make com
 com DMF2ADR+1
 ldd #$ffff-(512*8) set data count
 std DMF2CNT save in dma
 ldb dmswdr get direction
 stb DMF2CCR
 ldb #$fe
 stb DMF2INT set interrupt status
 lda dmswop get operation (r/w)
 stb DMF2PRI prime dma
 sta DMF2COM issue command
 clr dmfskf reset seek flag
 rts return
 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 device #
 cmpb #3 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 4,x clear out side info
 clr 8,x clear out density
 rts return
fchop4 lda #EBARG set error
 sta uerror
fchop5 rts return


*
* fchcl
*
* Character disk close routine
*

fchcl jmp dmf2cls 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
 bsr fchgb get device buffer
 puls d reset device number
 jsr fchcn configure buffer
 tst uerror check for errors
 beq fchwr4
 bsr frechbf release buffer
 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 device #
 abx point to entry
 cmpy #0 doing set or get?
 bne fchsp4
 cmpb dmfdrv current drive?
 beq fchsp6
 ldd usarg0 get arg
 sta 0,x set side
 stb 4,x set density
 rts return
fchsp4 lda 0,x get side
 ldb 4,x get density
 std 0,y save in buffer
 rts return
fchsp6 ldd usarg0 get arg
 sta 0,x set side
 stb 4,x set density
 sta dmfsid set current side
 stb dmfden set current density
 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
 jsr [blkio,x] call block io routine
 ldy 0,s reset buffer
 jsr fnshio finish io
 jsr wakbuf awakeb 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


*
* 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
 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
