 lib environment
 lib bfhdr
 lib dtab
 sttl DMF-3 5" Winchester Drivers
 pag

PRT_REQ equ 0 Print I/O requests
PRT_ERR equ 0 Print I/O errors
PRT_DCT equ 0 Print Device Characteristics
PRT_ADR equ 0 Print decoded addresses

*
*   Winchester Drive Characteristics table - Type set by "sdenf"
*                    flag in SIR.  If sdenf = -1, then drive data
*                    is present in SIR at offset 42 (Hex).
*
 base 0

dcncyl rmb 2 number of usable cylinders
dcnhds rmb 1 number of data heads
dcrwcc rmb 2 reduced write current cyl
dcwpcc rmb 2 write precompensation cyl
dceccb rmb 1 max ECC burst cylinder
dcstpr rmb 1 step rate / buffered step
dcspar rmb 2 spare bytes
 pag
 data
 name w5drvr
 global w5open,w5close,w5sto,w5int
 global w5cop,w5ccl,w5cio,w5cwr,w5crd,w5csp

*
*  This file contains the drivers for the
*  SWTPc DMF-3 5" Winchester disk.
*

*
*  W5 Drive characteristics
*
ECC equ $80 Honor Error Correction (WD-1001 Only)

W5hd fdb 4 Number of Heads
W5cyl fdb 153 Number of Cylinders
W5st fdb 17 Sectors/Track
W5srt fcb 6 Seek rate
W5mv fcb 0 Single/Multi-Volume indication
*
MAXretries equ 3 Max # retries / operation
W5_FT equ 17*2 Format Track
w5esw fdb 0 W5 Error Status Word
w5opt fcb 0,0,0,0 w5 open table
w5rst fcb 0,0,0,0 W5 drive reset (0=no)
w5dtyp fdb w5DC0,w5DC1,w5DC2,w5DC3 drive configuration tables
w5irs fcb 0 W5 interface reset?
w5drv fdb $FFFF current drive select for W5
w5tmp fcb 0 space to "read" device size
w5sdn fdb 0 saved device #
W5sdh fcb 0 Size/Drive/Head select
W5adr fcb 0,0,0 Sector/Cylinder
W5blk fcb 0,0,0 Block #
W5cmd fcb 0 current command
BDtable fdb 0 Block Device Table address
swptr fdb 0 swap address pointer
swpcnt fcb 0 swap block count
swpdir fcb 0 swap direction (DMA control setup)
swpexadr fcb 0 extended address bits during swap
w5DC0 rzb 11 Device characteristics tables
w5DC1 rzb 11
w5DC2 rzb 11
w5DC3 rzb 11
*
* open the w5 disk drive - insure the device is online, etc.
*
w5open
 std w5sdn save device #
 lbsr w5dvn drive select
 tst w5opt,x already open?
 beq w5op1 no - go do open
 inc w5opt,x yes - bump open count
 rts
w5op1 inc w5opt,x set open status
 pshs x save device offset
 lda #8 set type=ST506
 bsr w5cpyDCn copy device configuration table
 ldx #$FFFF force recalculation
 stx w5drv
 ldx #1 read SIR
 ldy #0
 ldd w5sdn restore device #
 lda #W5major make sure block read
 jsr rdbuf
 lda bfflag,y get flags
 bita #BFERR error?
 bne w5op8 yes - return error
 pshs y save buffer pointer
 ldx 0,s get buffer header
 ldy #w5tmp no - read drive type
 ldu #sdenf from "density" byte in SIR
 ldd #1
 jsr cpybts
 lda w5tmp check for DCT index/data in SIR
 cmpa #$FF
 bne 10f
 ldx 0,s it is - read actual table from SIR
 ldd w5sdn compute table address
 subb #W5minor
 aslb
 ldy #w5dtyp
 ldy b,y
 ldu #swinc
 ldd #11
 jsr cpybts
 bra 20f
10 lda w5tmp pick up size field
 cmpa #MAXDCT legal DCT index?
 bls 15f yes - jump (OK)
 lda #8 no - assume ST-506
