Die nachfolgenden Informationen wurden dem Handbuch How to use the Nova Computers (Data General Corporation 1970,1971) entnommen und ins Deutsche übersetzt.
Vom Typ her sind die Nova-Computer Universalrechner. Ihre Architektur basiert
auf dem Von-Neumann-Modell. Sie benutzen eine Wortlänge (Daten- und
Adressbus) von 16 Bit. Da das höchste Adressbit als Flag für eine indirekte
Adressierung reserviert ist, können maximal 215 = 32 768 Speicherplätze
von jeweils 16 Bit adressiert werden (nach heutiger Bezeichnung sind das 64 kB).
Der Programmzähler (program counter = PC) hat demzufolge eine
Länge von 15 Bit
Alle Maschinen der Baureihe besitzen in der Recheneinheit vier 16-bit-Akkumulatoren
(AC0, ..., AC3), von denen zwei als Indexregister verwendet werden können.
Die logischen und arithmetischen Operationen arbeiten ausschließlich mit den
Akkumulatoren. Jeder von ihnen kann dabei als Ergebnisregister dienen.
Gleichzeitig zu einer solchen Operation können als Zusatzfunktionen ein
bitweises Schieben des Akkumulatorinhalts und/oder ein vom Resultat abhängiges
Überspringen des nächsten Befehls ausgeführt werden. Außerdem gibt es Befehle,
um Daten (16-Bit Worte) vom Speicher in einen Akkumulator und zurück zu
transportieren, den Inhalt von Speicherzellen zu erhöhen oder zu erniedrigen,
Unterroutinen aufzurufen, Sprünge im Programmablauf vorzunehmen und um Daten
über eine Schnittstelle (Interface) einzulesen oder auszugeben.
Externe Geräte können über ein I/O-Interface einen Interrupt auslösen. Für die
Identifizierung der Interrupt-Quelle gibt es Hardware-Unterstützung durch ein
Daisy-Chain-Verfahren. Für den schnellen Datentransfer von oder zu einem
Peripheriegerät steht zusätzlich ein direkter Speicher-Zugriff
(direct memory access = DMA) zur Verfügung.
Da Computer in der Regel mit Binärzahlen arbeiten, ist die Dezimaldarstellung
von Zahlen etwas unpraktisch. Heute hat sich Darstellung im Hex-Format (Basis 16)
allgemein durchgesetzt. In den Nova-Handbüchern wurde damals stattdessen
durchgängig das Oktalformat (Basis 8) benutzt, das daher auch nachfolgend
verwendet wird.
Bsp.:
Dez. Hex Oktal 7 7 7 8 8 10 10 A 12 16 10 20 255 FF 377
Alle Befehle sind genau ein Wort lang (16 Bit). Sie unterteilen sich in vier klar strukturierte funktionsabhängige Gruppen, so dass eine sehr einfache Dekodierung durch die logische Hardware der Zentraleinheit (Central processing unit = CPU) ermöglicht wird:
Alle Befehle, die eine Speicherzelle adressieren (die ersten beiden o.g. Gruppen), können eine der folgenden Methoden benutzen:
Alle Adressierungsarten können zusätzlich für eine indirekte Adressierung verwendet werden. Darüber hinaus kann durch Setzen des Bits 0 einer solchen indirekten, im Speicher abgelegten Adresse (nur die Bits 1..15 bilden eine gültige Adresse) die indirekte Adressierung mehrfach verkettet werden.
Es können maximal 32768 Speicherzellen adressiert werden (15 Bit). Jede Speicherzelle enthält 16 Bits (Programm oder Daten). Einige Speicherzellen haben eine besondere Bedeutung:
Heute ist es üblich, die Bits eines Wortes (oder eines Bytes) von rechts nach links zu
nummerieren (Bit 0 hat die niedrigste Wertigkeit, Bit 15 die höchste). In den
Nova-Handbüchern ist dies genau anders herum erfolgt: Bit 0 ist das höchstwertige,
Bit 15 das niederwertigste Bit. Daher wird diese heute ungewohnte Bezeichnungsweise
für die nachfolgenden Beschreibungen übernommen.
Die vier oben genannten Befehlsgruppen werden durch die ersten drei Bits (Bit 0..2)
gekennzeichnet:
Sind die drei Bits Null (0 0 0) handelt es sich um einen Sprungbefehl oder um einen Befehl zum Ändern einer Speicherzelle. Die Bits 3 und 4 bestimmen die Funktion (JMP, JSR, ISZ, DSZ), die Bits 6 und 7 die Adressierungsart (s.o.). Das Bit 5 kennzeichnet eine indirekte Adressierung, der 8-Bit-Wert Offset die absolute oder relative Adresse (s.o.):
Haben die ersten drei Bits die Werte 0 0 1 oder 0 1 0 handelt es sich um einen Befehl, Daten vom Speicher zum Akkumulator (LDA) oder umgekehrt (STA) zu bewegen. Die Bits 3 und 4 geben den zu verwendenden Akkumulator an, die Bits 6 und 7 die Adressierungsart. Das Bit 5 kennzeichnet eine indirekte Adressierung, der 8-Bit-Wert Offset die absolute oder relative Adresse (s.o.):
In diesem Fall ist das erste Bit (Bit 0) gesetzt. Die Bits 1 und 2 geben den Quellakkumulator und die Bits 3 und 4 den Zielakkumulator an. Die auszuführende Funktion wird mit den Bits 5 bis 7 (8 Funktionen: COM, NEG MOV, INC, ADC, SUB, ADD, AND) ausgewählt. Die Bits 8 und 9 legen eine Schiebebedingung (L, R, S), die Bits 10 und 11 eine Modifikation (Z, O, C) des Übertragbits (Carry) und die Bits 13 bis 15 eine vom Ergebnis abhängige Sprungbedingung (SKP, SZC, SNC, SZR, SNR, SEZ, SBN) fest:
Bei Befehlen, die eine Ein- oder Ausgabe von oder an ein Peripheriegerät bewirken, haben die ersten drei Bits den Wert 0 1 1. Die Bits 3 und 4 geben den Akkumulator an, der für die zu übertragenden Daten benutzt werden soll. Mit den Bits 5 bis 7 wird eine von 8 Ein-Ausgabe-Funktionen (NIO, DIA, DOA, DIB, DOB, DIC, DOC, SKP) ausgewählt. Mit den Bits 8 und 9 kann ein Steuersignal für das Interface (S, C, P) gewählt werde, die Bits 10 bis 15 geben die Adresse des Geräts an (0 .. 63):
Bei allen Befehlen, die eine Speicherzelle adressieren, haben die Bit 5..15 die gleiche Bedeutung:
Symbol | Erläuterung | |
INDX | = 00 |
Direkte Adressierung auf der Speicherseite Null Offset (0..377) ist die absolute Adresse |
= 01 | Relative Adressierung zum Programmzähler (PC) Offset (-200..177) ist der Abstand im Zweierkomplement |
|
= 10 | Relative Adressierung zum Akkumulator 2 (AC2) Offset (-200..177) ist der Abstand im Zweierkomplement |
|
= 11 | Relative Adressierung zum Akkumulator 3 (AC3) Offset (-200..177) ist der Abstand im Zweierkomplement |
|
OFFSET | 0 .. 377 | Absolute Adresse auf Seite Null (INDX = 0) |
-200..177 | Relative Adresse im Zweierkomplement (INDX > 0) | |
I | = 0 | Direkte Adressierung: Der Inhalt der über INDX und OFFSET bestimmten Adresse ist Ziel der Operation |
= 1 | Indirekte Adressierung: Der Inhalt der über INDX und OFFSET bestimmten Adresse ist die Adresse, die Ziel der Operation ist. |
Alle Adressen sind als Oktalzahlen angegeben.
Es lassen sich damit also direkt 1024 Speicherplätze adressieren: 256 direkt auf Seite Null,
und jeweils 256 Plätze im Bereich um den Programmzähler, sowie um die Adressen in
Akkumulatoren 2 und 3. Wenn das Indirektbit I gesetzt ist, kann der gesamte
Speicher adressiert werden. Ist bei einer solchen indirekten Adresse das Bit 0
gesetzt, bedeutet dies eine verkettete indirekte Adressierung.
Bsp. für Adressierung:
PC = 1415 (oktal) 1. Befehl: OFFSET = -132, I = 0, INDX = 01 (rel Adr. zum PC) Speicher: [1415-132 = 1263] = 2431 (Inhalt der Speicherzelle 1263) ==> eff. Wert = 2431 2. Befehl: OFFSET = -132, I = 1, INDX = 01 (rel Adr. zum PC) Speicher: [1415-132 = 1263] = 2431 (Inhalt der Speicherzelle 1263) [2431] = 7425 (Inhalt der Speicherzelle 2431) ==> eff. Wert = 7425 3. Befehl: OFFSET = -132, I = 1, INDX = 01 (rel Adr. zum PC) Speicher: [1415-132 = 1263] = 102431 (Inhalt der Speicherzelle 1263) [2431] = 7425 (Inhalt der Speicherzelle 2431) [7425] = 6215 (Inhalt der Speicherzelle 7425) ==> eff. Wert = 6215 ISZ 123 ; erhöhe den Inhalt von Adr. 123(8) ISZ @123 ; erhöhe den Inhalt der Adresse, auf die in Adr. 123(8) zeigt ISZ -12,3 ; erhöhe den Inhalt der Adresse [AC3]-12
Die Ermittlung der effektiven Adresse ist zuvor beschrieben. Die Bits 3 und 4 legen die auszuführende Funktion fest:
Symbol | Mnem. | Erläuterung | |
FKT | = 00 | JMP | Sprung an die angegebene Adresse (Adresse → PC) |
= 01 | JSR | Sprung in eine Unterroutine (PC+1 → AC3, Adresse → PC) | |
= 10 | ISZ | Addiere 1 zu der angegebene Adresse und überspringe den nächsten Befehl, wenn das Ergebnis Null ist | |
= 11 | DSZ | Subtrahiere 1 von der angegebenen Adresse und überspringe den nächsten Befehl, wenn das Ergebnis Null ist |
Hinweis: Im Gegensatz zu den heute üblichen Prozessoren, gab es bei
der beschriebenen Baureihe der Nova noch keinen Stack (spätere Modelle, z.B.
Nova 4 waren damit ausgestattet). Daher erforderte das Verschachteln von Unterroutinen
zusätzliche programmatische Maßnahmen. Da nach dem Aufruf einer Unterroutine
(JSR) die Rückspringadresse im Akkumulator 3 (AC 3) abgelegt
wurde, musste dieser vor Aufruf einer weiteren verschachtelten Unterroutine
an geeigneter Stelle zwischengespeichert werden. Eine einfache, allerdings
nicht ablaufinvariante (reentrant) Methode ist das folgende
Beispiel:
... JSR Subr1 ; Sprung in Unterroutine ... JSR Subr2 ; Sprung in Unterroutine ... Subr1: ... JMP 0,3 ; Rücksprung ... 0 Subr2: STA AC3,Subr2-1 ... JSR Subr1 ; Sprung in Unterroutine ... JMP @Subr2-1 ; Rücksprung ...
Die Ermittlung der effektiven Adresse ist oben beschrieben. Die Bits 1 und 2 legen die auszuführende Funktion fest, die Bits 3 und 4 den zu verwendenden Akkumulator:
Symbol | Mnem. | Erläuterung | |
FKT | = 01 | LDA | Lade den Inhalt der angegebenen Adresse in den Akkumulator |
= 10 | STA | Speichere des Inhalt des Akkumulators in die angegebene Adresse | |
AC | = 00 | AC0 | Daten nach oder von Akkumulator 0 |
= 01 | AC1 | Daten nach oder von Akkumulator 1 | |
= 10 | AC2 | Daten nach oder von Akkumulator 2 | |
= 11 | AC3 | Daten nach oder von Akkumulator 3 |
Bsp.:
LDA 1,23 ; Inhalt von Adr. 23 nach AC1 LDA 0,5,3 ; Inhalt von [AC3]+5 nach AC0 STA 1,.-3 ; Inhalt von AC1 nach [PC]-3 STA 1,@23 ; Inhalt von AC1 nach Adr., auf die Adr. 23 zeigt STA 1,@1,3 ; Inhalt von AC1 nach Adr., auf die [AC3]+1 zeigt
Die größte Gruppe bilden die Befehle für Arithmetik und Logik. Sie sind durch eine 1 im Bit 0 gekennzeichnet. Alle Befehle beinhalten die Möglichkeit nach einer solchen Operation das Carry-Bit zu manipulieren, ein bitweises Schieben auszuführen und eine ergebnisabhängige Sprungbedingung zu erzeugen. Letzteres ist die einzige Möglichkeit Programmverzweigungen zu bewirken. Ist die Bedingung erfüllt, wird der nächste folgende Befehl übersprungen (Skip).
Symbol | Mnem. | Erläuterung | |
AC-S | = 00 | AC0 | Akkumulator 0 (1. Operand) |
= 01 | AC1 | Akkumulator 1 | |
= 10 | AC2 | Akkumulator 2 | |
= 11 | AC3 | Akkumulator 3 | |
AC-D | = 00 | AC0 | Akkumulator 0 (2. Operand und Ergebnis) |
= 01 | AC1 | Akkumulator 1 | |
= 10 | AC2 | Akkumulator 2 | |
= 11 | AC3 | Akkumulator 3 | |
FKT | = 000 | COM | Bilde das logisches Komplement von AC-S, platziere bei N=0 das Ergebnis nach der angebenen Schiebeoperation (SHFT) in AC-D |
= 001 | NEG | Bilde das Zweierkomplement von AC-S (Negieren) und platziere bei N=0 das Ergebnis nach der angebenen Schiebeoperation (SHFT) in AC-D | |
= 010 | MOV | Platziere bei N=0 den Wert in AC-S nach der angebenen Schiebeoperation (SHFT) in AC-D | |
= 011 | INC | Erhöhe den Wert von AC-S um Eins und platziere bei N=0 das Ergebnis nach der angegebenen Schiebeoperation (SHFT) in AC-D | |
= 100 | ADC | Addiere das logische Komplement von AC-S zu dem Wert in AC-D und platziere bei N=0 das Ergebnis nach der angegebenen Schiebeoperation (SHFT) in AC-D | |
= 101 | SUB | Subtrahiere den Wert in AC-S zu dem Wert in AC-D (entspricht der Addition des Zweierkomplements) und platziere bei N=0 das Ergebnis nach der angegebenen Schiebeoperation (SHFT) in AC-D | |
= 110 | ADD | Addiere den Wert in AC-S zu dem Wert in AC-D und platziere bei N=0 das Ergebnis nach der angegebenen Schiebeoperation (SHFT) in AC-D | |
= 111 | AND | Bilde das logische Und der Werte in AC-S und AC-D und platziere bei N=0 das Ergebnis nach der angegebenen Schiebeoperation (SHFT) in AC-D | |
SHFT | = 00 | keine Schiebeoperation | |
= 01 | L | Schiebe um ein Bit über den Carry nach links (s.u.) | |
= 10 | R | Schiebe um ein Bit über den Carry nach rechts (s.u.) | |
= 11 | S | Vertausche das linke mit dem rechten Byte | |
CRY | = 00 | Das Carrybit bleibt unverändert | |
= 01 | Z | Das Carrybit wird auf Null gesetzt | |
= 10 | O | Das Carrybit wird auf Eins gesetzt | |
= 11 | C | Bilde das Komplement des Carrybits | |
N | = 0 | Das Ergebnis ersetzt den Inhalt von AC-D | |
= 1 | # | Der Inhalt von AC-D wird nicht verändert (no load) | |
SKIP | = 000 | Der nächste Befehl wird immer ausgeführt | |
= 001 | SKP | Der nächste Befehl wird immer übersprungen | |
= 010 | SZC | Der nächste Befehl wird übersprungen, wenn das Carrybit Null ist | |
= 011 | SNC | Der nächste Befehl wird übersprungen, wenn das Carrybit nicht Null ist | |
= 100 | SZR | Der nächste Befehl wird übersprungen, wenn das Ergebnis Null ist | |
= 101 | SNR | Der nächste Befehl wird übersprungen, wenn das Ergebnis nicht Null ist | |
= 110 | SEZ | Der nächste Befehl wird übersprungen, wenn entweder das Carrybit oder das Ergebnis Null ist | |
= 111 | SBN | Der nächste Befehl wird übersprungen, wenn das Carrybit und das Ergebnis nicht Null sind |
Schieben nach links:
Schieben nach rechts:
Bytes vertauschen:
Bsp.:
COM# 1,1,SZR ; Skip, wenn AC1 = 177777(8) ... MOVZ 1,3 ; Schiebe AC1 nach AC3 und setze das Carrybit auf Null ... MOVL# 2,2,SZC ; Ist AC2 positiv (Bit 0 = 1)? MOVOR 2,2,SKP ; Nein, Carry = 1 und einmal rechts schieben (Division durch 2) MOVZR 2,2 ; Ja, Carry 0 0 une einmal rechts schieben (Division durch 2) ... SUBZ# 1,0,SZC ; Skip, wenn AC0 < AC1 ADCZ# 1,0,SZC ; Skip, wenn AC0 ≤ AC0
Die nachfolgenden Befehle erlauben den Datentransfer und die Steuerung einer
Elektronik für die Datenein- und -ausgabe. Es können damit 62 Geräte (Adressen
1 .. 62) adressiert werden. Die Geräteadresse 0 wird nicht benutzt, die Adresse 63
wird für einige Spezialfunktionen verwendet (z.B Einlesen der Datenschalter der
Konsole und die Interruptsteuerung). Jedes Interface kann bis zu drei Datenpuffer
A, B und C (16-Bit-Register) sowie vier Flags (in der
Hardware durch D-Flip-Flops realisiert): Busy, Done, Int-Disable und Int-Request besitzen.
Die Flags werden für das typische Handshake-Verfahren eingesetzt.
Im Ruhezustand sind Busy- und Done-Flag nicht gesetzt.
Zu Beginn eines Datentransfer wird vom Programm das Busy-Flag gesetzt.
Der weitere Ablauf hängt davon ab, ob es sich um eine Ein- oder Ausgabe handelt:
1. Eingabe
Das Busys-Flag signalisiert dem Gerät, dass ein Datenwort erwartet wird. Sobald
dies übertragen ist, setzt das externe Gerät das Done-Flag. Das Done-Flag kann
vom Programm abgefragt werden (SKPDx), so dass das übertragene
Datenwort mit einem DIx-Befehl abgeholt werden kann (Polling).
Ist das Int-Disable-Flag nicht gesetzt, wird gleichzeitig mit dem Done-Flag
das Int-Request-Flag gesetzt, um einen Interrupt anzufordern. Das Programm
muss dann eine geeignete Interrupt-Routine bereitstellen.
2. Ausgabe
Nach Setzen des Busys-Flags wird vom Interface ein Datenwort an das externe
Gerät ausgegeben. Dieses bestätigt den Empfang durch Setzen des Done-Flags.
Die weitere programmatische Verarbeitung kann dann, wie zuvor bei der Eingabe
beschrieben, durch Polling oder Interrupt erfolgen.
Symbol | Mnem. | Erläuterung | |
AC | = 00 | AC0 | Daten nach oder von Akkumulator 0 |
= 01 | AC1 | Daten nach oder von Akkumulator 1 | |
= 10 | AC2 | Daten nach oder von Akkumulator 2 | |
= 11 | AC3 | Daten nach oder von Akkumulator 3 | |
FKT | = 000 | NIO | kein Datentransfer |
= 001 | DIA | Lese Daten aus Puffer A in den Akkumulator | |
= 010 | DOA | Schreibe Daten aus dem Akkumulator in den Puffer A | |
= 011 | DIB | Lese Daten aus Puffer B in den Akkumulator | |
= 100 | DOB | Schreibe Daten aus dem Akkumulator in den Puffer B | |
= 101 | DIC | Lese Daten aus Puffer C in den Akkumulator | |
= 110 | DOC | Schreibe Daten aus dem Akkumulator in den Puffer C | |
CTRL | = 00 | ||
= 01 | S | Setze das Busy-Flag und Lösche das Done-Flag(Start) | |
= 10 | C | Lösche alle Flags (Clear) | |
= 11 | P | Spezielles geräteabhängiges Steuersignal (Pulse) | |
FKT | = 111 | SKP | Überspringe den nachfolgenden Befehl (Skip) in Abhängigkeit eines Flags |
CTRL | = 00 | BN | Skip, wenn das Busy-Flag nicht Null ist |
= 01 | BZ | Skip, wenn das Busy-Flag Null ist | |
= 10 | DN | Skip, wenn das Done-Flag nicht Null ist | |
= 11 | DZ | Skip, wenn das Done-Flag Null ist | |
DEVICE | = 0 .. 63 | Geräteadresse |
Es gibt eine Reihe von Sonderbefehlen für die Programmablauf- und Interruptsteuerung. Diese sind als Ein- und Ausgabebefehle mit der Geräteadresse 77 (CPU) realisiert.
Name | Befehl | oktal | Funktion |
IORST | DIC 0,CPU | 62677 | Alle I/O-Flags der angeschlossenen Geräte werden zurück gesetzt |
HALT | DOC 0,CPU | 63077 | Die CPU wird angeahlten |
READS n | DIA n,CPU | Liest die Stellung der Konsolenschalter nach ACn | |
INTEN | NIOS CPU | 60177 | Erlaubt der CPU, Interrupts von Geräten anzunehmen |
INTDS | NIOC CPU | 60277 | Verhindert, dass die CPU Interrupts von Geräten annimmt |
INTA n | DIB n,CPU | Liest die Nummer des ersten, einen Interrupt anfordernden Geräts nach ACn | |
MSKO n | DOB n,CPU | Schreibt den Inhalt von ACn in das Interrupt-Maskenregister aller interrupt-fähigen Geräte | |
SKPBN CPU | Überspringt den nächsten Befehl, wenn das Interrupt-Flag gesetzt ist | ||
SKPBZ CPU | Überspringt den nächsten Befehl, wenn das Interrupt-Flag nicht gesetzt ist | ||
SKPDN CPU | Überspringt den nächsten Befehl, wenn das Power-Fail-Flag gesetzt ist | ||
SKPDZ CPU | Überspringt den nächsten Befehl, wenn das Power-Fail-Flag nicht gesetzt ist |
Optional gab es eine einsteckbare Platine für eine hardware-kodierte Multiplikation (16x16 bit ⇒ 32 bit) und Division (32 bit ⇒ 16 bit Quotient und 16 bit Rest). Nachfolgend sind die zugehörigen Befehle aufgeführt. Die Mul/Div-Einheit wird mit der Geräteadressen 01 angesprochen.
Name | Befehl | oktal | Funktion |
MUL | DOCP 2,1 | 73301 | Multipliziert die vorzeichenlosen ganzen Zahlen in AC1 und AC2 und addiert den Wert in AC0. Das Ergebnis in doppelter Länge (32 bit) steht in AC0 (H) und AC1 (L). AC2 bleibt unverändert. |
DIV | DOCS 2,1 | 73101 | Wenn der vorzeichenlose Wert in AC0 größer oder gleich dem in AC2 ist, wird das Carry-Bit gesetzt und der nächste Befehl ausgeführt, ohne den Inhalt der Akkumulatoren zu verändern. Andernfalls wird das Carry-Bit gelöscht und der doppelt lange vorzeichenlose Wert (32 bit) in AC0 (H) und AC1 (L) durch den vorzeichenlosen Wert in AC2 geteilt. Der Quotient steht in AC1 (16 bit) und der Rest in AC0 (16 bit). AC2 bleibt unverändert. |
Adr. (okt) | Adr. (hex) | Adr. (dez) | Masken- Bit | Kurzform | Gerät |
01 | 01 | 01 | MDV | 16/32-bit Multiplizierer und Dividierer | |
10 | 08 | 08 | 14 | TTO | Konsolen-Ausgabe (Teletype output) |
11 | 09 | 09 | 15 | TTI | Konsolen-Eingabe (Teletype input) |
12 | 0A | 10 | 11 | PTR | Lochstreifenleser (Paper tape reader) |
13 | 0B | 11 | 13 | PTP | Lochstreifenstanze (Paper tape punch) |
14 | 0C | 12 | 13 | RTC | Zeitgeber (Real time clock) |
15 | 0D | 13 | 12 | PLT | Digitalplotter (Incremental plotter) |
16 | 0E | 14 | 10 | CDR | Lochkartenleser(Card reader) |
17 | 0F | 15 | 12 | LPT | Zeilendrucker (Line printer) |
20 | 10 | 16 | 9 | DSK | Festplatte (Disk) |
22 | 12 | 18 | 10 | MTA | Magnetbandgerät (Magnetic tape) |
33 | 1B | 27 | ? | ? | Wechselplatte (Cartridge disk) |
... | |||||
77 | 3F | 63 | CPU (Sonderbefehle) |