Schlagwort-Liste: Simulator 4-Bit-Rechner - Bedienoberfläche - allg. Nutzung - Simulator - Simulator-Nutzung - umfangreiches Programm-Beispiel - Programmierungs-Aufgaben

Nutzung Simulator 4-Bit Prozessor ERNA1

 

Dieses Bild ist ja schon aus der allgemeinen Beschreibung bekannt.
Rechts ist der Prozessor mit seinen wenigen Registern dargestellt, links im blauen Rahmen befindet sich der Hauptspeicher mit 256 Speicherplätzen und unten die Bedienung.
Die Bedienung ist nur mit Tasten möglich:

M   man kann in den Speicher wechseln und diesen mit den Daten aus dem SIM-Programm beschreiben.
R   R starten das im Speicher vorhandene Programm, das Programm läuft bis zum Ende durch, es lässt sich mit Q vorzeitig beenden.
T   T starten das im Speicher vorhandene Programm, das Programm läuft bis zum Ende durch, es lässt sich mit Q vorzeitig beenden. Das SIM-Programm wird schrittweise in der Größenordnung etwa der Mikrobefehle abgearbeitet.
Die Befehle werden angezeigt. Mit Q ist auch ein vorzeitiges Ende möglich, jedoch erst nach Beendigung des jeweiligen Mikroprogramms. Mikroprogramme können nicht unterbrochen werden (man muss teils sehr oft die Q-Taste betätigen).
L   Mit Load kann man eine SIM-Datei in den Speicher laden, man erspart sich das Eintippen über M.
S   Mit Save kann das unter M veränderte oder geschriebene Programm in eine Datei gespeichert werden
Q   Q beendet den Simulator und führt zurück in die Bedienoberfläche.

Der Bereich unter dem Speicher dient der Kommunikation. Zum einen können hier Datei-Namen eingegeben werden oder auch weitere Steuerzeichen, zum anderen der Ein- und Ausgabe des Rechners.

Unter dem Prozessor werden die „Mikrobefehle“ angezeigt.

Eingabe in den Speicher

Das ist relativ einfach, denn man betätigt die Taste M und kann dann mit den Kursortasten die Speicherplatzposition anwählen und dann dort den Wert eintippen (es kann nur eine Zahl von 0 bis 9 und die Buchstaben a bis f sein):

Im Beispiel wurde die Adresse 20 angewählt, dort kann man nun ein Zeichen eingaben, es wird sofort an die Stelle gespeichert. War der Wert falsch, tippt man einfach noch mal die richtige Taste. So kann man auch korrigieren.
Ist alles richtig kommt man mit Q wieder zurück in den Simulator, dort könnte man dann mit S den Speicher in eine Datei sichern. Man sollte die Erweiterung .SIM (Simulation) verwenden.

Jetzt wollen wir das Programm b11.sim abarbeiten. Das Programm erzeugt aus der Tastatureingabe im Versuch 2 eine digitale Ausgabe.
Ausgangssituation ist der Simulator in der Bedienoberfläche.
Wir betätigen die Taste L. Unter dem Speicher erscheint die Aufforderung zur Eingabe des Namens:

Name Programm: _

Dahinter schreiben wir den Namen:

Name Programm: B11.sim<enter>

Es wird überprüft ob die Datei existiert, wenn nicht kommt eine Fehlermeldung.
Es wird nicht kontrolliert ob die Erweiterung .SIM ist, andere Dateien machen aber keinen Sinn.

Jetzt fehlt uns noch der Versuch! Dazu wird einfach die Taste V getippt, es erscheint unter dem Speicher:

Versuchs-Nr: _

Da tippt man nun die 2 und wir haben unsere Startfähige Anordnung:

(Das Programm wurde schon mit R gestartet und die 5 eingegeben. Man sieht im Versuch das Ergebnis.)

Im Bild ist weiterhin dargestellt der letzte Mikrobefehl und die geschalteten Leitungen, der BZ zeigt auf die Adresse 00 (gelbe Leitungen und neue Eingabe), die Datenleitung schafft eine Verbindung zum Akku (rote Leitung) – der eingegebene Wert landet im Akku.
Das BZH hat die Adresse des Speicherplatzes A2, der nach der Eingabe verwendet wird.
Im Anschluss erfolgt die Beschreibung des Programms.

Größeres Assembler-Programm – Beispiel

Als Ausgabe gibt es Quasi-LED-Balken:

   

Diese Balken „hängen“ an den Speicherplätzen 02 bis 05 in gezeigter Reihenfolge.

Das zu schreibende Programm soll aus der Eingabe eines Zeichens (über Tastatur) die entsprechenden Balken ansteuern.

