Dateien für direkten Datenzugriff bestehen aus Datensätzen gleicher Länge. Dies ermöglicht es, auf jeden einzelnen Datensatz einzeln zugreifen zu können, ohne ihn vorher suchen zu müssen. Man weiß, der Zugriff soll auf den 175. Datensatz erfolgen.
Typische Textdateien, wie sie mit einem Texteditor erstellt werden, haben unterschiedliche Zeilenlängen. Man kann jede Zeile als Datensatz auffassen, denn das Ende jeder Zeile wird bei Windows durch die Zeichenfolgen für das Zeilenende (CR und LF) oder das absolute Dateiende markiert. Anders bei Direktzugriffsdateien: Sie benötigen keine Markierung des Satzendes, denn jeder Satz hat die gleiche Länge. Ist jeder Satz 50 Bytes lang, so beginnt der dritte Satz der Datei mit dem 101. Byte der Datei und hört mit dem 150. Byte auf. Textdateien mit ihrer unterschiedlichen Satzlänge verändern ihre Länge, wenn Zeichen hinzugefügt oder entfernt werden. Anders bei Direktzugriffsdateien: Sie werden vor ihrer Benutzung als inhaltslose Datei in der erkennbaren Länge angelegt. Die Inhalte der Datensätze können sich ändern, aber die Länge der Datei bleibt gleich. Zum Ändern der Länge der Datei sind besondere Maßnahmen erforderlich! Auf dieser Webseite wird ein Programm zum Anlegen einer neuen Direktzugriffsdatei beschrieben. Damit man mit der Datei Versuche anstellen kann, steht in jedem ihrer Sätze die Nummer des Satzes. Dazu ist es erforderlich, aus einer binären Zahl eine ASCII-codierte Zahl zu machen. Dazu wurde ein kleines Makro entwickelt und mit dem nachfolgenden speziellen Testprogramm erprobt. Hinweise zum Testprogramms stehen unterhalb der Programmauflistung. Man hätte die Umwandlung der binären Zahl in ASCII auch mit vorgefertigten Funktionen des MASM32-SDKs realisieren können. Wie dies funktioniert geht aus „Plausibilieren der Eingabe und Übersetzungen zwischen Ganzzahl und String” hervor. |
Testprogramm für das Makro int2asc und Aufbau des AusgabepuffersEine Interpretation steht hinter dem Programmlisting.comment * --------------------------- Testprogramm MACRO zur Codewandlung Binärzahl in AX nach ASCII und Einspeichern der ASCII-Zahl rechtsbündig bis Adresse in edi und anschliessend Aufbau des Ausgabepuffers aus dem die initialisierte Direktzugriffsdatei erstellt wird Build as a CONSOLE mode application mymasm32rt.inc - siehe fredriks.de/8086asm/masm321.php?f=2#mymasm32rt ---------------------------------------- * debugFT equ FALSE include c:\masm32\include\mymasm32rt.inc Main PROTO ;erforderlich weil Main eine Prozedur ist ; ------------ ; Lokales Makro ; erzeugt aus binaerzahl in ax eine dezimalzahl und baut sie rueckwaerts ab di ; im speicher auf. ; verfahren: division mit 10d, rest -> speicher ; ------------ int2asc MACRO mov bx,10d ;dezimalzahl 10 zum dividieren mov cl,30h ;fuer codewandlung hex 00 bis hexa 09 in ASCII @@: xor dx,dx ;kurze zahl (dx war 0) div bx ;nun rest in dx, ganzzahl in ax or dl,cl ;rest nun im ASCII-code mov [edi],dl dec di or ax,ax ;fertig wenn ganzahl = 0 jnz @B ENDM ; Konstanten ; ------------------------------------------------ .data satzanfang db '0000' ;gesamte satzlaenge 50 bytes satzrest db '. Datensatz' db 35 dup(0) satzlaenge equ $ - satzanfang satzanzahl equ 50 lbuffer equ satzanzahl * satzlaenge buffer db lbuffer dup (1) ;Defaultwerte fname db 'C:\temp\versuch.txt',0 lfname equ $ - fname satznr dw 1 .code start: SetConsoleCaption "Erzeugt aus Binärzahl in ax eine Dezimalzahl" invoke Main inkey invoke ExitProcess,eax ;-------------- Main PROC DumpMem offset satzanfang, 32, "satzanfang vorher" DumpMem offset buffer, 32, "bufferanfang vorher" mov edi,offset satzrest dec edi push edi mov ecx,satzanzahl ;-------------- ; schleife tobuffer wandelt binaere satznr nach ASCII, fuegt ; die ASCII-satznr in den satz ein und kopiert den satz in den ; ausgabedateipuffer buffer ;-------------- tobuffer: mov ax,satznr push ecx ;schleifenzaehler fuer tobuffer int2asc pop ecx mov ax,satznr ;lfd. Satznummer pop edi ;Zieladresse fuer satz wieder herstellen push edi ;adresse satzzaehler push ecx ;schleifenzaehler fuer tobuffer ;-------------- ; ermittelt zieladresse innerhalb des ausgabedateipuffers aus ; bufferanfang + (satznr - 1) * satzlaenge ;-------------- xor eax,eax mov ax,satznr ;lfd satznr dec ax mov bl,satzlaenge mul bl ;abstand ab anfang buffer nun in eax mov edi,offset buffer add edi,eax mov esi,offset satzanfang mov ecx,satzlaenge ;-------------- ; uebertraegt satz in satzlaenge zum buffer ;-------------- @@: mov al,[esi] mov [edi],al inc esi inc edi loop @B pop ecx ;schleifenzaehler fuer tobuffer pop edi ;zieladresse fuer satz wieder herstellen push edi ;zieladresse sichern mov ax,satznr ;lfd satznr vorsetzen inc ax mov satznr,ax loop tobuffer pop edi ;zieladresse fuer satz wieder herstellen DumpMem offset satzanfang, 32, "satzanfang nachher" DumpMem offset buffer,32, "erster Satz im buffer" DumpMem offset buffer+2450,32, "letzter Satz im buffer" print offset satzanfang,13,10,0 mov eax,TRUE ret Main ENDP end start |
Interpretation des Testprogramms für das Makro int2ascDas Makro in dieser Form kann nur Binärzahlen von 00h bis 0ffh verarbeiten. Als Ergebnisse sind somit dezimal 0 bis 255 möglich.div bx ;nun rest in dx, ganzzahl in ax: Der Divisionsbefehl führt die Division AX/BX=AX,DX durch. Das heißt, nach jeder Division durch 10 steht in DX der Rest als Ziffer zwischen von 0 bis 9. In AX steht der ganzzahlige Anteil aus der Division durch 10. Beispiel:
mov [edi],dl dec di Es reicht aus, die Adressreduzierung auf di zu begrenzen, da die beiden höherwertigen Bytes von edi aus lauter Nullen bestehen und bestehen bleiben. .data: Bei den Datendefinitionen fällt die Verwendung von equ besonders auf. Dadurch wird es ermöglicht, Satzanzahl und Satzlänge für das gesamte Programm durch Änderung von nur zwei Zahlen vollständig anzupassen. Main PROC: Die „Prozedur” besteht auf einer Schleife mit der Schleifenvariablen von satzanzahl in Einerschritten absteigend. Die Satznummern sollen jedoch in Einerschritten ab 1 bis satzanzahl aufsteigend in den Datensätzen erscheinen. Deshalb wird in der Variablen satznr die Satznummer in der Gegenrichtung aufwärts gezählt. Das gewählte Verfahren mit Pflege des Satzzählers ist zwar offensichtlich, aber es ist auch dümmlich. Eleganter und effizienter wäre es gewesen, die Satznummer aus dem laufenden Schleifenzähler zu ermitteln:
Satznummer=Satzanzahl + 1 - Schleifenzähler. Die erstellten Datensätze könnten einzeln in die Datei geschrieben werden. Effizienter ist es jedoch, sie in einem großen Speicherpuffer abzulegen und den gesamten Puffer am Programmende mit einem einzigen Schreibbefehl in die Datei zu schreiben. Das Testprogramm baut den gesamten Puffer auf. Allerdings fehlt noch die Dateibehandlung. Damit die erstellten Datensätze auf die richtige Position innerhalb des Ausgabedateipuffers kopiert werden, ist eine Adressberechnung mittels Satzlänge und Satznummer erforderlich.
Das Programm zum Erzeugen der anfänglichen DirektzugriffsdaeiDie Ergänzungen des Testprogramms zum fertigen Programm sind minimal. Es brauchte nur die Ausgabe in die Datei ergänzt zu werden. Außerdem wird am Programmende ein Hinweis auf den Dateinamen der erstellten Datei gezeigt. Letztlich erfolgte eine Optimierung bei der Kopie des Datensatz in den Ausgabedateipuffer durch Anwendung des Befehls rep movsb. Die Vorlage für eine noch schnellere laufzeitminimierte Variante ist bei den MASM32-SDK Beispielen unter \masm32\m32lib\memcopy.asm zu bewundern.Zusätzlich aufgenommen wurde die Prüfung, ob die angeforderte Satznummer im zugelassenen Bereich ist. Ist sie es nicht, erfolgt keine Verarbeitung. Eine Verarbeitung mit einer angeforderten Satznummer größer als 50 würde zu einer Vergrößerung der Datei führen.
Hinweise zur Programmierung der Dateiausgabe stehen unterhalb der Programmauflistung. |
Das Programm zum Erzeugen der anfänglichen Direktzugriffsdaei C:\temp\versuch.txtcomment * --------------------------- Testprogramm zur Codewandlung Binärzahl in AX nach ASCII und Einspeichern der ASCII-Zahl rechtsbündig bis Adresse in edi Build as a CONSOLE mode application mymasm32rt.inc - siehe fredriks.de/8086asm/masm321.php?f=2#mymasm32rt ---------------------------------------- * debugFT equ FALSE include c:\masm32\include\mymasm32rt.inc Main PROTO ;erforderlich weil Main eine Prozedur ist ; ------------ ; Locales Makro ; erzeugt aus binaerzahl in ax eine dezimalzahl und baut sie rueckwaerts ab di ; im speicher auf. ; verfahren: division mit 10d, rest -> speicher ; ------------ int2asc MACRO mov bx,10d ;dezimalzahl und baut sie rueckwaerts ab di mov cl,30h ;fuer codewandlung hex 00 bis hexa 09 in ASCII @@: xor dx,dx ;kurze zahl (dx war 0) div bx ;nun rest in dx, ganzzahl in ax or dl,cl ;rest nun im ASCII-code mov [edi],dl dec di or ax,ax ;fertig wenn ganzahl = 0 jnz @B ENDM ; Konstanten ; ------------------------------------------------ .data satzanfang db '0000' ;gesamte satzlaenge 50 bytes satzrest db '. Datensatz' db 35 dup(0) satzlaenge equ $ - satzanfang satzanzahl equ 50 lbuffer equ satzanzahl * satzlaenge buffer db lbuffer dup (1) ;Defaultwerte fname db 'C:\temp\versuch.txt',0 lfname equ $ - fname crlf db 13,10,0 satznr dw 1 .code start: SetConsoleCaption "Erzeugt aus Binärzahl in ax eine Dezimalzahl" invoke Main inkey invoke ExitProcess,eax ;------------------------------- Main PROC LOCAL hFile :DWORD LOCAL rval :DWORD ;Anzahl geschriebener Bytes DumpMem offset satzanfang, 32, "satzanfang vorher" DumpMem offset buffer, 32, "bufferanfang vorher" mov edi,offset satzrest dec edi push edi mov ecx,satzanzahl ;-------------- ; schleife tobuffer wandelt binaere satznr nach ASCII, fuegt die ASDCII-satznr ; in den satz ein und kopiert den satz in den ausgabedateipuffer buffer ;-------------- tobuffer: mov ax,satznr push ecx ;schleifenzaehler fuer tobuffer int2asc pop ecx mov ax,satznr ;lfd. Satznummer pop edi ;Zieladresse fuer satz wieder herstellen push edi ;adresse satzzaehler push ecx ;schleifenzaehler fuer tobuffer ;-------------- ; ermittelt zieladresse innerhalb des ausgabedateipuffers aus ; bufferanfang + (satznr - 1) * satzlaenge ;-------------- xor eax,eax mov ax,satznr ;lfd satznr dec ax mov bl,satzlaenge mul bl ;abstand ab anfang buffer nun in eax mov edi,offset buffer add edi,eax mov esi,offset satzanfang mov ecx,satzlaenge rep movsb comment # ------------- uebertraegt satz in satzlaenge zum buffer die auskommentiere befehlsfolge leistet das Gleiche wie obiger rep movsb ----------------------- @@: mov al,[esi] mov [edi],al inc esi inc edi loop @B -------------# pop ecx ;schleifenzaehler fuer tobuffer pop edi ;zieladresse fuer satz wieder herstellen push edi ;zieladresse sichern mov ax,satznr ;lfd satznr vorsetzen inc ax mov satznr,ax loop tobuffer pop edi ;zieladresse fuer satz wieder herstellen DumpMem offset satzanfang, 32, "satzanfang nachher" DumpMem offset buffer,32, "erster Satz im buffer" DumpMem offset buffer+2450,32, "letzter Satz im buffer" ; print offset satzanfang,13,10,0 ;nur zum Testen! ; ----------------- ; ausgabedatei erzeugen, buffer schreiben, Datei schließen und fertig ; ----------------- .if rv(exist,ADDR fname) != 0 ;if file already exists test fdelete(ADDR fname), eax ;delete it .endif mov hFile, fcreate(ADDR fname) ;create the file mov rval, fwrite(hFile, ADDR buffer,lbuffer) fclose hFile print "Die Ausgabedatei ist ",0 print offset fname print offset crlf mov eax,TRUE ret Main ENDP end start |
Erzeugen der anfänglichen Direktzugriffsdatei: Hinweise zu den DateizugriffenVor dem Öffnen der Ausgabedatei wird mittels .if rv(exist,ADDR fname) != 0> überprüft, ob es bereits eine solche Datei gibt. Falls ja, wird diese gelöscht.Anschließend wird die Ausgabedatei kreiert. Der im Dateiausgabepuffer angelegte vollständige Dateiinhalt wird mit einem einzigen Schreibbefehl in die Datei geschrieben. Die Datei wird dann mit fclose hFile geschlossen. Zum Abschluss wird der Dateiname am Bildschirm angezeigt. ![]() Natürlich möchte man prüfen, ob es geklappt hat. Wie ersichtlich, hat die Datei die erwartete Länge von 2500 Bytes. Der Notepad-Editor zeigt, dass die Daten keine Codierung für den Zeilenwechsel enthalten. Beim ersten Datensatz wurde in ASCII-Codierung der Vermerk 0001. Datensatz eingetragen. Bei den nachfolgenden Datensätzen wurde die Satznummer bis einschließlich 0050 hochgezählt. |