;******************************************************************************
;*                                                                            *
;*             Universeller PC-/MSDOS-Diskettentreiber                        *
;*                                                                            *
;* Programmname       : unidrv.asm                                            *
;* Ersteller          : Martin Ernst                                          *
;* Erweitert          : Peter K”hlmann                                        *
;* Erstellungsdatum   : 11.03.86                                              *
;* letzte Žnderung am : 16.08.87                                              *
;* letzter Test am    : xx.08.87                                              *
;* Versionsnummer     : 3.0                                                   *
;* Revisionsnummer    : 6.0                                                   *
;*                                                                            *
;******************************************************************************

                page    72,132
                title   PC-/MSDOS-Universal-Treiber


false           equ     0
true            equ     not false
v20             equ     true     ; true fr Rechner mit V20/30,
;v20            equ     false    ; 80188/86 oder 80286

length          equ     13h

.186
ausgabe macro   message
        mov     dx,offset message
        mov     ah,9
        int     21h
        endm

;
;
; Dieser Treiber ist einsetzbar auf PCs, ATs (und deren kompatiblen)
; und auf dem c't86 mit FDC- und IFC-Karte.
;
; Der Treiber wurde mit allen PCDOS-Versionen von 2.0 bis 3.3 getestet.
; šber den Treiber kann nicht formatiert werden. Microsoft verlangt zwar
; von allen Treibern, daá diese selbst formatieren k”nnen, benutzt selbst
; dazu aber bei seinen Treibern undokumentierte DOS-Aufrufe.
;
; Die Einbindung erfolgt ber CONFIG.SYS mit
;
; device=laufwerk:\pfad\treibername.sys tracks phys.drive [opt1] [opt2]
;
; Wird fr tracks keine der beiden m”glichen Spurzahlen 40 oder 80 angegeben,
; so wird defaultm„áig 40 eingestellt (sinnvoll nur fr Anwender von auf
; reinen 80-Spur-Betrieb gepatchte DOS-Versionen). Die Angabe 40 erzeugt kein
; Doublestepping auf 80-Spur-Drives!!!
;
; Bei Angabe 80 bedient der Treiber grunds„tzlich nur 80-Spur-Formate,
; wobei die Formate ausschlieálich am Media-Byte zu Beginn der ersten FAT
; erkannt werden. Es wird bewuát nicht der Boot-Sektor ausgewertet, wie es DOS
; ab Version 3.2 macht! Das ist wichtig, denn nur so sind etwa Atari-ST-
; Disketten oder welche ohne Boot-Sektor fr diese DOS-Versionen lesbar.
;
; Bei Installation (80) auf einem AT lassen sich ber den Treiber auch
; 80-Spur-Disketten mit 720 KByte auf einem Multifunktionslaufwerk schreiben
; und lesen, es wird also das double-stepping ausgeschaltet.
;
; "phys.drive" teilt dem Treiber mit, fr welches physikalische Laufwerk er
; verantwortlich ist (gez„hlt wird ab 0 frs erste Laufwerk).
;
; Da mehrere verschiedene Formate mit dem Media-Byte F9h gekennzeichnet werden,
; muá mit N (Nixdorf), O (Olivetti) und S (Siemens) als Option das jeweils
; zu untersttzende Format bei diesem Media-Byte angegeben werden. Keine
; Option oder "I" heiát, daá der Default-Wert fr das IBM 720-KB-Format
; genommen wird.
;
; Die Option "A" ist nur fr den c't86 mit der IFC-Karte gedacht und
; erm”glicht den Betrieb mit MF-Laufwerken im High-Density-Modus (1,2 MByte).
; c't86-User brauchen also keine separate Version ATDRV.SYS mehr.
;
; Das gleiche Problem, n„mlich dasselbe Media-Byte F7h zu Beginn der FAT bei
; ein- und doppelseitigen Disketten, tritt beim Atari ST auf. Um hier eine
; Unterscheidung treffen zu k”nnen, kann als letzte Angabe in der Zeile
; 1 oder 2 stehen: fr 1-seitig oder 2-seitig. Wird kein Parameter angegeben,
; wird als Default doppelseitig angenommen.
;
; Zur Sicherheit noch die folgende Anmerkung:
; Der Treiber kann im 80er-Modus immer ALLE in den DPBs angegebenen 80-Spur-
; Formate AUTOMATISCH erkennen und ber DASSELBE logische Laufwerk bedienen,
; die nicht ber die mehrdeutigen Media-Bytes F9 und F7 erkannt und dann
; anhand von Parametern unterschieden werden mssen. Lediglich von den mit
; F7 und F9 gekennzeichneten Formaten ist dann immer nur jeweils eines pro
; Treiberaufruf und logischem Laufwerksbuchstaben verfgbar.
;
;
;-----------------------------------------------------------------------------

