Die PDP-8 CPU kennt zwei Adressierungsmodi: “direct” und “indirect”. Sie sind einfach zu verstehen; allerdings gibt es einige Dinge, die sich erst auf den zweiten Blick erschliessen.
Die CPU kennt nur 6 Befehle, die jeweils ein Argument erwarten, nämlich die Adresse einer Speicherzelle:
AND 0000 logical AND TAD 1000 2's complement add ISZ 2000 increment and skip if zero DCA 3000 deposit and clear AC JMS 4000 jump to subroutine JMP 5000 jump
Schauen wir uns dazu an, wie der Assembler folgenden Code übersetzt. Diese Routine wandelt ein 6-bit-Zeichen in ASCII um und gibt es aus:
PAGE 2 / same as: * 0400 / ... mehr Code put6c, 0 / return addr AND K77 / keep 6 bits CLL / proper adding TAD KM40 / -40 SPA / Skip next on positive Accu TAD K100 / if 40..77, add 100 TAD K40 / restore original value JMS putc / print it JMP I put6c / return / ... noch mehr Code
Wer schon Assembler auf anderen CPU geschrieben hat könnte verleitet sein zu denken, dass die Argumente als weitere Datenworte im Binärcode landen.
Das ist aber nicht der Fall: Die Befehle sind immer 1 Wort (12 Bit) breit. Das erste Triple definiert den Opcode (z.B. TAD mit 1xxx). Somit bleiben für die Codierung des Arguments 9 Bits.
Adressierungsmodi
Mit 9 Bits lassen sich 512 Werte abbilden (2^9, Wertebereich 0..511), was nur einen kleinen Teil des Speichers ausmacht (min. 4k Worte). Schlimmer noch: Es stehen nur 7 Bits für die Adresse zu verfügung. Aber erst mal der Reihe nach… 🙂
In der DEC Literatur ist die Befehlscodierung so dargestellt:
_0___1___2_ _3_ _4_ _5___6___7___8___9___10__11 | | | | | |OP CODE 0-5| IA| MP| ADDRESS | |___|___|___|___|___|___|___|___|___|___|___|___|
Dabei bedeuten:
- IA: Indirect Addressing (0: direct; 1: indirect)
- MP: Memory Page (0: Page 0, 1: current page)
- Address: relative address
Memory Pages
Durch diese Adressierung ergibt sich ein Konzept von Speicherseiten, die jeweils 128 Bytes gross sind (2^7 bits). Wenn wir den Programmcode auf Seite 2 legen (Oktal 0400, dezimal 256), erzeugt der Assembler folgenden Binärcode:
00420 0000 put6c, 0 00421 0204 AND K77 00422 7100 CLL 00423 1200 TAD KM40 00424 7510 SPA 00425 1205 TAD K100 00426 1202 TAD K40 00427 4206 JMS putc 00430 5620 JMP I put6c
Direkte Adressierung
Das Mnemonic AND mit dem “Argument” K77 (meine anderswo defnierte Konstante 0077) wird als 0204 codiert, nämlich:
_0___1___2_ _3_ _4_ _5___6___7___8___9___10__11 | | | | | | 0 0 0 | 0 | 1 | 0 0 0 0 1 0 0 | |___|___|___|___|___|___|___|___|___|___|___|___| |---- 0 ----|---- 2 ----|---- 0 ----|---- 4 ----|
Der AND Befehl lädt also meine Konstante K77 direkt aus dem Speicher (IA:0) aus der aktuellen Seite (MP:1) von Adresse 4. Da der Code in Seite 2 ab 0400 platziert ist, befindet sich der Wert der Konstante also an absoluter Adresse 0404. Schauen wir uns das in simh an:
sim> ex -m 420-430 420: AND 0 421: AND 404 422: CLL 423: TAD 400 424: SPA 425: TAD 405 426: TAD 402 427: JMS 406 430: JMP I 420 sim> ex 404 404: 0077
Wäre hier das MP Bit gesetzt, würde statt dessen aus der ersten Seite (Zero Page) gelesen, an absoluter Adresse 4.
Indirekte Adressierung
Im Code schliesst die Subroutine mit einem indirekten Sprung (JMP I) ab. Das Assembler Listing verrät uns, dass dieser Befehl als 5620 codiert ist:
_0___1___2_ _3_ _4_ _5___6___7___8___9___10__11 | | | | | | 1 0 1 | 1 | 1 | 0 0 2 0 0 0 0 | |___|___|___|___|___|___|___|___|___|___|___|___| |---- 5 ----|---- 6 ----|---- 2 ----|---- 0 ----|
Damit liegt das Sprungziel nicht an Adresse 0 020 000 (Oktal 20, Dezimal 16), sondern an dieser Adresse ist das Sprungziel hinterlegt, und dieses wird angesprungen.
Exkurs: Unterprogramme
Beim Aufruf der Subroutine (JMS Mnemonic) wird die Adresse des Aufrufers in diese Speicherstelle geschrieben; Subroutinen werden immer auf diese Weise verlassen.
Die PDP-8 hat keinen Stack; die Entwickler haben sich da was schönes ausgedacht um Code wieder verwertbar zu machen. Allerdings ist dieser Code nicht re-entrant, darf sich also niemals selbst aufrufen, auch nicht geschachtelt!
Exkurs: Programm-Listing
Der macro8x Assembler aus dem simh Paket erzeugt automatisch ein Listing. Die Option -d fügt ausserdem eine Symboltabelle am Ende des Listings ein.l
Zusammenfassung
- Was wie ein Argumente eines Mnemonics aussieht wird direkt ins Befehlswort codiert.
- Es gibt nur 7 Adressbits in einem Befehl und dadurch eine logische Einteilung des Speichers in Seiten zu 128 Worten.
- Über direkte und indirekte Adressierung ist trotzdem der gesamte Speicher adressierbar.
- Die Zero Page ist aus allen anderen Seiten erreichbar.
- Der Assembler nimmt einem die Berechnung der 7-Bit-Offsets ab und zeigt Fehler an, wenn Sprungziele oder Daten ausserhalb der aktuellen Seite liegen.
- Das vom Assembler generierte Listing ist seihr hilfreich, um diese Zusammehänge und Fehlermeldungen zu verstehen.
In diesem Sinn: Happy Hacking! 🙂