Dos-Funktionen int21h



  • Hallo,

    ich habe vor eine einfachte .txt Datei zu öffnen, diese zu lesen und den Inhalt auszugeben. Dies sollte doch, meiner Meinung nach mit den Dos-Funktionen möglich sein.

    Das Datei öffnen, scheint auch gut zu klappen, der Inhalt ist jedoch nicht identisch. Vielleicht sieht jemand von euch den Fehler oder kann mir weiterhelfen.

    TITLE Datei oeffnen				; 
    IDEAL						; Modus einschalten
    MODEL SMALL					; Speichermodul
    STACK 100					; 100 Byte für den Stack
    DATASEG						; Beginn des Datensegments
    
    	Infotext 	db 13,10,"Programm zum oeffnen einer Textdatei - Info.txt $"		; 
    	ok		db 13,10,"Datei wurde erfolgreich geoeffnet! $"				;
    	Dateiname 	db "Info.txt",0								; Infodatei, 0 maskiert das Ende
    	Ausgabe		db 10 DUP (0)								; Ausgabepuffer
    
    CODESEG						; Beginn des Codesegments
    start:						;
    	STARTUPCODE
    	mov DX,OFFSET Infotext
    	mov ah, 009h				; 
    	int 21h					;
    	mov ah, 03dh				; Dos-Funktion Datei oeffnen
    	mov DX, OFFSET Dateiname		; Namen übergeben
    	XOR CX,CX				; Dateiattribut = normal
    	int 21h					;
    	jz keinFehler				;
    	jmp Ende				;
    
    keinFehler:
    	mov DX,OFFSET ok			;
    	mov ah, 009h				;
    	int 21h					;
    
    	mov ah,03fh				; Dos-Funktion Datei lesen
    	mov bx,ax				; Datei-Handle uebergeben
    	mov cx,10				; Anzahl zu lesender Bytes
    	mov dx,OFFSET Ausgabe			;
    	int 21h
    
    	mov ah,009h	
    	mov dx,OFFSET Ausgabe			;
    	int 21h					;
    
    Ende:	mov ah,4ch				;
    	int 21h					;
    
    END start					;
    

    Besten Dank!



  • 1. Nicht dramatisch, aber es waere bestimmt sinnvoll, eine neue Zeile anzufangen, bevor du die Datei ausgibst.

    2. Entweder ist die Interrupt-Tabelle, die du benutzt, falsch oder du liest sie falsch.
    Open file ist ah=3dh, al=access mode, dx=file name. Der Wert von cx ist idR. egal (wer benutzt schon Netzwerk unter DOS?)
    Fehler werden ueber das cf (carry flag -> jc/jb, und jnc/jnb) nicht ueber das ZF (jz/je und jnz/jne) angezeigt.

    3. In ax wird von Funktion 3dh der Dateihandle zurueckgegeben. Da ist es wenig klug, diesen direkt anschliessend wieder zu ueberschreiben - zumindest wird ah (damit auch ax) bei der Ausgabe der "ok"-Textes und spaeter nochmal direkt vor dem Kopieren von ax nach bx geaendert. Das duerfte der eigentliche Fehler sein.
    siehe auch EAX ... AX in den FAQ.



  • Nobuo T schrieb:

    1. Nicht dramatisch, aber es waere bestimmt sinnvoll, eine neue Zeile anzufangen, bevor du die Datei ausgibst.

    2. Entweder ist die Interrupt-Tabelle, die du benutzt, falsch oder du liest sie falsch.
    Open file ist ah=3dh, al=access mode, dx=file name. Der Wert von cx ist idR. egal (wer benutzt schon Netzwerk unter DOS?)
    Fehler werden ueber das cf (carry flag -> jc/jb, und jnc/jnb) nicht ueber das ZF (jz/je und jnz/jne) angezeigt.

    3. In ax wird von Funktion 3dh der Dateihandle zurueckgegeben. Da ist es wenig klug, diesen direkt anschliessend wieder zu ueberschreiben - zumindest wird ah (damit auch ax) bei der Ausgabe der "ok"-Textes und spaeter nochmal direkt vor dem Kopieren von ax nach bx geaendert. Das duerfte der eigentliche Fehler sein.
    siehe auch EAX ... AX in den FAQ.

    Danke Nobuo T, ich probier das gleich mal aus.
    Mit dem CarryFlag hast du natürlich Recht. Jedoch verstehe ich die anderen Kritikpunkte unter 2. nicht ganz, was ich da falsch lesen soll. Habe doch 03dh zum Datei öffnen verwendet.



  • gamefreak schrieb:

    Jedoch verstehe ich die anderen Kritikpunkte unter 2. nicht ganz, was ich da falsch lesen soll. Habe doch 03dh zum Datei öffnen verwendet.

    Nur ah auf 3dh setzen und int 21h aufrufen reicht eben nicht. Die Funktion hat ja schliesslich noch weitere Parameter in anderen Registern, die auch richtig gesetzt werden wollen.

    Also nochmal einzeln:

    mov ah, 03dh                ; Dos-Funktion Datei oeffnen
    

    ->ok (aber s.u.)

    mov DX, OFFSET Dateiname        ; Namen übergeben
    

    ->ok

    XOR CX,CX                ; Dateiattribut = normal
    

    Potentiell nicht ok!
    Ehrlich gesagt keine Ahnung, was die Attributmaske in cl genau bewirkt, jedenfalls spielt sie in aller Regel keine Rolle.
    Was dagegen durchaus von Bedeutung ist, ist der Zugriffsmodus in al.
    Es bietet sich an, ah und al ueber ax gleichzeitig zu setzen.
    zB.

    mov ax, 3d00h
    

    Dabei wird al=0 gesetzt, die Datei also ReadOnly geoeffnet.



  • Ok super, danke für die Hinweise.

    Jetzt geht das ganze aber weiter, weißt du wie ich gezielt nach einem Textstring/Zeichenkette in einer Datei suchen kann?
    Also quasi nicht einfach nur alles ausgeben?



  • Wie wuerdest du den Algorithmus in einer prozeduralen Hochsprache (wie zB. C) schreiben?
    Wahrscheinlich irgendeinen Puffer einrichten, die Datei jeweils stueckchenweise darin einlesen und den Puffer mit strcmp o.Ae. durchsuchen...
    Genau so kannst du das auch in Assembler umsetzen.



  • Nobuo T schrieb:

    Wie wuerdest du den Algorithmus in einer prozeduralen Hochsprache (wie zB. C) schreiben?
    Wahrscheinlich irgendeinen Puffer einrichten, die Datei jeweils stueckchenweise darin einlesen und den Puffer mit strcmp o.Ae. durchsuchen...
    Genau so kannst du das auch in Assembler umsetzen.

    Ja genau, so habe ich es mir ja auch gedacht. Nur ist das in Assembler für mich noch nicht so einfach umzusetzen.
    Soll ich z.B. 10 Zeichen auslesen und die vergleichen und dann die nächsten 10 Zeichen? Wie kann ich denn überhaupt nacheinander Zeichen auslesen, funktioniert das eventuell mit einem StackPointer?

    Bin wie gesagt absoluter Neuling, daher ein dickes Sorry für die ganze rumfragerrei 😉



  • gamefreak schrieb:

    Ja genau, so habe ich es mir ja auch gedacht. Nur ist das in Assembler für mich noch nicht so einfach umzusetzen.

    Das war allerdings eine sehr grobe Skizzierung eines Algorithmus. Da solltest du dir schon naehere Gedanken drueber machen. Schreibe das Programm zB. erstmal in c, dann sollten sich Fragen wie

    gamefreak schrieb:

    Soll ich z.B. 10 Zeichen auslesen und die vergleichen und dann die nächsten 10 Zeichen? Wie kann ich denn überhaupt nacheinander Zeichen auslesen, funktioniert das eventuell mit einem StackPointer?

    von selbst erledigen.
    In diesem Ramen muesste sich wirklich alles praktisch 1:1 in Assembler umsetzen lassen.
    Warum zB. nur 10 Zeichen? In c sind sehr einfach wesentlich groessere char-Arrays moeglich, so auch in Assembler.
    Das Auslesen aus Dateien hast du doch in deinem Programm schon gemacht. Die DOS-Funktion 3fh funktioniert praktisch genau so wie "read" (o.Ae.).
    Du gibst der Funktion einen Handler, die Anzahl zu lesender bytes und einen Pointer zu einem Puffer, in den eingelesen wird. Der Datei-Pointer wird von selbst um die Anzahl gelesener Bytes erhoeht. Eine Funktion zum Verschieben dieses file-Pointers kennt DOS auch... Also wirklich alles eigentlich nichts Neues, dieses Konzept sollte unabhaengig von Assembler eigentlich schon bekannt sein.

    gamefreak schrieb:

    Bin wie gesagt absoluter Neuling, daher ein dickes Sorry für die ganze rumfragerrei 😉

    NP, nur sollte es bereits an oben erwaehnten Grundlagen hapern, wuerde ich vorschlagen, du sammelst erstmal etwas Programmiererfahrung mit einer Hochsprache, bevor du mit Assembler anfaengst.



  • Hi Nobuo,

    so richtig ist mir leider im Moment nicht klar, wie ich mit Hilfe der INterrupt-Routine einen bestimmten Textteil auslesen kann.

    Wenn das z.B. mein Textinhalt ist und ich hier die 4. Zeile auslesen soll, weiss ich gerade nicht wie ich das realisieren kann.

    Das ist ein kleiner Informationstext um die Dos-Funktion 3FH testen zukönnen.
    Dies ist die zweite Zeile.
    Dies ist die dritte Zeile.
    Dies ist die vierte Zeile.
    ENDE

    In C bzw. C++ habe ich leichte Grundkenntnisse, diese beschränken sich jedoch auf simple Operationen sowie Schleifen. Leider nicht im Umgang mit Dateien, Handles und schon gar nicht Pointer.

    Im Studium haben wir lediglich immer nur Sprachen angerissen, jeweils 1 Semester Java, C und C++. Die Inhalte waren immer diesselben, es hat sich lediglich die Syntax geändert. Lustigerweise sollen wir nun aber intensiv in ASM einsteigen... 🙄

    Also noch mal besten Dank!!



  • gamefreak schrieb:

    In C bzw. C++ habe ich leichte Grundkenntnisse, diese beschränken sich jedoch auf simple Operationen sowie Schleifen. Leider nicht im Umgang mit Dateien, Handles und schon gar nicht Pointer.

    Das sind IMHO wirklich denkbar schlechte Voraussetzungen zum Einstieg in Assembler. Ich wuerde wie gesagt empfehlen, dass du dir erstmal einen Ueberblick in C verschaffst.

    gamefreak schrieb:

    so richtig ist mir leider im Moment nicht klar, wie ich mit Hilfe der INterrupt-Routine einen bestimmten Textteil auslesen kann.

    Wenn das z.B. mein Textinhalt ist und ich hier die 4. Zeile auslesen soll, weiss ich gerade nicht wie ich das realisieren kann.

    Von sich aus kann DOS sowas nicht. Wenn du also keine Lib mit entsprechenden Funktionen hast (anzunehmen, wuesste gerade auch keine), musst du dir fuer diese Aufgabe also etwas selbst basteln. Das ist schon wieder recht fortgeschritten, also von Anfang an... erstmal musst du die Grundlagen beherrschen.

    Also mal der Versuch einer kleinen Uebersicht dazu:
    Ein Zeilenende wird in Windows/DOS idR. durch die Bytefolge 0dh, 0ah angezeigt.

    Zur Datei-E/A in DOS:
    DOS Arbeitet mit Datei-Handles.
    Ein Datei-handle ist eine Nummer (bei DOS 16Bit lang), die eine aktuell geoeffnete Datei identifiziert. Du bekommst diese Nummer beim Oeffnen (Rueckgabe von Funktion 3dh in ax) gegeben und musst sie Funktionen, die mit dieser geoeffneten Datei irgendwie arbeiten sollen, wiederum uebergeben (meistens im Register bx).

    DOS (uvm) verwalten intern fuer geoeffnete Dateien (also pro Datei-handle) einen Datei-Pointer, der auf das aktuelle Offset (in Byte gezaehlt) innerhalb der Datei zeigt. Beim Lesen/Schreiben wird dieser Pointer automatisch um die Zahl gelesener Bytes erhoeht.

    DOS bietet folgende Funktionen, die fuer dich erstmal wichtig sein koennten (bitte naeheres in Interruptliste nachschlagen - alle sind jeweils Indizes von int 21h) :
    3dh: Datei oeffnen
    3eh: Datei schliessen (uebergebener Handle wird ungueltig/wieder frei)
    3fh: Von Datei lesen
    42h: Datei-Pointer setzen (oder mit al=01 und offset =0 auslesen)

    Folgendes solltest du dir zudem anlesen, falls noch nicht bekannt:
    Ein Ueberblick ueber den Befehlssatz der 286-CPU (Referenzen sind in den FAQ verlinkt).
    Was ein Pointer/Zeiger grundsaetzlich ist.
    Wie die Speicheradressierung damit im RealMode funktioniert (FAQ ->
    Wie grundlegende Strukturen wie bedingte Bloecke (If-Bloecke) und Schleifen zu bauen sind.

    Wenn du damit durch bist, sollte die grundsaetzliche Funktion von Datei-E/A, Speicheradressierung in DOS, etc. also erstmal klar sein. Wenn nicht, frage nochmal nach, bevor du dich darauf aufbauenden abstrakteren Aufgaben wie zB. dem Einlesen bestimmter Zeilen widmest.

    Du willst also die 4. Zeile als String?
    Mein Vorschlag dazu waere, du liest vielleicht ca. 512 Bytes in einen Puffer ein und bastelst dir dann eine Schleife, die die darin auftauchenden 0dh, 0ah zaehlt (oder evtl. eben auch nur die 0ah), bzw. bei jedem 0dh, 0ah wird ein Zaehler, der die gewuenschte Zeilennummer enthaelt, dekrementiert, bis dieser 0 wird oder die Datei zuende ist.
    Dazu waere vielleicht die Befehlskombination "repnz scasb" recht praktisch.
    Hm, ich hoffe, die Grundlegende Idee dazu ist mit diesem groben Hint in etwa klar geworden.


Anmelden zum Antworten