15 bsr w5cpyDCn copy device configuration table
20 puls y restore buffer pointer
 jsr freebf free buffer
 puls x
 ldx #$FFFF force controller to be reconfigured
 stx w5drv
 rts device opened OK
w5op8 jsr freebf release buffer
 puls x restore device index
w5op9 lda #EIO indicate device offline
 sta uerror stuff into user error flag
 clr w5opt,x clear open status
 clr w5rst,x
w5rts rts return
*
* copy device configuration table from MW_DCT
* to local device tables
*
w5cpyDCn
 ldb w5sdn+1 compute table address
 subb #W5minor
 aslb
w5cpyDC
 ldy #w5dtyp
 ldy b,y
 ldx #MW_DCT compute device characteristics table
 leax a,x
 asla A*8
 asla
 asla
 leax a,x offset*9
 ldb #9
17 lda ,x+ copy table
 sta ,y+
 decb
 bne 17b
 clr ,y+ ** filler **
 clr ,y+
 rts
*
*  Set up controller
*
w5idc pshs d,x,y save registers
 cmpx w5drv same drive as last operation?
 beq w5id0 yes - exit
 stx w5drv no - remember drive #
 tfr x,d compute device characteristics table address
 ldy #w5dtyp
 aslb
 ldy b,y
 if PRT_DCT
 bsr prt_dct
 endif
 lda dcspar,y check flags
 sta W5mv save single/multi-unit indicator
 bita #$80 multi-unit volume?
 pshs cc
 ldd dcncyl,y get number of cylinders
 puls cc check single/multi-unit volume
 beq 00f jump for single-unit
 lsra
 rorb -- compute actual # cylinders
00 std W5cyl
 clra
 ldb dcnhds,y get number of heads
 std W5hd
 ldd dcrwcc,y reduced write cylinder
 asra divide by 4
 rorb
 asra
 rorb
 stb wd_wr_pre set up WD1000 write precomp register
 lda dcstpr,y get seek rate value
 lsra
 lsra
 lsra
 lsra
 sta W5srt
w5id0 tst w5irs has interface been reset?
 bne w5id1
 tst wd1000_res reset controller
 inc w5irs
w5id1 tst w5rst,x has drive been reset?
 bne w5id3 yes - reset seek rate
 inc w5rst,x no - set flag
 lda W5sdh
 sta wd_sdh
 lda #wd_restore send restore command
 sta wd_cmd
w5id2 lda wd_status wait for drive ready
 bmi w5id2
w5id3 clra -- lda #0 seek to head 0, sector 0, cylinder 0
 sta wd_sec_num
 sta wd_cyl_hi
 sta wd_cyl_low
 lda #wd_seek
 ora W5srt
 sta wd_cmd
w5id4 lda wd_status wait for drive ready
 bmi w5id4
w5id9 puls d,x,y,pc

 if PRT_DCT
*
* Print device characteristics
*   Y - Address of table
*
prt_dct pshs d,x,y,u
 ldx #00f
 jsr Pdata
 ldd 4,s
 jsr Phex2
 ldx #01f
 jsr Pdata
 ldb #11
10 lda ,y+
 jsr Phex
 jsr Pspace
 decb
 bne 10b
99 puls d,x,y,u,pc
*
00 fcc $d,'W5 DCT - Unit: ',0
01 fcc ', Tbl: ',0
 endif

*
* w5 close
*
w5close
 bsr w5dvn set up drive
 dec w5opt,x clear open status
 beq w5cl0
 bpl w5cl1
 clr w5opt,x
w5cl0 clr w5rst,x
w5cl1 rts return
*
* w5dvn - select W5 drive
*   (B) - minor device #
*   return with (X) - device index
*
w5dvn
 cmpb #W5minor+3 check device number
 lbhi w5er0 go set error
 subb #W5minor compute relative drive #
 clra set up device index
 tfr d,x
 stb w5drv save "current" drive #
 aslb
 aslb
 aslb
 orb #wd_sz_512+ECC set for 512 byte sectors/ecc type
 stb W5sdh set command byte
 rts