; diese Definition ist der Einsprung in die Monitorfunktionen des c't 86

monitoreprom            segment at 0f000h
                        org     0e000h
monitor                 label   far
monitoreprom            ends


code            segment para public
assume          cs:code,ds:code,es:code,ss:code

;=============================================================================

dskdev: dw      -1,-1
        dw      0000100000000000b      ;Bit 11 von Attribut-Word gesetzt
        dw      strategy
        dw      dskint
drvmax  db      1

ptrsav          dd      0       ;hier wird der Zeiger auf die Anforderungs-
                                ;kopfzeile gesichert
        db      3 dup(?)

;-----------------------------------------------------------------------------

dsktbl: dw      dskinit          ; "Init Device-Treiber"
        dw      mediac           ; "Check Media Type"
        dw      getbpb           ; "Get BIOS Parameter Block " des
                                 ; selektierten "Media Type"
        dw      cmderr           ; "I/O-Control Input" (nicht implementiert)
        dw      dskred           ; "Read Data"
        dw      busexit          ; "Non Destructive Read Data" (n.i.)
        dw      exit             ; "Input Status"
        dw      exit             ; "Input Flush Buffer"
        dw      dskwrt           ; "Write Data"
        dw      dskwrv           ; "Write Data" mit Verify-Funktion
        dw      exit             ; "Output Status"
        dw      exit             ; "Output Flush Buffer"
        dw      exit             ; "I/O-Control Output"
        dw      exit             ; "open"
        dw      exit             ; "close"
        dw      exit             ; "Removable Media" --> gleich nach "exit",
                                 ; kein "busy", da diskette

;-----------------------------------------------------------------------------

iodat   struc
cmdlen  db      ?                ; L„nge der Tabelle
unit    db      ?                ; Einheiten-Code
cmd     db      ?                ; Befehls-Code
status  dw      ?                ; Status der Operation
        db      8 dup (?)
media   db      ?                ; Media Descriptor Byte
trans   dd      ?                ; Transfer Adresse
count   dw      ?                ; Anzahl der Bl”cke oder Character,
                                 ; die bertragen werden sollen
start   dw      ?                ; erster Block, der transferiert werden soll
drive   db      ?                ; zugeordnete Drive-Bezeichnung
iodat   ends

;-----------------------------------------------------------------------------

bpbs    struc
        db      13 dup (?)
bpb1    db      ?                ; Struktur fr die Rcklieferung von Werten
bpb2    dw      ?                ; ber die Anforderungskopfzeile
        dw      ?
bpb3    dw      ?
        dw      ?
bpbs    ends

;-----------------------------------------------------------------------------

dbp     struc
secsize dw      ?                ; Sektorgr”áe (512 bytes)
alloc   db      ?                ; Anzahl Sektoren pro Cluster
ressec  dw      ?                ; Anzahl reservierte Sektoren
fats    db      ?                ; Anzahl der FATs
maxdir  dw      ?                ; Anzahl der Directory-Eintr„ge
sectors dw      ?                ; Gesamtzahl der Sektoren
mediaid db      ?                ; Media Descriptor Byte
fatsec  dw      ?                ; Anzahl der Sektoren pro FAT
sectrk  dw      ?                ; Anzahl der Sektoren pro Track
                                 ; (nicht Zylinder!)
koepfe  dw      ?                ; Anzahl der K”pfe (bzw. Seiten bei Floppy)
hidden  dw      0                ; versteckte (hidden) Sektoren
dbp     ends


; IBM PS/2, 720 KByte, 80 Spuren
ibmps   dbp     <512,2,1,2,112,1440,0f9h,3,9,2>

; Philips YES
philips dbp     <512,2,1,2,176,1440,0feh,3,9,2>

; DEC Rainbow (MS-PCDOS), 80 Spuren
decrain dbp     <512,1,20,2,96,800,0fah,3,10,1>

; ct86, 80 Spuren, einseitig
ddrive2 dbp     <512,1,1,2,112,720,0fch,4,9,1>

; ct86 80 Spuren, doppelseitig
ddrive1 dbp     <512,2,1,2,224,1440,0fdh,4,9,2>

; Atari ST  (default: doppelseitig, 80 Spuren)
atari   dbp     <512,2,1,2,112,1440,0f7h,5,9,2>

inittab1:       dw      ibmps.secsize

;============================================================================

stratp  proc    far
; Registerbelegung beim Aufruf:
; bx = Offset  der Anforderungszeile
; es = Segment der Anforderungszeile

strategy:
        mov     word ptr cs:[ptrsav],bx          ; Adresse der Zeile wird 
        mov     word ptr cs:[ptrsav+2],es        ; gesichert
        ret
