           lib     hardware
           lib     environment
           lib     bfhdr
           lib     dtab
           ttl     SBC 5" Winchester Drivers
           pag
*
*   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 SBC 5" Winchester disk.
*

*
*  W5 Drive characteristics
*
swinc      equ     $42
ECC        equ     $80                 Honor error correction (WD1001 only)

W5hd       fdb     4                   Number of Heads
W5cyl      fdb     153                 Number of Cylinders
W5st       fdb     17                  Sectors/Track
W5srt      fcb     6                   Seek rate
*
MAXretries equ     3                   Max # retries / operation
W5_FT      equ     17*2                Format Track
w5esw      fdb     0                   W5 Error Status Word
w5sbf      rzb     4                   W5 Sense Status Buffer
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
w5cpyDC    aslb
           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
           ldd     dcncyl,y            get number of cylinders
           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     wd_reset            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      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
*
* w5 close
*
w5close
           lbsr    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
           pshs    b
           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    b
           puls    d,pc
w5cnz      sta     wd_cyl_hi
           stb     wd_cyl_low
           std     W5adr+1             and keep for uniflex also
           lda     W5sdh               send off command
           sta     wd_sdh
           lda     W5cmd
           sta     wd_cmd
           puls    a                   get the xfer dir back
           cmpa    #0                  is this a read?
           beq     w5rdwt              if so, set up for later xfer
           bsr     w5sdma              build pointers
           puls    d,pc
*
*   set up fake dma
*
w5sdma     pshs    cc                  retain flags
           sei                         and mask interrupts
           ldb     DATRW+XBUF          retain the current junk
           pshs    b
           pshs    a                   retain xfer dir again
           lda     bfxadr,y            get the high four bits
           ldb     bfadr,y             get the next lower eight
           aslb
           rola
           aslb
           rola
           aslb
           rola
           aslb
           rola
           sta     DATBOX+XBUF         and map into weird page
*   patch shit code
           ldd     bfadr,y             get the low order addresses
           anda    #%00001111          make the "D" page
           ora     #$D0
           tfr     d,x                 put in x
           puls    a                   get the xfer dir back again
           lbsr    fakedma             to the fake dma transfer
           puls    b
           stb     DATBOX+XBUF         put weird page back
           puls    cc                  get the flags back again
           puls    pc
*
*   w5rdwt -- set up for later dma read
*
w5rdwt     inc     w5dt+dtbusy         cause dtbusy to go to "2"
           puls    d,pc                return
*
*   do a dma read
*
w5rdma     clra                        indicate read direction
           bsr     w5sdma              go set up dma
           lbra    w5in4
*
*   Simulate a dma transfer to the WDxx board
*   Entry conditions:  x = buffer address
*                      a = dma direction (0=read 1=write)
*
fakedma    ldb     wd_status           see if the transfer is ready
           bitb    #wd_busy            busy?
           bne     fakedma             loop until not busy
           bitb    #wd_drq             drq?
           beq     fakethru
           clrb                        set byte count
           cmpa    #0                  is this a read?
           bne     fwrite              if not, must be a write

doload     lda     wd_data             get a data byte from the wdxx
           sta     0,x+
           lda     wd_data             get another byte
           sta     0,x+
           decb                        decrement the count
           bne     doload
           bra     fakethru            exit the routine

fwrite     lda     0,x+                get byte from the buffer
           sta     wd_data             and give to wd
           lda     0,x+                get another byte
           sta     wd_data             and cram it to
           decb                        decrement the count
           bne     fwrite              loop until finished

fakethru   rts

*
* 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
           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
           ldd     scaddr
           addd    #512
           std     scaddr
           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

*          ldb     bfxadr,y            get the page pointer
*          stb     swpexadr            retain the page for UniFLEX
           sta     swpexadr
           ldd     #$D000
           std     scaddr              save address in temp
*
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

           pshs    cc                  retain flags
           sei                         and mask interrupts for transfer
           ldb     DATRW+XBUF          get current contents of map
           pshs    b                   and retain
           lda     swpexadr            get extended address bits
           sta     DATBOX+XBUF         be sure the page is correct
           lda     W5cmd               get command
           sta     wd_cmd              issue command
           ldd     scaddr              get move address
           tfr     d,x                 put in x for transfer
           lda     swpdir              get channel direction control
           anda    #%00000001          limit range
           lbsr    fakedma             do the fake dma transfer
           puls    b                   get the address junk back
           stb     DATBOX+XBUF
           puls    cc                  get flags back
           rts                         all done

scaddr     fdb     $D000
*
*  Interrupt handler for SBC 5" Winchester
*
*
w5int      lda     #i_wdxx             clear w5 interrupt bit
           sta     IFR29
           ldy     w5dt+dtqfl          get first transaction
           lbeq    w5int99             jump if nothing going on
           lda     wd_status           get controller status
           bpl     w5int1              jump if controller not busy
           rts                         wait for completion interrupt
w5int1     ldb     bfdvn+1,y           check for legal minor device #
           cmpb    #W5minor
           lblo    w5int99
           cmpb    #W5minor+3
           lbhi    w5int99
           ldb     w5dt+dtbusy         check for SWAP in progress
           beq     w5int99
           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          lbsr    rpterr              go report error
           lbra    w5er                return error
w5int2     cmpb    #2                  is this a read complete, (DMA WAITING)
           lbeq    w5rdma              if so, go do dma from disk
           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   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
           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     rts                         ** Nop for error reporting **
           if      0
           pshs    d,x,y
           ldx     #00F
           jsr     Pdata
           lda     W5blk
           jsr     Phex
           lda     W5blk+1
           jsr     Phex
           lda     W5blk+2
           jsr     Phex
           ldx     #03F
           jsr     Pdata
           lda     W5sdh
           jsr     Phex
           ldx     #01F
           jsr     Pdata
           lda     W5adr
           jsr     Phex
           lda     W5adr+1
           jsr     Phex
           lda     W5adr+2
           jsr     Phex
           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
03         fcc     ', SDH: ',0
01         fcc     ', Cylinder: ',0
02         fcc     ', Cmd: ',0
04         fcc     ', Status: ',0
0          puls    d,x,y,pc
           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
           rts

blktmp     rzb     3


           ttl     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
           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             free 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             free 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
