8086 Assembler: INT 21h (DOS-Interrupt)


Zurück zur Assemblerauswahlseite

Die nachfolgende Zusammenfassung betrifft den sogenannten DOS-Interrupt INT 21h.

Die Beschreibung betrifft die Verhältnisse bis MS-DOS Version 6. Ich hatte mir diese Zusammenstellung gemacht, als ich mich seinerzeit mit dem 8086-Assembler beschäftigt hatte.

Aufruf und Fehlercodes bei den READ-Und Write-Funktionen
Aufbau des Attributbytes
Directoryeinträge
DTA / FCB
Beispiel zur FCB-Behandlung:
Handles
Beispielmacros zur Handle-Dateibehandlung:
PSP und ENVIRON
Funktionsindex (Dort eine weitere Verzweigungsliste)

Aufruf und Fehlercodes bei den READ-Und Write-Funktionen

Der Aufruf läuft nach folgendem Schema ab:

Man versorgt die Register mit den benötigten Eingabeparametern.

Die Funktionsnummer kommt nach AH, die eventuell benötigte Unterfunktionsnummer nach AL.

Die Funktion wird ausgeführt. Bis auf wenige Ausnahmen ändert sie nur die Register, die zur Übergabe der Ergebnisse dienen.

Die CP/M-kompatiblen Funktionen verwenden AL zur Kennzeichnung von Fehlern. Die neueren Funktionen melden mit dem gesetzten Carryflag (CF), dass ein Fehler aufgetreten ist. Falls ja, steht im Register AX eine Fehlernummer.

Fehlercodes bei den READ-und WRITE-Funktionen (READ-Funktionen: 14H, 21H, 27H und WRITE-Funktionen: 15H, 22H, 28H):

  • 1. Ungültiger Funktionscode. Die gewählte Funktion gibt es nicht. bei READ-Funktion: Dateiende (EOF) ist erreicht. bei WRITE-Funktion: Disk voll.
  • 2. Überlauf des DTA-Buffers bei READ-und WRITE Funktion: Im DTA-Buffer wurde das Offset 0FFFFH erreicht. Ein weiteres Lesen/ Schreiben hätte den Offset 0 zur Folge.
  • 3. Pfad nicht gefunden. bei READ-Funktion: Dateiende (EOF) wurde beim Lesen des letzen Satzes erreicht. Der letzte Satz steht im DTA-Buffer bereit und wurde mit 00H-Bytes auf die Satzlänge (RECLEN) aufgefüllt.
  • 4. Zu viele Dateien offen. Die maximale Anzahl gleichzeitig geöffneter Dateien ist erschöpft.
  • 5. Zugriff verweigert.
  • 6. Ungültiges Handle. Das Handle bezieht sich nicht auf eine geöffnete Datei.
  • 7. Speicherkontrollblöcke zerstört. Die interne Speicherverwaltung von MS-DOS wurde beschädigt.
  • 8. Nicht genügend Platz frei. Die von MS-DOS angeforderte Speichermenge kann nicht zur Verfügung gestellt werden.
  • 9. Ungültige Adresse Speicherkontrollblock. Die übergebende Adresse kennzeichnet nicht den Beginn eines Speicherkontrollblocks.
  • 10. Ungültige Umgebungsadresse. Die übergebende Adresse kennzeichnet nicht den Beginn eines Environments.
  • 11. Ungültiges Format. Das angegebene Programm ist intern nicht korrekt aufgebaut (z.B. Dateilänge=0 oder fehlerhafter Programmvorspann bei einem .EXE-Programm).
  • 12. Ungültiger Zugriffscode. Die bits 0 bis 3 von AL hatten beim Aufruf keinen Wert von 0 bis 3.
  • 13. Ungültige Daten. Tritt bei Funktion 44 auf. Die genaue Bedeutung ist unbekannt.
  • 14. Reserviert
  • 15. Ungültiges Laufwerk. Es wurde versucht, auf ein nicht existierendes Laufwerk zuzugreifen.
  • 16. Aktuelles Directory nicht löschbar. (das zu löschende Directory muss vor dem Löschen verlassen werden).
  • 17. Nicht dasselbe Gerät. Die Funktion erlaubt keine Angabe von zwei verschiedenen Laufwerken.
  • 18. Keine weitere Datei vorhanden. Die Suche nach weiteren Einträgen im Directory bzw. in der Zuordnungsliste (Netzwerk) war erfolglos.
  • 33. Sperren/Freigeben nicht möglich. Es wurde entweder versucht, einen Dateibereich zu sperren, der bereits ganz oder teilweise gesperrt ist, oder der freizugebende Teil entspricht in seiner Länge dem vorher gesperrten Teil.
  • 80. Datei existiert bereits. Die Funktion 5BH erzeugt nur dann eine neue Datei, wenn diese noch nicht vorhanden ist.

