;******************************************************************************
;*                                                                            *
;*  Includedatei fr SECMAIN.ASM                                              *
;*  liefert low-level-Routinen fr SecMain                                    *
;*  Version hier fr WD1002XT-kompatible Kontroller:                          *
;*  MFM, RLL (?)                                                              *
;*                                                                            *
;*  Historie: 28.12.1994                                                      *
;*            26. 3.1994 Formatierroutinen                                    *
;*             8. 4.1994 defekte Spuren markieren                             *
;*                                                                            *
;******************************************************************************

                section wd1002xt

;------------------------------------------------------------------------------
; Portadressen

Port_Base       equ     320h            ; prim„re Basisadresse
Port_Data       equ     Port_Base+0     ; Datenregister (R+W)
Port_Status     equ     Port_Base+1     ; Statusregister (R)
Port_Reset      equ     Port_Base+1     ; Reset ausl”sen (W)
Port_Config     equ     Port_Base+2     ; Jumper auslesen (R)
Port_Select     equ     Port_Base+2     ; Kontroller selektieren (W)
Port_IRQDRQ     equ     Port_Base+3     ; IRQ/DRQ-Leitungen freigeben (W)

;------------------------------------------------------------------------------
; Kommandocodes

Cmd_Diagnose    equ     0e4h            ; Kommando: Kontroller-Selbsttest
Cmd_GetStatus   equ     003h            ; Status letzter Operation lesen
Cmd_TestReady   equ     000h            ; Test, ob Laufwerk bereit
Cmd_Restore     equ     001h            ; Laufwerk rekalibrieren
Cmd_SetParams   equ     00ch            ; Laufwerksparameter setzen
Cmd_Seek        equ     00bh            ; Spur anfahren
Cmd_Read        equ     008h            ; Sektoren lesen
Cmd_Write       equ     00ah            ; Sektoren schreiben
Cmd_Verify      equ     005h            ; Sektoren auf Lesbarkeit prfen
Cmd_WriteBuffer equ     00fh            ; Sektorpuffer beschreiben
Cmd_FormatDisk  equ     004h            ; Laufwerk formatieren
Cmd_FormatTrack equ     006h            ; Spur formatieren
Cmd_FormatBad   equ     007h            ; Spur als defekt markieren

;------------------------------------------------------------------------------
; I/O-Bremse

IODelay         macro
                jmp     $+2
                endm

;------------------------------------------------------------------------------
; Puffer

CmdBufSize      equ     6               ; enth„lt Kommandoblock fr WD1002
CmdBuf          db      CmdBufSize dup (0)

StatBufSize     equ     4               ; enth„lt Statusinfo vom WD1002
StatBuf         db      StatBufSize dup (0)

GeomBufSize     equ     8               ; enth„lt Parametertabelle fr Laufwerk
GeomBuf         db      GeomBufSize dup (0)

;******************************************************************************
;* Kommandopuffer initialisieren                                              *
;******************************************************************************

                proc    InitCmdBuf

                push    ax              ; Register retten

                sub     ax,ax           ; mit Nullen initialisieren
                mov     word ptr[CmdBuf],ax
                mov     word ptr[CmdBuf+2],ax
                mov     ah,45h          ; Retry on, 70us Steprate
                mov     word ptr[CmdBuf+4],ax

                pop     ax              ; Register zurck

                ret

                endp

;******************************************************************************
;* einen Datenblock an den Kontroller schicken                                *
;*              In  :   ES:SI = Datenblock                                    *
;*                      CX = Anzahl Bytes                                     *
;*              Out :   C=1 bei Protokollfehler                               *
;******************************************************************************

                proc    SendBlock

                push    ax              ; Register retten
                push    cx
                push    dx
                push    si

                mov     dx,Port_Status
                jcxz    ZeroLoop        ; Nullschleife abfangen
                cld                     ; !!!
OutLoop:        in      al,dx           ; Status lesen
                btst    al,0            ; warten, bis REQ-Bit auf 1
                jz      OutLoop
                btst    al,1            ; IO-Bit muá 0 sein
                stc
                jnz     ErrEnd
                mov     dl,Lo(Port_Data); ein Byte auf Datenport ausgeben
                seges
                outsb
                mov     dl,Lo(Port_Status) ; zurck fr n„chsten Durchlauf
                loop    OutLoop
ZeroLoop:       clc                     ; Ende ohne Fehler
ErrEnd:
                pop     si              ; Register zurck
                pop     dx
                pop     cx
                pop     ax

                ret

                endp

