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!