kread
-FunktionDie Funktion kread
liest Daten von verschiedenen Eingabequellen und speichert sie in einem bereitgestellten Puffer. Sie gibt die Anzahl der gelesenen Bytes zurück. Die Funktion hat folgende Parameter:
r0
: Eingabestream ([0] für Datei, [1] für UART)r1
: Zieladresse für den eingelesenen Stringr2
: Anzahl der zu lesenden Bytes.global kread
.extern k_uart_read_char
.extern k_uart_write_char
.extern textmode_state
.extern text_newline
.extern text_current_index
.section .data
newline: .asciz "/n"
Globale Deklarationen werden vorgenommen, um die Funktion kread
und externe Funktionen wie k_uart_read_char
, k_uart_write_char
, sowie einige Zustandsvariablen (textmode_state
, text_newline
, text_current_index
) bekannt zu machen. Im Datenabschnitt wird die Zeichenkette newline
definiert, die das Zeichen /n
enthält.
kread
.section .text
kread:
push {lr}
push {r11}
mov r11, sp
and r0, r0, #0x1
adr r3, input_tbl
ldr pc, [r3, r0, lsl #2]
b .
Die Funktion kread
beginnt mit dem Sichern des Link-Registers (lr
) und des Frame-Registers (r11
) auf dem Stack. Anschließend wird r11
auf den aktuellen Stack-Pointer (sp
) gesetzt, um den Frame-Pointer zu initialisieren. Mit einer AND-Operation wird sichergestellt, dass der Wert in r0
entweder 0 oder 1 ist, um den Eingabestream festzulegen. Danach wird die Adresse der Sprungtabelle input_tbl
in r3
geladen, und es erfolgt ein Sprung zur entsprechenden Handler-Funktion basierend auf dem Wert von r0
. Der Befehl b .
dient als Platzhalter für einen nicht erreichbaren Codebereich.
input_tbl:
in_0: .word in_0_handler @ read from File
in_1: .word in_1_handler @ read from uart
b .
Die Sprungtabelle input_tbl
enthält die Adressen der Handler-Funktionen für die Eingabe aus einer Datei (in_0_handler
) und für die UART-Eingabe (in_1_handler
). Der Befehl b .
dient als Platzhalter und Endemarkierung, sollte jedoch nicht erreicht werden.
in_0_handler: @ read from File
b .
Nicht implementiert: Enthält nur eine Endlosschleife, da das Lesen von Dateien nicht implementiert ist.
in_1_handler: @ read from uart
sub r2, r2, #1
mov r3, #0 @ count
push {r0-r3}
ldr r0, =textmode_state
ldr r1, [r0]
cmp r1, #0
pop {r0-r3}
bne scan_loop_text_out
Der Handler für Eingabestream 1 (UART) beginnt damit, r2
um 1 zu verringern, um Platz für das Nullterminierungszeichen zu schaffen, und initialisiert r3
als Zähler für gelesene Bytes. Anschließend werden die Register r0
bis r3
auf dem Stack gesichert. Der Wert von textmode_state
wird in r1
geladen und mit 0 verglichen. Ist r1
ungleich 0, wird zu scan_loop_text_out
gesprungen. Danach werden die Register r0
bis r3
wiederhergestellt.
scan_loop_uart_out:
push {r1,r2,r3}
bl k_uart_read_char
pop {r1,r2,r3}
cmp r0, #0xD @ CR?
bne scan_not_enter_uart_out
In der UART-Eingabeschleife (Nicht-Textmodus) wird zunächst der aktuelle Zustand der Register r1
, r2
und r3
auf dem Stack gesichert. Dann wird die Funktion k_uart_read_char
aufgerufen, um ein Zeichen von der UART-Schnittstelle zu lesen, das in r0
gespeichert wird. Nach dem Wiederherstellen der Register wird r0
mit 0xD
(Carriage Return) verglichen. Falls das gelesene Zeichen kein Carriage Return ist, wird zu scan_not_enter_uart_out
gesprungen.
scan_enter_uart_out:
mov r0, #2
ldr r1, =newline
mov r2, #2
push {r3}
bl kwrite
pop {r3}
cmp r3, #0
movne r0, #0
bne scan_end_correct
b scanstr_error
In diesem Abschnitt wird ein Zeilenvorschub ausgegeben, indem r0
, r1
und r2
mit den entsprechenden Werten für die Funktion kwrite
vorbereitet werden, um die Zeichenkette newline
auszugeben. Danach wird der Zustand von r3
auf dem Stack gesichert und die Funktion aufgerufen. Nach der Rückkehr wird r3
mit 0 verglichen: Bei einem ungleichen Wert (Erfolg) wird zu scan_end_correct
gesprungen, andernfalls erfolgt ein Sprung zu scanstr_error
im Fehlerfall.
scan_not_enter_uart_out:
cmp r0, #0x8
beq read_del
cmp r0, #0x20
blo scan_loop_uart_out
Hier wird zunächst geprüft, ob das gelesene Zeichen ein Backspace (0x8
) ist. Falls ja, wird zu read_del
gesprungen. Anschließend wird geprüft, ob das Zeichen ein Steuerzeichen ist (unterhalb von 0x20
). Solche Steuerzeichen werden ignoriert, indem die Schleife erneut zu scan_loop_uart_out
springt.
ami_ger_tastatur:
cmp r0, #0x59 @ Y -> Z
addeq r0, r0, #1
beq ami_ger_skip
cmp r0, #0x5a @ Z -> Y
subeq r0, r0, #1
beq ami_ger_skip
cmp r0, #0x79 @ Y -> Z
addeq r0, r0, #1
beq ami_ger_skip
cmp r0, #0x7a @ z -> y
subeq r0, r0, #1
ami_ger_skip:
bl k_uart_write_char
strb r0, [r1, r3]
cmp r3, r2
add r3, r3, #1
bne scan_loop_uart_out
Nun wird eine Anpassung für unterschiedliche Tastaturbelegungen vorgenommen, indem die Tasten ‘Y’ und ‘Z’ ausgetauscht werden. Bei einer Übereinstimmung mit ‘Y’ oder ‘Z’ wird das Zeichen entsprechend angepasst. Nach der Anpassung wird das Zeichen über k_uart_write_char
ausgegeben und anschließend im Puffer an der Adresse [r1, r3]
gespeichert. Der Zähler r3
wird erhöht, und falls das Ende des Puffers nicht erreicht ist, springt die Schleife zurück zu scan_loop_uart_out
, um weitere Zeichen zu verarbeiten.
scanstr_error:
ldr r0, =0xffffffff
b scan_end
Fehlercode setzen: Lädt 0xffffffff
in r0
, um einen Fehler anzuzeigen.
scan_end_correct:
sub r0, r3, #1 @ stringlänge in r0
Stringlänge berechnen: Subtrahiert 1 von r3
und speichert das Ergebnis in r0
.
scan_end:
mov sp, r11
pop {r11}
pop {lr}
bx lr
Am Funktionsende wird das Stack-Frame aufgelöst, indem der Stack-Pointer (sp
), das Frame-Register (r11
), und das Link-Register (lr
) wiederhergestellt werden. Anschließend kehrt die Funktion mit dem Befehl bx lr
zum Aufrufer zurück.
read_del:
cmp r3, #0
beq scan_loop_uart_out
bl k_uart_write_char
sub r3, r3, #1
b scan_loop_uart_out
Es wird geprüft, ob es Zeichen gibt, die gelöscht werden können, indem r3
mit 0 verglichen wird. Ist r3
gleich 0, gibt es nichts zu löschen, und die Schleife kehrt zu scan_loop_uart_out
zurück. Andernfalls wird ein Backspace-Zeichen ausgegeben, und r3
wird um 1 verringert, bevor die Schleife erneut zu scan_loop_uart_out
springt.
skip_save:
bl k_uart_write_char
b scan_loop_uart_out
Das Zeichen wird direkt mit k_uart_write_char
ausgegeben, ohne es im Puffer zu speichern. Anschließend wird die Schleife fortgesetzt, indem zurück zu scan_loop_uart_out
gesprungen wird.
scan_loop_text_out:
push {r0-r3}
bl text_current_index
pop {r0-r3}
push {r1,r2,r3}
bl k_uart_read_char
pop {r1,r2,r3}
cmp r0, #0xD // CR?
bne scan_not_enter_text_out
In dieser Schleife wird zunächst der aktuelle Textindex über den Aufruf von text_current_index
abgerufen. Anschließend wird ein Zeichen von der UART-Schnittstelle eingelesen. Wird dabei kein Carriage Return (0xD
) erkannt, erfolgt ein Sprung zu scan_not_enter_text_out
.
scan_enter_text_out:
push {r0-r3}
mov r1, #1
bl text_printchar
bl text_newline
mov r0, #2
ldr r1, =newline
mov r2, #2
push {r3}
bl kwrite
pop {r3}
pop {r0-r3}
cmp r3, #0
movne r0, #0
bne scan_end_correct
b scanstr_error
In diesem Abschnitt wird das Zeichen zunächst mit text_printchar
ausgegeben, gefolgt von einem Zeilenvorschub durch den Aufruf von text_newline
. Danach wird die Zeichenkette newline
über kwrite
ausgegeben. Nach der Ausgabe wird der Erfolg geprüft: Bei erfolgreicher Verarbeitung erfolgt ein Sprung zu scan_end_correct
, andernfalls zu scanstr_error
, falls ein Fehler auftritt.
scan_not_enter_text_out:
cmp r0, #0x8
beq read_del_text
cmp r0, #0x20
blo scan_loop_text_out
Hier wird geprüft, ob das gelesene Zeichen ein Backspace (0x8
) ist. Wenn ja, wird zu read_del_text
gesprungen. Zeichen, die unterhalb von 0x20
liegen (Steuerzeichen), werden ignoriert, und die Schleife springt zurück zu scan_loop_text_out
, um weitere Eingaben zu verarbeiten.
ami_ger_tastatur_text:
cmp r0, #0x59 @ Y -> Z
addeq r0, r0, #1
beq ami_ger_skip_text
cmp r0, #0x5a @ Z -> Y
subeq r0, r0, #1
beq ami_ger_skip_text
cmp r0, #0x79 @ Y -> Z
addeq r0, r0, #1
beq ami_ger_skip_text
cmp r0, #0x7a @ z -> y
subeq r0, r0, #1
ami_ger_skip_text:
bl k_uart_write_char
push {r0-r3}
mov r1, r0
bl text_printchar
pop {r0-r3}
strb r0, [r1, r3]
cmp r3, r2
add r3, r3, #1
bne scan_loop_text_out
b scanstr_error
In dieser Routine wird eine Anpassung für unterschiedliche Tastaturbelegungen vorgenommen, indem die Tasten ‘Y’ und ‘Z’ je nach Eingabe vertauscht werden. Nach der Anpassung wird das Zeichen mit k_uart_write_char
ausgegeben und anschließend mit text_printchar
im Textmodus dargestellt. Das Zeichen wird außerdem im Puffer an der Adresse [r1, r3]
gespeichert. Der Zähler r3
wird erhöht. Solange das Ende des Puffers nicht erreicht ist, wird zurück zu scan_loop_text_out
gesprungen, andernfalls erfolgt ein Sprung zu scanstr_error
, falls ein Fehler auftritt.
read_del_text:
cmp r3, #0
beq scan_loop_text_out
bl k_uart_write_char
push {r0 - r3}
mov r1, #1
bl text_printchar
bl text_del
pop {r0 -r3}
sub r3, r3, #1
b scan_loop_text_out
Zunächst wird geprüft, ob es Zeichen zum Löschen gibt, indem r3
mit 0 verglichen wird. Wenn r3
gleich 0 ist, gibt es nichts zu löschen, und die Schleife springt zurück zu scan_loop_text_out
. Andernfalls wird ein Backspace-Zeichen ausgegeben, und die Funktionen text_printchar
sowie text_del
werden aufgerufen, um das Zeichen im Textmodus zu entfernen. Danach wird r3
um 1 verringert, und die Schleife setzt sich fort, indem erneut zu scan_loop_text_out
gesprungen wird.
skip_save_text:
bl k_uart_write_char
push {r0 - r3}
mov r1, r0
bl text_printchar
pop {r0 -r3}
b scan_loop_text_out
In diesem Abschnitt wird das Zeichen zunächst mit k_uart_write_char
ausgegeben. Anschließend wird das Zeichen im Textmodus durch den Aufruf von text_printchar
dargestellt. Nach dem Wiederherstellen der Register wird die Schleife fortgesetzt, indem zurück zu scan_loop_text_out
gesprungen wird.
zurück | Hauptmenü | weiter |