;******************************************************************************
;* einen Datenblock vom Kontroller lesen                                      *
;*              In  :   ES:DI = Datenblock                                    *
;*                      CX = Anzahl Bytes                                     *
;*              Out :   C=1 bei Protokollfehler                               *
;******************************************************************************

                proc    RecvBlock

                push    ax              ; Register retten
                push    cx
                push    dx
                push    di

                mov     dx,Port_Status
                jcxz    ZeroLoop        ; Nullschleife abfangen
                cld                     ; !!!
InLoop:         in      al,dx           ; Status lesen
                btst    al,0            ; warten, bis REQ-Bit auf 1
                jz      InLoop
                btst    al,1            ; IO-Bit muá 1 sein
                stc
                jz      ErrEnd
                mov     dl,Lo(Port_Data); ein Byte von Datenport einlesen
                insb
                mov     dl,Lo(Port_Status) ; zurck fr n„chsten Durchlauf
                loop    InLoop
ZeroLoop:       clc                     ; Ende ohne Fehler
ErrEnd:
                pop     di              ; Register zurck
                pop     dx
                pop     cx
                pop     ax

                ret

                endp

;******************************************************************************
;* Status bilden                                                              *
;*              Out : C+AX = Status                                           * 
;******************************************************************************

                proc    BuildStatus

                push    dx              ; Register retten

                mov     dx,Port_Status  ; auf Datum warten
Loop:           in      al,dx
                btst    al,0            ; bis REQ=1
                jz      Loop
                btst    al,1            ; und IO=1
                jz      Loop
                mov     dl,Lo(Port_Data); CCB auslesen
                in      al,dx
                mov     ah,al           ; retten fr Fehlerabfrage
                and     al,2            ; Bit 1 ausmaskieren
                clc
                ljz      End             ; wenn = 0, kein Fehler und AL=0

                push    cx              ; zus„tzliche Register retten
                push    si
                push    di
                push    es

                call    InitCmdBuf      ; ansonsten Kommando absetzen, um
                mov     [CmdBuf],Cmd_GetStatus ; Status zu lesen
                and     ah,20h          ; Status fr korr. Laufwerk abfragen
                mov     [CmdBuf+1],ah
                mov     dx,Port_Status
WaitNBusy:      in      al,dx
                btst    al,3
                jnz     WaitNBusy
                mov     ax,ds           ; NICHT ExecCmd benutzen, da sonst
                mov     es,ax           ; Rekursion !
                lea     si,[CmdBuf]
                mov     cx,CmdBufSize
                mov     dl,Lo(Port_Select)
                out     dx,al
                call    SendBlock
                lea     di,[StatBuf]    ; 4 Statusbytes auslesen
                mov     cx,StatBufSize
                call    RecvBlock
                mov     dl,Lo(Port_Status); CCB nicht vergessen!!
Loop2:          in      al,dx
                btst    al,0            ; bis REQ=1
                jz      Loop2
                btst    al,1            ; und IO=1
                jz      Loop2
                mov     dl,Lo(Port_Data)
                in      al,dx
                mov     al,[StatBuf]    ; Fehlercode = 1.Byte,
                and     al,7fh          ; Bit 0..6
                stc                     ; Carry signalisiert Fehler
                pop     es              ; zus„tzliche Register zurck
                pop     di
                pop     si
                pop     cx

End:            mov     ah,0            ; MSB ohne Bedeutung
                pop     dx              ; Register zurck
                ret

                endp

;******************************************************************************
;* XT- in AT-Fehlerkode umsetzen                                              *
;* Eingabe:     AL = XT-Fehlerkode                                            *
;* Ausgabe:     C+AX = AT-Fehlerstatus                                        *
;******************************************************************************

                proc    TranslateStatus

                push    bx
                push    si

                mov     bl,al          ; alten Status sichern
                mov     bh,-1
                lea     si,[TransTable]
                cld
TransLoop:      lodsw                   ; einen Eintrag laden
                cmp     al,bh           ; Tabellenende?
                je      TransEnd
                cmp     al,bl           ; Treffer?
                jne     TransLoop       ; nein, weitersuchen
                mov     al,ah           ; bersetzten Code laden
                cmp     al,0            ; Code fr kein Fehler?
                clc
                je      Ende            ; ja, C=0
                jmp     TransErr        ; ansonsten C=1
TransEnd:       mov     al,04h          ; Aborted Command annehmen
TransErr:       stc                     ; Fehlerflag setzen

Ende:           pop     si              ; Register zurck
                pop     bx

                ret