stratp  endp

;=============================================================================

diskint proc    far
dskint:
        if      v20
        pusha
        else
        push    ax
        push    cx
        push    dx
        push    di
        push    bp
        push    bx               ; erst alle Register retten
        push    si
        endif
        push    ds
        push    es
        mov     si,offset dsktbl ; Tabelle der Befehle
        lds     bx,cs:[ptrsav]   ; in BX der Offset der Anforderungskopfzeile
        mov     al,[bx.unit]     ; AL: welche Einheit
        mov     ah,[bx.media]    ; Media Descriptor Byte
        mov     cx,[bx.count]    ; Anzahl Bytes oder Bl”cke
        mov     dx,[bx.start]    ; Startadresse des ersten Bytes oder Blocks
        xchg    di,ax            ; AX sichern, da nun die Adresse der Befehls-
        mov     al,[bx.cmd]      ; routine berechnet wird. Dazu muá der
        xor     ah,ah            ; Befehlscode zweimal auf die Startadresse
        add     si,ax            ; der Tabelle (Wert in SI) addiert werden
        add     si,ax
        cmp     al,18            ; Funktionsnummer gr”áer als 18 ?
        ja      cmderr           ; ja --> Fehler
        xchg    ax,di            ; altes AX zurck
        les     di,[bx.trans]    ; DI enth„lt nun den Offset, ES das
                                 ; Segment der Transferadresse
        push    cs               ; Datensegment gleich dem Code-Segment
        pop     ds
        jmp     word ptr [si]    ; und ab zur Befehlsroutine

;-----------------------------------------------------------------------------

busexit:mov     ah,00000011b     ; wenn das entsprechende Device "busy" ist,
        jmp     short exit1      ; dann hierher und weiter zum gemeinsamen
                                 ; ausgang

;Fehler-Codes :
;               0 = schreibgeschtzt
;               1 = unbekannte Einheit
;               2 = Laufwerk nicht bereit
;               3 = unbekannter Befehl in der Anforderungskopfzeile
;               4 = CRC-Fehler
;               5 = falsche Laufwerks-Anforderungszeilenl„nge
;               6 = Seek-Fehler
;               7 = unbekanntes Media Descriptor Byte
;               8 = Sektor nicht gefunden
;               9 = Drucker hat kein Papier mehr
;              10 = Fehler beim Schreiben
;              11 = Fehler beim Lesen
;              12 = allgemeiner Fehler

cmderr: mov     al,3             ; unbekannter Befehl -- > Sprung hierher

errexit:mov     ah,10000001b     ; Bits fr "fertig" und "Fehler" setzen
        stc
        jmp     short exit1      ; und weg

exit:   mov     ah,00000001b
exit1:  lds     bx,cs:[ptrsav]
        mov     [bx.status],ax   ; in der Anforderungskopfzeile den
                                 ; Status melden

set_double:
        cmp     atflg,1          ; ist es ein AT?
        jnz     ok2              ; nein, dann weg ohne Double-Stepping an
        mov     bx,40h
        mov     es,bx
        mov     bx,90h
        add     bl,drivesel
        mov     dl,es:[bx]
        and     dl,0f0h          ; sonst das Double-Stepping aktivieren
        cmp     dl,50h           ; da wir es fr 720 KByte Disketten in HD-
        jne     ok2              ; Laufwerk abgeschaltet hatten
        mov     dl,61h
        mov     es:[bx],dl
ok2:
        pop     es
        pop     ds
        if      v20
        popa
        else
        pop     si
        pop     bx
        pop     bp
        pop     di
        pop     dx
        pop     cx               ; alle Register zurck vom BIOS-Stack
        pop     ax
        endif
        ret                      ; zurck zum DOS
diskint endp

;=============================================================================

localer proc    near

dskwrv:
        mov     verify,1         ; Wenn Verify ON
dskwrt:
        call    setup            ; berprfen, ob angeforderte Sektoren 
        jc      is_error         ; auáerhalb der Grenzen der Diskette
        call    diskwrt          ; wenn ok, dann Sektoren schreiben
        jc      is_error         ; bei Schreibfehler weg
        cmp     verify,0         ; ohne Verify sofort zum Ausgang, sonst
        jz      exit

        mov     verify,0         ; kontrollesen
        lds     bx,cs:[ptrsav]   ; dazu die ben”tigten Parameter wieder
        mov     al,[bx.unit]     ; holen
        mov     ah,[bx.media]
        mov     cx,[bx.count]
        mov     dx,[bx.start]
        les     di,[bx.trans]
        push    cs
        pop     ds

dskred:
        call    setup            ; genauso wie beim Schreiben
        jc      is_error
        call    diskrd
        jnc     exit

