Erfahrenes Mitglied
Registriert seit: Oct 2009
Beiträge: 640
Bedankt: 228
|
Hab mich mal ein wenig gespielt und zumindest die ersten 3 Aufgaben gelöst:
Aufgabe 1:
Code:
jmp main
; Speicher für die Siebensegmentcodierung [02] -> 0; [03] -> 1; ...
; Beginnt an der Adresse 0x02, da "jmp main" auch Platz braucht.
db FA ; 0
db 0A ; 1
db B6 ; 2
db 9E ; 3
db 4E ; 4
db DC ; 5
db FC ; 6
db 8A ; 7
db FE ; 8
db DE ; 9
; MAIN
main:
; Initialisierung (beide Ziffern auf Null setzen)
;out 07 ; Keyboard anzeigen (wenn man es über das grafische Keyboard machen will)
xor AL, AL ; AL löschen (auf 00 setzen; auch "mov AL, 00" möglich)
xor BL, BL ; BL löschen
call 40 ; Ausgabe am Display (0, an der Einerstelle)
mov BL, FF ; Zehnerstelle für "call 40" einstellen (alles außer 00 möglich, z.B. "mov BL, 42" bewirkt das gleiche)
call 40 ; Ausgabe am Display (0, an der Zehnerstelle)
; Ausführungsschleife
main_loop:
;in 07 ; entweder in 07 (grafisches Keyboard) oder in 00 (pysikalisches Keyboard) verwenden. Beides gleichzeitig macht wenig sinn. Wenn in 07 verwendet wird, out 07 bei der Initialisierung auskommentieren.
in 00 ; Eingabe über pyhsikalische Tastatur
cmp AL, 0D ; wurde Enter (ASCII-Code 0x0d) eingegeben?
jz ende ; JA: Springe zu ende; NEIN: Mach weiter
sub AL, 30 ; ASCII to INTEGER; 30 = 0 (ASCII-Codiert), 31 = 1 (ASCII-Codiert), ...; ASCII - 30 = ASCII-Ziffer als Zahl
mov BL, 00 ; Einerstelle
call 40 ; Ausgabe am Display (Eingabe, an der Einerstelle)
push AL ; jetzige Eingabe am Stack Speichern
push CL ; letzte Eingabe am Stack Speichern
pop AL ; letzte Eingabe ins Register AL legen
mov BL, FF ; Zehnerstelle
call 40 ; Ausgabe am Display (letzte Eingabe, an der Zehnerstelle)
pop CL ; jetzige Eingabe ins Register CL legen (Zwischenspeicher für nächsten Durchgang ("letzte Eingabe")
jmp main_loop ; Neustart
; END MAIN
; 7-Segmentanzeige setzen
; Parameter im Register AL (Zahl die Angezeigt werden soll, INTEGER 0-9 [Verhalten bei anderen Zahlen undeffiniert])
; Parameter im Register BL (0 -> Einerstelle; else -> Zehnerstelle)
; Register bleiben unverändert (AL wird im Programm zwar verändert, aber am ende wiederhergestellt)
org 40 ; Adresse der Funktion (muss gleich mit dem "call" sein! Z.B. "org 50" -> "call 50"). Adresse muss größer als die letzte stelle von MAIN sein aber nicht zu groß, da es sonst keinen Stack gibt (Stack beginnt auf 0xBF und arbeitet in Richtung 0x00).
push AL ; Parameter des Funktionsaufrufers am Stack Speichern
add AL, 02 ; Parameter um 2 erhöhen, um Adresse an Speicher (Array) anzupassen (siehe Programmanfang).
mov AL, [AL] ; AL durch den Wert ersetzen, auf den [AL] zeigt ([AL] wird als Pointer interpretiert, Wert von AL ist die Zeigeadresse). War der Parameter z.B. 5, wird dieser um 2 erhöht (7). Dadurch ergibt sich "mov AL, [7]". [7] wird durch den Wert ersetzt, der auf der Adresse 0x07 gespeichert ist. In unserem Falle ergibt das "mov AL, DC". 0xDC ist die Ziffer 5, codiert für das 7-Segment.
cmp BL, 00 ; ist BL 0x00 (Einerstelle)
jnz Zehner ; JA: mach einfach weiter; NEIN: Spring zu Zehner
or AL, 01 ; LSB (Niedrigstes/Kleinstes BIT) auf 1 setzen (Ergebnis: XXXX XXX1) -> Einerstelle
jmp end_einer ; Code für Einerstelle Fertig (Code für Zehner überspringen)
Zehner:
and AL, FE ; LSB auf 0 setzen (Ergebnis: XXXX XXX0) -> Zehnerstelle
end_einer:
out 02 ; AL auf Port 2 ausgeben (Bit 7-1 -> 7-Segment [0 AUS / 1 EIN] ... A F E D C G B; Bit 0 -> Stelle [0 Zehnerstelle / 1 Einerstelle]).
pop AL ; Parameter des Funktionsaufrufers wiederherstellen
ret ; zurück zum Funktionsaufruf
; END FUNCTION
; ENDE CODE
ende:
end ; Programmende
Aufgabe 2:
Code:
jmp main
; IVT (InterruptVektorTabelle)
db 10 ; Adresse von Interrupt 2
; Interrupt 2
; AL HEX to AL (high Byte) and BL (low Byte) DECIMAL
; Andere Register werden nicht verändert (CL wird nach dem Ende wiederhergestellt
org 10 ; Adresse von Interrupt 2
mov BL, 00 ; BL löschen
push CL ; CL für wiederherstellung auf dem Stack speichern
mov CL, 00 ; CL löschen
int_BL_loop:
cmp AL, 00 ; ist AL negativ (größer 0x7F) ?
js negativ ; Negative Zahlen bearbeiten wie positive Zahlen größer 0x0A
cmp AL, 0A ; ist AL größer 0x0A ?
js smaler_then_A ; JA: weiter machen; NEIN: Code für Dezimalzahlen kleiner 10 (0-9)
negativ:
sub AL, 0A ; von AL 0x0A abziehen (Dezimal: -10)
inc CL ; Zähler wie oft AL größer 0x0A ist
cmp BL, 90 ; ist BL dabei überzulaufen (größer werden als 0x90 -> 90 Dezimal ... 100 Dezimal passt nicht mehr in eine 2 Stellige Hexzahl, da 100 drei stellen hat)
jz BL_overflow ; JA: BL zurück setzen auf 0x00; NEIN: weiter machen
add BL, 10 ; AL war größer 0x0A somit muss zu BL 10 dazugezählt werden
jmp int_BL_loop ; wieder Testen, ob AL größer 0x0A ist
BL_overflow:
mov BL, 00 ; BL war zu groß, zurücksetzen auf 0x00
jmp int_BL_loop ; wieder Testen, ob AL größer 0x0A ist
smaler_then_A:
add BL, AL ; AL ist kleiner 0x0A darum kann es direkt zu BL dazugezählt werden (0-9 ist in HEX und DEZIMAL gleich; aber 10 DEZIMAL wäre 0A in HEX)
div CL, 0A ; AL berechnen (Hunderterstelle); (CL enthält die anzahl, wie oft AL größer/gleich 0x0A war, das dividiert durch 0x0A, ergibt AL (Hunderterstelle); (z.B. CL = 5, daher war AL 50 bis 59 (0x32 bis 0x3B), 5 / 10 = 0 (ohne Nachkommer), daher ist AL 0 (kleiner 100 passt alleine in BL); CL = 21, daher war AL 210 bis 219, 21 / 10 = 2 (ohne Nachkommer), daher ist AL 2 (größer 100 muss in AL angegeben werden); [für BL ist es deswegen nicht möglich, da AL nicht ein genaues vielfaches von 0x0A sein muss, aber CL schon. Würde die Modulooperation unterstützt werden, könnte man es auf ähnliche weiße lösen]
push CL ; da kein mov AL, CL (Register zu Register) möglich ist, wird CL auf den Stack geleget ....
pop AL ; ... und in AL zurückgeholt
int_end:
pop CL ; CL wiederherstellen
iret ; zurück von Interrupt 2
main:
mov CL, AA ; Beweiß dass CL unverändert bleibt
mov AL, 42 ; Hexzahl die umgewandelt werden soll
int 2 ; Umwandlung starten
end ; Programmende
Aufgabe 3:
Code:
jmp main
db 30 ; Hardware Timer Interrupt
db 42 ; maximaler Füllwert [03] (signed -> maximum bei 7F; aber ACHTUNG: wenn Füllwert 7F "überschreitet" bedeutet das eine negative Zahl und es wird weiter befüllt)
db 00 ; globle Variable für den Füllstand [04]
db "Füllstandswert überschritten!" ; Fehler-Text [05] bis [0E]
db 00 ; Stringende (0x00)
org 30 ; Hardware Timer Interrupt
cli ; Hardware Timer deaktivieren
push AL ; AL auf dem Stack speichern
mov AL, [04] ; AL auf den Wert des Füllstandes setzen (da ein direkter Vergleich eines Wertes zweier Adressen nicht möglich ist)
cmp AL, [03] ; ist der Füllstand [04] kleiner als max. Füllstand [03]
jns voll ; JA: weiter machen; NEIN: Fehlerausgabe und Programm beenden
pop AL
sti ; Hardware Timer wieder aktivieren
iret ; Interrupt beenden
voll:
mov AL, C0 ; AL: Position des VDU Screens
mov BL, 05 ; BL: Position des Strings
mov CL, [BL] ; CL: Wert von BL (Zeichen des Strings an der Position von BL)
text_loop:
mov [AL], CL ; CL auf dem VDU an Position AL ausgeben
inc AL ; nächste Position des VDU
inc BL ; nächste Position des Strings
mov CL, [BL] ; nächsten Zeichen des Strings in CL laden
cmp CL, 00 ; ist das Stringende erreicht (0x00)?
jnz text_loop ; JA: weiter machen; NEIN: Zeichen ausgeben, nächste Positionen, nächstes Zeichen und auf Stringende prüfen
jmp ende ; Programm beenden
main:
sti ; I-Flag setzen (Hardware Interrupts erlauben, notwendig für den Hardware Timer)
main_loop:
mov AL, [04] ; AL auf den Wert des Füllstandes setzen (da eine direkte Erhöhung eines Wertes einer Adressen nicht möglich ist)
inc AL ; Füllwert erhöhen (nur lokal)
mov [04], AL ; Füllwert in globale Variable Speichern
jmp main_loop ; Dauerschleife
ende:
end ; Programmende
Bei Aufgabe 4 geht mir immer der Speicher aus ... 0x00 - 0xBF für Programm und Stack ist schon ein bisschen wenig für so eine Aufgabe.
EDIT: Aufgabe 4 habe ich jetzt doch noch "irgendwie" lösen können ... siehe unten
|