TransTable:     db      00h,00h         ; kein Fehler
                db      02h,02h         ; kein Seek Complete-Signal
                db      03h,04h         ; Write Fault
                db      04h,04h         ; Laufwerk nicht bereit
                db      06h,02h         ; Spur 0 nicht gefunden
                db      08h,02h         ; Laufwerk positioniert noch
                db      11h,40h         ; unkorrigierbarer Datenfehler
                db      12h,01h         ; Adreámarke nicht gefunden
                db      15h,10h         ; Positionierfehler
                db      18h,00h         ; korrigierbarer Fehler (ignoriert)
                db      19h,80h         ; Spur als defekt markiert
                db      -1,-1           ; Tabellenende

                endp

;******************************************************************************
;* ein Kommando ausfhren                                                     *
;*              In  :   AL = Kommando                                         *
;******************************************************************************

                proc    ExecCmd

                push    cx              ; Register retten
                push    ax
                push    dx
                push    si
                push    es

                mov     [CmdBuf],al     ; Kommandokode in Datenblock einschreiben
                mov     dx,Port_Status  ; warten, bis Kontroller frei
WaitNBusy:      in      al,dx
                btst    al,3
                jnz     WaitNBusy
                mov     dx,Port_Select  ; Kontroller selektieren
                out     dx,al
                mov     ax,ds           ; Adresse Kommandoblock
                mov     es,ax
                lea     si,[CmdBuf]
                mov     cx,CmdBufSize   ; L„nge Kommandoblock
                call    SendBlock       ; Kommandoblock abschicken

                pop     es              ; Register zurck
                pop     si
                pop     dx
                pop     ax
                pop     cx

                ret

                endp

;******************************************************************************
;* Laufwerk und Sonderwerte in Kommandoblock einprogrammieren                 *
;*              In  :   AL = Laufwerk                                         *
;*                      AH = Kopf                                             *
;******************************************************************************

                proc    SetDriveEnv

                push    ax              ; Register retten

 
                shl     al,5            ; Laufwerksbit an Stelle 5
                or      al,ah
                mov     [CmdBuf+1],al   ; als 2. Byte im Kommandopuffer schreiben

                pop     ax              ; Register zurck

                ret

                endp

;******************************************************************************
;* Zylinder- und Sektorparameter an Kontroller ausgeben                       *
;*              In  :   BX = Startzylinder                                    *
;*                      CL = Sektorzahl/Interleave                            *
;*                      CH = Startsektor                                      *
;******************************************************************************

                proc    SetTransParams

                push    ax              ; Register retten

                mov     [CmdBuf+3],bl   ; LSB Startzylinder
                mov     al,bh           ; MSB Startzylinder
                shl     al,6            ; in Bit 6..7 schieben
                add     al,ch           ; Sektor dazu
                dec     al              ; !!! Sektoren ab 0
                mov     [CmdBuf+2],al
                mov     [CmdBuf+4],cl   ; Sektorzahl

                pop     ax             ; Register zurck
                ret

                endp

;******************************************************************************
;* Begráungsmeldung ausgeben:                                                *
;******************************************************************************

                globproc LowLevelIdent

                push    ax              ; Register retten

                PrMsg   IdentMsg

                pop     ax

                ret

IdentMsg        db      "Low-Level-Routinen fr WD1002S-WX2 und kompatible Controller",CR,LF,'$'

                endp

;******************************************************************************
;* Controller-Diagnose:                                                       *
;*              Out :  AL = Diagnosecode                                      *
;******************************************************************************

                globproc ContDiag

                push    cx              ; Register retten
                push    bx
                push    dx

                sub     cx,cx
                mov     dx,Port_Status  ; auf Status
BWait:          in      al,dx
                btst    al,3            ; 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:          call    InitCmdBuf      ; Kommando Selbsttest ausfhren
                mov     al,Cmd_Diagnose
                call    ExecCmd
                call    BuildStatus     ; Status holen

                cmp     al,5            ; WD1002 definiert nur Code 0..5
                jb      DoTrans
                mov     al,7            ; "undefinierter Code"
                jmp     End
DoTrans:        lea     bx,[TransTbl]   ; ansonsten umsetzen
                xlat

End:            pop     dx              ; Register zurck
                pop     bx
                pop     cx
                ret

