Ein häufiger Typ von UNIX®-Anwendungen ist ein Filter -- ein Programm, das Eingaben von stdin liest, sie verarbeitet und das Ergebnis nach stdout schreibt.
In diesem Kapitel möchten wir einen einfachen Filter entwickeln und lernen, wie wir von stdin lesen und nach stdout schreiben. Dieser Filter soll jedes Byte seiner Eingabe in eine hexadezimale Zahl gefolgt von einem Leerzeichen umwandeln.
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12
jmp short _start
.done:
push dword 0
sys.exit
Im Datenabschnitt erzeugen wir ein Array mit Namen hex.
Es enthält die 16 hexadezimalen Ziffern in aufsteigender Reihenfolge. Diesem Array
folgt ein Puffer, den wir sowohl für die Ein- als auch für die Ausgabe
verwenden. Die ersten beiden Bytes dieses Puffers werden am Anfang auf 0 gesetzt. Dorthin schreiben wir die beiden hexadezimalen Ziffern
(das erste Byte ist auch die Stelle an die wir die Eingabe lesen). Das dritte Byte ist
ein Leerzeichen.
Der Code-Abschnitt besteht aus vier Teilen: Das Byte lesen, es in eine hexadezimale Zahl umwandeln, das Ergebnis schreiben und letztendlich das Programm verlassen.
Um das Byte zu lesen, bitten wir das System ein Byte von stdin zu lesen und speichern es im ersten Byte von buffer. Das System gibt die Anzahl an Bytes, die gelesen wurden,
in EAX zurück. Diese wird 1 sein, wenn eine Eingabe empfangen wird und 0, wenn keine Eingabedaten mehr verfügbar sind. Deshalb
überprüfen wir den Wert von EAX. Wenn dieser 0 ist, springen wir zu .done,
ansonsten fahren wir fort.
Anmerkung: Zu Gunsten der Einfachheit ignorieren wir hier die Möglichkeit eines Fehlers.
Die Umwandlungsroutine in eine Hexadezimalzahl liest das Byte aus buffer in EAX, oder genaugenommen nur
in AL, wobei die übrigen Bits von EAX auf null gesetzt werden. Außerdem kopieren wir das Byte
nach EDX, da wir die oberen vier Bits (Nibble) getrennt von
den unteren vier Bits umwandeln müssen. Das Ergebnis speichern wir in den ersten
beiden Bytes des Puffers.
Als Nächstes bitten wir das System die drei Bytes in den Puffer zu schreiben, also die zwei hexadezimalen Ziffern und das Leerzeichen nach stdout. Danach springen wir wieder an den Anfang des Programms und verarbeiten das nächste Byte.
Wenn die gesamte Eingabe verarbeitet ist, bitten wie das System unser Programm zu beenden und null zurückzuliefern, welches traditionell die Bedeutung hat, dass unser Programm erfolgreich war.
Fahren Sie fort und speichern Sie den Code in eine Datei namens hex.asm. Geben Sie danach folgendes ein (^D bedeutet, dass Sie die Steuerungstaste drücken und dann D eingeben, während Sie Steuerung gedrückt halten):
% nasm -f elf hex.asm % ld -s -o hex hex.o % ./hex Hello, World! 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come! 48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %
Anmerkung: Wenn Sie von MS-DOS® zu UNIX wechseln, wundern Sie sich vielleicht, warum jede Zeile mit
0Aan Stelle von0D 0Aendet. Das liegt daran, dass UNIX nicht die CR/LF-Konvention, sondern die "new line"-Konvention verwendet, welches hexadezimal als0Adargestellt wird.
Können wir das Programm verbessern? Nun, einerseits ist es etwas verwirrend, dass
die Eingabe, nachdem wir eine Zeile verarbeitet haben, nicht wieder am Anfang der Zeile
beginnt. Deshalb können wir unser Programm anpassen um einen Zeilenumbruch an Stelle
eines Leerzeichens nach jedem 0A auszugeben:
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
mov cl, ' '
.loop:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov [buffer+2], cl
cmp al, 0Ah
jne .hex
mov [buffer+2], al
.hex:
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12
jmp short .loop
.done:
push dword 0
sys.exit
Wir haben das Leerzeichen im Register CL abgelegt. Das
können wir bedenkenlos tun, da UNIX-Systemaufrufe im
Gegensatz zu denen von Microsoft® Windows® keine Werte von Registern ändern in denen sie
keine Werte zurückliefern.
Das bedeutet, dass wir CL nur einmal setzen müssen.
Dafür haben wir ein neues Label .loop eingefügt,
zu dem wir an Stelle von _start springen, um das
nächste Byte einzulesen. Außerdem haben wir das Label .hex eingefügt, somit können wir wahlweise ein
Leerzeichen oder einen Zeilenumbruch im dritten Byte von buffer ablegen.
Nachdem Sie hex.asm entsprechend der Neuerungen geändert haben, geben Sie Folgendes ein:
% nasm -f elf hex.asm % ld -s -o hex hex.o % ./hex Hello, World! 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come! 48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %
Das sieht doch schon besser aus. Aber der Code ist ziemlich ineffizient! Wir führen für jeden einzelne Byte zweimal einen Systemaufruf aus (einen zum Lesen und einen um es in die Ausgabe zu schreiben).
| Zurück | Zum Anfang | Weiter |
| Unser erstes Programm | Nach oben | Gepufferte Eingabe und Ausgabe |
Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an
<de-bsd-questions@de.FreeBSD.org>.
Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an <de-bsd-translators@de.FreeBSD.org>.