*
* w5cn - set up WD1000 command
*    (A) - DMA direction
*
w5cn pshs d
  jsr deblock deblock the record and set-up some stuff
  orb W5sdh store the head number
  stb W5sdh
  ldd 1,x get the cylinder address
  cmpd W5cyl
  bls w5cnz do a range check
  sev
  puls d,pc
w5cnz sta wd_cyl_hi
  stb wd_cyl_low
  std W5adr+1 and keep for uniflex also
 if PRT_ADR
 ldx #10f
 jsr Pdata
 lbsr prt_blk
 endif
 lda bfxadr,y get memory address
 ora #chan1 set for channel 1
 sta extaddr set high 4 bits
 ldd bfadr,y
 std dmac1a set channel 1 address
 ldd #512 set up count
 std dmac1c
 ldd 0,s
 stb dmacc1 set up channel control register
 lda #dmadie1 allow interrupt on channel 1
 sta dmaicr
 lda #dmacsel set four channel DMA
 sta dmadcr
 lda #dmare1 set up channel 1 DMA
 sta dmaprc
 lda W5sdh send off command
 sta wd_sdh
 lda W5cmd
 sta wd_cmd
 puls d,pc
*
* fire up W5 operation to initiate transfer
*
w5sto stx BDtable save Block Device Table address
 clr w5dt+dtrtry reset retry counter
w5sto0 clr w5dt+dtbusy reset busy marker
 if PRT_REQ
 lbsr prt_req print request
 endif
 ldb bfdvn+1,y pick up device code
 lbsr w5dvn select drive
 lbsr w5idc set up device characteristics if needed
 lda bfblch,y check block number range
 cmpa #$0f
 lbhi w5er if too big - error
*
* decide whether we are doing a read or write (or swap)
*
 ldd bfxfc,y get transfer count
 cmpd #16 special value for swap?
 ble w5swp
 ldb bfflag,y get buffer flags
 bitb #BFRWF are we reading?
 beq w5wrt go do write
 lda #wd_read set Read Sector Command
 ldb #$00 set for DMA read
 bra w5rw go perform I/O
*
* set up for write command
*
w5wrt bitb #BFSPC special i/o
 beq w5wr0 jump if write
 ldd bfxfc,y get transfer count
 cmpd #512 is it regular block transfer?
 beq w5wr0 yes - normal write
 cmpd #W5_FT special count for Format Track command
 lbne w5er bad I/O
 lda W5st+1 set up sectors/track register
 sta wd_sec_cnt
 lda #wd_format set Format Track command
 bra w5wr1 go do operation
w5wr0 lda #wd_write set Write Sector
w5wr1 ldb #dmadmar set DMA write
*
w5rw sta W5cmd set up command
w5rw0 lbsr w5cn send command
 lbvs w5er oops, can't do it
 inc w5dt+dtbusy mark device busy
 rts
*
* take care of W5 swap request
*
w5swp lda #4 set SWAP activity code
 sta w5dt+dtbusy
 ldd bfadr,y get swap table pointer
 std swptr
 lda bfflag,y check direction
 bita #BFRWF Read/Write?
 beq w5sw0 jump for Write
 lda #wd_read set Read Sector opcode
 ldb #$00 set up for DMA read
 bra w5sw1
w5sw0 lda #wd_write Write Sector opcode
 ldb #dmadmar set up for burst DMA write
w5sw1 sta W5cmd set up command
 stb swpdir
 bra w5sc2 go do operation
*
* w5sc - Continue SWAP request
*
w5sc0 ldd bfblck,y update block #
 addd #1
 std bfblck,y
 lda bfblch,y
 adca #0
 sta bfblch,y
 dec swpcnt end of 8K block
 bne w5sc3 no - do next block