TransTbl:       db      Diag_NoError    ; Code 0: kein Fehler
                db      Diag_ContError  ; Code 1: WD1010 fehlerhaft
                db      Diag_ECCError   ; Code 2: WD11C00 fehlerhaft
                db      Diag_SBufError  ; Code 3: Sektorpuffer defekt
                db      Diag_ProcError  ; Code 4: WD1015 RAM defekt
                db      Diag_ProcError  ; Code 5: WD1015 ROM defekt

 
                endp

;******************************************************************************
;* Laufwerk rekalibrieren, gleichzeitig Test, ob vorhanden                    *
;*              In  :  AL = Laufwerk                                          *
;*              Out :  C + AX = Status                                        *
;******************************************************************************

 
                globproc Recalibrate

                push    ax              ; Register retten
                push    cx

                call    InitCmdBuf      ; testen, ob Laufwerk bereit
                mov     ah,0            ; Kopf dafr unwichtig
                call    SetDriveEnv
                mov     dl,al           ; Laufwerksnummer retten, gleichzeitig
                mov     dh,0            ; Kopf auf 0 setzen
                mov     al,Cmd_TestReady
                call    ExecCmd
                call    BuildStatus
                jc      TotEnde         ; C=1 --> Ende mit Fehler

                call    InitCmdBuf      ; sanfte Tour: Spur 0 mit Seek anfahren
                mov     ax,dx
                call    SetDriveEnv
                mov     al,0            ; Zylinder lo=0
                mov     [CmdBuf+3],al
                inc     al              ; Zylinder Hi=0, Startsektor=1
                mov     [CmdBuf+2],al
                mov     al,Cmd_Seek
                call    ExecCmd
                call    BuildStatus
                jnc     TotEnde         ; kein Fehler, alles in Butter

                call    InitCmdBuf      ; ansonsten echtes Restore versuchen
                mov     ax,dx
                call    SetDriveEnv
                mov     al,Cmd_Restore
                call    ExecCmd
                call    BuildStatus
                call    TranslateStatus

TotEnde:        pop     dx              ; Register zurck
                pop     ax

                ret

                endp

;******************************************************************************
;* Dem Kontroller die Laufwerksgeometrie mitteilen                            *
;*              In  :   AL = Laufwerk                                         *
;*              Out :   C  = 1-->Fehler                                       *
;******************************************************************************

                globproc SetDriveParams

                push    cx              ; Register retten
                push    si
                push    es

                call    GetPTabAdr      ; Adresse Parametertabelle holen
                call    InitCmdBuf      ; Kommando anstoáen
                call    SetDriveEnv
                mov     al,Cmd_SetParams
                call    ExecCmd

 
                mov     ax,[di+DrPar_Cyls] ; Parametertabelle aufbauen
                xchg    ah,al
                mov     word ptr [GeomBuf],ax
                mov     al,[di+DrPar_Heads]
                mov     byte ptr[GeomBuf+2],al
                mov     ax,[di+DrPar_RedWr]
                xchg    ah,al
                mov     word ptr[GeomBuf+3],ax
                mov     ax,[di+DrPar_PrComp]
                xchg    ah,al
                mov     word ptr[GeomBuf+5],ax
                mov     al,[di+DrPar_ECCLen]
                mov     byte ptr[GeomBuf+7],al
                lea     si,[GeomBuf]    ; Block abschicken
                mov     cx,GeomBufSize
                mov     ax,ds
                mov     es,ax
                call    SendBlock
                call    BuildStatus
                call    TranslateStatus

                pop     es              ; Register zurck
                pop     si
                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    bx              ; Register retten
                push    cx
                push    dx
                push    di
                push    es

                call    InitCmdBuf      ; Puffer initialisieren
                call    SetDriveEnv
                call    SetTransParams
                mov     al,Cmd_Read     ; Lesen triggern
                PrChar  '1'
                call    ExecCmd
                PrChar  '2'

SecLoop:        mov     dx,Port_Status  ; warten, bis Request-Bit gesetzt
RLoop:          in      al,dx
                btst    al,0
                jz      RLoop
                btst    al,2            ; Daten oder Status ?
                jnz     ErrEnd          ; wenn jetzt Status, ist etwas schief gelaufen
                push    cx              ; ansonsten Sektor auslesen
                mov     cx,SecSize
                PrChar  '3'
                call    RecvBlock
                PrChar  '4'
                pop     cx
                dec     cl
                add     di,SecSize
                jnz     RLoop           ; und n„chsten Sektor verarbeiten

ErrEnd:         PrChar  '5'
                call    BuildStatus
                PrChar  '6'
                call    TranslateStatus

                pop     es              ; Register zurck
                pop     di
                pop     dx
                pop     cx
                pop     bx

                ret

                endp