Aufbau des Attributbytes:

Bit  
7 6 5 4 3 2 1 0 wenn gesetzt wenn 0 (=nicht gesetzt)
              x Datei nur lesbar Datei les- und schreibbar
            x   versteckte Datei sichtbare Datei
          x     Systemdatei normale Datei
        x       Datenträgername normale Datei
      x         Verzeichnisname normale Datei
    x           nach Sicherung geändert seit Sicherung unverändert
x x             reserviert reserviert

Directoryeinträge:

Offset Länge Beschreibung Format
0 8 Byte Dateiname ASCII,
siehe Hinweis unter
dieser Tabelle!
8 3 Byte Dateityp (Erweiterung) ASCII
0Bh 1 Byte Attributbyte siehe Attributbyte,
Tabelle direkt oberhalb
0Ch 10 Byte reserviert ASCII
16h 1 Wort Zeit letzte Aktualisierung kodiertes Wort
18h 1 Wort Datum letzt Aktualisierung kodiertes Wort
1Ah 1 Wort Nr. des Startclusters binäre Ganzzahl
1Ch 1 Doppel-
wort
Dateigröße binäre Ganzzahl

Das 1.Byte des Dateinamens hat folgende Sonderbedeutung:
00h=Eintrag / Name wurde nie verwendet
05h=Das erste Zeichen des Namens ist eigentlich E5h
E5h=Datei wurde verwendet, ist jedoch gelöscht
2Eh=Eintrag ist ein Verzeichnis, wenn das 2.Byte auch 2Eh ist, enthält das Clusterfeld die Clusternummer des übergeordneten Verzeichnisses.

DTA / FCB

Die CP/M kompatiblen Diskfunktionen verwenden File Control Blocks (FCB)s.

Da der FCB keine Möglichkeit der Pfadangabe hat, beziehen sich diese Funktionen immer auf das aktuelle Directory.

Als Wildcard-Angabe für die CP/M-kompatiblen Funktionen ist nur das ? möglich, nicht das *.

Sämtlicher Datentranfers findet bei den FCB-behafteten Funktionen über die Disk Transfer Address (DTA) statt. Die DTA ist die Anfangsadresse eines beliebigen Puffers im Hauptspeicher. Die maximale Größe ist 64 K Byte. Effektiv genutzt werden kann nur der Bereich zwischen dem Offset der DTA und 0FFFFH. Hatte z.B. die DTA die Adresse 3000:FFF0H so könnten maximal 16 Bytes genutzt werden. Als Default-DTA-Adresse gibt MSDOS DS:80H vor. Da die Programme bei DS:100H beginnen, ist der Puffer somit 128 Bytes lang. Die DTA-Adresse befindet sich im Program-Segment Prefix (PSP), welches den Anfang eines jeden Programmes bildet. Im PSP befinden sich an den Offsets 5CH und 6CH ebenfalls zwei ungeöffnete FCBs.

Vor Verwendung bitte die hier gemachten Angaben zum FCB prüfen, ggf. korrigieren!

normaler FCB:
drive    db  ?        ;gewähltes Laufwerk, 0= aktuelles, 1=A, usw.
fname    db  8 dup (' ')     ;Dateiname, linksbündig mit Space aufgefüllt
fext     db  3 dup (' ')     ;Erweiterung, linksbündig mit
                             ;Space aufgefüllt
