Wenn man sofort anfängt seine Ideen in Hardware zu realisieren, hat man große Probleme, wenn sich Teile der Gedanken als nicht realisierbar oder unpraktisch erweisen. Deshalb habe ich als allererstes einen Simulator mit eingebauten Assembler geschrieben.
Mein Assembler ist offensichtlich stark vom Z80 beeinflusst. Das ist die CPU mit der ich als Jugendlicher zuerst in Berührung gekommen bin. Allerdings gibt es ein paar Vorgehensweisen die erheblich von normaler Assemblerprogrammierung abweichen.
- Ein Label für ein Sprungziel steht immer alleine (abgesehen von Kommentaren) und wird durch ein Doppelpunkt abgeschlossen. Funktionen, die per Call aufgerufen werden, haben ein Label welches durch ein fn (für function) eingeleitet werden. Nur solche Labels können von beliebiger Stelle aufgerufen werden. Normale Label werden automatisch „Funktions-lokal“. Das heißt, dass man nicht darauf achten muss, dass alle internen Sprungziele unterschiedliche Namen bekommen. Es ist also erlaubt, dass das Label „loop:“ in mehreren Funktionen verwendet wird und der Assembler verwendet dann jeweils die lokale Version.
- Da Label eindeutig durch einen Doppelpunkt gekennzeichnet werden, können OpCodes direkt in der ersten Spalte beginnen. Es ist nicht notwendig mit einem TAB anzufangen – aber es ist erlaubt.
- Alle Werte sind Hexadezimal.
- Da es keinen Linker gibt, wird auch unterschiedliche Code- und Datenbereiche verzichtet. Es gibt den Pseudobefehl org zur Festlegung des Startpunkts der nachfolgenden Befehle und eine data Anweisung welche nachfolgende Speicherinhalte kennzeichnet.
- Push und Pull Befehle gibt es nicht pro Register, sondern es gibt jeweils einen Befehl und dazu eine Registerliste mit den betroffenen Registern.
Der Simulator ist als JavaFX Anwendung realisiert. Er lädt direkt die Assemblerdatei und erzeugt daraus den Maschinencode, löst alle absoluten und relativen Sprungadressen auf und initialisiert den Speicher und die internen Register.

Load: öffnet einen Dateiauswahl Dialog welcher auf die Extension .zw4 voreingestellt ist. Es wird jeweils das zuletzt verwendete Verzeichnis voreingestellt. Falls es eine Funktion mit dem Label main: gibt, wird der PC im Simulator so voreingestellt, dass diese direkt ausgeführt wird.
Reload: lädt die aktuelle Datei neu. Es gibt keinen eingebauten Editor, zum Bearbeiten der Assemblerdatei muss ein externer Editor verwendet werden. Es reicht dann, nach einer Änderung, ein Speichern im Editor (er muss nicht geschlossen werden) und ein Klick auf Reload aus um den Simulator zu aktualisieren.
Quit: beendet den Simulator.
Step: liest den aktuellen PC und führt den nächsten Befehl aus. Der PC kann im User Interface zu beliebigen Zeiten geändert werden und der neue Wert wird beim nächsten Step verwendet. Das ist hilfreich, wenn man im Programm ein paar Schritte zurückspringen möchte, oder um sich aus einer toten Schleife zu befreien. Auch die übrigen Eingabefelder werden beim weiteren Programmablauf verwendet wenn sie manuell verändert wurden. Während des Programmablaufs wird das Textfenster mit dem Assemblercode automatisch aktualisiert, so dass der aktuelle Code sichtbar bleibt.
Run und Stop: führt das Programm aus bis es auf einen Breakpoint trifft oder mit Stop angehalten wurde. Einen Breakpoint kann man setzen indem man in der Programmliste auf die Adresse der jeweiligen Zeile klickt. Das Addressfeld wird dann in Folge rot angezeigt. Durch einen weiteren Klick wird der Breakpoint wieder gelöscht.
Funktions: diese Drop Down Box enthält eine Liste aller definierten Funktionen. Durch Anklicken wird der PC mit der jeweiligen Adresse initialisiert und die Assembleranzeige so gescrollt das diese Stelle sichtbar ist.
Registerliste: hier werden alle internen Registerwerte angezeigt. Die Registerwerte können auch manuell geändert werden und diese Werte werden dann beim nächsten Step verwendet. Umgekehrt werden die geänderten Werte nach dem Step angezeigt. Diese Liste wird auch im Run Modus aktualisiert, eine manuelle Änderung während des Programmlaufs ist deshalb praktisch unmöglich, da die Liste im Millisekunden Rahmen aktualisiert wird.
Speicheranzeige: der Simulator zeigt ein Speicherfenster von 256 Digits an. Die Startadresse kann frei eingestellt werden. Diese Anzeige wird auch während des Programmlaufs aktualisiert. Eine manuelle Änderung ist nicht vorgesehen.
Assembleranzeige: hier wird der Programmcode angezeigt. Die Stelle des aktuellen PCs wird markiert und bei Bedarf das Fenster so gescrollt, dass sie sichtbar ist. Die Anzeige wird auch während eines Programmlaufs mit Run aktualisiert. Falls der Assemblercode Fehler enthält, werden diese ebenfalls hier in der jeweiligen Codezeile angezeigt.

In einem ersten Schritt wird der Simulator dazu verwendet sich selber zu debuggen. Hierzu habe ich verschiedene kleine Assemblerfunktionen geschrieben, die die einzelnen Befehle aufrufen und prüfen, ob das gewünschte Ergebnis erzielt wurde. Im Augenblick habe ich ca. ein Viertel der Befehle getestet und dabei mehrere Abweichungen zwischen dem generierten Maschinencode und der simulierten CPU gefunden. An dieser Stelle sind Korrekturen jedoch viel einfacher als wenn sie erst auffallen nachdem die Hardware zusammengelötet wurde.
Aktuell umfasst der Simulator ca. 65 kB Programmcode. Es sind aber noch ein paar Erweiterungen geplant, wie z.B. die Unterstützung von Makros. Zudem wird der Assemblercode nur rudimentär auf Fehler geprüft. Hier muss auch noch weiterer Aufwand getrieben werden.