*
*  set up and send SWAP command
*
w5sc2 lda #8 do 4K at a time
 sta swpcnt
 ldx swptr pick up SWAP table pointer
 lda ,x+ get descriptor for next segment
 stx swptr save new pointer
 stx bfadr,y update pointer in transaction
 cmpa DSKTRM end of list?
 lbeq w5in4 yes - end of operation
 pshs a compute physical address
 lsra
 lsra
 lsra
 lsra
 ora #chan1 set up for DMA channel 1
 sta swpexadr save high bits for later
 puls a
 eora DATsense
 asla
 asla
 asla
 asla
 clrb
 std dmac1a
*
w5sc3 jsr deblock deblock and set-up some things
  orb W5sdh store the head number
  stb wd_sdh
  ldd 1,x split the address apart for wd
  sta wd_cyl_hi
  stb wd_cyl_low
  std W5adr+1 and keep for uniflex also
 if PRT_ADR
 ldx #10f
 jsr Pdata
 lbsr prt_blk
 endif
 ldd #512 set up for 512 byte block
 std dmac1c
 lda swpexadr get extended address bits
 sta extaddr
 lda #8 set up four channel mode
 sta dmadcr
 lda swpdir get channel direction control
 sta dmacc1
 lda #2
 sta dmaicr enable interrupts on channel 1
 sta dmaprc start DMA on channel 1
 lda W5cmd get command
 sta wd_cmd issue command
 rts all done
*
*  Interrupt handler for DMF-3 5" Winchester
*
*
w5int ldy w5dt+dtqfl get first transaction
 beq w5int99 jump if nothing going on
 ldd dmac1c DMA count = 0?
 bne w5int0 no - can't be real interrupt!
 lda wd_status get controller status
 bpl w5int1 jump if controller not busy
w5int0 rts wait for completion interrupt
w5int1 ldb bfdvn+1,y check for legal minor device #
 cmpb #W5minor
 blo w5int99
 cmpb #W5minor+3
 bhi w5int99
 ldb w5dt+dtbusy check for SWAP in progress
 beq w5int99
 pshs d save status
 clra -- ldd #0 reset DMA
 clrb
 std dmaprc
 puls d restore status
 bita #1 error bit set?
 beq w5int2 jump if no error
 cmpb #5 error doing restore?
 bge 0f yes - blow the guy away
 ldb w5dt+dtrtry max retries?
 cmpb #MAXretries
 blt w5restore no - try restoring the disk
0 bsr rpterr go report error
 bra w5er return error
w5int2 cmpb #4
 lbeq w5sc0 continue SWAP
 cmpb #5 doing restore?
 beq w5rest1 yes - complete restore
 cmpb #6 restore/seek complete?
 beq w5rest2 yes
w5in4 clr w5dt+dtbusy set not busy
 lda #wd_sz_512+%11000+ECC  Select non-existant drive
 sta wd_sdh ** Turns off select lights **
 ldx BDtable restore Block Device Table address
 jmp BDioend end of operation
w5int99 rts return - no current operation

*
* restore routine - Issue Restore command
*
w5restore
 ldb w5dt+dtbusy
 cmpb #4 doing swap?
 bne 0f no
 ldd #8 fix up block #
 subb swpcnt
 pshs d
 ldd bfblck,y
 subd ,s++
 std bfblck,y
 lda bfblch,y
 sbca #0
 sta bfblch,y
 ldx bfadr,y fix up swap pointer
 leax -1,x
 stx bfadr,y
0 lda #5 set "restore-in-progress"
 sta w5dt+dtbusy
 inc w5dt+dtrtry bump retry counter
 lda #wd_restore send restore command
 sta wd_cmd
 rts wait for restore interrupt
*
w5rest1
 clra -- lda #0 set up seek rate
 sta wd_sec_num
 sta wd_cyl_hi
 sta wd_cyl_low
 lda #wd_seek
 ora W5srt
 sta wd_cmd
 lda #6 wait for "seek complete" interrupt
 sta w5dt+dtbusy
 rts
*
w5rest2
 lbra w5sto0 retry operation

