/** * Modelliert eine Art Registermaschine, die aber schon eine komplette Assemblersprache benutzt. * Die erbenden Klassen sind konkrete Registermaschinen, sie koennen nicht umdefinieren, was eine * Registermaschine ist, weswegen alle implementierten Methoden [final] sind. * * @author MB * @version JAN 2019 */ public abstract class Registermaschine { protected int befz; // Befehlszähler protected long akku; // Akkumulator protected int status; // Statusregister: 0 := OK; 1 := negativ; 2 := overflow protected int[] register; protected final int MAX; // Anzahl der Register protected int programmlaenge; /** * Constructor for objects of class Registermaschine * * @param max: Anzahl der Register */ public Registermaschine(int max) { register = new int[max]; befz = 0; akku = 0; status = 0; programmlaenge = 0; MAX = max; } /** * erzeugt eine maximal grosse Registermaschine: 2.147.483.647 Register (also bei 8-Bit Integer ca. 2.5 GB RAM) */ public Registermaschine(){ this(Integer.MAX_VALUE); } /** * alle folgenden Methoden der Assemblersprachensimulation sind vom Ergebnistyp boolean, * um testen zu können, dass sie erfolgreich ausgeführt wurden. */ public final boolean LOAD(int x){ if (x < MAX){ akku = register[x]; befz++; testAkku(); // bestimmen des Wertes des [status]-Registers return true; } return false; } public final boolean DLOAD(int x){ akku = x; befz++; testAkku(); return true; } public final boolean STORE(int x){ if (x < MAX){ if (status < 2){ // muss den Inhalt von [status] pruefen, da aus dem Akkumulator geschrieben wird register[x] = (int) akku; // Casting! Disaster (Casting Error), wenn status == 2! befz++; return true; } return false; // overflow error! } return false; } public final boolean ADD(int x){ if (x < MAX){ akku = akku + register[x]; befz++; testAkku(); return true; } return false; } public final boolean SUB(int x){ if (x < MAX){ akku = akku - register[x]; befz++; testAkku(); return true; } return false; } public final boolean MULT(int x){ if (x < MAX){ akku = akku * register[x]; befz++; testAkku(); return true; } return false; } public final boolean DIV(int x){ if (x < MAX){ akku = akku / register[x]; befz++; testAkku(); return true; } return false; } public final boolean JUMP(int x){ if (x < MAX){ // die Befehle stehen auch in den Registern! befz = x; return true; } return false; } public final boolean JGE(int x){ if (x < MAX){ if (status == 0){ // Akku muss positiv sein befz = x; } else { befz++; } return true; } return false; } public final boolean JGT(int x){ if (x < MAX){ if (akku > 0){ befz = x; } else { befz++; } return true; } return false; } public final boolean JLE(int x){ if (x < MAX){ if (akku <= 0){ befz = x; } else { befz++; } return true; } return false; } public final boolean JLT(int x){ if (x < MAX){ if (status == 1){ // negative Zahl befz = x; } else { befz++; } return true; } return false; } public final boolean JEQ(int x){ if (x < MAX){ if (akku == 0){ befz = x; } else { befz++; } return true; } return false; } public final boolean JNE(int x){ if (x < MAX){ if (akku != 0){ befz = x; } else { befz++; } return true; } return false; } public final boolean END(){ befz++; return false; // beendet eine Sequenz, in der es auftritt } /** * Methode liefert (indirekt) boolsche Werte; z.B. i.d.R. [true] im Fall * eines negativen Rechenergebnisses und [false] im Fall eines Wertüberlaufes. */ protected final void testAkku(){ if ((akku < Integer.MIN_VALUE) || (akku > Integer.MAX_VALUE)){ status = 2; } else if (akku < 0){ status = 1; } else { status = 0; } } /** * Sondierende Methode, um evtl. auch das Ergebnis ausgeben zu können. */ protected final long getAkku(){ return akku; } /** * In den Registern sind auch die Befehle gespeichert. * Eine universelle Registermaschine benutzt damit zwei Parameter: der erste gibt das Register an, aus dem * ein Befehl geladen wird, der zweite den Parameter für diesen geladenen Befehl. * * Benötigt wird dabei für eine einfache Java-Implementierung irgendeine Codierung der Befehle. * Diese wird hier festgelegt. * * @param reg: das untersuchte Register * @param wert: der Parameter für die zu findende Methode * @return true, wenn ein Befehl im Register gefunden wird */ public final boolean run(int reg, int wert){ switch(register[reg]){ // wir betrachten die Zahl, im gewählten Register case 1000: return LOAD(wert); // und wenden die Codierung an case 1001: return DLOAD(wert); case 1002: return STORE(wert); case 1003: return ADD(wert); case 1004: return SUB(wert); case 1005: return MULT(wert); case 1006: return DIV(wert); case 1007: return JUMP(wert); case 1008: return JGE(wert); case 1009: return JGT(wert); case 1010: return JLE(wert); case 1011: return JLT(wert); case 1012: return JEQ(wert); case 1013: return JNE(wert); case 1014: return END(); default: return false; // in diesem Fall wird eine kontrollierte Programmausfuehrung abbrechen } } /** * Variante der universellen Funktion, bei der auch die Eingabewerte in Registern stehen, naemlich denen, die auf die Register * mit den Befehlen folgen; die beiden Varianten unterscheiden sich also darin, wie Programme geladen werden * @param reg - das aktuelle Register, d.h. entspricht dem Befehlszaehler */ public final boolean run(int reg){ return run(reg,register[reg+programmlaenge]); } /** * Ausfuehrung, die * 1. Programm und Daten laedt * 2. jeden Teilschritt auf seinen Erfolg ueberprueft * Voraussetzung: Das Programm wird in die ersten Register geschrieben! * @return - der Inhalt des Akkumulators */ public final long completelyUniversalizedControlledProgram(){ return completelyUniversalizedControlledProgram(1); // siehe Voraussetzung } /** * Variante der Methode, der auch der Anfang des Programmes mitgeteilt wird. * Voraussetzungen: * 1. [programmUndDatenLaden] richtet sich nach dem uebergebenen Wert fuer die erste Speicherzelle * 2. freier Speicher der ab dem gegebenen Wert + 2 * [programmlaenge] */ public final long completelyUniversalizedControlledProgram(int startregister){ programmlaenge = programmUndDatenLaden(startregister); boolean erfolgreich = true; befz = startregister; // siehe Voraussetzung akku = 0; while(erfolgreich){ erfolgreich = run(befz); // Erinnerung: die Variante der universellen Funktion, die den Datenspeicher aus dem Befehlszaehler herleitet } return getAkku(); } /** * @return - die Programmlaenge * Nebeneffekt einer solchen Ermittlung der Programmlaenge ist das Laden von Daten und Programm! */ public abstract int programmUndDatenLaden(int start); public abstract void programmLaden(int start); }