is_error:
        call    derror           ; Fehlercode des INT 13 in den des Treibers
        jmp     short   errexit  ; wandeln und Fehler-Ausgang benutzen

;-----------------------------------------------------------------------------

getbpb:
        mov     ah,al            ; bei PCDOS wird es schon ber Pointer
                                 ; in DI geliefert
        mov     al,es:[di]       ; Media Byte holen, Unit Code jetzt in AH
        push    ax
        push    bx
        xchg    ah,al
        call    getpara          ; den richtigen DBP ermitteln
        mov     si,bx
        pop     bx
        pop     ax

getbp4:

get_bp5:lds     bx,[ptrsav]      ; und beide Werte in der
        mov     [bx.bpb1],al     ; Anforderungszeile sichern
        mov     [bx.bpb3],si
        mov     [bx.bpb3+2],cs
        xor     al,al            ; kein Fehler
        jmp     exit             ; weg

;-----------------------------------------------------------------------------

mediac: xor     di,di            ; 1: not changed, 0: don't know, -1: changed

        mov     al,ibmifcflg     ; Wenn keine IFC-Karte, dann nicht berprfen
        or      al,al
        jz      media1
        cmp     al,1             ; die alte FDC braucht auch kein Media-Check
        jz      media1
        inc     di
        mov     al,24h           ; nur bei IFC ber callf monitor
        call    romcall          ; auf "motor aus" testen
        or      al,al
        jz      mediaexit

media1: xor     di,di            ; sonst eine Null, da nicht bekannt

mediaexit:
        lds     bx,[ptrsav]      ; in Anforderungszeile den Status sichern
        mov     word ptr [bx].trans,di
        mov     al,0             ; kein Fehler
        jmp     exit

;-----------------------------------------------------------------------------

setup:  xor     si,si

        call    getpara          ; hole den zum Media-Byte geh”renden DBP

        xchg    bx,di            ; nach DI mit dem pointer
        mov     al,byte ptr [di].koepfe
        mov     ah,byte ptr [di].sectrk
        imul    ah               ; Anzahl der Sektoren pro Spur
        mov     [maxsec],al      ; fr sp„ter (šberlauf in n„chsten Track)
        push    si
        mov     si,cx            ; CX = Anzahl zu lesende Sektoren
        add     si,dx            ; DX = Startsektor
        cmp     si,word ptr [di].sectors 
        pop     si               ; gr”áer als die gesamte Diskette
        jbe     inrange          ; Sektoren hat? Nein --> Fehler = 8
        mov     al,8             ; --> Sektor nicht gefunden
        stc
        ret

inrange:mov     [seccnt],cx      ; Anzahl zu lesende Sektoren sichern
        add     dx,si
        mov     si,bx            ; BX enth„lt Offset Transferadresse
        and     bx,0fh           ; Der Offset soll so klein wie m”glich sein,
        mov     [dmaadr],bx      ; darum wird entsprechend das Segment
        mov     ax,dx            ; in DX erh”ht
        xor     dx,dx
        mov     bl,[maxsec]      ; Nummer des logischen Sektors wird durch die
        xor     bh,bh            ; Maximalzahl Sektoren pro Track geteilt
        div     bx               ; Das ergibt den relativen Sektor
        inc     dl               ; innerhalb des Tracks
        mov     [cursec],dl
        mov     [curtrk],ax      ; und als Quotient den Track-Wert
        mov     cl,4
        clc
        shr     si,cl            ; hier jetzt die Segment-Vergr”áerung
        mov     cx,es
        add     cx,si
        mov     [dmasegment],cx  ; Segmentadresse sichern fr sp„ter

driveeq:mov     cx,[curtrk]      ; sicher ist sicher
        clc                      ; kein Fehler
dirf:   ret

;-----------------------------------------------------------------------------

getpara:
        mov     bx,offset ibmps  ; erster DBP Eintrag
loopac: cmp     ah,[bx].mediaid  ; ist es der richtige DBP ?
        jz      sfou             ; ja, dann weg
        add     bx,length        ; sonst n„chsten versuchen
        cmp     bx,offset atari+length  ; falls es nicht der letzte war
        jnz     loopac

sfou1:  mov     bx,offset atari  ; keiner stimmt, dann nehmen wir atari
sfou:   ret

;-----------------------------------------------------------------------------

diskrd:
        call    reset_double     ; Double-Stepping eventuell abschalten
rdlp:   call    preset           ; Werte fr Track, Sektor, Kopf, Anzahl der
        cmp     ibmifcflg,2      ; Sektoren nur bei IFC-Karte mit uebercallf
        jnz     ueberintles      ; sonst den INT 13 benutzen
        mov     al,16h           ; Code des CALLF Monitor fr Lesen
        call    uebercallf
        jmp     short rdlp2

