MASM32-SDK (Windows Konsolapplikationen):
HD.exe - hexadezimale Dateianzeige

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.

Ablaufbeispiel
Die 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 Quellprogramm
Eine 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

  • dem Adressteil ('........: '),
  • 50 Spaces,
  • dem senkrechten Trennungsstrich,
  • 16 Punkten für den anzuzeigenden Klartext sowie
  • dem Zeilenwechsel und der abschließenden Null.
Das Symbol llz wird aus dem Zuordnungszähler erzeugt. Es enthält die Lange der unter lz definierten Ausgabezeile.

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,NULL
gewesen.

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.

Letztes Upload: 14.07.2023 um 06:19:40 • Impressum und Datenschutzerklärung