curblk   dw  0               ;Nr. des aktuellen Blockes
recln    dw  0               ;logische Satzlänge (default: 80H)
fsize    dd  0               ;Dateilänge in Bytes
fdate    db  0,0             ;Dateidatum gepackt, jjjj jjjj mmmt tttt
ftime    db  0,0             ;Dateizeit gepackt,  hhhh hmmm mmms ssss
reserv   db  8 dup (0)       ;intern von DOS benutzt
currec   db  0               ;aktueller Satz in curblk
relrec   dd  0     ;relative Satznr. bei Randomverarbeitung
                       ;keine Vorbelegung durch OPEN,
                       ;benutzt werden:
                          ;4 Byte bei reclen <64 bytes
                          ;3 byte bei reclen >=64 Bytes

erweiterter FCB:
dieser Kopf wird beim erweiterten FCB vor dem normalen FCB platziert:

flag     db  0ffh             ;Muss-Kennzeichner für
                              ;erweiterten FCB
dummy    db  5 dup (' ')      ;reserviert für zukünftige
                              ;Benutzung
fattr    db  ?                ;Attributbyte

spezieller FCB für Funktion 17 (Umbenennen von Dateien)
drive    db  ?                ;gewähltes Laufwerk,
                              ;0= aktuelles, 1=A, usw.
afname   db  8 dup (' ')      ;alter Dateiname, linksbündig
aext     db  3 dup (' ')      ;alte Erweiterung, linksbündig
         db  5 dup (0)        ;reserviert
nfname   db  8 dup (' ')      ;neuer Dateiname, linksbündig
next     db  3 dup (' ')      ;neue Erweiterung, linksbündig

         db  9 dup (0)        ;reserviert

Beispiel zur FCB-Behandlung:

comment #
Zweck:  Rename eines Unterverzeichnisses

Aufruf: rendir Altname Neuname
        Beide Dateinamen dürfen nur die reinen Dateinamen sein,
        da mit einem FCB gearbeitet wird
Quelle: c't 1988, Heft 6, Seite 204 mit Modifikationen
#
        .286c
dbl     macro     text      ;;define a line
        db        text,cr,lf
        endm

jmps    macro     to        ;;kurzer Sprung
        jmp       short to
        endm

prstr   MACRO     TEXT      ;;PRINT STRING
        CALL      PRMSG
        DB        TEXT,0
        ENDM

cr      equ       0dh
lf      equ       0ah

dos     equ       21h
lparm   equ       0080h      ;Parameterlänge
parm    equ       0081h      ;Parameter

code    segment
        assume    cs:code, ds:code, es:code

        org       100h
start:  mov       cl,byte ptr cs:lparm
        xor       ch,ch
        jcxz      leer       ;kein Parameter
        cld

;Altname als 1. Parameter suchen
        mov       di,parm
        mov       al,' '
        repe      scasb      ;führende blanks überlesen
        je        leer       ;nur blanks !

        lea       si,[di-1]  ;Anfang Altname nach DI

        repne     scasb      ;nächstes Blank als Ende Altname suchen
        jne       leer       ;Neuname fehlt

        mov       byte ptr[di-1],0        ;Markieren Ende Altname

        repe      scasb      ;weitere blanks überlesen
        je        leer       ;kein Neuname angegeben

        lea       dx,[di-1]        ;Anfang Neuname nach DX
        add       di,cx            ;hinter Neuname gehen
        mov       byte ptr[di],0   ;Markieren Ende Neuname
        jmps      name_to_fcb

leer:   prstr     'Usage: RENDIR Altname Neuname'
        jmp       ret1

;Altname in FCB eintragen
name_to_fcb:
        lea       di,fcb.fname
        call      tofcb
        jc        fdrive                ;Fehler im Namen

;Neuname in FCB eintragen
        mov       si,dx
        lea       di,fcb.nfname
        call      tofcb
        jc        fdrive                ;Fehler im Namen

;Rename versuchen
        mov       fcb.attr,10h     ;Attribut SubDir
        mov       ah,17h           ;Rename mit FCB
        lea       dx,fcb
        int       dos
        or        al,al            ;fehlerfrei?
        jnz       nicht_ok         ;nein
        jmp       ret0

fdrive: or        al,al            ;Fehlermeldung ermitteln
        jne       fdrive1          ;und ausgeben
        jmp       mdrive