ueberintles:
        mov     ah,2             ; Lese-Funktionsnummer des INT 13h
        int     13h
rdlp2:  jc      errorr           ; in AH steht Errorcode

        dec     [seccnt]         ; n„chsten Sektor
        jnz     rdlp
        clc                      ; kein Fehler
        ret

errorr: stc                      ; bei Fehler hierher
        ret

diskwrt:
        call    reset_double
wrloop: call    preset           ; wie rdlp, nur eben schreiben
        cmp     ibmifcflg,2
        jnz     ueberintsch
        mov     al,18h
        call    uebercallf
        jmp     short wrlp2

ueberintsch:
        mov     ah,3
        int     13h

wrlp2:  jc      errorr
        dec     [seccnt]
        jnz     wrloop
        clc
        ret


; Doublestepping fr die Bearbeitung normaler 80-Spur-Disketten mit 720
; KByte Kapazit„t in Multifunktionslaufwerken NUR IN ATs abschalten

reset_double:
        cmp     atflg,1          ; auf AT
        jz      ok1              ; nein, dann weg
        ret

ok1:    mov     bx,40h           ; sonst bei Adresse 40:90 oder 91
        mov     es,bx
        mov     bx,90h
        add     bl,drivesel
        and     byte ptr es:[bx],0dfh  ;maskiert das Double-Step-Bit aus
        ret

preset: mov     bx,[dmaadr]      ; fr den INT 13h mssen die Register belegt
                                 ; werden
        mov     es,[dmasegment]  ; Segment laden
        mov     al,[cursec]
        cmp     al,[maxsec]
        jbe     gotsec1          ; zu lesender Sektor schon im n„chsten Track ?
        inc     [curtrk]         ; ja, dann eben Track erh”hen
        mov     al,1             ; relativer Sektor wird eins
        mov     [cursec],al
        mov     cl,al
        xor     dh,dh            ; Kopf ist dann logischerweise 0
        jmp     short wegvon

gotsec1:push    bx               ; kein šberlauf
        mov     bx,word ptr [di].sectrk 
        mov     [secpertrak],bl  ; dann den Kopf berechnen, indem man
        xor     dh,dh            ; solange sectrk abzieht, bis es richtig ist
loopinc:cmp     al,bl
        jbe     nohead
        inc     dh               ; Kopf immer um eins erh”hen (damit k”nnen
        sub     al,bl            ; wir auch Harddisks bedienen)
        jmp     short loopinc

nohead: mov     cl,al            ; richtiger Sektor und richtiger Kopf
        pop     bx               ; DMA-Adresse wieder vom Stack

; ist es eine Diskette vom DEC Rainbow ?

        mov     ax,offset decrain ; bei den Floppy-Disks mssen wir noch
        cmp     ax,di            ; berprfen, ob es sich eventuell um eine
        jnz     nodec            ; DEC-Rainbow-Diskette handelt (findet man
                                 ; anhand des BPB heraus)

; erst prfen, ob Track 0 oder 1, denn da hat der DEC keinen Skew
; (diese Angabe im BIOS-Listing des DEC Rainbow erwies sich leider als
; falsch, trotzdem bleibt der Code drin, falls doch mal eine Žnderung
; zu machen ist)

;       xor     ax,ax
;       cmp     ax,[curtrk]
;       jb      nodec

;jetzt Skew fr DEC Rainbow „ndern

istdec: xor     ch,ch
        push    si               ; aus der Tabelle die richtige Sektornummer
        mov     si,cx
        mov     cl,scewtable[si] ; holen
        pop     si

wegvon:
nodec:  mov     ax,word ptr [di].secsize
        add     [dmaadr],ax
        inc     [cursec]         ; schon den n„chsten Sektor einstellen
        mov     ax,[curtrk]
        mov     ch,al
        and     ah,03h           ; und in CL (Sektor) zus„tzlich die beiden
        ror     ah,1             ; oberen Bits als Track benutzen
        ror     ah,1
        or      cl,ah            ; oberes Byte der Track-Nummer
        mov     dl,[drivesel]
        mov     al,1
        ret

;-----------------------------------------------------------------------------

derror: lds     bx,cs:[ptrsav]
        mov     [bx.count],0
        push    cs               ; Fehlermeldung des INT 13h in Meldung
        pop     ds               ; fr DOS umsetzen
        cmp     ibmifcflg,0      ; nur beim PC oder AT kann der Floppy-
        jnz     no_pc            ; Controller zurckgesetzt werden
        push    ax
        xor     ax,ax            ; Reset auf Controller
        cwd
        int     13h
        pop     ax
