title 'tarbell dma double density loader driver' ; Copyright (c) Tarbell Electronics ; Version 1.1 5-31-83 Edwin E. Smith true equ -1 false equ 0 ;**************************************************************** ; mods - bugs ect. * ; 5-31-83 corrected bug in buffer controll blocks * ;**************************************************************** ;note - these routines are implied to be and must be in physical ;memory 00000h to 0ffffh and no translation is permitted ;ensure that bank 0 of the memory select table is contiguous ; user option area dubden equ true ;true if using a double density fdc dmactl set true ;true if using dma transfers mini equ false ;true if using 5-1/4" disks stprat equ 0 ;0=3ms,1=6ms,2=15ms,3=20ms(stp rate) ;rates are double for 5-1/4" hlab equ 8 ;0=no head load at beginning of seek dual equ false ;true if using a persci drive ercnt equ 10 ;error retry count ; port address equates. ; dmap equ 0e0h ;dma port base address wct0 equ dmap+1 ;dma word count register cmnd equ dmap+8 ;dma command register dadr equ 0f8h ;disk address dcom equ dadr ;disk command port dstat equ dadr ;disk status port track equ dadr+1 ;disk track register sectp equ dadr+2 ;disk sector register ddata equ dadr+3 ;disk data register wait equ dadr+4 ;in=wait for data-interrupt ddsel equ dadr+4 ;out=drive and density select dmachk equ dadr+5 ;in=non-wait status port extdma equ dadr+5 ;out=extended dma address ; dmar equ 01000000b ;dma read command dmaw equ 10000000b ;dma write command fread equ 10001000b ;floppy read command rdid equ 11000000b ;read id command ; ; disk drive dispatching tables for linked bios. public @dtbl public fd0 ; variables containing parameters passed by bdos. extrn @adrv,@rdrv extrn @dma,@trk,@sect ; utility routines in standard bios. extrn ?pmsg ; print message @ up to 00. extrn ?cono ; console out. ; cp/m 3 disk definition macros. maclib cpm3 ; z80 macro library instruction definitions. maclib z80 ; drive table @dtbl dtbl ; extended disk parameter headers (xpdhs). dw 0 ;loaders don't write dw fd$read dw fd$login dw fd$init db 0,0 ; relative drive 0. fd0: dw dbtrans ;deblocked translate table db 0,0,0,0,0,0,0,0,0 ;bdos scratch area db 0 ;media flag dw dd512 ;disk parameter block dw cks0 ;check vector dw all0 ;allocation vector dw dirbcb ;directory bcb dw dtabcb ;data bcb dw 0ffffh ;no hashing db 0 ;hash bank cks0 ds 44 ;checksum storage all0 ds 78 ;allocation storage ; disk parameter blocks ss128: dpb 128,26,77,1024,64,2 ;single density single sided ;128 bytes per sector ds128: ;double density single sided ;128 bytes per sector dw 51 ;sectors per track db 4 ;block shift factor db 15 ;block mask db 0 ;extent mask dw 237 ;disk size-1 dw 127 ;directory size-1 db 11000000b ;al0 db 00000000b ;al1 dw 32 ;check size dw 2 ;track offset db 0 ;psh db 0 ;phm ds512: ;double density single sided ;512 bytes per sector dw 64 ;sectors per track db 4 ;block shift factor db 15 ;block mask db 0 ;extent mask dw 299 ;disk size-1 dw 127 ;directory size-1 db 11000000b ;al0 db 00000000b ;al1 dw 32 ;check size dw 2 ;track offset db 2 ;psh db 3 ;phm sd128: ;single density double sided ;128 bytes per sector dw 26 ;sectors per track db 4 ;block shift factor db 15 ;block mask db 0 ;extent mask dw 242 ;disk size-1 dw 95 ;directory size-1 db 11000000b ;al0 db 00000000b ;al1 dw 24 ;check size dw 2 ;track offset db 0 ;psh db 0 ;phm dd128: ;double density double sided ;128 bytes per sector dw 51 ;sectors per track db 5 ;block shift factor db 31 ;block mask db 0 ;extent mask dw 237 ;disk size-1 dw 95 ;directory size-1 db 11000000b ;al0 db 00000000b ;al1 dw 24 ;check size dw 2 ;track offset db 0 ;psh db 0 ;phm dd512: ;double density double sided ;512 bytes per sector dw 64 ;sectors per track db 5 ;block shift factor db 31 ;block mask db 0 ;extent mask dw 303 ;disk size-1 dw 169 ;directory size-1 db 11000000b ;al0 db 00000000b ;al1 dw 42 ;check size dw 2 ;track offset db 2 ;psh db 3 ;phm trans skew 26,6,1 ;generate translate table for single density ddtrans skew 51,1,1 ;generate translate table for double density dbtrans skew 16,1,1 ;generate translate table for 512 x 16 ;buffers and buffer control blocks dirbcb db 0ffh ;initialize drive to ff ds 9 ; 9 bytes used by bdos dw buff1 ;address of a 512 byte buffer dtabcb db 0ffh ;initialize drive to ff ds 9 ; 9 bytes used by bdos dw buff2 ;address of a 512 byte buffer buff1 ds 512 ;512 bytes of buffer space buff2 ds 512 ;512 bytes of buffer space ; disk i/o routines for standardized bios interface ; initialization entry point. ; called for first time initialization. fd$init: ; all initialization done by drive 0 ret fd$login: ; this entry is called when a logical drive is about to ; be logged into for the purpose of density determination. ; it may adjust the parameters contained in the disk ; parameter header pointed at by push d ;save pointer to dph call gethf ;get hardware format pointer inx h ;get disk pointer address lda @rdrv ;get relative drive # add a add a add a add a mov m,a ;clear disk select byte for this drive push h ;save disk pointer address call get$id ;get the id byte from @rdrv jnz id$error ;return select error mov e,a ;save id in E lda @rdrv ;get drive # lxi h,drive$ids ;point to drive id storage mov c,a mvi b,0 ;generate offset dad b ;add in offset mov a,e ;get disk id back mov m,a ;save for future reference lxi h,disk$ids ;hl points to list of disk id's lxi b,6 ;six of them to check ccir ;check untill we find one lxi h,dph$patches ;hl points to patch words for the dph dad b ;and disk format word dad b dad b ;add b in 6 times for 6 bytes dad b dad b dad b ; hl now points to selected format word ; ; 0 = unlogged ; 1 = single density ; 2 = double density (51 secs - 128 b) ; 3 = double density (16 secs - 512 b) ; ;example: ;for double density 512x16: ; ; db 3 * 256 + 8 ; ;3 = use 512 byte sector size (hl+1) ;8 = set bit 3 to a '1' for doub density (hl) ; ;for double density 512x16 doub sided: ; ; db 3 * 256 + 48h ; ;3 = use deblock part of bios (hl+1) ;48h = set bit 3 '1' for doub den ; set bit 6 '1' for doub sided (hl) ; mov a,m ;get disk density mask inx h ;point hl at disk format flag xthl ;get pointer to disk hardware byte ora m ;or in density mask mov m,a ;save sta diskhs ;value for selected disk dcx h ;point hl at format flag xthl ;get table pointer back mov a,m ;get format type flag inx h ;point hl at translate vector xchg ;save pointer in de pop h ;get pointer to format byte mov m,a ;save sta diskhf ;value for selected disk xchg ;get table pointer back pop d ;get address of selected dph ;move translate vector from table to dph lxi b,2 ;2 bytes to move ldir ;patch translate vector ;move dpb vector to dph xchg ;swap dph pointer to hl lxi b,10 ;offset to dpb dad b ;add offset to hl xchg ;swap in table address again lxi b,2 ;2 bytes to move ldir ;patch dpb vector ;now we can return to root module to finish select ret ; ;id read error couldn't read first sector on disk ; id$error: pop h ;discard disk pointer pop h ;discard dph pointer pop d ;get-save return address pop h ;discard dph pointer (saved by root) lxi h,0 ;indicate select error push h ;push 0 on stack for root push d ;restore return address ret ;return select error ; ; read the id byte from the first sector of @rdrv ; return it in A with zero flag set if read error ; get$id: lda @rdrv ;get relative drive # add a add a add a add a ori 2 ;for single density fdc out ddsel ;select the drive call rdydrv ;check for drive ready mvi a,stprat+hlab ;restore drive call execc1 ;restore to track 0 mvi a,1 out sectp ;address sector 1 lxi b,fread shl 8 or wct0 lxi d,dmar shl 8 or 127 lxi h,buff2 ;use local buffer for read call dmarw ;do the read ani 9dh ;check for errors lda buff2+7eh ;get disk format id byte ret ; disk$ids ;table of six disk id bytes db 0deh ;double density 16 secs of 512? db 0dch ;512 x 16 doub sided? db 0dfh ;128 x 51 doub sided? db 0ddh ;double density 51 secs? db 0e7h ;doub sided single density db 0e5h ;single sided single density ; dph$patches ;table of patch bytes for the disk parameter headers ;also the hardware setting byte and the format byte dw 1*256 + 0 ;format flag and density mask dw trans ;single density translate table dw ss128 ;single density disk parameter block dw 1*256 + 40h ;=single density double sided dw trans ;single density translate table dw sd128 ;single density double sided dpb dw 2*256 + 8 ;=double density single sided dw ddtrans ;double density translate table dw ds128 ;double density single sided dpb dw 2*256 + 48h ;=double density double sided dw ddtrans ;double density translate table dw dd128 ;double density double sided dpb dw 3*256 + 48h ;=deblocked double sided dw dbtrans ;512 x 16 translate table dw dd512 ;deblocked double sided dpb dw 3*256 + 8 ;=deblocked single sided dw dbtrans ;512 x 16 translate table dw ds512 ;deblocked single sided dpb ; ;end of disk format table ; ; gethf: ;return hardware format for selected drive lda @rdrv ;get relative drive code add a ;double for indexing mov c,a mvi b,0 ;generate offset lxi h,dsklog ;point to disk log dad b ;add in offset mov a,m ;get format sta diskhf ;save for future reference inx h ;point at select byte mov a,m ;get it sta diskhs ;save for future reference dcx h ;point at format byte for @rdrv ret ; disk read entry point. ; this entry is called with the following arguments: ; relative drive number in @rdrv (8 bits) ; absolute drive number in @adrv (8 bits) ; disk transfer address in @dma (16 bits) ; disk transfer bank in @dbnk (8 bits) ; disk track address in @trk (16 bits) ; disk sector address in @sect (16 bits) ; pointer to xdph in ; it transfers the appropriate data, perform retries ; if necessary, then return an error code in fd$read: ;read - read a sector call ckdrv ;check to see if new drive call gethf ;get hardware format lda diskhs ;get disk density mov b,a ;put density in reg b lda @trk call seekls ;seek @rdrv to @trk lda seeker ;get seek error flag ora a ;test for seek error jnz comext ;abort read if seek error lded @dma ;get dma address ; ; -read logical sector- ;de is dma address ; readls: xchg ;put dma address in hl call rdydrv ;check for drive ready ; mvi a,ercnt ;retry count rrtry: sta rtcnt ;save it lxi b,fread shl 8 or wct0 lda diskhf ;check if 512 byte sectors cpi 3 ;if = 3, 512 bytes lxi d,dmar shl 8 or 511 jrnc rset lxi d,dmar shl 8 or 127 rset: push h ;save dma address call dmarw ;use common read/write pop h ani 9dh ;check for errors during read rz ;return to cp/m if no read errors lda rtcnt ;get retry count dcr a ;decrement it jrnz rrtry ;retry again comext: call check$id ;see if density has changed mvi a,0ffh ;return media change flag rnz ;to cp*m mvi a,1 ; return to cp/m error code ora a ;set flags ret ; ; we have a disk error - check if disk format has been changed ; check$id lda @rdrv ;get the reletive drive # mov c,a mvi b,0 ;generate offset lxi h,drive$ids ;point to drive id bytes dad b ;add in offset push h ;save pointer call get$id ;get the id byte of @rdrv pop h ;get pointer back cmp m ;test disk with previous ret ; ; check for new drive access ; ckdrv: lda olddrv ;get old drive # mov b,a lda @rdrv ;get relative drive # cmp b ;same ? rz ;return if no new drive sta olddrv ;save new drive # mvi a,0ffh sta chngfl ;set change flag ret ; ;check to see if selected drive is ready ;routine is also good for slim line drives. ; rdydrv: call statck ;check for drive ready rz ;return if ready mvi b,50 ;50 * 16ms = 800 ms rdydv1: call dlylop call statck ;check drive status rz ;return if ready djnz rdydv1 ;loop for 800 ms for slim line drives exx lda @adrv adi 'a' ;ascii adjust sta drv ;save for message lxi h,ready ;point to ready message call ?pmsg ;print it exx drvrdy: call statck jrnz drvrdy ;loop till ready ret ; ;selected drive is ready now ; statck: in dstat ;get drive status bit 7,a ;check ready bit ret ; dlylop: mvi a,16 ;16 ms count value dlylp1: mvi c,210 ;1 ms time constant wexitl: nop dcr c jnz wexitl dcr a jnz dlylp1 ret ; ;logical sector seek ; ;a=track, b=hardware and density for fdc ;d=sector (for future read) ; seekls: push psw ;save track xra a sta seeker ;reset seek error flag mvi a,10 ;retrys sta seekrty ;save it push b ;save density lda @rdrv ;get relative drive # if dual ani 0eh ;strip off lsb endif ; ;compute index into track table ; mov c,a ;put disk number into reg c. mvi b,0 lxi h,trktab ;point to start of trk table dad b ;get correct drive byte pop b mov a,m ;get old track value out track ;force into 1793 track reg. pop psw ;restore new track value cpi 1 mov c,a ;save track in reg c mov a,b ;get copy of drive and density jrnc seekok ;we are > than track 1? ani 30h ;clear density & side bit jr seek2 ;going to trk < 1 ; seekok: bit 6,b ;doing doub sided? jrz seek1 ;< > 1, doing single sided rrcr c ;divide track by 2 jrc seekk1 ;side bit set,doing odd trk res 6,b ;reset side bit, doing even trk seekk1: res 7,c ;clear msb of track value seek1: mov a,b ;get drive select code seek2: ori 2 ;for single density fdc out ddsel ;select disk and density push b call rdydrv ;check for drive ready pop b call dlysel ;delay on selects lda @sect ;get sector number out sectp ;tell the controoller chip in track ;read 1793 track register cmp c ;is track the same? rz ;not necessary to seek mov a,c ;not same, get new track number out ddata ;seek to track with head load ; ;execute seek command ; mvi a,14h+stprat+hlab ;seek command execc1: out dcom ;output disk command call sloop ;use common polling ani 90h ;mask out junk bits jrz savtrk ;save track for next time ; ;we have a seek error, ;save primary registers ;and execute a read id. ; ridcom: exx ;save regs lxi h,buff2 ;use local buffer area for read id lxi b,rdid shl 8 or wct0 lxi d,04006h ;dma read, length call dmarw ;read the id marks ani 9dh ;any errors in reading? jrz denok ;no, density is correct. exx ;yes, incorrect density mov a,b ;get density and drive byte res 3,a ;set single density for retry ori 2 ;for single density fcd out ddsel ;on read id. jr ridcom ;switch registers and retry. ; denok: in sectp ;get track byte out track ;place value into trk reg. lda seekrty ;get retry count dcr a sta seekrty exx ;restore registers jrnz seek1 ;reseek to track mvi a,0ffh sta seeker ;set seek error flag ret ; ;delay on new drive select ; dlysel: lda chngfl ;get drive change flag ora a ;was there a change to new drv? rz ;return if not, else xra a ;reset the drv chng flag sta chngfl ; ;drives are different, so get the present track ;number and seek to it to unload the head. ; in track ;get the present track number out ddata ;place into data register mvi a,10h + stprat ;get seek command out dcom ;send seek command jmp sloop ;wait till done ; savtrk: lda @rdrv ;get relative drive code if dual ani 0eh ;strip off lsb endif mov c,a mvi b,0 ;generate offset lxi h,trktab ;point to track table dad b ;add offset in track ;get 1793 track mov m,a ;save in table ret ; ;dma read / write routine ; dmarw: xra a ;clear accum out cmnd ;clear dma chip mvi a,0d0h ;force intrp cmnd (1793) out dcom outp e outp d ;read/write with length dcr c ;reg -> adr0 port now outp l outp h ;address for dma set up mvi a,41h ;request chan 0 operation out cmnd ;tell dma chip call hdld ;check head load bit ora b ;add in command for floppy out dcom ;send it to floppy ; ;general purpose wait routine ; sloop: in dmachk ;check bit 7 rlc ;and rotate it left jrc sloop ;and loop if carry xra a ;get a zero out cmnd ;clear dma chip in dstat ;get status of disk ret ; and return it to caller ; ;check for head load bit on in 1793 ; hdld: in dstat ;get status from 1793 bit 5,a ;check head load bit mvi a,4 ;get head load set bit rz ;return if not loaded xra a ;get a zero ret ;head is load already ; ;data tables ; ready: db 0dh,0ah,7,'drive ' drv: db ' : not ready ',0 dsklog: dw 0000h ;disk 0 log flag/den dw 1000h ;disk 1 " dw 2000h ;disk 2 " dw 3000h ;disk 3 " diskhf: ds 1 ;disk hardware format (1, 2, 3) diskhs: ds 1 ;disk/density setting = hardware trktab: ds 4 ;track table for 4 drives drive$ids ds 4 ;id bytes of 4 drives ; rtcnt: ds 1 ;disk retry counter seekrty:ds 1 ;seek retry counter seeker: ds 1 ;seek error flag chngfl: ds 1 ;non-0 if new drive olddrv: ds 1 ;old relative drive # end