Home |
8086 Assembler:
|
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.
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 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
|