*
* error routine - give up forever
*
w5er0 leas 2,s clean up stack
w5er lda bfflag,y get buffer flag
 ora #BFERR indicate failure
 sta bfflag,y stuff back in el flago
 clr w5dt+dtbusy clear status
 bra w5in4

*
* rpterr - Report I/O error
*
rpterr
 if PRT_ERR
 pshs d,x,y
 ldx #00f
 jsr Pdata
 lda W5blk
 jsr Phex
 ldd W5blk+1
 jsr Phex2
 jsr prt_blk print block info
 ldx #02f
 jsr Pdata
 lda W5cmd
 jsr Phex
 ldx #04f
 jsr Pdata
 lda 0,s
 jsr Phex
 lda wd_error
 jsr Phex
 bra 0f
00 fcc $d,'W5 error - Block: ',0
02 fcc ', Cmd: ',0
04 fcc ', Status: ',0
0 puls d,x,y,pc
 else
 rts
 endif

 if PRT_REQ|PRT_ERR
prt_blk pshs d,x,y,u
 ldx #03f
 jsr Pdata
 lda W5sdh
 jsr Phex
 ldx #01f
 jsr Pdata
 lda W5adr
 jsr Phex
 ldd W5adr+1
 jsr Phex2
 puls d,x,y,u,pc
03 fcc ', SDH: ',0
01 fcc ', Cylinder: ',0
 endif

 if PRT_REQ
*
* prt_req - print environment
*
prt_req pshs d,x,y,u
 ldx #10f
 jsr Pdata
 ldd bfdvn,y
 jsr Phex2
 ldx #11f
 jsr Pdata
 lda bfblch,y
 jsr Phex
 ldd bfblck,y
 jsr Phex2
 ldx #12f
 jsr Pdata
 lda bfflag,y
 ldb bfflg2,y
 jsr Phex2
 ldx #13f
 jsr Pdata
 lda bfxadr,y
 jsr Phex
 ldd bfadr,y
 jsr Phex2
 puls d,x,y,u,pc return
10 fcc $d,'WIN I/O: Dev = ',0
11 fcc ', Block = ',0
12 fcc ', Flags = ',0
13 fcc ', Buf = ',0
*
Phex2 pshs b
 jsr Phex
 puls a
 jmp Phex
 endif

*
* semi-universal deblocker routine
*      entry conditions:  bfblch,y -> 24 bit block number
*      exit with WD set up except for storing head in SDH
*      B will contain the head address
*      and X will point to the cylinder address
*
deblock ldb bfblch,y load the block number
  stb blktmp
  stb W5blk
  ldd bfblck,y
  std blktmp+1 and move the whole thing to temporary
  std W5blk+1
  ldb W5hd+1 load the current number of heads
  lda #17 load the number of sectors per track
  mul and get the number of sectors/cylinder
  ldx #blktmp get pointer to block temporary
*
*   subroutine to perform a 24 x 8 division
*
*   Enter with B =  Divisor
*              X => Dividend
*
*    Exit with A =  Remainder
*              B =  Divisor
*              X => Quotient
*
div24x8    pshs    b                   stuff divisor on stack
           ldd     #24                 load length of divisor in bits
*
*   shift the 24 + 8 bit quantity one bit left
*
nextbit    lsl     2,x                 shift in a zero bit
           rol     1,x                 then ripple shift the quotient
           rol     0,x
           rola                        finally shift into remainder
*
*   decide if we can subtract the divisor
*
          bcs     subtract            if carry set, do the subtraction
           cmpa    0,s                 compare divisor against remainder
           blo     nosubtract          if lower, don't do subtract
subtract   suba    0,s                 subtract from the remainder
           inc     2,x                 add a one bit into the quotient
nosubtract decb                        decrement the bit count
           bne     nextbit             loop until done
           puls b

*   The cylinder is now in x.
*   now divide the 8 bit remainder in A by "17"
*   returns head in B with remainder (sector)  in A
*
div8by17   ldb     #$F1
           mul
           pshs    a
           lsra
           lsra
           lsra
           lsra

           pshs    a
           lda     1,s
           anda    #$0f
           addb    #$f1
           adca    #1   set sector origin = 1
           ldb     0,s++
           andb    #%00000111       make sure head in range


