Dies ist ein 32-bit-Nachbau des 16-bit-Programms HD.exe.
Allerdings unterscheiden sie sich etwas im Format des Aufrufs: Bei der 32-bit-Version wird die Eingabedatei nicht über die Standardeingabe abgewickelt, sondern der Zugriff erfolgt über ein vom Betriebssystem zugeteiltes Handle. Die Ausgabe erfolgt weiterhin über die Standardausgabe. Sie kann somit in eine Datei umgeleitet werden. Bei dem verwendeten Speichermodell „flat” ist es häufig zweckmäßig, die zu bearbeitende Datei nicht satzweise zu lesen, sondern sie auf einen Schlag vollständig in den Speicher einzulesen. Der MASM32-SDK enthält in \masm32\examples\exampl07\fileio zwei fast vollständige Beispiele über die Dateibehandlung mit Handles. |
AblaufbeispielDie beiden ersten Beispiele zeigen die beiden Meldungen. Sie werden ausgegeben, wenn das Programm keine Eingabedatei verarbeiten kann.Das dritte Beispiele ist ein normaler Programmablauf. Die verarbeitete Datei ist im Windows-Zeichensatz codiert. Daraus erklärt sich der falsch dargestellte Umlaut im Wort „enthält”. |
0:20:30:01 C:\masm32\Uebung>hd Das Programm liest eine Datei ein und zeigt ihren Inhalt hexadezimal auf der Standardausgabe. Die Standardausgabe ist umleitbar. Aufruf: HD dateiname Weiter mit beliebiger Taste... 0:20:30:05 C:\masm32\Uebung>hd nix_da.txt Datei nicht gefunden Weiter mit beliebiger Taste... 0:20:32:20 C:\masm32\Uebung>hd hugo.txt 00000000: 74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 | the quick brown 00000010: 66 6F 78 20 6A 75 6D 70 73 20 6F 76 65 72 20 74 | fox jumps over t 00000020: 68 65 20 6C 61 7A 79 20 64 6F 67 20 62 61 63 6B | he lazy dog back 00000030: 20 2D 0D 0A 0D 0A 44 69 65 73 65 72 20 53 61 74 | -....Dieser Sat 00000040: 7A 20 65 6E 74 68 E4 6C 74 20 61 6C 6C 65 20 5A | z enthõlt alle Z 00000050: 65 69 63 68 65 6E 20 76 6F 6E 20 61 20 62 69 73 | eichen von a bis 00000060: 20 7A | z.............. |
Das QuellprogrammEine Interpretation steht unterhalb des Quellprogrammtextes.comment # Dies Programm gibt eine Datei als Hexadezimaldump auf der Standardausgabe aus. -------------------------------------------- # .386 .model flat, stdcall option casemap :none ; case sensitive ; ------------------------------------------ .XLIST include \masm32\include\windows.inc include \masm32\macros\macros.asm include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc include c:\masm32\include\msvcrt.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib includelib \masm32\lib\msvcrt.lib .LIST ; ------------------------------------------ tab equ 9 spc equ ' ' cr equ 0dh lf equ 0ah pipe equ "|" Main PROTO ;erforderlich weil Main eine Prozedur ist .data notfound db "Datei nicht gefunden" crlf db cr,lf,0 ;ergaenzt ggf. notfound hilfe db "Das Programm liest eine Datei ein und zeigt ihren Inhalt",cr,lf db "hexadezimal auf der Standardausgabe. Die Standardausgabe ist",cr,lf db "umleitbar.",cr,lf db "Aufruf: HD dateiname",cr,lf,0 ;filename db "testfile.txt",0 ;nun unbenutzte Testhilfe ;Arbeitsfelder address dd 0 ;adresse (wird hexadezimal angezeigt) text db 0 ;zu verarbeitendes zeichen cnt db 0 ;zeilenzaehler lz db '........: ',50 dup (spc),pipe,spc,16 dup ('.'),cr,lf,0 llz equ $ - lz dz db llz-19 dup (spc) asc db 19 dup (spc) ;zeichenausgabebereich + cr lf 0 dzpos dd 0 ;positionszeiler druckpuffer asccnt dd 0 ;zeichenzaehler fuer ausgabebereich zeichen db " ",0 ;1 auzugebendes zeichen if1 ;macros nur im 1.durchlauf print_crlf macro invoke StdOut, addr crlf ;;Ausgabe cr lf zur Standardausgabe endm endif ;if1 ; ########################################## .code start: invoke Main invoke StdErr, chr$(cr,lf,"Weiter mit beliebiger Taste...") invoke ExitProcess,0 ; ########################################## Main proc LOCAL hFile :DWORD ; file handle LOCAL bwrt :DWORD ; variable for bytes written ; LOCAL cloc :DWORD ; current location variable LOCAL txt :DWORD ; text handle LOCAL flen :DWORD ; file length variable LOCAL hMem :DWORD ; allocated memory handle LOCAL clBuffer[128]:BYTE ; command line buffer invoke GetCL,1,ADDR clBuffer ; get arg 1 .if eax != 1 ; es muss genau 1 Argument angegeben sein invoke StdErr, addr hilfe jmp TheEnd .endif .if rv(exist,ADDR clBuffer) == 0 ; if file not exists invoke StdErr, addr notfound jmp TheEnd .endif ; ------------------------------------------ ; open the file, read and display its content ; ------------------------------------------ ; Testversionen: ; invoke CreateFile,reparg(ADDR clBuffer), ; GENERIC_READ,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL ; mov hFile,eax ;handle ; ;Das fopen-Makro wird nicht verwendet, da es die Datei im Schreib-/ Lesemodus oeffnet. ; mov hFile, fopen(ADDR clBuffer) ; open the existing file ; ; Verwendete Version mit InputFile, siehe ; Interaktive Hilfe, High Level Macro Help, Macro Categories, File IO-Macros. ; nachfolgende Befehle sind bei Verwendung von InputFile nicht erforderlich: ; mov flen, fsize(hFile) ; get its length ; mov hMem, alloc(flen) ; allocate a buffer of that size ; mov bwrt, fread(hFile,hMem,flen) ; read data from file into buffer ; fclose hFile ; close the file mov hMem, InputFile(ADDR clBuffer) mov flen, ecx ; invoke StripLF,hMem ; invoke StdOut und seine Ableitungen verweigern bei langen Strings ; (vielleicht ueber 64 kB?) die Ausgabe auf Konsole. Bei Ausgabeumleitung ; in eine Datei funktioniert es jedoch! Bei Ausgabe in die more-pipe wird ; ebenfalls abgeschnitten. ; Hier beginnt die hexadezimale Ausgabe der in hMem gespeicherten Datei cld ;richtung aufwaerts ha00: lea esi,lz ;"leerzeile" initialisiert lea edi,dz ;"druckzeile" mov ecx,llz @@: mov al,[esi] mov [edi],al inc esi inc edi loop @B call pa00 ;adressanzeige inc dzpos ;ueber : spc hinweg inc dzpos mov ecx,16 ;16 Bytes ergeben eine Zeile ha01: push ecx ;sichern Schleifenzaehler mov esi,hMem ;Zeichenposition im Speicher mov ecx,flen or ecx,ecx ;ist das Dateiende erreicht? jz fertig ;ja! dec ecx ;nein: Dann 1 herabzaehlen mov flen,ecx mov al,[esi] mov text,al ;Zeichen sichern inc esi mov hMem,esi ;Zeichenposition vorruecken call byte_hex ;Zeichen hexadezimal in den druckpuffer inc dzpos ;ueber spc hinweg pop ecx push ecx cmp ecx,9 jne ha05 inc dzpos ;doppelter Abstand nach 8 zeichen ha05: mov al,text ;Zeichen aus Sicherung cmp al,00h ;00, cr, lf, tab werden als je ha12 ;punkt angezeigt. der punkt ;steht bereits im druckpuffer cmp al,cr je ha12 cmp al,lf je ha12 cmp al,tab je ha12 ha10: lea ebx,asc add ebx,asccnt ;ascii-zeichen zum mov [ebx],al ;ausgabebereich ha12: inc asccnt pop ecx ;Schleifenzaehler loop ha01 ;aufbereitete Zeile auf StdOut ausgeben: invoke StdOut, addr dz mov asccnt,0 ;zeichenzaehler loeschen add address,10h ;Adresszaehler um 16 erhoehen mov ah,cnt ;alle 7 Zeilen eine Leerzeile einfuegen inc ah mov cnt,ah test ah,07h jnz ha00 ;keine Leerzeile zusaetzlich print_crlf ;eine Leerzeile zusaetzlich jmp ha00 fertig: ;aufbereitetes Zeilenfragment auf StdOut ausgeben: invoke StdOut, addr dz free hMem ; free the allocated memory TheEnd: ret Main endp ; Diverse Unterprogramme ; Ausgabe Zeichen in al nach standardausgabe ;--------------------------- printchr: push ebx push ecx push edx cmp al,cr ;cr? je $print_crlf mov zeichen,al invoke StdOut, addr zeichen pop edx pop ecx pop ebx ret $print_crlf: invoke StdOut, addr crlf pop edx pop ecx pop ebx ret ;Einspeichern einer 8bit-Ziffer als 2 Byte ascii (hexadezimal) ;in den mit bx adressierten Puffer ;die ziffer wird in al uebergeben byte_hex: push ax shr al,1 shr al,1 shr al,1 shr al,1 call byte_hex1 pop ax byte_hex1: and al,0fh daa add al,0f0h adc al,40h ;Einspeichern Zeichen in al in den mit ebx adressierten Puffer printchr1: mov ebx,dzpos mov [ebx],al inc ebx mov dzpos,ebx ret ;Unterprogramm zur 4-stelligen Adressausgabe (8 Hexadezimalziffern) pa00: lea ebx,dz ;Anfangsadresse Druckzeile mov dzpos,ebx mov ebx,offset address + 3 mov ecx,4 ;Adresse vierstellig hexa @@: push ecx ;ausgeben (rueckwaerts) mov al,[ebx] dec ebx push ebx ;Upro byte_hex zerstoert ebx call byte_hex pop ebx pop ecx loop @B ret ; ########################################## end start |
Interpretation des Quellprogramms.data: Der Datendefinitionsbereich ist in Bedienungshinweise und Arbeitsfelder unterteilt. Beim Label lz steht die Grundform der im hexadezimalen Ausgabezeile bestehend aus
Die jeweils anzuzeigenden Ausgabezeile hat die gleiche Struktur wie lz. Die Aufbereitung erfolgt jedoch in den beiden Feldern dz und asc.Das Feld dz enthält den linken Teil der Zeile bis einschließlich zum senkrechten Trennstrich. asc beinhaltet den Klartextteil einschließlich Zeilenwechsel und abschließender Null. Die Lange von dz wird mittels des Symbols llz errechnet. dzpos, asccnt und zeichen: dzpos und asccnt sind Hilfsfelder. Sie werden zur Aufbereitung der Ausgabezeile benötigt. Einige auszugebenden Zeichen werden im Feld zeichen unmittelbar vor die Null gestellt, da der Invoke-Aufruf zur Zeichenausgabe es so haben möchte. start: Zur Programmierung der Ausgabe des Schlusstextes „Weiter mit beliebiger Taste...” wird das in \masm32\macros\macros.asm definierte Makro chr$ genutzt. Dies Macro bewirkt die Ablage seines Arguments im Datenbereich. Die Anzeige selbst wird nicht über StdOut, sondern über StdErr durchgeführt. Auch StdErr zeigt normalerweise auf den Bildschirm. Dadurch wird erreicht, dass Bedienungshinweise nicht in der umgeleiteten Standardausgabe erscheinen. Main proc beginnt mit einer Definition lokaler Variablen zur Verwaltung von Dateihandles und einem Pufferbereich für den Inhalt der Kommandozeile. Danach wird die Kommandozeile überprüft, ob genau ein Aufrufargument angegeben wurde. Aufrufargumente zur Dateiumleitung (sie beginnen mit <, > oder |) gelten hierbei nicht als Aufrufargumente! Wurde nicht genau ein Aufrufargument angegeben, wird das Programm mit Anzeige des bei hilfe hinterlegten Textes beendet. Ebenfalls mit einem Hinweis wird das Programm beendet, wenn das Aufrufargument nicht auf einen vorhandenen Dateinamen verweist. ; open the file, read and display its content: In dem hier zugrunde gelegten Beispiel unter \masm32\examples\exampl07\fileio wird hier das im MASM32-SDK definierte Makro fopen verwendet. Ich habe es nicht genutzt, das es die Datei im Update-Modus öffnet. Tatsächlich soll die Datei lediglich gelesen werden. Besser wäre in diesem Fall ein Invoke-Aufruf auf (einzeilig) CreateFile,reparg(ADDR clBuffer),GENERIC_READ,NULL,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULLgewesen. Noch besser geeignet ist InputFile. In der codierten Form mov hMem, InputFile(ADDR clBuffer)wird die im Feld clBuffer genannte Datei geöffnet, ein ihrer Gesamtlänge entsprechender Hauptspeicherbereich wird unter hMem reserviert und mit dem gesamten Dateiinhalt gefüllt. Dann wird die Datei geschlossen und die Dateilänge wird im Register ecx zurückgemeldet. Es gibt sie ja, diese Dateien ohne Inhalt. Wenn ecx den Inhalt Null hätte, könnte man das Programm hier sofort beenden. Dies Programmende ist etwas versteckt weiter unten hinter dem Label ha01 programmiert. So sähe in diesem Fall die Ausgabe aus: |
0:07:46:55 C:\masm32\Uebung>hd hugo6.txt 00000000: | ................ Weiter mit beliebiger Taste... 0:07:47:07 C:\masm32\Uebung> |
ha00: Hier beginnt die Aufbereitung der Ausgabezeile durch Kopie von lz nach dz. Fortgesetzt wird sie durch Aufruf der mit pa00: beginnenden Unterprogrammsammlung. Sie erzeugt die hexadezimale Anzeige der Adresse links in der Ausgabezeile. Die Unterprogrammsammlung wurde von dem Vorgängerprogramm übernommen und an die 32-Bit-Umgebung angepasst.
ha01 bis ha12: Auch hier stammt die Programmlogik aus dem eben verlinkten Vorgängerprogramm. Bei ha05 kann man ggf. noch das Glockenzeichen abfangen. Der Vergeichsbefehl dazu lautet cmp al,07h. |