title 'Tarbell / Morrow winchester disk handler' ; CP/M-80 Version 3 -- Modular BIOS ; Disk I/O Module for CMI 5619 ; Initial version 1.1 , 6-29-83 ; Copyright (c) 1983 , Tarbell Electronics dseg ; Disk drive dispatching tables for linked BIOS public win0,win1,win2,win3 ; local buffer is external in floppy module extrn @dskbf ; Variables containing parameters passed by BDOS extrn @adrv,@rdrv extrn @dma,@trk,@sect extrn @dbnk ; System Control Block variables extrn @ermde ; BDOS error mode ; Utility routines in standard BIOS extrn ?wboot ; warm boot vector extrn ?pmsg ; print message @ up to 00, saves & extrn ?pdec ; print binary number in from 0 to 99. extrn ?pderr ; print BIOS disk error header extrn ?conin,?cono ; con in and out extrn ?const ; get console status ; Utility routines in MOVE.ASM extrn ?gndma,?ax16,?rar4 extrn ?xmove,?move ; 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 ;drive specification table for the hard disk controller cyl equ 306 ;number of cylinders heads equ 6 ;number of heads per cylinder precomp equ 128 ;cylinder to start write precomensation lowcurr equ 0 ;low current not used stepdly equ 2 ;step delay (0-12.7 milliseconds) steprcl equ 30 ;recalibrate step delay headdly equ 0 ;settle delay (0-25.5 milliseconds) sectsiz equ 3 ;code for 512 byte sectors badsiz equ 32 ;#of bad sector entries hrdspt equ 17 ;physical sectors/track cpmhrd equ hrdspt*heads*4 ;cpm sectors/track ;define controller commands dmaread equ 0 ;read sector dmawrit equ 1 ;write sector dmarhed equ 2 ;find a sector dmawhed equ 3 ;write headers (format a track) dmalcon equ 4 ;load disk parameters dmassta equ 5 ;sense disk drive status dmanoop equ 6 ;null controller operation reset equ 54h ;reset controller attn equ 55h ;send a controller attention chan equ 50h ;default channel address stepout equ 10h ;step direction out stepin equ 0 ;step direction in band1 equ 40h ;no precomp, high current band2 equ 0c0h ;precomp, high current band3 equ 80h ;precomp, low current track0 equ 1 ;track zero status wflt equ 2 ;write fault from drive dready equ 4 ;drive ready sekcmp equ 8 ;seek complete ; Extended Disk Parameter Headers (XPDHs) dw mw$write dw mw$read dw mw$login dw mw$init db 0,0 ; relative drive zero win0 dw 0 ; no sector translation db 0,0,0,0,0,0,0,0,0 db 0 ; media flag dw mwdpb ; disk parameter block dw 0 ; permanent drive dw 0fffeh ; alv set by gencpm dw 0fffeh ; dirbcb set by gencpm dw 0fffeh ; dtabcb set by gencpm dw 0fffeh ; hash set by gencpm db 0 ; hash bank = 0 dw mw$write dw mw$read dw mw$login dw mw$init db 1,0 ; relative drive one win1 dw 0 ; no sector translation db 0,0,0,0,0,0,0,0,0 db 0 ; media flag dw mwdpb ; disk parameter block dw 0 ; permanent drive dw 0fffeh ; alv set by gencpm dw 0fffeh ; dirbcb set by gencpm dw 0fffeh ; dtabcb set by gencpm dw 0fffeh ; hash set by gencpm db 0 ; hash bank = 0 dw mw$write dw mw$read dw mw$login dw mw$init db 2,0 ; relative drive two win2 dw 0 ; no sector translation db 0,0,0,0,0,0,0,0,0 db 0 ; media flag dw mwdpb ; disk parameter block dw 0 ; permanent drive dw 0fffeh ; alv set by gencpm dw 0fffeh ; dirbcb set by gencpm dw 0fffeh ; dtabcb set by gencpm dw 0fffeh ; hash set by gencpm db 0 ; hash bank = 0 dw mw$write dw mw$read dw mw$login dw mw$init db 3,0 ; relative drive three win3 dw 0 ; no sector translation db 0,0,0,0,0,0,0,0,0 db 0 ; media flag dw mwdpb ; disk parameter block dw 0 ; permanent drive dw 0fffeh ; alv set by gencpm dw 0fffeh ; dirbcb set by gencpm dw 0fffeh ; dtabcb set by gencpm dw 0fffeh ; hash set by gencpm db 0 ; hash bank = 0 cseg ; DPB must be resident mwdpb dw cpmhrd ; # of cpm sectors per track db 5 ; bsh - 4k block size db 31 ; blm db 1 ; exm - extent mask dw 3887 ; dsm - disk size -1 dw 2047 ; drm - directory size -1 db 0ffh ; al0 - directory allocation db 0ffh ; al1 - directory allocation dw 8000h ; cks - permanent disk dw 1 ; off - 1 reserved track db 2 ; psh - 512 byte physical sector db 3 ; phm - 512 byte physical sector dseg ; rest is banked ; Disk I/O routines for standardized BIOS interface ; Initialization entry point. ; called for first time initialization. mw$init: ret mw$login: ; This entry is called when a ; drive is about to be logged on. call mwhome ;do a restore lxi b,0 ;track 0 call mwseek ;do the seek mvi a,dmaread ;setup for read sta dmaop lxi h,@dskbf shld dmadma ;use local buffer xra a sta extdma ;bank 0 sta mwsectr ;sector 0 for badmap mvi a,2 ;head 2 sta mwhead call rwsect ;go read the sector ora a ;test status of read jrnz overflo lhld badptr ;pick up bad map pointer lxi d,@dskbf ;start at beginning of buffer badl: ldax d ;pick up an entry from the buffer ora a jz bade ;all done mov a,m ;pick up entry from bad map table inr a jz overflo ;bad map overflow lda @rdrv ;put drive in table mov m,a inx h lxi b,8 xchg ldir ;move the rest of information into the table xchg jmp badl bade: shld badptr ;restore new bad map pointer ret overflo: lda @adrv ;get absolute drive adi 'A' ;make ascii sta map$message$drive lxi h,map$message call ?pmsg pop d ;pop return address pop h ;pop dph pointer lxi h,0 ;indicate select error push h push d ;restore return address ret map$message db cr,lf,'Badmap read error or overflow on ' map$message$drive db 0,':',0 badptr dw badmap ;pointer to next available bad map entry ; ; disk READ and WRITE entry points. ; ; these entries are 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 ; they transfer the appropriate data, perform retries ; if necessary, then return an error code in mw$read: mvi a,dmaread sta mwop jr findalt mw$write: mvi a,dmawrit sta mwop findalt: ; now check for a replacement sector lda @rdrv ;get drive # mov c,a ;put in c for fast search lxi h,badmap ;get badmap address all: lded badptr ora a ;clear carry xchg dsbc d ;check for end of badmap jrz alt2 ;if the end mov a,c ;get drive # cmp m ;check it jrnz altmis ;nope inx h ;bump to track lda @trk ;get lsb track cmp m ;check it jrnz altmis ;nope inx h ;bump to msb lda @trk+1 ;get msb track cmp m ;check it jrnz altmis inx h mov a,m ;get map sector sbi 1 ;make real sector number mov b,a ;put in b lda @sect ;get sector cmp b ;check it jrnz altmis inx h ;alternate found inx h ;bump to alternate mov c,m ;get lsb alternate track inx h mov b,m ;get msb alternate track push h ;save hl call mwseek ;do the seek pop h ;restore hl inx h lda mwop ;get the operation sta dmaop ;tell the controller mov a,m ;get the alternate sector sbi 1 ;make real sector number pop h ;make stack right jmp altfnd ;calculate head and do it! altmis: pop h ;restore hl lxi d,9 ;offset to next badmap entry dad d jmp all ;look some more ; jump here if no alternate found alt2: lbcd @trk ;get requested track call mwseek ;do the seek lda mwop ;get the operation sta dmaop ;tell the controller lda @sect ;get the requested sector ; jump here if an alternate is found altfnd: mov c,a ;put in c mvi d,0 ;clear head counter seclp: sui hrdspt ;subtract out a heads worth jrc secfnd ;jump if found inr d ;else bump to next head mov c,a ;update sector count jr seclp ;and do it again secfnd: mov a,d ;get head sta mwhead ;save it mov a,c ;get sector sta mwsectr ;save it also ; generate and check the real dma address ; for a proper transfer call ?gndma ;generate real dma address sta extdma ;save extended address sded dmadma ;save real dma address inx h ;point to next manager byte push h ;save pointer lxi h,01ffh ;byte count-1 dad d ;add in address push psw ;save flags mov a,h ;get h reg call ?rar4 ;move to lower nib mov h,a ;back to h pop psw ;get ext adr & flags back jrnc samebk inr a ;bump extended address samebk: call ?ax16 ;mov to upper nibble ora h ;or in h pop h ;get manager pointer cmp m ;check for continuity jrz rwsect ;ok for direct transfer ;if memory is not continuous past a 4k boundary a ;local buffer must be used. lda mwop ;get operation cpi dmaread ;test for read jrnz notread ;jump if not a read ;if a read operation - read the sector to the local buffer ;then use xmove to transfer to the requested address. xra a sta extdma ;use bank 0 for read lxi h,@dskbf shld dmadma ;use local buffer for read call rwsect ;do the read push psw ;save the results mvi c,0 ;bank 0 is the source lda @dbnk ;get dma bank mov b,a ;use for destination di ;disable interrupts call ?xmove ;setup for extended move lxi d,@dskbf ;use local buffer as source lhld @dma ;dma address is destination lxi b,512 ;move 512 bytes call ?move ;do the move pop psw ;restore results of the read ret ;return to cp/m ;if a write operation - transfer the requested data to the ;local buffer with xmove then write it to the disk. notread: lda @dbnk ;get dma bank mov c,a ;use as source mvi b,0 ;use bank 0 as destination di ;disable interrupts call ?xmove ;setup for extended move lxi h,@dskbf ;use local buffer as destination shld dmadma ;also use for write lded @dma ;use dma address as source lxi b,512 ;move 512 bytes call ?move ;move data to local buffer xra a sta extdma ;use bank 0 ; read or write operation with retrys ; dma address is already setup. rwsect: mvi a,10 ;10 retrys sta rtcnt retry: call mwrdwr ;go to it ! mvi a,0 rnc ;return if no error lda rtcnt ;get retry count dcr a sta rtcnt ;update count jnz retry inr a ;set error code ret ; these are the low level drivers for the morrow controller ; reset the controller and do a home on @rdrv mwhome call mwreset ;reset controller lxi h,dmarg1 ;load arguments mvi m,steprcl ;load step delay (slow) inx h mvi m,headdly ;head settle delay call mwissue ;do load constants again call mwptr ;get pointer to current cylinder number mvi m,0 ;setup fake at cyl 0 inx h mvi m,0 lxi b,2 ;seek in 2 cylinders call mwseek call mwptr ;get pointer again mvi m,0ffh ;fake at cyl 255 for max head travel inx h mvi m,0ffh lxi b,0 ;seek to cylinder 0 call mwseek ;recal slowly jmp mwreset ;back to fast stepping mode ; seek @rdrv to the track in bc mwseek calì mwptr ;geô tracë pointer mov e,m ;get old track number inx h mov d,m dcx h mov m,c ;store new track number inx h mov m,b mov l,c ;build cylinder word mov h,b shld dmarg0 ;set command channel cylinder number mov a,d inr a lxi h,0ffffh jnz mwskip0 mvi c,stepout jmp mwskip mwskip0: mov h,b ;(hl) = new track, (de) = old track mov l,c call mwhlmde mvi c,stepout mov a,h ani 80h ;check hit bit for negitive direction jnz mwsout ;step in mvi c,0 jmp mwskip mwsout: call mwneghl mwskip: shld dmastep lda @rdrv ora c sta dmasel0 mvé a,dmanooð ;no-operatioî commanä foò thå channel call mwprep ;step to proper track lxi h,0 ;clear step counter shld dmastep ret ; reset the controller and setup the default parameters mwreset out reset ;send reset pulse to controller lxi h,dmachan ;address of command channel shld chan ;default channel address xra a sta chan+2 ;clear extended address byte lxi h,dmarg1 ;load arguments mvi m,stepdly ;load step delay inx h mvi m,headdly ;head settle delay inx h mvi m,sectsiz ;sector size code inx h mvi m,dmalcon ;load constants command jmp mwissue ;do load constants ; do the operation spec by a reg. mwprep sta dmaop ;save command channel op code ; do the operation spec by dmaop. mwrdwr: mvi c,band1 lhld dmarg0 lxi d,precomp call mwhlcde jc mwpreps mvi c,band2 lxi d,lowcurr call mwhlcde jc mwpreps mvi c,band3 ;cylinder > low_current mwpreps lda mwhead ;load head address sta dmarg2 cma ;negative logic for the controller ani 7 ;3 bits of head select rlc ;shove over to bits 2 - 4 rlc ora c ;add on low current and precomp bits mov c,a lda @rdrv ;load drive address ora c ;slap in drive bits sta dmasel1 ;save in command channel head select lda mwsectr ;load sector address sta dmarg3 ; start the controller running and wait for completion. mwissue lxi h,dmastat ;status byte mvi m,0 out attn ;start controller lxi d,0 ;time out counter mov b,e ;controller busy status mwiloop mov a,m ;get status cpi 0ffh ;test for successful operation rz ;return no error (carry reset) ora a ;test for error stc rnz ;return error status ;controller busy wait for it push b mvi b,4 ;total 75 t-states mwilop: djnz mwilop ; 36+((b-1)*13) pop b dcx d ;bump timeout counter mov a,d ora e jnz mwiloop ;loop if still busy mov a,m ;get error status stc ;set error flag ret ; return with hl pointing to the track word for @rdrv. mwptr: lda @rdrv ;get currently select drives track address rlc mov e,a mvi d,0 lxi h,mwtab dad d ;offset into track table ret mwneghl: mov a,h cma mov h,a mov a,l cma mov l,a inx h ret mwhlmde: xchg call mwneghl xchg dad d ret mwhlcde: mov a,h cmp d rnz mov a,l cmp e ret mwtab ;collection of track addresses dw 0ffffh ;initialize to dw 0ffffh ; (way out on the end of the disk) dw 0ffffh dw 0ffffh mwop db 0 mwhead db 0 ;currently selected head mwsectr db 0 ;currently selected sector rtcnt db 0 ;retry count dmachan equ $ ;command channel area dmasel0 db 0 ;drive select dmastep dw 0 ;relative step counter dmasel1 db 0 ;head select dmadma dw 0 ;dma address extdma db 0 ;extended address dmarg0 db 0 ;first argument dmarg1 db 0 ;second argument dmarg2 db 0 ;third argument dmarg3 db 0 ;fourth argument dmaop db 0 ;operation code dmastat db 0 ;controller status byte dmalnk dw dmachan ;link address to next command channel db 0 ;extended address of link badmap ds badsiz*9 ;bad sector map db 0ffh ;end marker end