* now do certain set-up on the Western Digital and in UniFlex

           sta      wd_sec_num      stuff the wd sector number
           sta      W5adr

*
* At this point, X -> Cylinder #, B = Head #
* If this is a multi-unit volume and (X) is greater
* than the number of cylinders on a single volume,
* adjust (X) by that number of cylinders and
* adjust B to indicate the next drive.
*
 pshs b save head #
 lda W5mv get single/multi-volume indication
 bita #$80
 beq 00f exit if single volume
 ldd 1,x
 cmpd W5cyl decide which unit
 blo 00f
 subd W5cyl adjust cylinder #
 std 1,x
 puls b restore head #
 addb #$08 adjust drive #
 pshs b
00
 if PRT_REQ
 pshs d,x,y,u
 ldx #00f
 jsr Pdata
 lda W5hd+1
 jsr Phex
 ldx #01f
 jsr Pdata
 ldd W5cyl
 jsr Phex2
 ldx #02f
 jsr Pdata
 lda W5st+1
 jsr Phex
 puls d,x,y,u
 endif
 puls b,pc
00 fcc $d,'Parameters: W5hd = ',0
01 fcc ', W5cyl = ',0
02 fcc ', W5st = ',0
10 fcc $d,'WIN Decoded as',0

blktmp     rzb     3


 sttl 5" Winchester Character Drivers
 pag

*
* Character drivers for 5" Winchester (SWTPC)
*

*
* open
*

w5cop cmpb #W5minor+3 check for legal drive #
 bhi w5cop9 jump on error
 subb #W5minor compute relative drive #
 clra
 tfr d,x
 tst w5opt,x check open status
 bne w5cop2
 lda #8 set type=ST506
 lbsr w5cpyDC go setup device characteristics
 ldd #$FFFF reset "current" drive
 std w5drv
w5cop2 inc w5opt,x bump open count
 rts
w5cop9 lda #EBARG bad argument
 sta uerror
 rts


*
* close
*
w5ccl jmp w5close same as block

*
*  ttyset/ttyget entry
*    ttyget - return most recent device status
*
w5csp cmpx #0 ttyget?
 beq w5csp4 no - do ttyset
w5csp0 ldy #w5esw
 lda #6
w5csp1 ldb ,y+ pick up characters
 stb ,x+
 deca done?
 bne w5csp1
 rts
w5csp4 subb #W5minor
 aslb
 ldy #w5dtyp
 ldy b,y
 lda #11 copy device table
 ldx usarg2 get table address
10 pshs y,a save registers
 jsr gtubyt get next byte
 puls a,y
 stb ,y+ place in table
 leax 1,x bump source pointer
 deca done?
 bne 10b
 ldd #$FFFF force reconfigure of controller
 std w5drv
 rts

*
* read
*

w5crd pshs d save device number
 jsr fchgb get buffer header
 puls d reset dev number
 bsr w5cnf go configure header
 tst uerror any errors?
 beq w5crd4
 jsr frechbf release buffer
 rts error return
w5crd4 pshs a save task info
 orb #BFRWF set read mode
 stb bfflag,y save in buffer
 bra w5cio go do it

*
* write
*

w5cwr pshs d save device number
 jsr fchgb get buffer header
 puls d
 bsr w5cnf configure buffer
 tst uerror any errors?
 beq w5cwr4
 jsr frechbf release buffer
 rts error return
w5cwr4 pshs a save task status

*
* do character io
*

w5cio jmp fchio same as floppies

*
* configure character header
*

w5cnf std bfdvn,y set up device number
 ldd uicnt get transfer count
 std bfxfc,y set in header
 cmpd #512 is it valid?
 beq w5cnf0
 cmpd #W5_FT special count for Format
 bne w5cerr
w5cnf0 jmp fchcn2 same as floppies

w5cerr lda #EIO set error
 sta uerror
 rts return