no_pc:
        test    ah,80h           ; Zeitberschreitung (Time Out)
        jz      de1
        mov     al,2
        ret

de1:    test    ah,40h           ; Spur nicht gefunden (Seek-Error)
        jz      de2
        mov     al,6
        ret

de2:    test    ah,3             ; schreibgeschtzt
        jz      de3
        xor     al,al
        ret

de3:    test    ah,10h           ; CRC-Error
        jz      de4
        mov     al,4
        ret

de4:    test    ah,2             ; Sektor nicht gefunden
        jz      de5
        mov     al,8
        ret

de5:    test    ah,4             ; Bad Adress-Mark
        jz      de6
        mov     al,8
        ret

de6:    or      ah,ah            ; berhaupt ein Fehler aufgetreten
        jz      dee
        mov     al,12            ; dann ist es ein allgemeiner Fehler

dee:    ret


;Skewtable fr den DEC Rainbow

scewtable       db      0,1,3,5,7,9,2,4,6,8,10

verify          db      0

drivesel        db      0        ; Sicherungsspeicher fr div. Daten
cursec          db      0
curtrk          dw      0
dmaadr          dw      0
dmasegment      dw      0

maxsec          db      0        ; Anzahl Sektoren pro Zylinder

seccnt          dw      0
secpertrak      db      0

atflg           db      0        ; 0 = PC/c't86, 1 = AT

ibmifcflg       db      0        ; 0 = PC/AT, 1 = c't86-FDC, 2 = c't86-IFC

; ab hier transienter Code, wenn PC oder AT

uebercallf:
        push    ax               ; INT 13 Aufruf in einen CALLF Monitor
        push    cx               ; umsetzen
        push    dx
        push    bx
        mov     cx,es
        mov     al,14h           ; setze Segment
        call    romcall
        pop     cx
        mov     al,0ch           ; setze Offset
        call    romcall
        pop     ax
        push    ax
        inc     al
        mov     ah,[secpertrak]
        mov     cx,ax
        mov     al,10h           ; setze Laufwerk

ifc_or:
        or      cl,al            ; fr High-Density (AT-Format)
        call    romcall          ; mit IFC-Karte auf c't86
        pop     dx
        mov     cl,dh
        mov     ch,0
        mov     al,[secpertrak]
        mov     ah,0             ; Kopf gibt es nicht, nur relativen Sektor
        mul     cl               ; innerhalb einer Spur, daher mit Kopfnummer
        pop     cx               ; multiplizieren
        push    cx
        mov     ch,0
        add     cx,ax
        mov     al,12h           ; setze Sektor
        call    romcall
        pop     cx
        mov     cl,ch
        mov     ch,0
        mov     al,0eh           ; setze Spur
        call    romcall
        pop     ax               ; lesen oder schreiben
        call    romcall
        mov     ah,al
        or      al,al
        jz      noerr
        stc
        ret
noerr:  clc
        ret

; callf Monitoraufruf

romcall:push    bx
        push    di
        call    monitor
        pop     di
        pop     bx
        ret

; ab hier transienter Code, wenn c't86

dskinit:mov     es,dx
        mov     bx,cx            ; EX:BX zeigt auf die Zeile im CONFIG.SYS
        push    bx
        push    es
        mov     ax,0f000h
        mov     es,ax
        mov     bx,0fffeh        ; erst mal schauen, ob es ein PC oder ein
        mov     al,es:[bx]       ; c't86 ist, denn beim c't k”nnen wir mit
                                 ; CALLF Monitor und IFC arbeiten
        cmp     al,06h           ; die letzte Version des c't86-Monitor
        ja      istibmpc         ; wird hoffentlich nur 6 sein
istct86:mov     al,22h
        call    romcall          ; ist eine IFC-Karte vorhanden ?
        test    al,20h           ; wenn ja, dann auf jeden Fall ber callf
        jnz     mitifckarte
        mov     ibmifcflg,1      ; sonst nur alte FDC Karte
        jmp     short weiterinit

istibmpc:
        mov     ibmifcflg,0
        cmp     al,0fch          ; ist es ein IBM PC/AT  ??
        jnz     weiterinit
        mov     atflg,1          ; AT !!!
        jmp     short weiterinit

mitifckarte:
        mov     ibmifcflg,2      ; ibmifcflg auf entsprechenden Wert setzen
weiterinit:
        pop     es
        pop     bx
        ausgabe startmes         ; Signon Message ausgeben
        call    scan_for_para    ; Treibernamen bergehen
        mov     al,es:[bx]       ; sind Parameter angegeben ?
        or      al,al
        jnz     node             ; wenn ja, dann die Werte analysieren
        jmp     default_init     ; sonst voreingestellte Werte benutzen