Das Beispiel zeigt die Darstellung für eine 5.
Im Prinzip soll eine Wandlung der Zeichen von hexadezimal auf dual vorgenommen werden!
Eigentlich ist das ganz einfach, denn nach der Eingabe der Zahl 5 ist die Bitfolge 0101 bereits im Akku vorhanden. Das Problem besteht darin, die einzelnen Bits auf 4 Speicherplätze zu verteilen!

Wie macht man so was?

Einen Befehl gibt es nicht dafür und man sollte nicht glauben, dass das ein Problem unseres kleinen Rechners ist, das gibt es real auch nicht, zumindest der 8086 kann das auch nicht.
Wir haben also ein Standard-Problem zu klären!

Lösung:

Man muss Bitweise herausbekommen ob das Bit 1 oder 0 ist. Das realisiert man mit „sogenannten Masken“, man vergleicht eine unbekannte Bitfolge mit einer bekannten, der Maske.

Und schon wieder gibt es ein Problem: es gibt keinen Vergleichs-Befehl derart, dass man herausbekommt ob das Bit 1 oder 0 ist. Es gibt generell keinen Vergleichsbefehl oder gar eine if-Anweisung wie in Pascal oder C!?
Auf der Assembler-Ebene muss man versuchen für sein Problem ein Flag zu nutzen. Das geht für unseren Fall übers N-Flag:

Zunächst wird das entsprechende Bit vom Eingabewert aktiviert mit

1000 AND xxxx

wird nur das 1.Bit ausgewählt, denn 0 AND x ist immer 0. Damit leisten die drei weiteren Bits keinen Beitrag, hingegen ist das erste Bit x, also 0 oder 1 vom Wert.
Man sagt, wir maskieren xxxx mit 1000.

Damit haben wir noch kein Flag gesetzt. Das geht aber indem wir die Maske vom maskierten Wert subtrahieren:

x000 – 1000

Je nach x wird das N-Flag gesetzt. Ist x = 1 ist das Ergebnis 0 (N-Flag nicht gesetzt 0), ist hingegen x = 0 wird das Ergebnis negativ also N = 1 und damit haben wir eine Entscheidung, es kann ein Sprung ausgeführt werden wenn N gesetzt ist, der Befehl SPN
(Sprung wenn N = 1).

Das ganze noch einmal exakt:

Das 1.Bit vom Wert ist 0, somit ist nach der AND-Operation der Wert nun 0000.
Davon wird 1000 subtrahiert, es gilt:

   0000
- 1000

Wir bilden von 1000 das 2-er Komplement und addieren es zu 0000:

1000 => 0111 1-er Komplement

   0111
+ 0001
---------
   1000 => 2-er Komplement

    0000       Wert nach Maskierung
+ 1000      Subtraktion durch Addition des 2-er Komplements
---------
   1000      Das 1. Bit ist 1, d.h. negativ, das N-Flag wird gesetzt (N=1)


In gleicher Weise kann man für die anderen 3 Bits sofern sie 0 sind feststellen, dass jeweils das N-Flag gesetzt wird.

Mit dieser Arbeitsweise ist auch zugleich darauf hingewiesen, dass die Subtraktion und dann auch alle weiteren arithmetischen Operationen immer auf die Addition zurückgeführt werden kann, man braucht nur ein leistungsstarkes Addierwerk!

Dementsprechend kann nun eine 0 oder 1 auf den Speicherplatz 02 gespeichert werden
(N=1 eine 0 speichern, N=0 eine 1).
Mit den 3 weiteren Bits verfahren wir in gleicher Weise, es werden die Masken 0100, 0010 und 0001 gebraucht.
Man legt alle Masken auf Speicherplätze oder man vereinbart nur eine Maske und schiebt die 1 immer einen Platz weiter. In dem Beispiel wird die erste Variante genutzt, die zweite geht aber auch, es gibt einen Verschiebe-Befehl.

Damit ist das Prinzip der Bearbeitung schon klar – muss nur noch umgesetzt werden! Genau das bereitet dem Anfänger große Schwierigkeiten – es soll erklärt werden.

Auch wenn der Assembler vieles schon macht, die Speicherplatzverwaltung obliegt dem Programmierer!

Organisation

Wir gehen mal so ran, als hätten wir genügend Speicherplatz, das Programm soll wieder beim Speicherplatz 20 beginnen. Belegt sind 00 (Eingabe), 01 (Ausgabe), 02 … 08 Sonderausgaben/Versuche) und FA ... FF (Systemspeicherplätze).
Die Masken könnte man in der ersten Zeile ganz rechts 0C … 0F. Außerdem brauchen wir eine 0, soll auf 0B liegen und einen Speicherplatz zum Zwischenspeichern eines Wertes, soll auf 11 liegen. Damit können wir schon mal anfangen:
Wir brauchen die Masken 0001 = 1(16), 0010 = 2(16), 0100 = 4(16) und 1000 = 8(16).
Hier wird schon ein wenig vereinfacht, da wir ohnehin die Maske 0001 brauchen können wir diese auch gleich als 1 benutzen (16 = hexadezimal):

