; *********** BEGINNING OF MORROW HARD DISK CONTROLLER DRIVER ******* ; EQU's specific to each drive are found in files beginning with W. ; Changes by Don Tarbell: ; 7-28-85: tightened up code some. ; 7-29-85: put in checks for errors after calls to mwseek, home. ; 8-13-85: separated drive equ's from controller driver. ; 8-18-85: added more disk parameter blocks. ; 8-22-85: put in set up for 4 logical/physical drives. ; 8-24-85: tightened code in mwptr routine. ; also disabled interrupts while initializing controller. ; 8-28-85: put in home and retry on seek error. ; 8-29-85: made CP/M-80 driver from CCP/M-86 driver. ; 9-11-85: made PC-DOS driver from CP/M-80 driver. ; 10-21-85: updated for info written by mformat. ; Equates. hdrtmax equ 2 ; hard disk retry maximum. badsiz equ 64 ; # of bad sector entries allowed. hdskrt equ 3 ; hard disk retry count. ; Morrow Controller Commands. dmaread equ 0 ; read sector. dmawrit equ 1 ; write sector. dmarhed equ 2 ; find a sector. dmawhed equ 3 ; write headers (format track). dmalcon equ 4 ; load disk parameters. dmassta equ 5 ; sense disk drive status. dmanop 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. ; MAPPER ; Log in Morrow Hard disk interface drive. ; Read bad sector map from track 0, head 2, first sector. ; entry: cl = physical hard disk drive number from 0-3. ; exit: cf (carry flag) set if error, ercode = status. ; mapper proc near mov hrddsk,cl ; save drive number. mov si,offset moropn ; print opening message. call prtmsg ; clear bad map table to all zeroes unless done already. cmp mwiflg,0 ; have we initialized bad map? jne mwrest ; hop around if so. mov cx,badsiz*9 ; cx = bad map size. mov di,offset badmap ; di = starting address. mov ax,cs ; es = cs. mov es,ax mov al,0 ; put zeroes in it. cld ; forward direction. rep stosb ; clear out bad map. mov mwiflg,0ffh ; set initialization flag. mwrest: call mwhome ;do a restore. jnc mwnoer ; hop if no error. mwyerr: jmp mwerr ; hop if error. mwnoer: mov cx,0 ; track zero. call mwseek ; seek to zero. jc mwyerr ; hop if error in seek. mov al,dmaread ; setup for read. mov byte ptr dmaop,al mov dx,(offset mwbuf) ; get buffer addr. call fndact ; figure physical addr. mov word ptr dmadma,dx ; physical addr a00-a15. mov byte ptr extdma,al ; physical addr a16-a19. mov al,0 mov byte ptr mwsectr,al ; sector 0 for badmap. mov al,2 ; head 2. mov byte ptr mwhead,al call rwsect ; go read the sector. jc mwerr ; exit if read error. mov bx,word ptr badptr ; pick up bad map pointer. mov si, (offset mwbuf) ; start at beginning of buffer. bad1: mov al,[si] ; pick up an entry from buffer. or al,al jz bade ; all done. mov al,[bx] ; pick up entry from bad map table. inc al jz overflo ; bad map overflow. mov al,byte ptr hrddsk ; put drive in table. mov [bx],al inc bx mov cx,8 xchg bx,si ;ldir ; move rest of info into table. ldir: mov al,[bx] ; get a byte. mov [si],al ; put a byte. inc bx ; bump pointers. inc si loop ldir ; dec cx, loop back till zero. xchg bx,si jmp bad1 bade: mov word ptr badptr,bx ;restore new bad map pointer. ; read track 0, head 0, sector 0 (bios parameter block). mapex: mov byte ptr mwhead,0 ; set head number = 0. mov byte ptr mwsectr,0 ; set sector number = 0. call rwsect ; read the sector. jc mwerr ; hop if error in reading sector. ; transfer parameter table to local storage. mov si,offset mwbuf+3 ; si = start of table in buffer. mov di,offset mwptab ; di = top of local table. mov cx,offset mwptabe-mwptab ; cx = length of table. mov ax,cs ; es = cs. mov es,ax cld ; direction = forward. rep movsb ; move it. mov si,offset mwdrv ; print manufacturer and model. call prtmsg mov si,offset crlfmsg ; print crlf. call prtmsg mov si,offset mwbuf ; return buffer address. clc ; no error flag. ret overflo: mov al,hrddsk ; al = drive letter. add al,'A' ; make ascii. mov byte ptr mapmsgdrive,al mov bx,(offset mapmsg) call prtmsg ; common error return routine. mwerr: mov cl,hrddsk ; cl = drive number. mov bx,hrdtrk ; bx = cylinder number. mov dh,mwhead ; dh = head number. mov dl,mwsectr ; dl = sector number. mov al,dmastat ; al = error status. or al,al ; clear zero flag. stc ; set error flag. ret ; fndact - Find actual (physical) address of an operand. ; Enter with offset in dx. ; Return with al=a16-19, dx=a00-a15 of physical address. fndact: mov ax,cs ; get base of segment. mov cl,4 ; shift count. rol ax,cl ; shift segment left 4. mov cl,al ; save shifted out top 4 bits. and cl,0fh ; mask for top 4 bits only. and ax,0fff0h add dx,ax ; add shifted segment to offset. mov al,0 ; clear. adc al,cl ; add top 4 & carry. ret ; al=a16-a19, dx=a00-a15. mapmsg db cr,lf,'Badmap overflow on ' mapmsgdrive db 0,':',0 badptr dw (offset badmap) ;pointer to next bad map entry. mapper endp ; Common Morrow hard disk read/write routine. ; Entry: es:di = transfer address ; cl = logical drive number (starting at 0) ; bx = cylinder number (starting at 0) ; dx = sector number (starting at 0) ; ah = 1 for read, 2 for write ; Exit: carry set if error. ; al = controller status if error. findalt proc near cld ; set auto = increment. ; set physical hard disk drive number. mov hrddsk,cl ; set hard disk drive number. ; set operation code and cylinder number. dec ah ; read = 0, write =1. mov mwop,ah ; set morrow winchester operation. mov hrdtrk,bx ; hrdtrk = cylinder number. mov hrdsec,dl ; hrdsec = sector requested. ; (head number figured later) ; figure physical address for data. mov ax,es ; ax = segment. mov cl,4 ; cl = shift count. rol ax,cl ; rotate segment left 4. mov cl,al ; cl = top 4 bits. and cl,0fh and ax,0fff0h ; ax without rotated bits. add di,ax ; di = a00-a15 of address. mov al,0 ; clear. adc al,cl ; al = a16-a19, di = a00-a15. mov dmadma,di ; dmadma = a00-a15. mov extdma,al ; extdma = a16-a19. ; read or write the sector requested. io_srt: call mwtran ; read or write the data. jc io_the ; hop if error. ret io_the: jmp mwerr ; handler error above. ; Common Morrow hard disk read/write routine. mwtran: mov cl,hrddsk ; get hard disk number. mov bx,(offset badmap) ;get badmap address. all: mov dx,word ptr badptr ;get bad map pointer. or al,al ;clear carry. xchg bx,dx sub bx,dx jz alt2 ;if the end. xchg bx,dx ; swap back. push bx ; save pointer. mov al,cl ;get drive #. cmp al,[bx] ;check it. jnz altmis ;nope. inc bx ;bump to track. mov al,byte ptr hrdtrk ;get lsb track. cmp al,[bx] ;check it. jnz altmis ;nope. inc bx ;bumpt to msb. mov al,byte ptr hrdtrk+1 ;get msb track. cmp al,[bx] ;check it. jnz altmis inc bx mov al,[bx] ;get map sector. dec al ; make sectors start at 0. mov ch,al ;put in b. mov al,byte ptr hrdsec ;get sector. cmp al,ch ;check it. jnz altmis ; the current sector was found in badmap, so seek to alternate. inc bx ;alternate found. inc bx ;bump to alternate. mov cl,[bx] ;get lsb alternate track. inc bx mov ch,[bx] ;get msb alternate track. push bx ;save bx. call mwseek ;do the seek. pop bx ;restore bx. jc alte ; exit if seek error. inc bx mov al,byte ptr mwop ;get the operation. mov byte ptr dmaop,al ;tell the controller. mov al,[bx] ;get the alternate sector. dec al ; make sectors start at 0. pop bx ;make stack right. jmp altfnd ;calculate head and do it. altmis: pop bx ;restore bx add bx,9 ; offset to next badmap entry. jmp all ;look some more. alte: pop bx ; make stack right. ret ; jump here if no alternate found. alt2: mov cx,word ptr hrdtrk call mwseek ;do the seek. jc mwexit ; exit if seek error. mov al,byte ptr mwop ;get the operation. mov byte ptr dmaop,al ;tell the controller. mov al,byte ptr hrdsec ;get the requested sector. ; jump here if an alternate is found. altfnd: mov cl,al ;put in c. mov dh,0 ;clear head counter. seclp: sub al,byte ptr mwspt ;subtract out a heads worth. jb secfnd ;jump if found. inc dh ;else bump to next head. mov cl,al ;update sector count. jmp seclp ;and do it again. secfnd: mov al,dh ;get head. mov byte ptr mwhead,al ;save it. mov al,cl ;get sector. mov byte ptr mwsectr,al ;save it also. rwsect: mov hdrtct,hdrtmax ; initialize retry counter. retry: call mwrdwr ;go to it. jnc mwexit ; exit if good read. dec hdrtct ; decrement error counter. jnz retry ; loop back till zero. stc ; set error (carry) flag. mwexit: ret ; ; these are the low level drivers for the morrow controller. ; reset the controller and do a home on hrddsk. mwhome: call mwreset ;reset controller. jc mwexit ; exit if error during reset. mov bx,(offset dmarg1) ;load arguments. mov al,byte ptr mwrsd ; al = recal step delay. mov byte ptr [bx],al ;load step delay (slow). inc bx mov al,byte ptr mwhd ; al = head settle delay. mov byte ptr [bx],al ;head settle delay. call mwissue ;do load constants again. jc mwexit ; exit if error during load constants. call mwptr ;get pointer to current cyl no. mov word ptr [bx],0 ; set up fake at cylinder 0. mov cx,2 ;seek in 2 cylinders. call mwseek jc mwexit ; exit if error during seek. call mwptr ;get pointer again. mov cx,mwcyl ; cx = number of cylinders. add cx,100 mov word ptr [bx],cx ; maximum head travel. mov cx,0 ;seek to cylinder 0. call mwseek ;recal slowly. jc mwexit ; exit if error during seek. jmp mwreset ;back to fast stepping mode. ; seek hrddsk to the track in cx. mwseek: call mwptr ;get track pointer. mov dx,[bx] ; dx = old track number. mov [bx],cx ; store new track number. mov word ptr dmarg0,cx ; set command channel cyl no. mov al,dh inc al mov bx,0ffffh jnz mwskip0 mov cl,stepout jmp mwskip mwskip0: mov bx,cx ; bx=new track, dx = old track. sub bx,dx ; bx = new track - old track. mov cl,stepout ; cl = step out code. jc mwsout ; hop if new track smaller. mov cl,stepin ; else cl = code to step in. jmp mwskip mwsout: call mwnegh1 mwskip: mov dmastep,bx ; save step count. mov al,hrddsk ; al = physical hard disk number. or al,cl mov dmasel0,al ; put it into command channel. mov al,dmanop ;no-operation command for channel. call mwprep ;step to proper track. mov dmastep,0 ; clear step counter. ret ; ; reset the controller and setup the default parameters. ; mwreset: out reset,al ;send reset pulse to controller. mov dx,(offset dmasel0) ;get command channel address. call fndact ;convert to physical address. push ds ;save data segment register. mov cx,0 ;put zero into DS register. mov ds,cx cli ; disable interrupts during startup. mov di,chan ; di = channel starting point. mov cx,[di] ; cx = contents of 50h,51h. mov [di],dx ; put in channel address low. mov ah,2[di] ; ah = contents of 52h. mov 2[di],ah ; put in channel address high. pop ds mov word ptr dmalnk,dx ;put link at end of channel. mov byte ptr dmalnk+2,al mov bx,(offset dmarg1) ;load arguments. mov al,byte ptr mwsd ; al = step delay. mov byte ptr [bx],al ;load step delay. inc bx mov al,byte ptr mwhd ; al = head settle delay. mov byte ptr [bx],al ;head settle delay. inc bx mov al,byte ptr mwss ; al = sector size code. mov byte ptr [bx],al ;sector size code. inc bx mov byte ptr [bx],dmalcon ;load constants command. call mwissue ; do load constants. push ds ; save data segment. mov dx,0 ; ds = 0. mov ds,dx mov di,chan ; di = starting channel addres. mov [di],cx ; restore whatever was at 50,51. mov 2[di],ah ; restore whatever was at 52. sti ; re-enable interrupts. pop ds ret ; ; do the opration specified by a register. ; mwprep: mov byte ptr dmaop,al ;save command channel op code. ; do the operation specified by dmaop. mwrdwr: mov cl,band1 mov bx,word ptr dmarg0 mov dx,mwwpc ; dx = write precomp cylinder. call mwhlcde jb mwpreps mov cl,band2 mov dx,mwwlc ; dx = write low current cyl. call mwhlcde jb mwpreps mov cl,band3 ;cylinder > low_current. mwpreps: mov al,byte ptr mwhead ;load head address. mov byte ptr dmarg2,al not al ;negative logic for controller. and al,7 ;3 bits of head select. rol al,1 ;shove over to bits 2-4. rol al,1 or al,cl ;add on low current and precomp. mov cl,al mov al,byte ptr hrddsk ;load drive address. or al,cl ;slap in drive bits. mov byte ptr dmasel1,al ;save in cmd channel head sel. mov al,byte ptr mwsectr ;load sector address. mov byte ptr dmarg3,al ; start the controller running and wait for completion. mwissue: mov dmastat,0 ; initialize status to busy. out attn,al ;start controller. mwwtlp: mov al,dmastat ; al = status of transfer. cmp al,0 ; is controller still busy? jz mwwtlp ; loop back if so. cmp al,0ffh ;test for successful operation. jz mwiret ; hop if successful completion. stc ; set carry for error indicator. mwiret: ret ;return no error (carry reset). ; return with bx pointing to the track word for hrddsk. mwptr: mov dl,byte ptr hrddsk ;get track address. shl dl,1 mov dh,0 mov bx,(offset mwtab) add bx,dx ret mwnegh1: mov al,bh not al mov bh,al mov al,bl not al mov bl,al inc bx ret mwhlmde: xchg bx,dx call mwnegh1 xchg bx,dx add bx,dx ret mwhlcde: mov al,bh cmp al,dh jz l_6 ret l_6: mov al,bl cmp al,dl ret l_7 equ $ ; bios stack area. tmpsp dw 0 dw 40 dup(?) ; 20 levels. tmp_tos dw 0 ; top of stack. ; Morrow 5-inch Winchester Controller Scratch Area. ; track address table for 4 physical drives. mwtab dw 0ffffh ; initialize to dw 0ffffh ; (way out on the end of disk.) dw 0ffffh dw 0ffffh mwop db 0 mwhead db 0 ; currently selected head. mwsectr db 0 ; currently selected sector. dmachan equ (offset $) ; 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 0 ; link adr to next cmd ch. db 0 ; extended address of link. badmap db badsiz*9 dup(?) ; bad sector map. db 0ffh ; end marker. mwiflg db 0 ; bad map initialization flag. ; these flags control the hard disk drivers. hrdsec db 0 ; logical sector number. hrdtrk dw 0 ; cylinder number. hrddsk db 0 ; hard disk drive number (0-3). mvdat db 0 ; move data flag. cerr db 0 ; offset strobe mask. chkmsk db 0 ; offset head number. chghd db 0 ; head change flag. mapptr dw 0 ; map buffer index. hdrtct db 0 ; retry counter. moropn db cr,lf,'Morrow Hard Disk Controller' db cr,lf,'Driver of 10-21-85' db cr,lf,'Copyright (c) 1985 Tarbell Electronics' crlfmsg db cr,lf,cr,lf,0 ; hard disk parameter table. mwptab equ $ mwid db 8 dup(?) ; oem name. mwbps dw 512 ; bytes per sector. db 2 ; sectors per allocation unit. dw 66h ; number of reserved sectors. db 2 ; number of fats. dw 2047 ; number of directory entries. dw 79ECh ; total number of sectors. db 0f8h ; hard disk media descriptor (f8). dw 3dh ; number of sectors occupied by each fat. mwspt dw 17 ; sectors per track. mwhpc dw 6 ; number of heads. mwhidn dw 66h ; number of hidden sectors. dw 5 dup(?) ; reserved for ibm. mwcyl dw 306 ; number of cylinders. mwwpc dw 128 ; cylinder to start write precompensation. mwwlc dw 128 ; cylinder to start write low current. mwsd dw 2 ; step delay. mwrsd dw 60 ; recalibrate step delay. mwhd dw 2 ; head settle delay. mwss dw 3 ; sector size code (3 shown for 512). mwdrv db 32 dup(?) ; place for drive manufacturer and model. dw 5 dup(?) ; reserved for Tarbell. dw 5 dup(?) ; reserved for Dealer. dw 5 dup(?) ; reserved for end user. mwptabe equ $ ; end of table. mwbuf db 512 dup(?) ; 512-byte buffer. findalt endp ; ********* END OF MORROW HARD DISK CONTROLLER DRIVER **************