;****************************************************************************** ;* * ;* Includedatei fr SECMAIN.ASM * ;* liefert low-level-Routinen fr SecMain * ;* Version hier fr WD1003-kompatible Kontroller: * ;* MFM, RLL, ESDI, AT-Bus * ;* * ;* Historie: 28.12.1994 herberkopiert aus Hauptmodul * ;* 30.12.1994 LowLevelIdent * ;* 19. 1.1995 Workaround fr LCS6220 * ;****************************************************************************** section wd1003at Base1 equ 170h ; Basisadresse Task-File Base2 equ 370h ; Basisadresse Floppy-Teil Task_Data equ Base1+0 ; Datentransferregister (R/W) Task_Error equ Base1+1 ; genauerer Fehlercode (R) Task_PreComp equ Base1+1 ; erster Zylinder Pr„komp. (/4, nur W) Task_SecCnt equ Base1+2 ; Zahl zu transferierender Sektoren (R/W) Task_SecNum equ Base1+3 ; Startsektor (R/W) Task_CylLo equ Base1+4 ; Startzylinder Bit 0..7 (R/W) Task_CylHi equ Base1+5 ; Startzylinder Bit 8..n (R/W) Task_DrHead equ Base1+6 ; Laufwerk/Startkopf (R/W) Task_Status equ Base1+7 ; Status Laufwerk & Controller (R) Task_Command equ Base1+7 ; Kommando Controller (W) Task_FDiskReg equ Base2+6 ; Bit 3=1: >8 K”pfe Cmd_Restore equ 10h ; Kommando: Rekalibrieren Cmd_Seek equ 70h ; Kommando: Zylinder anfahren Cmd_Read equ 20h ; Kommando: Sektoren lesen Cmd_Write equ 30h ; Kommando: Sektoren schreiben Cmd_Format equ 50h ; Kommando: Spur formatieren Cmd_Verify equ 40h ; Kommando: Sektoren auf Lesbarkeit prfen Cmd_Diagnose equ 90h ; Kommando: Selbsttest Cmd_SetParams equ 91h ; Kommando: Laufwerksparameter setzen proc WriteParams mov [axsave],ax mov [cxsave],cx PrChar ' ' mov ax,bx mov cl,5 call WriteDec PrChar ' ' mov al,byte ptr[axsave+1] mov ah,0 mov cl,2 call WriteDec PrChar ' ' mov al,byte ptr[cxsave+1] mov ah,0 mov cl,2 call WriteDec PrChar ' ' mov al,byte ptr[cxsave] mov ah,0 mov cl,2 call WriteDec PrChar ' ' mov ax,es mov cl,4 call WriteHex PrChar ':' mov ax,bp mov cl,4 call WriteHex mov ax,[axsave] mov cx,[cxsave] ret cxsave dw ? axsave dw ? endp ;****************************************************************************** ;* Workaround fr LCS6220: Wird direkt nach dem Einschalten ein Seek ausge- * ;* fhrt, gibt der Kontroller f„lschlicherweise Daten aus und blockiert alle * ;* weiteren Kommandos. Diese Routine r„umt einfach den Puffer leer... * ;****************************************************************************** proc ClearBuffer push dx ; Register retten push ax RdLoop: mov dx,Task_Status ; Bit 3 noch gesetzt ? in al,dx btst al,3 jz RdLoopEnd ; nein --> fertig mov dx,Task_Data in ax,dx jmp RdLoop RdLoopEnd: pop ax ; Register zurck pop dx ret endp ;****************************************************************************** ;* Interleave-Tabelle berechnen * ;* In : AL = Sektorzahl * ;* AH = Interleave * ;* DH = Bad-Flag * ;****************************************************************************** proc SetInterleaveBuffer pusha ; Register retten push es push ax ; Sektorpuffer initialisieren mov ax,ds mov es,ax sub ax,ax lea di,[SectorBuffer] mov cx,SecSize/2 cld rep stosw pop ax sub di,di ; DI=Adresse in Puffer=(phys. Sektor-1)*2 mov dl,dh ; DL = Bad-Flag mov dh,1 ; DH=log. Sektornummer mov cl,al ; CX=Schleifenz„hler mov ch,0 mov bl,al ; Sektorzahl*2 nach BX mov bh,0 add bx,bx mov si,ax ; Interleave*2 nach SI shr si,8 add si,si InterLoop: cmp byte ptr SectorBuffer[di],0 ; Eintrag frei ? je Inter_FreeFound ; ja, beenden add di,2 ; nein, linear weitersuchen cmp di,bx jb InterLoop mov di,0 ; Wrap-Around bercksichtigen jmp InterLoop Inter_FreeFound:mov word ptr SectorBuffer[di],dx ; Sektor einschreiben add di,si ; Interleave-Sprung dazu cmp di,bx ; Modulo Sektorzahl jb Inter_NoWrap sub di,bx Inter_NoWrap: inc dh ; n„chster log. Sektor loop InterLoop pop es ; Register zurck popa ret endp ;****************************************************************************** ;* Laufwerk und Sonderwerte einprogrammieren * ;* In : AL = Laufwerk * ;* AH = Kopf * ;****************************************************************************** proc SetDriveEnv push di ; Register retten push dx mov dx,ax ; Laufwerk/Kopf retten call GetPTabAdr ; Tabellenadresse holen mov al,dl ; Laufwerk und Kopf zusammenbauen shl al,4 or al,dh or al,0a0h mov dx,Task_DrHead out dx,al mov ax,[di+DrPar_PrComp] ; Startzylinder Pr„kompensation shr ax,2 mov dl,Lo(Task_PreComp) out dx,al mov al,[di+DrPar_CByte] ; Wert fr Fixed Disk Register mov dx,Task_FDiskReg out dx,al call WaitBusy clc ; Ende ohne Fehler pop dx pop di ret endp ;****************************************************************************** ;* Zylinder- und Sektorparameter an Kontroller ausgeben * ;* In : BX = Startzylinder * ;* CL = Sektorzahl * ;* CH = Startsektor * ;****************************************************************************** proc SetTransParams push dx ; Register retten mov dx,Task_CylLo ; Startzylinder programmieren mov al,bl out dx,al mov dx,Task_CylHi ;*** mov al,bh out dx,al mov dx,Task_SecNum ; Startsektor... ;*** mov al,ch out dx,al mov dx,Task_SecCnt ; ...und Sektorzahl ;*** mov al,cl out dx,al pop dx ; Register zurck ret endp ;****************************************************************************** ;* warten, bis Controller bereit oder Fehler * ;* Out : AL = letzter Status * ;****************************************************************************** proc WaitBusy push dx ; Register retten mov dx,Task_Status ; auf Statusregister Loop: in al,dx ; Status lesen btst al,7 ; Bit 7 noch gesetzt ? jnz Loop ; ja--> weiter pollen pop dx ; Register zurck ret endp ;****************************************************************************** ;* warten, bis Laufwerk bereit * ;* Out : AL = letzter Status * ;****************************************************************************** proc WaitDrive push dx ; Register retten mov dx,Task_Status ; auf Statusregister Loop: in al,dx ; Status lesen btst al,7 ; Bit 7 = 0 ? ( Controller Busy ) jnz Loop btst al,6 ; Bit 6 = 1 ? ( Drive not Ready ) jz Loop btst al,4 ; Bit 4 = 1 ? ( Seek not complete ) jz Loop pop dx ret endp ;****************************************************************************** ;* warten, bis Datentransfer erforderlich * ;* Out : AL = letzter Status * ;* C = 1, falls Fehler * ;****************************************************************************** proc WaitData push dx ; Register retten mov dx,Task_Status ; auf Statusregister Loop: in al,dx ; Status lesen btst al,7 ; Bit 7 = 0 ? jnz Loop btst al,3 ; Bit 3 = 1 ? jz Loop pop dx ; Register zurck ret endp ;****************************************************************************** ;* Status bilden * ;* Out : C+AX = Status * ;****************************************************************************** proc BuildError push dx ; Register retten mov dx,Task_Status ; Statusregister lesen in al,dx mov ah,al btst ah,0 ; Fehlerflag gesetzt ? clc jz End ; kein Fehler mov dx,Task_Error ; ja: Error-Register lesen ;*** in al,dx stc End: pop dx ; Register zurck ret endp ;****************************************************************************** ;* Begráungsmeldung ausgeben: * ;****************************************************************************** globproc LowLevelIdent push ax ; Register retten PrMsg IdentMsg pop ax ret IdentMsg db "Low-Level-Routinen fr WD1003-WAH und kompatible Controller",CR,LF,'$' endp ;****************************************************************************** ;* Controller-Diagnose: * ;* Out : AL = Diagnosecode * ;****************************************************************************** globproc ContDiag push cx ; Register retten push dx mov dx,Task_Status ; das erste Mal mit Timeout warten sub cx,cx BWait: in al,dx btst al,7 ; auf NOT BUSY warten loopnz BWait ; oder bis 64K Durchl„ufe durch or cx,cx ; Timeout ? jne NTOut mov al,Diag_Timeout ; ja: Fehlercode setzen... jmp End ; ...und Feierabend NTOut: mov al,CMD_Diagnose ; Selbsttest starten mov dl,Lo(Task_Command) out dx,al call WaitBusy ; auf Fertigstellung warten mov dl,Lo(Task_Error) ; Ergebnis laden in al,dx End: pop dx ; Register zurck pop cx ret endp ;****************************************************************************** ;* Dem Kontroller die Laufwerksgeometrie mitteilen * ;* In : AL = Laufwerk * ;* Out : C = 1-->Fehler * ;****************************************************************************** globproc SetDriveParams push di ; Register retten push dx mov dl,al ; Laufwerk retten call GetPTabAdr ; Adresse Parametertabelle holen call WaitBusy ; Kontroller muá frei sein mov al,dl ; Kopfzahl/Laufwerk vorbesetzen shl al,4 mov ah,[di+DrPar_Heads] dec ah ; Maximalnummer anstelle Gesamtzahl or al,ah or al,0a0h mov dx,Task_DrHead out dx,al mov dl,Lo(Task_SecCnt) ; Sektorzahl setzen mov al,[di+DrPar_NSecs] out dx,al mov dl,Lo(Task_Command) ; Parameter bertragen mov al,Cmd_SetParams out dx,al call WaitBusy ; auf Fertigstellung warten clc ; Ende ohne Fehler pop dx pop di ret endp ;****************************************************************************** ;* Laufwerk rekalibrieren, gleichzeitig Test, ob vorhanden * ;* In : AL = Laufwerk * ;* Out : C + AX = Status * ;****************************************************************************** globproc Recalibrate push cx ; Register retten push dx mov cx,ax ; Laufwerk retten call WaitBusy ; warten, bis Controller frei mov dx,Task_DrHead ; Laufwerk eintragen mov al,cl shl al,4 add al,0a0h out dx,al mov dl,Lo(Task_Status) ; Laufwerk muss jetzt bereit sein, in al,dx ; da sich einige Kontroller sonst im and al,50h ; folgenden aufh„ngen, falls cmp al,50h ; keine Platte angeschlossen ist. stc ; falls nicht bereit, Fehler simulieren mov al,4 ; "Aborted Command" jne TotEnde mov al,0 mov dl,Lo(Task_CylLo) ; erstmal auf die sanfte Tour: out dx,al ; Spur 0 anfahren mov dl,Lo(Task_CylHi) out dx,al mov dl,Lo(Task_Command) mov al,Cmd_Seek out dx,al call WaitBusy call BuildError jnc Ende ; wenn OK: fertig call ClearBuffer ; falls sich der Longshine verheddert... mov dl,Lo(Task_Command) ; 2. Anlauf: echtes Restore mov al,Cmd_Restore out dx,al call WaitBusy ; auf Controller warten Ende: call BuildError ; Status einlesen TotEnde: pop dx ; Register zurck pop cx ret endp ;****************************************************************************** ;* Sektor(en) lesen * ;* In : AL = Laufwerk * ;* AH = Startkopf * ;* BX = Startzylinder * ;* CL = Sektorzahl * ;* CH = Startsektor * ;* ES:DI = Zeiger auf Datenpuffer * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc ReadSectors push si ; Register sichern push dx push bp if debug PrChar 'R' mov bp,di call WriteParams endif sub bp,bp ; Fehlerz„hler auf 0 Retry: push ax ; Parameter sichern push bx push cx push di mov si,ax ; Laufwerk/Kopf retten call WaitBusy ; warten, bis Ruhe im Wald mov ax,si call SetDriveEnv ; Laufwerk jetzt schon setzen, damit ; korr. Ready-Signal abgefragt wird call WaitDrive ; bis Laufwerk bereit call SetTransParams ; restliche Parameter ausgeben mov ch,0 ; Sektorzahl nach SI mov si,cx mov dx,Task_Command ; Kommando triggern mov al,Cmd_Read out dx,al mov dx,Task_Data ; Vorbereitung fr INSW cld Loop: call WaitBusy ; auf gelesenen Sektor warten btst al,0 ; Fehler ? jnz Again ; -->neu aufsetzen call WaitData btst al,0 jnz Again call WaitDrive btst al,0 jnz Again mov cx,SecSize/2 ; Daten transferieren rep insw ; bagger, schaufel dec si ; n„chster Sektor jnz Loop End: pop di ; Parameter nicht mehr gebraucht pop cx pop bx pop ax Term: if debug PrChar CR PrChar LF endif call BuildError pop bp pop dx pop si ret Again: inc bp ; Fehlerz„hler rauf cmp bp,MaxRetry ; zu oft aufgetreten ? jae End pop di ; nein: Parameter vom Stack pop cx pop bx pop ax mov si,ax ; Laufwerk retten call Recalibrate ; auf Spur 0 zurck jc Term ; bei erneutem Fehler Abbruch mov ax,si call SetDriveParams ; Parameter neu initialisieren mov ax,si jmp Retry ; neuer Versuch endp ;****************************************************************************** ;* Sektor(en) verifizieren * ;* In : AL = Laufwerk * ;* AH = Startkopf * ;* BX = Startzylinder * ;* CL = Sektorzahl * ;* CH = Startsektor * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc VeriSectors push si ; Register sichern push dx push bp if debug PrChar 'V' mov bp,0 call WriteParams endif sub bp,bp ; Fehlerz„hler auf 0 Retry: push ax ; Parameter sichern push bx push cx mov si,ax ; Laufwerk/Kopf retten call WaitBusy ; warten, bis Ruhe im Wald mov ax,si call SetDriveEnv ; Laufwerk jetzt schon setzen, damit ; korr. Ready-Signal abgefragt wird call WaitDrive ; bis Laufwerk bereit call SetTransParams ; restliche Parameter ausgeben mov dx,Task_Command ; Kommando triggern mov al,Cmd_Verify out dx,al call WaitBusy ; auf Fertigstellung warten mov cx,16 ; einige Kontroller brauchen DelStat: loop DelStat ; etwas fr das Fehlerflag mov dx,Task_Status in al,dx btst al,0 ; Fehler ? jnz Again ; -->neu aufsetzen call WaitDrive btst al,0 jnz Again Ende: pop cx ; Parameter nicht mehr gebraucht pop bx pop ax Term: if debug PrChar CR PrChar LF endif call BuildError pop bp pop dx pop si ret Again: inc bp ; Fehlerz„hler rauf cmp bp,MaxRetry ; zu oft aufgetreten ? jae Ende pop cx ; nein: Parameter vom Stack pop bx pop ax mov si,ax ; Laufwerk retten call Recalibrate ; auf Spur 0 zurck jc Term ; bei erneutem Fehler Abbruch mov ax,si call SetDriveParams ; Parameter neu initialisieren mov ax,si jmp Retry ; neuer Versuch mov ax,si endp ;****************************************************************************** ;* Sektor(en) schreiben * ;* In : AL = Laufwerk * ;* AH = Startkopf * ;* BX = Startzylinder * ;* CL = Sektorzahl * ;* CH = Startsektor * ;* ES:SI = Zeiger auf Datenpuffer * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc WriteSectors push di ; Register sichern push dx push bp if debug PrChar 'W' mov bp,si call WriteParams endif xor bp,bp ; Fehlerz„hler auf 0 Retry: push ax ; Parameter sichern push bx push cx push si mov di,ax ; Laufwerk/Kopf retten call WaitBusy ; warten, bis Ruhe im Wald mov ax,di call SetDriveEnv ; Laufwerk jetzt schon setzen, damit ; korr. Ready-Signal abgefragt wird call WaitDrive ; bis Laufwerk bereit call SetTransParams ; restliche Parameter ausgeben mov ch,0 ; Sektorzahl nach DI mov di,cx mov dx,Task_Command ; Kommando triggern mov al,Cmd_Write out dx,al mov dx,Task_Data ; Vorbereitung fr OUTSW cld Loop: call WaitBusy ; auf Datenbereitschaft warten btst al,0 ; Fehler ? jnz Again ; ja-->neu aufsetzen call WaitData btst al,0 jnz Again call WaitDrive btst al,0 jnz Again mov cx,SecSize/2 ; Daten transferieren seges rep outsw ; bagger, schaufel call WaitBusy ; warten, bis Transfer fertig btst al,0 jnz Again dec di ; n„chster Sektor jnz Loop End: pop si ; Parameter nicht mehr gebraucht pop cx pop bx pop ax Term: if debug PrChar CR PrChar LF endif call BuildError pop bp pop dx pop di ret Again: inc bp ; Fehlerz„hler rauf cmp bp,MaxRetry ; zu oft aufgetreten ? jae End pop si ; nein: Parameter vom Stack pop cx pop bx pop ax mov di,ax ; Laufwerk retten call Recalibrate ; auf Spur 0 zurck jc Term ; bei erneutem Fehler Abbruch mov ax,di call SetDriveParams ; Parameter neu initialisieren mov ax,di jmp Retry ; neuer Versuch endp ;****************************************************************************** ;* Laufwerk formatieren * ;* In : AL = Laufwerk * ;* AH = Interleave * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc FormatUnit push bx push cx push dx push si push di push bp mov bx,ax ; Interleave retten PrMsg ESCMsg mov ax,bx call GetPTabAdr ; Parametertabelle->DI mov ax,bx mov dh,0 ; gute Spuren schreiben mov al,[di+DrPar_NSecs] call SetInterleaveBuffer ; Tabelle berechnen mov ax,bx call Recalibrate ; Kontroller reinitialisieren jc Fin mov ax,bx mov bp,[di+DrPar_Cyls] ; Zylinderz„hler in BP (abw„rts) dec bp mov dl,al ; Laufwerk in DL cld CylLoop: mov dh,0 ; Kopf in dh HeadLoop: call WaitBusy ; warten, bis WD1003 frei call WriteCoords ; Bildschirmausgabe mov ax,dx ; Laufwerk+Kopf progr. call SetDriveEnv mov bx,bp ; Zylinder+Sektor progr. mov cl,[di+DrPar_NSecs] mov ch,1 call SetTransParams mov bx,dx mov dx,Task_Command mov al,Cmd_Format out dx,al call WaitData ; Sektortabelle schicken mov cx,SecSize/2 mov dx,Task_Data lea si,[SectorBuffer] rep outsw call WaitBusy ; warten, bis Kontroller fertig shr al,1 ; Fehlerbit in Carry laden jnc GoOn PrMsg ErrorMsg ; falls Fehler, Meldung ausgeben mov dx,bx call WriteCoords PrChar LF GoOn: mov dx,bx ; Laufwerk und Kopf zurck call BreakOnESC ; will der Benutzer abbrechen ? jc UserTerm ; ja, Abbruch inc dh ; n„chster Kopf cmp dh,[di+DrPar_Heads] jb HeadLoop dec bp ; n„chster Zylinder jns CylLoop TermLoop: mov al,dl ; damit die Seek-Rate wieder stimmt call Recalibrate Fin: push ax ; Fehlerstatus halten pushf PrChar LF popf ; Fehlerstatus zurck pop ax pop bp pop di pop si pop dx pop cx pop bx ret UserTerm: mov al,dl ; Abbruch durch Benutzer: noch schnell call Recalibrate ; rekalibrieren jc Fin ; Fehler dabei ? stc ; Ansonsten Sonderfehlercode mov al,DErr_UserTerm jmp Fin WriteCoords: push ax ; Kopf/Zylinder ausgeben push cx PrMsg CylMsg mov ax,bp mov cl,6 call WriteDec PrMsg HeadMsg mov al,dh mov ah,0 mov cl,3 call WriteDec PrChar CR pop cx pop ax ret ESCMsg: db "Abbruch mit ",CR,LF,'$' CylMsg: db "Zylinder $" HeadMsg: db ", Kopf $" ErrorMsg: db "Formatierfehler auf $" endp ;****************************************************************************** ;* Spur formatieren * ;* In : AL = Laufwerk * ;* AH = Kopf * ;* BX = Zylinder * ;* CL = Interleave * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc FormatTrack push bx ; Register retten push cx push dx push si push di push bp mov bp,ax ; Laufwerk & Kopf retten call Recalibrate ; Laufwerk sicherheitshalber rekalibrieren mov ax,bp call GetPTabAdr ; Sektortabelle aufbauen mov dh,0 ; fehlerhafte Sektoren schreiben mov ah,cl ; Interleave vorgeben mov al,[di+DrPar_NSecs] call SetInterleaveBuffer mov ax,bp ; Laufwerk und Kopf zurck call SetDriveEnv ; in Kontroller einprogrammieren mov cl,[di+DrPar_NSecs] ; Sektor& Zylinder einschreiben mov ch,1 call SetTransParams mov dx,Task_Command ; Kommando schicken mov al,Cmd_Format out dx,al call WaitData ; Sektortabelle schicken mov cx,SecSize/2 mov dx,Task_Data lea si,[SectorBuffer] rep outsw call WaitBusy ; warten, bis Kontroller fertig jc Fin ; Abbruch bei Fehler mov ax,bp ; Laufwerk nochmal rekalibrieren call Recalibrate ; damit Steprate stimmt Fin: pop bp pop di pop si pop dx pop cx pop bx ret endp ;****************************************************************************** ;* Spur als defekt markieren * ;* In : AL = Laufwerk * ;* AH = Kopf * ;* BX = Zylinder * ;* Out : C+AX = Fehlerstatus * ;****************************************************************************** globproc MarkBad push bx ; Register retten push cx push dx push si push di push bp mov bp,ax ; Laufwerk & Kopf retten call Recalibrate ; Laufwerk sicherheitshalber rekalibrieren mov ax,bp call GetPTabAdr ;Sektortabelle aufbauen mov dh,80h ; fehlerhafte Sektoren schreiben mov ah,3 ; Interleave ist ziemlich egal... mov al,[di+DrPar_NSecs] call SetInterleaveBuffer mov ax,bp ; Laufwerk und Kopf zurck call SetDriveEnv ; in Kontroller einprogrammieren mov cl,[di+DrPar_NSecs] ; Sektor& Zylinder einschreiben mov ch,1 call SetTransParams mov dx,Task_Command ; Kommando schicken mov al,Cmd_Format out dx,al call WaitData ; Sektortabelle schicken mov cx,SecSize/2 mov dx,Task_Data lea si,[SectorBuffer] rep outsw call WaitBusy ; warten, bis Kontroller fertig jc Fin ; Abbruch bei Fehler mov ax,bp ; Laufwerk nochmal rekalibrieren call Recalibrate ; damit Steprate stimmt Fin: pop bp pop di pop si pop dx pop cx pop bx ret endp endsection