node:   mov     al,es:[bx]
        inc     bx               ; auf erstes Zeichen nach Treibername
        cmp     al,'4'           ; 40 oder 80 Track
        jz      track40
        cmp     al,'8'  
        jz      track80
        jmp     errordata

track40:mov     al,es:[bx]
        cmp     al,'0'           ; die Null von 40 oder 80 berprfen
        jz      ok11
        jmp     errordata

ok11:   mov     tr4080,0         ; bei 40 Spuren braucht das Double-Stepping
        mov     atflg,0          ; nicht abgeschaltet werden
        mov     dx,offset viertrack
        jmp     short driveermit ; Text ausgeben

track80:mov     al,es:[bx]
        cmp     al,'0'
        jz      ok22
        jmp     errordata        ; keine 0 , dann Fehler

ok22:   mov     tr4080,1
        ausgabe achttrack

driveermit:
        inc     bx
        inc     bx
        mov     al,es:[bx]       ; physikalisches Laufwerk ermitteln
        cmp     al,'0'           ; muá zwischen 0 und 3 liegen
        jl      errordata
        cmp     al,'3'
        jg      errordata
        sub     al,'0'
store:  mov     drivesel,al
        cmp     al,1             ; nur bei 0 und 1 kann man das Double-Stepping
        jle     hdlauf           ; ausschalten, sonst nicht
        mov     atflg,0
hdlauf:
        add     al,'A'           ; fr die Textausgabe umwandeln
        mov     physic,al
        cmp     tr4080,1
        jnz     move_them        ; bei 40 Spur Formaten mssen wir DBPs
        inc     bx               ; bertragen
        inc     bx
        mov     al,es:[bx]       ; bei 80 Spuren erst im dritten Parameter
                                 ; nachsehen, welcher zu bertragen ist
        and     al,0dfh          ; upper case wandeln
        cmp     al,'A'
        jz      atlauf           ; als normales HD Laufwerk im c't 86 benutzen
        cmp     al,'I'
        jz      ibmpsin          ; als IBM PC 720 KByte Format
        cmp     al,'S'
        jz      siemin           ; mit Siemens PC D Format
        cmp     al,'T'
        jz      nixin            ; mit Nixdorf PWS Format
        cmp     al,'O'
        jz      olliin           ; mit Olivetti M 24 Format
        cmp     al,'N'
        jnz     ibmpsin1         ; und wenns keiner war, dann ists IBM PS

nixin:  mov     dx,offset nixtext
        mov     si,offset nixdorf
        jmp     short   move_bpb ; Text ausgeben, DBP bertragen

errordata:
        ausgabe fehlertxt
default_init:
        mov     tr4080,0         ; bei Fehler oder ohne Parameter ist es 40
        mov     drivesel,1       ; Spuren auf Laufwerk B:
        mov     physic,'B'
        mov     dx,offset deftxt
        jmp     short move_bpb

atlauf:
        mov     dx,offset at_text
        mov     si,offset hd
        jmp     short move_bpb

siemin:
        mov     dx,offset siemtext
        mov     si,offset siemens
        jmp     short   move_bpb

olliin: mov     dx,offset ollitext
        mov     si,offset olivet
        jmp     short move_bpb   ; Text ausgeben und DBP bertragen

ibmpsin1:
        dec     bx
        dec     bx
ibmpsin:
        mov     dx,offset ibmpstext
move_them:
        mov     si,offset ibmps
move_bpb:
        mov     ah,9
        int     21h
        push    es
        push    ds
        pop     es
        mov     di,offset ibmps
        cmp     si,offset hd     ; bei den 80 Spur Formaten muá nur ein DBP
        jnz     normal           ; bertragen werden, bei den HD Formaten
        mov     cx,length * 3    ; aber drei
        jmp     short move

normal:
        mov     word ptr ds:ifc_or,9090h  ;nop, nop
        cmp     tr4080,0
        mov     cx,length
        jnz     move             ; falls 80 Track, sonst berschreibe die
        mov     si,offset lsdriv1 ; ersten 2 80-Track-BPBs mit 40-Track BPB
        add     cx,cx            ; 2 BPBs zu kopieren
move:   cld
        rep     movsb
        pop     es
        cmp     tr4080,0         ; bei 40 Spur Format gibt es keinen vierten
        jz      no_atari         ; Parameter fr Atari
        call    scan_for_para
        mov     al,es:[bx]       ; Atari-Parameter "1" oder "2" (-seitig)
        cmp     al,'1'           ; nur 1 wird ausgewertet, sonst DS
        mov     atariflg,al
        mov     dx,offset atarissmes
        mov     si,offset atariss
        jz      atari1
        mov     dx,offset ataridsmes
        mov     si,offset atari