org 0B
kon 0 ;auf Speicherplatz 0B wird eine 0 (0000) gespeichert
kon 1 ;auf Speicherplatz 0C eine 1 (0001) (kon erhöht die Adr. um 1)
kon 2 ;auf Speicherplatz 0D eine 2 (0010)
kon 4 ;auf Speicherplatz 0E eine 4 (0100)
kon 8 ;auf Speicherplatz 0F eine 8 (1000)

Wie groß der Wert vom Zwischenspeicher wird, ist unbekannt, also vereinbaren wir nur eine symbolische Adresse:

ret   equ   11     ;das Symbol ret ist die Adresse für den Speicherplatz 11

Es ist nicht notwendig den Speicherplatz 11 mit einer symbolischen Adresse zu verbinden, aber zum einen sieht man aus dem Namen nun schon was auf dem Speicherplatz gespeichert werden soll und zum anderen kann man sofern es mal Platzprobleme im Speicher gibt und die Adresse auf einem anderen Platzz besser wäre, einfach die Zuweisung ändern, man muss nicht mehr in jeder Zeile im Programm, wo der Platz benutzt wird, einzel diese Änderung vornehmen!

Nun folgt das Programm ab Adresse 20:

org 20

Auch hier wollen wir einen anderen Weg beschreiten und die Ermittlung der Ansteuerung in ein Unterprogramm stecken, das ist nicht unbedingt sinnvoll, soll aber die Nutzung zeigen.
Im Hauptprogramm soll die Tastatureingabe des Wertes erfolgen, danach erfolgt der UP-Aufruf und Nutzung des Wertes, der Wert steht noch im Akku nach der Eingabe.

Unterprogramm

Unterprogramme müssen einen Namen bzw. Adresse haben, das erledigen wir mit Marken:

m8:   spa   ret

m8 ist der Name des UP. UP’s benutzen die bekannten Befehle. Als erstes wird der Akku-Inhalt auf dem Speicherplatz 11 gespeichert, gerettet?
Warum
Ganz einfach, die oben schon genannte Operation „Wert AND Maske“ speichert das Ergebnis wieder in den Akku, damit wird der eingegebene Wert überschrieben und ist für die folgenden 3 Vergleiche nicht mehr vorhanden, also wird er erst mal gerettet – ist doch klar!
In den nächsten Zeilen kann man nun ohne Angst arbeiten, der Wert ist ja gerettet!

and   F   ;Akku = Akku AND SPl. 0F (1000)

Das ist die Maske für das 1.Bit. Und eine Vereinfachung ist möglich, denn die AND-Operation kann nur mit dem Inhalt eines Speicherplatzes erfolgen. Die Adresse braucht 2 Nibbles, also 0F, aber F reicht eben auch.

Ob nun das 1.Bit 1 oder 0 ist weiß man nicht, hängt von der Eingabe ab, also testen:

sub   F   ;Akku = Akku SUB SPl. 0F (1000)

Das setzt die Flags, das Ergebnis wird nicht gebraucht.

spn   m8   ;wenn neg. weiter bei m8e

Ist das N-Flag = 1, das 1.Bit ist 0, dann wird die 0 auf Speicherplatz 02 (1.Balken) gespeichert, wenn nicht, dann war es eine 1 und es geht in der nächsten Zeile weiter:

lda   C   ;nein - Spl. 0C in Akku (auf Speicherplatz 0C steht eine 1)
spa   2   ;Akku nach SPl. 02 (Anz.1.Stelle)

Damit kann das 2.Bit untersucht werden, man muss das Programm dort fortsetzen, also Sprung zu der Zeile. Die kennen wir aber nicht, also nehmen wir eine symbolische Adresse:

sp   m4   weiter bei m4 ( soll auf das Bit 0100 (Wert 4) hindeuten)

Jetzt muss noch die 0 behandelt werden, das beginnt an der symbolischen Adresse m8e:

m8e:ldaB ;SPl. 0B nach Akku (auf 0B steht eine 0)
spa2 ;Akku nach SPl. 02 (Anz.1.Stelle)

Ganz wichtig ist der Sprung zur Marke m4, denn würde er nicht da sein würden immer die Befehle ab Marke m8e abgearbeitet werden, also immer eine 0 gespeichert.

Ab Marke m4 wird nun das 2.Bit untersucht.
Das Wichtigste ist, dass zunächst einmal, dass der eingegebene Wert wieder bereit gestellt wird. Den haben wir ja vorsorglich auf den Speicherplatz „ret“ abgelegt, also laden wir ihn von da in den Akku:

m4:   lda   ret   ;SPl.11 nach Akku

