Home

8086 Assembler (MS-DOS):
HD - hexadezimale Dateianzeige
8086 Assembler vs TurboC


Zweite Version der
Hexadezimalanzeige

Zurück zur
Assemblerauswahlseite

Hexadezimaldump in Turbo C programmiert®

Eines meiner alten in Turbo C®geschriebenen Filterprogramme stellt Dateiinhalte hexadezimal dar. Turbo C wurde um 1988 von Borland International, Inc. herausgegeben.

Das Quellprogramm ist nachfolgend gelistet:


/* Hexadezimale Anzeige. Eingabedatei und Ausgabedatei
   ueber Standardein- und -ausgabe
*/
#include <stdio.h>

/* Hauptprogramm */
main()

{
int c , i = 0 , y = 0 , cnt = 0 ;
char asc[16] ;
asc[16] = '\0' ;

while (( c = getc(stdin)) != EOF)
   {
        if (i == 0)
        {
        printf ( "%08X  " , y ) ;
        memset ( asc , (int)' ' , 16 ) ;
        }

        if ( c >= (int)' ' ) asc[i] = c ;
        else asc[i] = ' ' ;

        printf ( "%02x " , (unsigned int)c ) ;
        if (i==7) putchar (' ');

        if (++i == 16)
        {
        i=0;
        printf ( " | %s\n" , asc ) ;
        y += 16 ;
        if ( ! ( ++cnt % 8 ) ) putchar ( '\n' ) ;
        }
    }
if (i !=0) printf (" | %s\n",asc);
exit ( 0 ) ;
}  /* main */

Wendet man das Programm auf seine Quelldatei an, so ergibt nach Eintippen des Kommandos
F:\dos\TURBOC>hd <hd.c
die folgende Anzeige. Mit z.B.
F:\dos\TURBOC>hd <hd.c >hugo.txt
hätte man die vollständige Ausgabe in eine Datei umleiten können, mit
F:\dos\TURBOC>hd <hd.c | more
hätte man eine seitenweise blätterbare Anzeige ermöglicht.

Hexadezhimale Bildschirmanzeige


Das oben gezeigte Programm hat eine Schwachstelle. Nur beim Anzeigen von binären Dateien tritt diese Schwachstelle in Erscheinung: Enthält die Binärdatei ein Byte mit dem Inhalt 1AH, so wird dieses als Dateiende interpretiert und der Rest der Datei wird nicht mehr hexadezimal ausgegeben.

Die Schwachstelle lässt sich umgehen, indem man die Eingabedatei nicht über die Standardeingabe einliest. Dann muss man sie jedoch zumindest öffnen. Das Schließen der Datei erfolgt implizit beim Beendigen des Programmes durch den exit-Befehl. Der Anfang eines solchen Programmes sähe dann so aus:

/* Hexadezimale Anzeige. Eingabedatei normal eroeffnet,
   Ausgabedatei ueber Standardausgabe
*/
#include <stdio.h>