atari1: mov     di,offset atari
        push    es
        push    ds
        pop     es
        mov     cx,length        ; Atari Format bertragen und
        rep     movsb
        pop     es
        mov     ah,9             ; Text ausgeben
        int     21h

no_atari:
        mov     ah,30h
        int     21h              ; get Version
        lds     bx,[ptrsav]
        cmp     al,3             ; wird erst ab Version 3.x untersttzt
        push    ds
        jb      below_3x
        mov     dl,[bx.drive]    ; sonst kann das logische Laufwerk nicht
        add     dl,'A'           ; ermittelt werden
        mov     byte ptr cs:laufwerk,dl
        push    cs
        pop     ds
        ausgabe logical
below_3x:
        mov     ax,offset dskinit
        cmp     ibmifcflg,0      ; bei PC oder AT ab DSKINIT transienter Code
        pop     ds
        jnz     here_end         ; im c't 86 erst ab UEBERCALLF
        mov     ax,offset uebercallf
here_end:
        mov     word ptr [bx+0eh],ax
        mov     word ptr [bx+10h],cs  ; gib "end of driver" ans DOS
        push    cs
        pop     ds
        mov     al,drvmax        ; Anzahl Untereinheiten des Treibers
        xor     ah,ah
        mov     si,offset inittab1 ; StandardDBP Einstellung
        jmp     get_bp5

scan_for_para:
        push    ds
        push    es
        pop     ds
        mov     si,bx
s1:     lodsb
        cmp     al,' '           ; berspringe voraneilende Leerzeichen
        jz      s1
s2:     lodsb
        cmp     al,0
        jz      s3
        cmp     al,' '           ; berspringe den Treibernamen
        jnz     s2
s3:     lodsb
        cmp     al,' '           ; berspringe nachfolgende Leerzeichen
        jz      s3
        dec     si
        mov     bx,si
        pop     ds
        ret

; High-Density-Laufwerk
hd      dbp     <512,1,1,2,224,2400,0f9h,7,15,2>

        dbp     <512,2,1,2,224,2720,0fdh,7,17,2>

        dbp     <1024,2,1,2,224,1440,0feh,7,9,2>

; IBM PC 40 Track, 9 Sektoren, doppelseitig
lsdriv1 dbp     <512,2,1,2,112,720,0fdh,2,9,2>

; IBM PC 40 Track, 9 Sektoren, einseitig
lsdriv2 dbp     <512,1,1,2,64,360,0fch,2,9,1>

; Olivetti M24
olivet dbp      <512,2,1,2,144,1440,0f9h,3,9,2>

; Siemens PC-D
siemens dbp     <512,4,1,2,144,1440,0f9h,2,9,2>

; Nixdorf PWS oder Softec
nixdorf dbp     <512,2,1,2,144,1600,0f9h,3,10,2>

; Atari ST doppelseitig (80 Spur, 3,5 Zoll)
atarids dbp     <512,2,1,2,112,1440,0f7h,5,9,2>

; Atari ST einseitig (80 Spur, 3,5 Zoll)
atariss dbp     <512,2,1,2,112,720,0f7h,5,9,1>

atariflg        db      0
tr4080          db      0        ; vierzig oder achtzig Track

deftxt          db      "Default-Werte:",0dh,0ah
                db      "40-Track-Drive B:",0dh,0ah,'$'
fehlertxt       db      "Fehler in Parameterfeld",0dh,0ah,'$'

viertrack       db      "40-Track-Drive eingestellt",0dh,0ah,'$'
achttrack       db      "80-Track-Drive eingestellt",0dh,0ah
                db      "Bei Media Byte F9h: ",'$'

ollitext        db      "Olivetti M24",0dh,0ah,'$'
siemtext        db      "Siemens PC-D",0dh,0ah,'$'
nixtext         db      "Nixdorf / Softec",0dh,0ah,'$'
ibmpstext       db      "IBM 80-Spur",0dh,0ah,'$'
at_text         db      'AT-Laufwerk',0dh,0ah,'$'

startmes        db      0dh,0ah,"Universeller c't-Disk-Treiber"
                db      " V 3.6 (16.08.87)", 0dh,0ah,'$'

logical         db      'Auf physik. Laufwerk '
physic          db      ' '
                db      ': als Laufwerk '
laufwerk        db      "d"
                db      ": installiert",0ah,0dh,'$'

ataridsmes      db      "Bei Media Byte F7h: Atari ST doppelseitig",0dh,0ah,'$'
atarissmes      db      "Bei Media Byte F7h: Atari ST einseitig",0dh,0ah,'$'

localer endp
code    ends

        if1
        %out    end of pass 1
        endif

        if2
        %out    end of pass 2
        endif

        end