Nun wiederholt sich der besprochene Teil dreimal nur mit den anderen Masken.
Da wir diesen Teil als Unterprogramm (UP) definiert haben, muss am Ende dieses Teils ein Rücksprung zum Hauptprogramm (HP) organisiert werden:

rup   ;Ruecksprung zum HP hinter UP.Aufruf

Beim Start des UP legt der Befehl SUP die Adresse des nächsten Befehls auf den Speicherlätzen FA und FB ab. Der Befehl RUP läd sie wieder in den Befehlszähler BZ und arbeitet ab dieser Adresse das Programm weiter ab.

Diese Arbeitsweise garantiert, dass ein UP von beliebiger Stelle aus aufgerufen werden kann, es wird immer nach Beendigung des UP der nächste Befehl nach Aufruf des UP abgearbeitet. Im realen Rechner gibt es für die Speicherung der Rücksprungadressen zumeist einen Stak-Speicher, der dann auch den Aufruf eines UP aus einem UP zulässt, das geht in unserem kleinen Rechner nicht so einfach (in der LST-Datei unten ist das UP und auch das HP geschlossen dargestellt).

Hauptprogramm

Das Hauptprogramm ist auch nur eine Folge von den schon bekannten Befehlen, also keine besondere Kennung. Das Einzige ist, dass man eine Adresse festlegen muss, ab der es beginnen soll – und dass ist erst mal schwierig. Da wir mit dem UP angefangen haben, müssen wir zählen wie viele Plätze es im Speicher braucht. Man sollte es aber nicht unmittelbar hinters UP legen, manchmal ändert sich da noch etwas.

Wir legen fest, es soll bei A0 beginnen:

;----- HP ----------
orga0;(1) HP ab SPl.A0
start:lda0 ;(2) HP Start Spl.00 in Akku
supm8;(3) UP.Aufruf
spstart;(4) erneuter Start HP
 
orgff;(5) Startadresse
kona

Das ist das ganze Hauptprogramm.
Zeile 1 legt fest, dass die Übersetzung der folgenden Befehle ab Speicherplatz A0 abgespeichert werden soll.
Zeile 2 ist der Beginn des HP, es wird der Inhalt von Speicherplatz 00 in den Akku geladen, das bedeutet, es wird die Tastatur abgefragt (siehe Eingabe). Außerdem wird der Befehl mit der Marke „start“ versehen.
Zeile 3 ruft das UP auf (UP hat den Namen m8).
Ist das UP fertig, geht es weiter in Zeile 4. Hier wird nichts weiter getan, als einen Sprung zur Marke „start“ anzuweisen, das HP wird erneut gestartet, es gibt eine Endlosschleife.
Zeilen 5 und 6 sind wichtig, es wird die Startadresse auf A0 gesetzt.

Startadresse 20 wäre falsch, wir würden mit dem UP beginnen, das wird komplett falsch. Man kann das ja einfach probieren, indem im Simulator die Startadresse auf 20 gesetzt wird.
Ändert man nach der Abarbeitung mit der falschen Startadresse diese wieder auf A0, kann es sein, dass das Programm nun auch nicht mehr richtig läuft!
Und das ist das Problem oder die Fähigkeit eines Assemblerprogramms, es werden in den Bereich des Programms Werte gespeichert, denn es gibt keine Trennung zwischen Programm und Daten. Das passiert so auch in der realen Rechnerwelt, z.B. kann man auch in der Programmiersprache C ein Array von 10 Speicherplätzen vereinbaren und aus Versehen speichert man einen Wert auf Speicherplatz 20 des Array. Es gibt keine Fehlermeldung aber das Programm geht nicht mehr wie es soll – ist doch ganz klar, der Wert überschreibt einen Teil des Programms und damit gibt es Probleme.

Mit Assemblerprogrammen kommt man in alle „Ecken“ des Speichers und Prozessors des Rechners – also ist bei der Nutzung eines realen Rechners Vorsicht geboten!

Man muss das aber nicht nur negativ sehen, denn das gibt uns auch die Möglichkeit neuer Programmiermethoden – das Programm kann sich selbst verändern, also selbstprogrammierende Programme kann man erstellen, das ist noch ein weites Forschungsfeld.

Nun noch einmal das ganze Programm, aber zunächst der logische Ablauf als
Programm-Ablauf-Plan (PAP):

Folgende Bezeichnungen gelten:

<11> - Inhalt des Speicherplatzes 11
Akku <= <0C> - der Inhalt des Speicherplatzes 0C wird in den Akku transportiert
(Links soll immer das Ziel stehen)

Und nun das komplette Programm als LST-Datei:

Es wurde jede Zeile kommentiert, das muss man nicht, hilft hier aber zum Verständnis.

zurück zur Start-Seite   /   weiter Bedienoberfläche