/* Hauptprogramm */
main(argc, argv)
int argc;
char *argv[];               /* Zeichenanzahl als Argument */
{
FILE    *fp, *fopen();
int c , i = 0 , y = 0 , cnt = 0 ;
char asc[16] ;
asc[16] = '\0' ;

        if (argc ==1)              /* ohne Argumente */
        {
        fputs("hd -- Hexdump\n",stderr);
        fputs("Usage: hd filename [>file]\n",stderr);
        exit (-1);
        }
        if ((fp = fopen(*++argv,"rb")) == NULL)
        {
        fputs("hd -- can't open ",stderr);
        fputs(*argv,stderr);
        /*    fputs"\n",stderr); */
        exit (-1);
        }

while (( c = getc(fp)) != EOF)

Hexadezimaldump in Assembler programmiert

Ein wenig frustriert es doch: Das praktisch funktionsgleiche Assemblerprogramm erfordert deutlich mehr Schreibarbeit als das C-Programm. Erschwerend kommt beim Assemblerprogramm hinzu, dass die Bildschirmausgabe über die Handle-Aufrufe erfolgen muss, damit sie über die Standardausgabe läuft. Auch gibt es in C die Funktion printf. Eine der Formatangaben von printf ermöglicht die hexadezimale Darstellung - z.B. in der Zeile printf ( "%08X " , y ) ;. Im Assemblerprogramm mussten die beiden verwendeten Varianten der hexadezimale Anzeige mit nachfolgender Standardausgabe eigens programmiert werden.

Es gibt ein kleines Dankeschön: Das ausführbare Programm aus dem Assembler ist unter 900 Bytes groß. Das unter Turbo C erzeugte ausführbare Programm ist mit über 7000 Bytes mehrfach größer.

Das Programm ist im MODEL SMALL programmiert, so dass daraus ein .EXE-Programm entsteht. Aus der  i Routinensammlung für COM-Programme wird der Teil bis zur Zeile
;------ beginn der codeerzeugung ------
mit include MAC32.MAC eingebunden. Außerdem sind in MAC32.MAC diese drei Zeilen hinzugekommen:

wcrlf  db      cr,lf,'$'       ;ausgabestring zeilenvorschub
wcolon db      ':'             ;ausgabestring : ggf mit spaces
wspc   db      spc,spc         ;ausgabestring spaces

.MODEL SMALL
.386
;======================================
;Hexadezimaldump
;Eingabedatei ueber Standardeingabe,
;Ausgabe/Anzeige ueber Standardausgabe
;======================================
;
false   equ     0
true    equ     not false

dump$   equ     false   ;fuer mdump, rdump
prompt$ equ     false   ;fuer enter, inkjn
intasc$ equ     false   ;fuer int2asc, asc2int
string$ equ     false   ;fuer fill, cmpstr, lenstr, instrg, movstr, ucaseg, lcaseg

pipe    equ     0b3h    ;Pipe-Zeichen (senkrechter Strich)

.STACK  256
.DATA
        include MAC32.mac
;Arbeitsfelder
address dd      0       ;adresse
text    db      0       ;zu verarbeitendes zeichen
cnt     db      0       ;zeilenzaehler
asc     db      17 dup (spc)    ;zeichenausgabebereich
asccnt  dw      0               ;zeichenzaehler fuer ausgabebereich
wbuf    db      10 dup (0)      ;allgemeiner puffer zur zeichenausgabe
wpipe   db      pipe,spc        ;pipe-symbol plus space
;in MAC32.mac sind definiert:
;wcrlf  db      cr,lf,'$'       ;ausgabestring zeilenvorschub
;wcolon db      ':'             ;ausgabestring : ggf mit spaces
;wspc   db      spc,spc         ;ausgabestring spaces

.CODE
_start: mov     ax,@DATA        ;Initialisieren des Datensegment-Registers
        mov     ds,ax
        mov     es,ax           ;auch ES auf datenseqment zeigen lassen
        jmp     around

;fill: der ueber di adressierte speicher wird auf den in al mitgegebenen wert
;gesetzt. mit cld wird eine aufsteigende fuellrichtung erzwungen.
;======
fill    macro   nach,byte,laenge
        ifdif   <di>,<nach>
        mov     di,offset nach
        endif
        ldreg   cx,laenge
        cld
        ldreg   al,<b byte>
        rep     stosb
        endm

;druckt die mit si adressierte zeichenkette
;die zeichenkette darf keine steuerzeichen ausser cr und attribute enthalten
;sie endet mit 0h
printa:
        push    ax
        cld

printa5:
        lodsb            ;zeichen
printa_b9:
        or      al,al    ;ob fertig ?
        jz      printa7  ;ja
        call    printchr
        jmp     printa5

printa7:
        pop     ax
        ret

;======
if1                      ;macros nur im 1.durchlauf
print_crlf macro
        call    $print_crlf     ;;ausgabe cr lf
        endm            ;;zum bildschirm


print_chr macro char    ;;1 zeichen zum
        ifnb    <char>
        ldreg   al,char ;;bildschirm
        endif
        call    printchr
        endm
endif                   ;if1

;unterprogramm zur anzeige einer 8bit-ziffer in hexadezimal
;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:
        mov     bx,offset wbuf
        and     al,0fh
        daa
        add     al,0f0h
        adc     al,40h

;ausgabe zeichen in al nach handle 1
printchr:
        cmp     al,cr   ;cr?
        je             $print_crlf
printchr1:
        push    bx
        push    cx
        mov     wbuf,al
        fwrit   1,wbuf,1        ;1 Zeichen aus wbuf nach handle 1
        pop     cx
        pop     bx
        ret

$print_crlf:
        push    bx
        push    cx              ;Ausgabe crlf nach handle 1
        fwrit   1,wcrlf,2
        pop     cx
        pop     bx
        ret

;unterprogramm zur 4-stelligen adressausgabe (8 hexadezimalziffern)
pa00:
        mov     bx,offset address + 3
        mov     cx,4            ;adresse vierstellig hexa
pa10:   push    cx              ;ausgeben (rueckwaerts)
        mov     al,[bx]
        dec     bx
        push    bx              ;upro byte_hex zerstoeert bx
        call    byte_hex
        pop     bx
        pop     cx
        loop    pa10
        fwrit   1,wcolon,2      ;: mit folgendem space anzeigen
        ret

around:
;hauptprogramm

ha00:   call    pa00            ;adressanzeige
        mov     cx,16           ;16 Bytes ergeben eine Zeile

ha01:   push    cx              ;sichern Schleifenzaehler
        mov     bx,0            ;Handle console (eingabe)
        mov     cx,1            ;1 Zeichen lesen
        mov     dx,offset text
        int21   3fh
        or      ah,al           ;ax ist 0 wenn EOF
        jz      fertig

        mov     al,text
        call    byte_hex        ;Zeichen hexadezimal ausgeben
        fwrit   1,wspc,1        ;mit folgendem space
        pop     cx
        push    cx
        cmp     cx,9
        jne     ha05
        fwrit   1,wspc,1        ;doppelter abstand nach 8 zeichen
ha05:   mov     al,text

        cmp     al,00h          ;00, cr, lf, tab werden als
        je     space            ;punkt angezeigt

        cmp     al,cr
        jz      space

        cmp     al,lf
        jz      space

        cmp     al,tab
        jne     ha10
space:  mov     al,'.'
ha10:   mov     bx,offset asc
        add     bx,asccnt       ;ascii-zeichen zum
        mov     [bx],al         ;ausgabebereich
        inc     asccnt
        pop     cx              ;schleifenzaehler
        loop    ha01

;neue Zeile:
        fwrit   1,wpipe,2       ;pipe mit space
        fwrit   1,asc,16        ;ausgabebereich anzeigen
        fill    asc,spc,16      ;ausgabebereich loeschen
        mov     asccnt,0        ;zeichenzaehler loeschen
        add     address,10h
        print_crlf              ;neue Zeile
        mov     ah,cnt
        inc        ah
        mov        cnt,ah
        test         ah,07h
        jnz     ha00            ;keine leerzeile zusaetzlich
        print_crlf              ;eine leerzeile zusaetzlich
        jmp     ha00

fertig:
        fwrit   1,wpipe,2       ;pipe mit space
        fwrit   1,asc,16        ;ausgabebereich anzeigen
        print_crlf              ;neue Zeile
        MOV     AX,4C00h        ;EXIT
        INT21
        END     _start


Seite zuletzt geändert am 5.8.2012