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.
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 obersten Programmlisting auf dieser Seite. 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 Routinensammlung für COM-Programme wird der Teil bis zur Zeile 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 |