fdrive1: prstr    'alter oder neuer Name zu lang !'
        jmp       ret1

nicht_ok: call    prmsg
        dbl       'SubDir ist nicht im aktuellen Directory'
        db        'oder den neuen Namen gibt es schon'
        db        0
        jmp       ret1

mdrive: prstr     'Laufwerks- bzw. Pfadangabe nicht möglich !'
        jmp       ret1

tofcb   proc near
comment #
Übernahme von Name und Erweiterung aus der Kommandozeile in den FCB
input  <si>=Beginn des Dasteinamens, mit 0 beendet
       <di>=Ziel für Dateiname in FCB
output   cf=0 Name und Erweiterung in FCB eingetragen
         cf=1 : al=0 Name enthält Laufwerks- oder Pfadangaben
                al=1 Nume+Erw. zu lang (>11 Byte)
#
        mov       cx,8       ;max. Länge für Name
        lea       bx,[di+8]  ;Offset für Erweiterung
toloop: lodsb
        or        al,al      ;Ende des Namens?
        je        toret      ;ja. fetig mit cf=0
        cmp       al,'.'     ;Erweiterung?
        jne       to10
        mov       di,bx      ;ja- ab jetzt in Erweiterung eintragen
        mov       cx,3
        jmps      toloop

to10:   cmp       al,':'     ;Laufwerk ?
        je        toerror
        cmp       al,'\'     ;Pfad?
        je        toerror
        stosb
        loop      toloop
        lodsb
        or        al,al      ;Ende mit 0?
        jz        toret      ;ja, dann OK
        mov       al,1       ;Name oder Erweiterung zu lang
        jmps      tofehler

toerror:
        mov       al,0
tofehler:
        stc
toret:  ret
tofcb   endp
;---------------

sfcb    struc                 ;Struktur erweiterter FCB
        db        0ffh        ;Flag für erweitereten FCB
        db        5 dup (0)
attr    db        ?
drive   db        0
fname   db        '        '   ;alter Name
fext    db        '   '        ;alte Erweiterung
        db        5 dup (0)
nfname  db        '        '   ;neuer Name
nfext   db        '   '        ;neue Erweiterung
        db        15 dup (0)
sfcb    ends

fcb     sfcb      ><

;---------------------
;Unterprogrammsammlung
;---------------------
;PRINT MESSAGE FOLLOWING CALL PRMSG  AND ENDING WITH 0
PRMSG:  mov       word ptr PRMSG2,di
        POP       di
        CALL      PSTR
        PUSH      di        ;Returnadr.

        mov       di,0      ;mod
PRMSG2  EQU       $-2
        RET

;PRINT MESSAGE ADRESSED BY di, ENDING WITH 0
PSTR:   mov       dl,[di]
        INC       di
        cmp       dl,0
        jz        pstr99     ;FALLS FERTIG
        mov       ah,02
        int       21h
        jmp       short PSTR

pstr99: ret
;---------------
;diverse Programmbeendigungen
ret0:   mov       al,0
        jmps      raus

ret1:   mov       al,1

;Programmende
raus:   prstr     >cr,lf<
        mov       ah,4ch
        int       dos

code    ends
        end        start

Handles

Um eine Dateinummer zu erhalten, muss die Datei mit einer UNIX-kompatiblen Funktion (3CH, 3DH, 5AH, 5BH) eröffnet bzw. eingerichtet werden. Hierzu übergibt man den Funktionen den Dateipfad. MSDOS liefert als Ergebnis den HANDLE (=Dateinummer) zurück. Alle weiteren Zugriffe zu dieser Datei, bis hin zum Schließen, werden durch die Angabe des Handles gesteuert.

5 Handles sind vordefiniert:

Handle         Bezeichnung           Gerät
---------------------------------------------
0 STDIN         Standard_Input       Tastatur
1 STDOUT        Standard_Output      Bildschirm
2 STDERR        Standard_Error       Bildschirm
3 STDAUX        Standard_Auxiliary   RS-232
4 STDPRN        Standard_Printer     Drucker

Beispielmacros zur Handle-Dateibehandlung:

Zur Anwendung vergleiche das Beispielprogramm. Die Beispielmacros sind in der Datei firm.asm enthalten, die unter Routinensammlung für COM-Programme eingesehen und heruntergeladen werden kann.
dos    equ      21h

;ldreg is an "inner" macro.
;It allows the caller of a macro to specify a register
;or a literal value as the parameter to the macro.

;This macro loads the correct register specified
;by the caller of the macro with the contents of
;the register, literal or ;memory location also specified.

;No code will be generated if the two ;parameters are
;the same.

ldreg   macro    destreg,source
        ifdif    <destreg>,<source>
        mov      destreg,source
        endif
        endm

;---------------------------
;handle-dateibehandlung
;---------------------------
fopen   macro fname,acode        ;;open file
        ifdif    <dx>,<fname>
        mov      dx,offset fname ;;address of file name
        endif
        ldreg    al,acode       ;;access code
                                ;;0=read,
                                ;;1=write,
                                ;;2=read & write
        mov      ah,3dh         ;;funtion call open file
        int      dos            ;;returns fhandle in ax
        endm

fcrat   macro fname,attrb       ;;create a file (eine alte Datei
        ifdif    <dx>,<fname>        ;;gleichen Namens wird
                                ;;dabei gelöscht). Die erzeugte Datei
                                ;;ist geoeffnet fuer Lesen und Schreiben.
        mov      dx,offset fname ;;address of file name
        endif
        ldreg    cx,attrb        ;;attribute
        mov      ah,3ch          ;;funtion call create file
        int      dos             ;;returns fhandle in ax
        endm

fwrit   macro fhand,wbuff,count  ;;write to file
        ldreg    bx,fhand        ;;address of file name
        ldreg    cx,count        ;;attribute
        ifdif    <dx>,<wbuff>
        mov      dx,offset wbuff ;;address of write buffer
        endif
        mov      ah,40h          ;;funtion write file
        int      dos
        endm

fclose  macro    fhand           ;;close file
        ldreg    bx,fhand        ;;file handle
        mov      ah,3eh          ;;function call close file
        int      dos
        endm

fread   macro    fhand,rbuff,count ;;read from file
        ldreg    bx,fhand          ;;get file handle
        ldreg    cx,count          ;;get this many byte(s)
        ifdif    <dx>,<rbuff>
        mov      dx,offset rbuff   ;;address of read buffer
        endif
        mov      ah,3fh            ;;function read handle
        int      dos
        endm

PSP / ENVIRON

Der PSP ist 256 Bytes lang und beginnt stets vor dem Programm.
Offset Länge Standard-
inhalt
Bemerkungen
0 dw cd02h INT 20h Beendigungsadress
2 dw   Segmentadr. des zugewiesenen Speicherblockes
4 db 00h reserviert
5 5 Bytes   CALL FAR zum DOS-Funktionsverteiler (überholt)
A dd   INT 22h Adresse (Terminator Handler)
E dd   INT 23h Adresse CTRL-Break-Behandlung
12 dd   INT 24h Adresse Critcal Error Handler
16 dd   Segmentadr. PSP des übergeordneten Blockes,
beim Grundprozeß ist dies die eigene PSP-Adresse
18 20 Bytes   Handle Tabelle, 1 Byte pro Handle
2c dw   Segmentadr. des eigenen Environmentblockes
2e dw   reserviert
32 dw 14h, 00h Größe Handle Tabelle,
ab DOS 3.3 größere Tabellen mögl.
34 dd 12h, 00h Wenn nicht 12H:
Adresse der Handle-Tabelle (ab DOS 3.3)
38 23 Bytes   reserviert
50 dw cd21h INT 21h - Aufruf
52 db cbh RET FAR - Befehl
53 9 Bytes   reserviert
5c 36 Bytes   geschlossener Standard-FCB 1
6c 20 Bytes   geschlossener Standard-FCB 2,
überlagert FCB 1
80 db   Länge Eingabezeile,
zusätzlich Anfang Standard-DTA
81 127 Bytes   Befehlszeilenparameter,
beginnt mit Space, Abschluß CR
Seite zuletzt geändert am 1.5.2007