title 'disk drivers for cp/m 3.0 for 5 1/4" 1.0 meg drives' ; ;last update 6/30/83 C.N.C. ; true equ -1 ; false equ not true ; ; banked equ true ; interrupt equ true ;if open door interrupt is used ; dseg ;may be in banked memory ; ; disk dispatching tables public ?fdhca,?fdhcb ;two 5 1/4" high capacity 1.0 meg public ?fdsd ;one 8 inch SS/SD if interrupt public ?si$buf,?sic3 ;for interrupt routine endif public ?needsp ; ; variables containing parameters passed by bdos extrn @adrv,@rdrv extrn @dma,@trk,@sect extrn @dbnk,@cbnk ; ; system control block variables extrn @ermde ;bdos error mode extrn @cnt ;multi sector count ; ; utility routines in standard BIOS extrn ?wboot ;warm boot vector extrn ?pmsg ;print message @ up to 0, saves extrn ?pdec ;print binary number in from 0 to 99 extrn ?pderr ;print BIOS disk error header extrn ?conin,?cono ;console in and out extrn ?const ;get console status extrn ?bank ;select bank for use ; ; port address equates maclib ports ; ; ; cp/m 3 disk definition macros maclib cpm3 ; ; ; z80 macro library instruction definitions maclib z80 ; ; common control characters cr equ 13 ; lf equ 10 ; bell equ 7 ; ; ; extended disk parameter headers (XPDHs) ; ; 1st 5 1/4" 1.0 meg. floppy disk ; dw fd$write ;write for 5 1/4" drive dw fd$read ;read for 5 1/4" drive dw fd$login ;login for 5 1/4" drive dw fd$init0 ;initialization for drive 0 db 0,0 ;relative drive 0 ?fdhca dph 0,dpbhc ; ; 2nd 5 1/4" Mitsubishi drive ; dw fd$write ;write for 5 1/4" drive dw fd$read ;read for 5 1/4" drive dw fd$login ;login for 5 1/4" drive dw fd$init1 ;initialization for drive 1 db 1,0 ;relative drive 1 ?fdhcb dph 0,dpbhc ; ; 8 inch drive ; dw fd$write$8 ;write for 8" drive dw fd$read$8 ;read for 8" drive dw fd$login ;login for 8" drive dw fd$init3 ;initialization for drive 2 db 3,0 ;relative drive 2 ?fdsd dph trans,dpbsd ; ; cseg ;DPB must be in common memmory ; if interrupt dpbhc dw 32 ;128 byte sectors per track db 4 ;block shift db 15 ;block mask db 0 ;extnt mask dw 311 ;disk size dw 127 ;directory max db 0C0H ;alloc0 db 0 ;alloc1 dw 8020h ;check size (high order bit set) dw 4 ;offset track 0 if banked db 4,15 ;physical sector size shift 2K else db 3,7 ;physical sector size shift 1K endif dpbsd dw 26 ;128 byte sectors per track db 3 ;block shift db 7 ;block mask db 0 ;extnt mask dw 242 ;disk size dw 63 ;directory max db 192 ;alloc0 db 0 ;alloc1 dw 8010h ;check size (high order bit set) dw 2 ;offset track 0 db 0,0 ;physical sector size shift else if banked dpbhc dpb 2048,2,160,2048,128,4 ;make cp/m think big else dpbhc dpb 1024,4,160,2048,128,4 ;for Mitsubishi 5 1/4 drives endif dpbsd dpb 128,26,77,1024,64,2 ;for 8 inch SD/SS drive endif ; ; dseg ;reset may be in banked ; ; trans skew 26,6,1 ;sector skew table for CP/M standard ; ; ; ; Disk I/O routines for Mitsubishi 5 1/4" disk drive ; fd$init0: call spec5 ;set up controller call home15 ;home disk xra a ;zero a sta ?needsp ;do we need spec flag sta sec$cnt ;reset count for multio fd$init1: ;all init. done by drive 0 fd$init3: ;will set up 8" at read ret ; ; fd$login: ret ;this routine is called to determine ;density. Mitsubishi drive are all the same ;so no need for this ; ; ; fd$read: ;perform read operation mvi a,40h ;set read bit for DMAC sta type ;store dma count mvi a,0c6h ;load read command mfm mode sta temp ; jmp common5 ;jump to common ; fd$write: ;perform a write operation mvi a,80h ;set write bit for DMAC sta type ;store dma count mvi a,0c5h ;load write command sta temp ; ; common5: lxi h,0000h ;zero hl shld dma$offset ;zero dma offset lda ?needsp ;get flag ora a ;set flags cnz spec5 ;set up controller for 3" xra a ;set flag sta ?needsp ;store it lda @cnt ;get multio count dcr a ;see if one sector operation jz one$sector ; ;must be multio lda sec$cnt ;get sector count ora a ;zero ? jz do$multio ;do the multi sector i/o dcr a ;-1 sta sec$cnt ;save jnz dec$cnt ; mvi a,01 ; sta @cnt ; dec$cnt: xra a ;set no errors ret ;return to bdos do$multio: lda @cnt ;get sector count dcr a ;-1 sta sec$cnt ; call seekcl5 ;seek frist track to read/write lda @cnt ;get # sectors to transfer mov b,a ;put in b for clac. lda @sect ;get starting sector # mov c,a ;find max # of sectors this track if banked mvi a,02 ;2k sector else mvi a,04 ;1K sector endif sub c ; mov c,a ;put in c mov a,b ;find # of sectors sub c ;to transfer this track jm less$max ;jump if less sector that maxs jz less$max ;max and cnt the same, do it ; loop to transfer rest to sectors next$track: sta sec$left ;store # of sector to read call ex$command ;transfer this track ora a ;see if error jnz error$multio ;jump if error lda @trk ;get track number inr a ;next track (bdos will not overrun track) sta @trk ;store for transfer call seekcl5 ;seek track xra a ;zero a reg sta @sect ;start with sector 0 for new track lda sec$left ;get # of sector left to transfer mov b,a ;put in b if banked ; sui 02 ;2k sector else ;or 1k sector sui 04 ;find # of sector to transfer this track endif jm less$max ;jump if less that one track jz less$max ;jump if = one track if banked ; mvi c,02 ;must be 2k sector else mvi c,04 ;transfer one track endif jmp next$track ;loop till all read ; error$multio: mov b,a ;store error status (normaly 01h) mvi a,01h ;reset count sta @cnt ;store it mov a,b ;get error status back ret ;return to bdos with error status in ; ; sector to trans. less max that can be this track less$max: mov c,b ;get number of sector to trans to jmp ex$command ;do it ; only one sector transfer one$sector: sta sec$cnt ;mullio sector count to zero call seekcl5 ;seek track mvi c,01h ;must be one sector ; ; =number of sectors to read on one track ; ex$command: lhld @dma ;get dma address xchg ;to de lhld dma$offset ;get dma address offset dad d ;add offset shld @dma ;store new address lxi h,0000h ;zero if banked lxi d,2048 ;2k sector else lxi d,1024 ;get length one sector endif loop$length: dad d ;add one setor dcr c ;-1 on count jnz loop$length ;loop till done shld dma$offset ; dcx h ;-1 lda type ;get type of command mov d,a ;put in d mvi e,00h ;zero e dad d ;add to length shld dmaset ;set dma count and type lxi h,comtb ;point to command table lda temp ;get type of command mov m,a ;put in command table call waitio5 ;go do command ret ;return with error status ; ;enter here from read or write to perform the actual i/o ;for 3" drive ; the disk number in '@rdrv' (0,1) ; the track number in '@trk' (0-160) ; the sector number in '@sect' (0-4),(0,1) ; the dma address in '@dma' (0-65535) waitio5: inx h ;point to next location in memory lda @trk ;get track # ani 01h ;see if side #1 lda @rdrv ;get drive # jz side0 ;jump if side #0 ori 04h ;set side #1 side0: mov m,a ;store in command table inx h ;next command word storage lda @trk ;get track # ora a ;clear carry flag rar ;set track # 0-79 mov m,a ;store in table inx h ;next mvi b,00h ;select head 0 jnc nots1 ;jump if side 0 inr b ;set for head 2 nots1: mov m,b ;store head # inx h ;point to sector lda @sect ;get sector # ; if banked ;banked systems only (takes a big buff) ora a ;set flags ral ;*2 ;will read 2 sectors like one (speed things up) endif ; mov m,a ;store in command table inx h ;next mvi m,03h ;set n=3 for 1024 bytes inx h ;next mvi m,04h ;8 secotrs per track inx h ;next mvi m,80h ;gap =53 inx h ;next mvi m,0ffh ;dtl mvi a,7 ;store retry count sta retry ;store call erloop5 ;do i/o lda seeker ;get seek error status ora a ;set flags rz ;return if done call spec5 ;set up controller call home15 ;home disk if was seek error call seekcl5 ;seek track to read lxi h,comtb ;point to command table lda temp ;get command mov m,a ;store command (read or write) jmp waitio5 ;try 10 more times ; ;start of i/o routine used by 5 1/4" and (8" drives) ; erloop5: ;loop to here if to try again lhld dmaset ;get dma count mov a,l ;get lsb out dma0tc5 ;output terminal count mov a,h ;get msb out dma0tc5 ;output terminal count lhld @dma ;get dma address mov a,l ;get lsb out dma0ad5 ;output to dma contoller mov a,h ;get msb out dma0ad5 ;output to dma controller mvi a,41h ;enable dma controller out dmacm5 ;output lxi h,comtb ;point to command table mvi c,9 ;load byte count for command write if banked call send$command ;send read/write command else call wfdcc3 ;write command call polli3 ;poll interrupt status till done endif call result3 ;read result lda rbuf ;get status byte #0 ani 0d8h ;mask bits jnz rty3 ;retry if error lda rbuf+1 ;get status byte #1 ani 7fh ;mask status bytes jz dtest3 ;return no errors rty3: lda retry ;get retry count dcr a ;-1 sta retry ;store new retry count jnz erloop5 ;loop # trys lda seeker ;see if second time ora a ;set flags jnz sectry ;set error and retrun mvi a,0ffh ;set flag sta seeker ; lda comtb ;get coomand sta temp ;store in temp ret ; sectry: xra a ;zero a register sta seeker ;reset seek error stat ; ; suppress error message if BDOS is returning error to application lda @ermde ;get status cpi 0ffh ; jnz err$display ;go and display error hard$error: mvi a,01h ;get error status sta er$flag ;update error flag ret ;return ; ;if command was write delay for tunnel erase ; dtest3: xra a ;zero a reg sta seeker ;reset error flag sta erflag ; mov b,a ;zero b for error status lda comtb ;load frist byte command table ani 0Fh ;mask bits 0-3 cpi 05h ;see if write command mov a,b ;get error status rnz ;return if not write mvi a,9eh ;get delay count delay3: dcr a ;bump count jnz delay3 ; mov a,b ;get error status (allways 0) ret ; ; FDC set up routines ; spec8: ;set up 8 inch drive mvi a,0001b ;leave motor on set standard mode out intsp5 ; lxi h,spctb ;point to table jmp spec51 ;output it ; ; spec5: ;set head unload time, head load mvi a,1001b ;set motor on mini mode out intsp5 lxi h,spctb3 ;load pointer to command table spec51: mvi c,03h ;load byte count call wfdcc3 ;write command to controller if interrupt ; ei ;enable interrupts endif ret ;return ; ; disk prams for Mitsubishi 5 1/4" drives ; spctb3: ;specify prams. table db 03h ;command db 0efh ;step rate 04ms, head unload 240ms db 0ch ;head load time 50ms,dma mode ; ; disk prams for 8 inch SS/SD drive ; spctb: ;specify prams. table db 03h ;comand db 06fh ;step rate 10ms, head unload 240ms db 4ch ;head load time 76ms, dma mode ; ; home15: ;home disk drive in @rdrv lxi h,comtb+1 ;load pointer command table lda @rdrv ;get disk to home mov m,a ;put in table dcx h ;point to next buffer location mvi m,07h ;load recalibrate command mvi c,2 ;load byte count for command write call wfdcc3 ;write command call polli3 ;poll interrupt status call sic3a ;call sense interrupt command ret ;return when have good home ; ; seekcl5: ;seek the track in track loc. lda @trk ;get trak to seek ora a ;see if track 0 jz home15 ;home if track 0 seek: rar ;get track # for seek lxi h,comtb+2 ;point to command table mov m,a ;store track to seek dcx h ;point next location lda @rdrv ;get disk # mov m,a ;store command in table dcx h ;point next location mvi m,0fh ;load seek command mvi c,3 ;set byte count for command write call wfdcc3 ;write command call polli3 ;wait till done call sic3a ;sense interrupt status command ret ; ; sic3a: lxi h,sictb3 ;load pointer command table mvi c,1 ;load byte count call wfdcc3 ;write command to FDC lxi h,rbuf ;load pointer result storage mvi c,2 ;load byte count for result read call rest13 ;read result in to buufer if interrupt ei ;enable interrupts endif ret ;sense interrupt command for open door int. if interrupt ?sic3: ;sense interrupt command lxi h,sictb3 ;load pointer command table mvi c,1 ;load byte count call wfdcc3 ;write command to FDC lxi h,?si$buf ;load pointer result storage mvi c,2 ;load byte count for result read call rest13 ;read result in to buufer ret ;return endif ; sictb3: ;sense interrupt command table db 08h ;command ; ; result3: ;read result phase FDC lxi h,rbuf ;load pointer Result BUFfer mvi c,7 ;load byte count for read/write call rest13 ;read result if interrupt ei ;enable interrupts endif ret ;return rest13: in fdcsr5 ;input FDC status reg. ral ;test bit 7 jnc rest13 ;loop till ready in fdcd5 ;input result read data mov m,a ;put in buffer inx h ;bump pointer dcr c ;dec byte count rz ;return if done jmp rest13 ;loop till all bytes read ; ; cseg ;must be in commmon ; ;the sense interrupt and write command routines must be in ;common memory so all dma transfers are to the right bank ; if banked send$command: ;do not change if interrupt ; di ;do not want to interrupt till dma over endif lda @dbnk ;get bank number for transfer call ?bank ;select bank call wfdcc3 ;write command to controller call polli3 ;poll interrupt pin FDC xra a ;select bank 0 jmp ?bank ;reset to old bank # and return endif ; ; ; polli3: ;poll interrupt pin FDC in intsp5 ;input status ral ;test bit 7 jnc polli3 ;loop till interrupt pin true ret ;return when true ; wfdcc3: ;write command to FDC if interrupt di ;disable interrupts endif wfdcc: in fdcsr5 ;input status ral ;test bit 7 jnc wfdcc ;loop till ready mov a,m ;get command byte out fdcd5 ;output to FDC inx h ;bump command pointer dcr c ;dec byte counter rz ;return when done jmp wfdcc ;loop till all bytes output ; ; dseg ;may be in banked ; fd$read$8: ;perform read operation call same lxi h,407fh ;set to read 128 bytes shld dmaset ;store dma count lxi h,comtb ;point to command table mvi m,06h ;load read command jmp waitio8 ;to perform the actual i/o ; same: lda ?needsp ;get flag cpi 0ffh ;set flags cnz spec8 ;set up controller for 8" mvi a,0ffh ;set flag sta ?needsp ;store it mvi a,01h ;reset any multio count sta @cnt ;for other drivers that use multio jmp seekcl8 ;seek track ; fd$write$8: ;perform a write operation call same lxi h,807fh ;set write 128 bytes shld dmaset ;store dma count lxi h,comtb ;point to command table mvi m,05h ;load write command jmp waitio8 ; ;enter here from read or write to perform the actual i/o ;for 8" drive ; the disk number in '@rdrv' (3) ; the track number in '@trk' (0-77) ; the sector number in '@sect' (1-26) ; the dma address in '@dma' (0-65535) waitio8: inx h ;point to next location in memory lda @rdrv ;get disk # mov m,a ;store in command table inx h ;next command word storage lda @trk ;get track # mov m,a ;store in table inx h ;next mvi m,00h ;select head 0 inx h ;next lda @sect ;get sector # mov m,a ;store in command table inx h ;next mvi m,00h ;set n=0 for 128 bytes inx h ;next mvi m,1ah ;26 secotrs per track inx h ;next mvi m,07h ;gap =7 inx h ;next mvi m,128 ;128 bytes per sector mvi a,7 ;store retry count sta retry ;store call erloop5 ;do i/o lda seeker ;get seek error status ora a ;set flags rz ;return if done call spec8 ;setupt controller call home15 ;home disk if was seek error call seekcl8 ;seek track to read lxi h,comtb ;point to command table lda temp ;get command mov m,a ;store command (read or write) jmp waitio8 ;try 10 more times ; seekcl8: lda @trk ;get track # ora a ;clear carry jz home15 ;home disk ral ;shift to use same routine as 5 1/4 jmp seek ;do it ; ; ; error display routines ; err$display: call ?pderr ;print BIOS error message lda com$tb ;get type from command table ani 0fh ;mask upper bits cpi 05h ;see if write command lxi h,read$msg ;point to read message jnz was$read ;jump if was read lxi h,write$msg ;point to write message was$read: call ?pmsg ;print it ; ; find the type of error for result buffer lda r$buf ;load status byte #0 mov c,a ;put in c temp lxi h,err$not$ready ;point ot not ready message ani 08h ;look only at bit d3 cnz ?pmsg ;print if is lxi h,err$equ$check ;point to error message mov a,c ;get status ani 80h ;see if equipment check error cnz ?pmsg ;print if is lda r$buf+1 ;get status byte #1 mov c,a ;stor e c ani 04h ;see if not data error cnz no$data$err ;jump if no data error mov a,c ;get status ani 20h ;see if data error cnz data$err ;jump if data error lxi h,err$over$run ;if over run error mov a,c ;get status ani 10h ;test bit cnz ?pmsg ; mov a,c ;get status ani 02h ;see if write protected lxi h,err$write$prot;set write protected cnz ?pmsg ; lxi h,err$miss$am ;must be missing address mark mov a,c ;get status ani 01h ;see if missing address mark cnz ?pmsg jmp print$retry ; ; no$data$err: lda r$buf+2 ;get status register #2 ani 10h ;see if seek error lxi h,err$nd$wc ;point to seek error message jnz seek$err$print ; lxi h,err$nd$rnf ;point to record not found seek$err$print: jmp ?pmsg ; ; ; data$err: lda r$buf+2 ;get status registor #2 ani 20h ;see if data crc lxi h,err$crc$data ; jnz data$crc ; lxi h,err$crc$id ;must me id crc data$crc: jmp ?pmsg ; ; ; print$retry: lxi h,retry$msg ;point to retry mesage call ?pmsg ;print it call u$conin$echo ;get operator respone cpi 'Y' ; jnz hard$error ; mvi a,0ffh ;set for retry (will recalibrate drive) sta seeker ; lda comtb ;get command sta temp ; ret ;retrun ; u$conin$echo: call ?const ;input status ora a ;see if any character in buffer jz u$c1 ;if non get 1 call ?conin ;clear buffer jmp u$conin$echo ;loop till buffer clear u$c1: call ?conin ;get character push psw ;save character call ?cono ;echo it pop psw ;get charcater cpi 'a' ; rc ;return if less then a sui 'a'-'A' ;make upper case ret ; ; ; ; error messages ; err$not$ready db ' Not ready,',0 err$equ$check db ' Fault/Track0,',0 err$crc$data db ' CRC data,',0 err$crc$id db ' CRC ID,',0 err$write$prot db ' Protect,',0 err$over$run db ' Over run,',0 err$miss$am db ' Missing AM,',0 err$nd$wc db ' Seek,',0 err$nd$rnf db ' Record not found,',0 read$msg db ', Read,',0 write$msg db ', Write,',0 retry$msg db ' Retry (Y/N) ? ',0 ; ; scratch ram area for disk driver use ; retry: ds 1 ;retry count rbuf: ds 7 ;result buffer ?si$buf: ds 2 ;sense interrupt command buffer seeker: ds 1 ;seek error try flag temp: ds 1 ;temp storage dmaset: ds 2 ;dma terminal count erflag: ds 1 ;error reporting ?needsp: ds 1 ;need spec flag sec$cnt: ds 1 ;multio sector count type: ds 1 ;type of command secleft: ds 1 ;sector left to transfer dma$offset: ds 2 ;dma offset multio ; cseg ;needs to be in common comtb: ds 9 ;command table storage ; end