;******************************************************************************
;* Sektor(en) verifizieren                                                    *
;*              In  :  AL = Laufwerk                                          *
;*                     AH = Startkopf                                         *
;*                     BX = Startzylinder                                     *
;*                     CL = Sektorzahl                                        *
;*                     CH = Startsektor                                       *
;*              Out :  C+AX = Fehlerstatus                                    *
;******************************************************************************

                globproc VeriSectors

                push    bx              ; Register retten
                push    cx
                push    dx

                call    InitCmdBuf      ; Puffer initialisieren
                call    SetDriveEnv
                call    SetTransParams
                mov     al,Cmd_Verify   ; Verifikation triggern
                call    ExecCmd

                call    BuildStatus
                call    TranslateStatus

                pop     dx              ; Register zurck
                pop     cx
                pop     bx

                ret

                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    bx              ; Register retten
                push    cx
                push    dx
                push    si
                push    es

 
                call    InitCmdBuf      ; Puffer initialisieren
                call    SetDriveEnv
                call    SetTransParams
                mov     al,Cmd_Write    ; Lesen triggern
                call    ExecCmd

SecLoop:        mov     dx,Port_Status  ; warten, bis Request-Bit gesetzt
WLoop:          in      al,dx
                btst    al,0
                jz      WLoop
                btst    al,2            ; Daten oder Status ?
                jnz     ErrEnd          ; wenn jetzt Status, ist etwas schief gelaufen
                push    cx              ; ansonsten Sektor auslesen
                mov     cx,SecSize
                call    SendBlock
                pop     cx
                dec     cl
                add     si,SecSize
                jnz     WLoop           ; und n„chsten Sektor verarbeiten

ErrEnd:         call    BuildStatus
                call    TranslateStatus

                pop     es              ; Register zurck
                pop     si
                pop     dx
                pop     cx
                pop     bx

                ret

                endp

;******************************************************************************
;* Laufwerk formatieren                                                       *
;*              In  :  AL = Laufwerk                                          *
;*                     AH = Interleave                                        *
;*              Out :  C+AX = Fehlerstatus                                    *
;******************************************************************************

                globproc FormatUnit

                push    bx              ; Register retten
                push    cx
                push    dx
                push    si
                push    di
                push    es
 
                mov     bx,ax           ; Interleave & Laufwerk retten
                
                mov     ax,ds           ; vorher noch den Sektorpuffer im
                mov     es,ax           ; Controller ausnullen
                lea     di,[SectorBuffer]
                mov     cx,SecSize/2
                sub     ax,ax
                rep     stosw
                call    InitCmdBuf
                mov     al,Cmd_WriteBuffer
                call    ExecCmd
                lea     si,[SectorBuffer]
                mov     cx,SecSize
                call    SendBlock
                call    BuildStatus
                jc      End             ; unwahrscheinlich, aber...

                call    InitCmdBuf      ; Puffer initialisieren
                mov     al,bl           ; Laufwerk wieder zurck
                mov     ah,0            ; Startkopf ist 0
                call    SetDriveEnv
                mov     [CmdBuf+4],bh   ; Interleave einschreiben
                mov     al,Cmd_FormatDisk ; Formatieren triggern
                call    ExecCmd

ErrEnd:         call    BuildStatus
End:            call    TranslateStatus

                pop     es              ; Register zurck
                pop     di
                pop     si
                pop     dx
                pop     cx
                pop     bx

                ret

                endp

;******************************************************************************
;* Spur formatieren                                                           *
;*              In  :  AL = Laufwerk                                          *
;*                     AH = Kopf                                              *
;*                     BX = Zylinder                                          *
;*                     CL = Interleave                                        *
;*              Out :  C+AX = Fehlerstatus                                    *
;******************************************************************************

                globproc FormatTrack

                push    bx
                push    cx

                call    InitCmdBuf      ; Parameter einschreiben
                call    SetDriveEnv
                mov     ch,1            ; Sektorinformation muá nur gltig sein
                call    SetTransParams
                mov     al,Cmd_FormatTrack
                call    ExecCmd
                call    BuildStatus

                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
                push    cx

                call    InitCmdBuf      ; Parameter einschreiben
                call    SetDriveEnv
                mov     cx,0103h        ; Sektorinformation muá nur gltig sein
                call    SetTransParams
                mov     al,Cmd_FormatBad
                call    ExecCmd
                call    BuildStatus

                pop     cx
                pop     bx
                ret

                endp

                endsection
