KSP Aufgabe 1
Konstruieren Sie die Ninja-VM Version 1, die die hier gelisteten
Instruktionen ausfuehren kann.
Jede Instruktion eines Programms wird in einem "unsigned int"
gespeichert, wobei der Opcode (die Zahl, die in der Tabelle hinter
jeder Instruktion steht) in den obersten 8 Bits des "unsigned int"
abgelegt wird. Die restlichen 24 Bits dienen zur Aufnahme eines
Immediate-Wertes. Der wird im Augenblick nur bei der Instruktion
"pushc" benoetigt, wo er den auf den Stack zu legenden Wert darstellt.
Den Effekt einer jeden Instruktion auf den Stack listet diese
Tabelle . Drei Punkte "..." in der Tabelle
stehen jeweils fuer den durch die Instruktion nicht veraenderten
Stackinhalt; der "Top-of-Stack" befindet sich jeweils rechts.
Die Operanden fuer arithmetische Operationen sind immer ganze
Zahlen, genauso wie die bei "rdint" und "rdchr" eingelesenen
bzw. bei "wrint" und "wrchr" ausgegebenen Werte.
Die drei hier gelisteten Programme sollen
fest in die VM einprogrammiert werden. Der Benutzer kann durch
Eingabe eines Kommandozeilenargumentes beim Aufruf der VM eines
der Programme auswaehlen, das dann in den Programmspeicher der
VM kopiert wird. Nach dem Anlaufen der VM wird das Programm erst
in einer lesbaren Form aufgelistet; eine moegliche Darstellung
koennen Sie der Referenzimplementierung entnehmen. Anschliessend
wird es ausgefuehrt. Beachten Sie bitte, dass "Auflisten" und
"Ausfuehren" zwei getrennte Aktionen auf dem Programm sind, die
strikt hintereinander passieren sollen.
Hier ist wieder die Referenzimplementierung:
njvm
Hinweise
1. Die Compilerschalter sind wie in Aufgabe 0 zu setzen.
2. Ihre VM benoetigt einen Programmspeicher und einen Stack.
Beide koennen Sie global definieren, damit Sie von ueberall
darauf zugreifen koennen. Waehlen Sie die Basis-Datentypen
mit Bedacht, insbesondere im Hinblick auf vorzeichenbehaftete
("signed") vs. vorzeichenlose ("unsigned") Groessen!
3. Ihre VM wird zwei Interpreter fuer den Programmcode beinhalten:
einen fuer das Listen des Programms und einen fuer die eigentliche
Ausfuehrung. Beide sind strukturell gleich aufgebaut: eine Schleife
(worueber?) und darin eine Mehrfachverzweigung (wozu?). Beginnen Sie
mit dem Programmlister - wenn Sie den haben, ist der Interpretierer
fuer die Ausfuehrung nicht mehr schwer. Beachten Sie aber die zwei
unterschiedlichen Abbruchkriterien in den beiden Interpretierern
(welche genau?, und warum sind die eigentlich unterschiedlich?).
4. Ihr Programm darf unter keinen Umstaenden abstuerzen!
Natuerlich gibt es Fehlersituationen, in denen Sie das Programm
nicht vernuenftig weiterlaufen lassen koennen - dann geben Sie
eine verstaendliche Fehlermeldung aus und beenden das Programm.
Eine solche Situation betrifft beispielsweise das Teilen durch 0,
eine andere den Stack-Ueberlauf bzw. -Unterlauf (wann koennen
diese beiden Fehler auftreten?).
5. Sie muessen die drei Programme "zu Fuss" assemblieren, solange
es noch keinen Assembler gibt. Wie macht man das? Nehmen wir als
Beispiel "pushc 3": Der Opcode fuer pushc ist 1 (er kommt in die
hoechsten 8 Bits); die Immediate-Konstante ist 3 (sie kommt in die
untersten 24 Bits). Also ist das Bitmuster des Befehls (4 Bytes)
"00000001 00000000 00000000 00000011", oder hexadezimal 0x01000003.
Auf gar keinen Fall arbeitet man hier mit Dezimalzahlen! Es geht
aber viel eleganter: verwenden Sie den C-Praeprozessor, um eine
lesbare Codierung der drei kleinen Programme zu ermoeglichen!
Beispielsweise koennte der Anfang des ersten Programms im
C-Quelltext lauten:
unsigned int code1[] = {
(PUSHC << 24) | IMMEDIATE(3),
(PUSHC << 24) | IMMEDIATE(4),
(ADD << 24),
...
mit den entsprechenden Definitionen fuer die Opcodes, z.B.
#define PUSHC 1
#define ADD 2
und fuer das Kodieren des Immediate-Wertes (warum braucht man das?)
#define IMMEDIATE(x) ((x) & 0x00FFFFFF)
6. Der Immediate-Wert kann auch negativ sein. Vielleicht haben Sie
Verwendung fuer diesen Makro:
#define SIGN_EXTEND(i) ((i) & 0x00800000 ? (i) | 0xFF000000 : (i))
Was macht der eigentlich? Und wie genau funktioniert er?
7. ACHTUNG: Es ist unbedingt erforderlich, die Makros aus 5. und 6.
genau zu verstehen! Machen Sie sich die Wirkungsweise an Beispielen
klar und rechnen Sie damit, im Praktikum dazu befragt zu werden!