ctr

DeMon48_128k

intel 8049 (8048, 8748, 8749, 8035, 8039) / MCS-48 Hardware-Debugger & Monitor

Dokumentation - Kapitel 3


3.0.0 MCS-48 im 21. Jahrhundert

Als mir im Jahr 2021 wieder einmal die 8049-Mikrocontroller in die Hand fielen, war schon eine gewisse Neugier vorhanden, ob diesem wirklich archaischen Baustein überhaupt noch irgendwelche Lebenszeichen zu entlocken wären, wie es war, ihn zu programmieren und er möglicherweise sogar zu etwas Nützlichem genutzt werden könnte. Immerhin war die MCS-48 Familie von den späten 1970ern bis zum Ende der 1980er sehr populär.

Es sollte allerdings ein RAM als Programmspeicher eingesetzt werden, um dem umständlichen Hantieren mit (E)EPROMs, Programmier- und Löschgerät (wo sind die eigentlich?) dieses Mal zu entgehen : ein integrierter EPROM-Simulator.

3.1.0 Erste Inbetriebnahme

Es wurde nur die Stromversorgung angeschlossen und als einzige On-Chip-Hardware der Oszillator mit einem Quarz und Kondensator bei *RESET=L und EA=H in Betrieb genommen.

Eigentlich wurde erwartet, dass dann nur an den Oszillator-Anschlüssen XTAL1 und XTAL2 des µCs Aktivität zu messen wäre, aber der 8049 generiert bei EA=H auch im Reset-Zustand Signale an den Anschlüssen *PSEN und ALE und gibt kontinuierlich die Adresse 0x000 auf BUS und P2.0 bis P2.3 aus (So steht es auch im "intel 8-Bit Embedded Controllers"-Handbuch von 1990 : "[...]ALE and PSEN (if EA = 1) are active while in Reset." Quelle: "intel 8-Bit Embedded Controllers"-Handbuch (1990); Kap."Single Component MCS48-System" / "2.12 Reset"; S.1-10, was ich offensichtlich überlesen hatte). Leider durchkreuzte das die Idee, das für den Programmspeicher eingesetzte RAM im Reset-Zustand isoliert vom 8049 einfach mit Daten laden zu können.

PSEN und ALE bei RESET=L,EA=H
Bild: *PSEN und ALE bei *RESET=L und EA=H

3.2.0 NOP-Schleife

Die einfachste Möglichkeit für einen weiteren Test ist eine "NOP-Schleife" : die CPU findet nur "NOP"-Anweisungen (No OPeration) in ihrem Adressraum vor. Werden diese "NOP"-Befehle durch die CPU ausgeführt, kann das Hochzählen des Befehlszählers (PC/Program Counter), der stets auf die Programmspeicher-Adresse des nächsten auszuführenden Befehls zeigt, an den externen Adress-Signalen beobachtet werden.

Funktionieren müssen dafür :

  1. der Oszillator
  2. der Programmzähler (PC, Program Counter)
  3. die Ausgabe und das Demultiplexen der Adressbus-Signale mit ALE und externem FlipFlop
  4. das Lesen des "NOP"-Opcodes über den Datenbus
  5. der Befehls-Decoder, interne Zustandsmaschine etc.

Um auch dafür kein EPROM o.ä. programmieren zu müssen und es ja nur um den Opcode für "NOP" (0x00) geht, wurde ein 74HC541 (Octal 3-State Buffer) als festverdrahteter Befehlsspeicher eingesetzt, der während *PSEN=L den Opcode für "NOP" (0x00) auf die BUS-Leitungen legt.

Statt des von intel vorgeschlagenen "Transparent Latch", 74LS373, für das Zwischenspeichern der unteren 8 Adressbits von BUS wurde, wie auch für das untere Nibble (P2.0 bis P2.3) von P2, ein flankengesteuertes D-FlipFlop mit Tri-State Ausgängen vorgesehen, weil sich so ein störärmerer Adressbus erzielen lässt.

Da die Adressleitungen des RAM während des Ladens mit Daten über das Interface zwischen Personal Computer und der Debugger-/Monitor-Hardware vom 8049 isoliert werden müssen, waren die Tri-State-Ausgänge dieser D-FFs natürlich auch sehr willkommen.

Und weil der bidirektionale Datenbus des RAMs beim Laden mit Daten ebenfalls vom 8049 getrennt werden muss, kam dort ein "Bi-Directional 8-Bit Bus-Transceiver", 74HC245, als Trennstelle auch gleich beim NOP-Schleifen-Test zum Einsatz. *PSEN, *RD, *WR, ALE wurden provisorisch gepuffert, so dass auch bei Unfällen auf diesen Leitungen dem 8049 möglichst kein Schaden drohte.

Daten- und Adressbus in NOP-Schleife
Bild: Ausführen der NOP-Schleife (D0...D7, P2.0...P2.3, ALE, *PSEN, *RESET)

NOP-Test Schaltung
Bild: Die für den NOP-Test verwendete Schaltung

3.3.0 Programm- und Daten-Speicher in einem RAM

Um das zukünftig als externen Programmspeicher eingesetzte RAM auch als Datenspeicher, der über die "MOVX @Ri,A"- und
"MOVX A,@Ri"-Befehle und die *WR- und *RD-Signale des µCs beschrieben beziehungsweise gelesen werden kann, einsetzen zu können, wurden die *PSEN- und *RD-Signale des µC durch ein UND-Gatter miteinander verknüpft, so dass die *OE-Leitung des RAM immer Low-Pegel annimmt, sobald eines dieser Signale aktiv (Low) wird.

Das *PSEN-Signal des µC wurde gleichzeitig die 17. Adressleitung, A16, des RAM, so dass sich der externe Programmspeicher-Bereich im RAM von Adresse 0x00000 bis Adresse 0x0FFFF und der externe Datenspeicher-Bereich von Adresse 0x10000 bis Adresse 0x1FFFF (ohne Debugger und ohne memory-mapped-IO) erstreckt.

Der 8049 kann mit den maximal 12 unter seiner Kontrolle stehenden Adressleitungen 4kByte adressieren, die fehlenden Adress-Bits werden durch drei zusätzliche 4Bit-D-FlipFlops in der MMU (Memory Management Unit) des Debugger-/Monitor-Systems bereitgestellt. So ist das Ansprechen von jeweils bis zu 64kByte - unabhängig voneinander für Programm- und Datenspeicherbereich unter Softwarekontrolle auszuwählen - ermöglicht (siehe "3.6.0 DeMon48_128k Speicherorganisation / MMU").

3.4.0 Debug-µC

Als Mikrocontroller zur Steuerung des DeMon48_128k-Systems (Debug-µC) und als Interface zum Personal Computer fiel die Wahl auf einen betagten, ebenfalls noch als Restbestände hier vorhandenen PIC16F874. Eine erste Version des Debug-/Monitor-Systems basierte auf serieller Kommunikation zwischen Debug-µC und restlicher Hardware, was aber später zugunsten einer wesentlich höheren Geschwindigkeit des Systems auf parallelen Datenaustausch umgestellt wurde.

3.4.1 NOP-Schleife mit Debug-µC

Die erste Test-Firmware für den Debug-µC hatte folgende Funktion :

  1. Aktivieren der *RESET-Leitung des 8049 (*RESET=L)
  2. Isolation des 8049 vom Adress- und Datenbus des Programmspeichers
  3. Beschreiben des gesamten 4kByte-Programmspeichers mit 0x00 ("NOP")
  4. 8049 mit Adress- und Datenbus des Programmspeichers verbinden
  5. Freigabe der *RESET-Leitung des 8049 (*RESET=H)

Es ließen sich nach dem Start die gleichen Signale auf den Adress- und Datenleitungen des 8049 messen, wie beim Test mit dem 74HC541 als "Programmspeicher", also wurde jetzt wirklich von unterschiedlichen Adressen des RAMs der korrekte Opcode für "NOP" gelesen.

3.4.2 256Byte-Page Write mit Debug-µC

Dieser Test sollte die Trennung zwischen Programm- und Datenspeicher-Bereichen im RAM nachweisen. Dafür beschreibt das folgende Programm für den 8049 eine 256Byte-Seite im Datenbereich des RAM mit dem Komplement der jeweiligen Adresse, also Adresse 0x00 mit 0xFF, Adresse 0x01 mit 0xFE, Adresse 0x02 mit 0xFD bis zur Adresse 0xFF und dem geschriebenen Wert 0x00 :

000 B8 MOV R0,#00 ;R0 = 0x00
001 00
002 27 CLR A      ;A = 0x00
003 3A OUTL P2,A  ;P2 = A (Select RAM-Page 0x00)
004 F8 MOV A,R0   ;A = R0
005 37 CPL A      ;A = NOT A
006 90 MOVX @R0,A ;External RAM Write to Address in R0
007 18 INC R0     ;R0 = R0 + 1
008 04 JMP 004    ;Jump to 0x004
009 04

Wenn diese Trennung nicht funktioniert, hätte sich das Programm selbst überschrieben, was u.a. durch das Ausbleiben der Low-Impulse auf der *WR-Leitung nachweisbar gewesen wäre.

256Byte Page-Write Bild1
Bild: 256Byte Page Write (D0...D7, P2.0...P2.3, ALE, *PSEN, *RD, *WR)

256Byte Page-Write Bild2
Bild: 256Byte Page Write (D0...D7, P2.0...P2.3, ALE, *PSEN, *RD, *WR) (Fortsetzung)

3.5.0 Single-Step Hardware

Die Hardware wurde um die von intel beschriebene Einzelschritt-Schaltung mit dem 74HC74 D-FlipFlop ergänzt. Die Einzelschritt-Schaltung (74HC74, 74HC00) ist bei H-Pegel am *SET-Eingang des dem *SS-Eingang des 8049 nächstgelegenen D-FFs aktiv. Liegt hier L-Pegel an, wird der Ausgang des D-FFs ständig auf H-Pegel gesetzt. Durch den H-Pegel am *SET-Eingang kann der Ausgang des D-FFs durch L-Pegel des invertierten ALE-Signals ("Address Latch Enable") des 8049 auf L-Pegel gesetzt werden, wodurch der *SS Eingang des 8049 aktiviert wird.

Der 8049 beendet nun die Ausführung des gerade laufenden Befehls (im Fall von Befehlen, die 2 Befehlszyklen zur Ausführung benötigen, wird das Ende des zweiten Befehlszyklus abgewartet) und signalisiert den aktiven Einzelschritt-Modus durch H-Pegel an ALE. Während dieses Zustands, für den es seitens des 8049 keine zeitliche Beschränkung gibt, wird auf BUS und der unteren Hälfte von P2 die Adresse des nächsten auszuführenden Befehls ausgegeben.

Um in einem Programm den nächsten Befehl auszuführen, wird durch einen L-zu-H-Wechsel am Takteingang des D-FFs der ständig am D-Eingang des D-FFs anliegende H-Pegel in das FF geladen und erscheint an dessen Ausgang, der mit *SS des 8049 verbunden ist. Dadurch wird die Einzelschritt-Logik im 8049 freigegeben, der nächste Befehl wird adressiert und durch den beim Zwischenspeichern der unteren 8-Bit der Adress-Signale entstehenden L-Pegel an ALE des 8049 wird das D-FF wieder zurückgesetzt, *SS wird auf L-Pegel gesetzt und das System befindet sich am Ende wieder in der Wartephase des Einzelschritt-Modus.

Das Verlassen des Einzelschrittmodus kann jederzeit durch dauerhaften L-Pegel am *SET-Eingang des D-FFs erzwungen werden.

Single-Step Hardware
Bild: Single-Step Hardware

Single-Step Signale
Bild: Single-Step Aktivierung durch *S=H des D-FFs (letzter Befehl besteht aus 2 ALE-Zyklen)

Single-Step Signale
Bild: Ausführen einzelner Befehle (1&2 Zyklen) durch Impulse am CLK-Eingang des D-FFs

3.6.0 DeMon48_128k Speicherorganisation / MMU

Um den zur Verfügung stehenden, 128kB umfassenden Speicherplatz im statischen RAM des DeMon48_128k-Systems vollständig nutzen zu können, reichen die vom 8049-Adresszähler (PC) und MB-FF gelieferten 12 Bit Adressinformationen offensichtlich nicht aus (2^12 = 4096dez.).

Daher werden von drei 74HC173 4fach D-FlipFlops unabhängig voneinander 4 Bit für Programmspeicher- und 8 Bit für Datenspeicher-Zugriffe als Adress-Signale bereitgestellt, die das sonst nur für den Betrieb der eher obsoleten 8243-Portexpander (16 zusätzliche I/O-Leitungen in einem großen (600mil) 24pol.-DIL Gehäuse) benötigte "PROG"-Signal nutzen, um den gewünschten Zustand der zusätzlichen 3x 4 Adressleitungen softwaregesteuert zu übernehmen.

Die 17. Adressleitung A16 wird durch die MMU (Memory Management Unit) in Abhängigkeit der aktuellen Speicherkonfiguration (siehe: "5.0.0 Speicherkonfigurationen") bereitgestellt.

Da die Erzeugung der zusätzlichen Adress-Bits unter voller Kontrolle eines auf dem 8049 ablaufenden Anwender-Programms erfolgt, können tatsächlich Programme erstellt werden, die bis zu 16 4kByte-Bänke (=64kB) des Programm- und bis zu 256 256Byte-Seiten (=64kB) des Daten-Speicherbereichs nutzen (der Debugger belegt 512 Byte im RAM).

3.6.1 DBR - Data Bank Register

Die Auswahl der aktiven 256Byte-Datenspeicher-Seite erfolgt über das "DBR" (Data Bank Register), das sich aus DBL für die 4 Adressbits A8 bis A11 und DBH für die 4 Adressbits A12 bis A15 zusammensetzt.

Data-Bank-Register Write
Bild: Zugriff auf das DBR via PROG und Wirkung auf A12...A15

Wenn eine 4(8)kByte Memory Map aktiv ist (siehe "5.0.0 Speicherkonfigurationen"), um beispielsweise das "PROG"-Signal für 8243 Portexpander verwenden zu können, kann DBL (A8 ... A11) durch entsprechende Jumperkonfiguration bei jedem ALE-Zyklus automatisch mit dem aktuellen Zustand von P2.0 ... P2.3 geladen werden, so dass über P2.0 ... P2.3 ("OUTL P2,A") und das dann transparente DBL-Register die Auswahl der aktiven 256Byte-Datenspeicher-Seite oder des I/O-Bereichs erfolgt.

3.6.2 PBR - Program Bank Register

Die Auswahl der aktiven 4kByte-Programmspeicher-Bank erfolgt über das "PBR" (Program Bank Register). Bei einem Wechsel der 4kB-Programmspeicherbank können aufgrund der MMU-Hardware 16 Adressbits gleichzeitig beeinflusst werden, so dass zum Beispiel ein unbedingter Sprung von 0x08E7 nach 0x53F0 durchgeführt wird :

08E3 SEL MB0   ;MB-FF (PC.11) = 0
08E4 MOV A,#05 ;A = 05
08E6 MOVD P6,A ;PBR = A
08E7 JMP 3F0   ;PC.0 ... PC.10 = 3F0

53F0 Nächster Befehl

Der "SEL MBx"-Befehl zu Beginn der Sequenz ist in anderen Fällen möglicherweise optional, aber die Reihenfolge "MOVD/ORLD" gefolgt von einem Befehl zur Beeinflussung des Befehlszählers ("JMP"/"CALL"/"RET") ist unbedingt einzuhalten !

MMU PBR Bankswitch Bank7 to Bank8
Bild: Wechsel von der 4kB-Programmspeicherbank 7 zur 4kB-Bank 8

Die MMU unterstützt Unterprogrammaufrufe per "CALL" über 4kB-Bankgrenzen hinweg, allerdings muss die 4kB-Bank, zu der mit "RET" zurückgekehrt werden soll, beispielsweise in einem Register hinterlegt werden :

08E1 MOV R7,#00 ;R7 = 0 = aktuelle 4kB-Programmspeicherbank
08E3 SEL MB0    ;MB-FF (PC.11) = 0
08E4 MOV A,#05  ;A = 05
08E6 MOVD P6,A  ;PBR = A
08E7 CALL 3F0   ;Stack@SP = PC.0 ... PC.11, PC.0 ... PC.10 = 3F0

53F0 Nächster Befehl
[...]
542A MOV A,R7   ;A = R7
542B MOVD P6,A  ;PBR = A
542C RET        ;PC.0 ... PC.11 = Stack@SP

08E9 Nächster Befehl

3.6.3 Nutzung mit "8243"-Portexpander

Soll eine Hardware, die einen oder mehrere 8243-Portexpander nutzt, mit Hilfe des Debuggers getestet werden, kann die Erzeugung der zusätzlichen Adressbits durch das "PROG"-Signal mit einem Jumper abgeschaltet werden (siehe "5.0.0 Speicherkonfigurationen"). Das System verhält sich dann wie ein 8049 mit 4kB Programm- und 3,75kB bis 4kB (abhängig davon, ob noch ein I/O-Bereich eingeblendet wird) Datenspeicher und das "PROG"-Signal ist zur Ansteuerung der 8243-Portexpander frei.

3.6.4 DA16 - Data Address 16

Über die MMU kann erzwungen werden, dass alle Speicherzugriffe des 8049 mit A16=Low erfolgen, unabhängig davon, ob es sich aus Sicht des 8049 um Programm- oder Daten-Speicherzugriffe handelt. Dafür wird über "ORLD Pp,A"-Befehle das Signal DA16 (Data Address 16) auf "Low" gesetzt. Damit wird etwas normalerweise Unmögliches erreicht, nämlich dass der 8049 seinen eigenen Programm-Speicherbereich unter Softwarekontrolle beschreiben kann. Das ermöglicht später unter anderem das Laden von Programmen durch Software, die auf dem 8049 selbst läuft.
Natürlich kann DA16 auch wieder durch geeignete Softwarekommandos auf "High" gesetzt werden, so dass bspw. erst ein Programm mit DA16=Low geladen werden kann und dann während dessen Ausführung durch DA16=High wieder 64kB als Programmspeicher und zusätzlich bis zu 64kB als Datenspeicher zur Verfügung stehen.

Ausserdem ist durch diese Umschaltmöglichkeit aus der nativen Harvard-Architektur der MCS48-µCs ein echter Harvard-/von-Neumann-Architektur-Zwitter entstanden. Es besitzt eine von-Neumann-Architektur mit einem für Programm und Daten gemeinsam genutzten Speicher, wenn DA16=Low und eine Harvard-Architektur mit zwei logisch getrennten Speichern für Programm und Daten, wenn DA16=High.

3.6.5 Expansion Register

Für zukünftige Hardwareerweiterungen kann zusätzlich zum PBR und DBR an die mit Hilfe des "PROG"-Signals realisierte Schnittstelle noch ein 4Bit "Expansion Register", ein zusätzliches 4Bit D-FlipFlop, angeschlossen werden.

3.6.6 Befehle zum Zugriff auf die MMU

DA16 = H, A16 = normal :

MOVD P4,ALoad Data Bank Register Low Nibble (DBL : A8 ... A11)
MOVD P5,ALoad Data Bank Register High Nibble (DBH : A12 ... A15)
MOVD P6,ALoad Program Bank Register (PBR : A12 ... A15)
MOVD P7,ALoad Expansion Register

DA16 = L, A16 = forced low :

ORLD P4,ALoad Data Bank Register Low Nibble (DBL : A8 ... A11)
ORLD P5,ALoad Data Bank Register High Nibble (DBH : A12 ... A15)
ORLD P6,ALoad Program Bank Register (PBR : A12 ... A15)
ORLD P7,ALoad Expansion Register

3.6.7 Bildung der Adresse in verschiedenen Situationen

Zugriff auf Programmspeicher :

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
MM16PBR.3PBR.2PBR.1PBR.0PC.11PC.10PC.9PC.8PC.7PC.6PC.5PC.4PC.3PC.2PC.1PC.0

Zugriff auf Datenspeicher und I/O :

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
MM16DBH.3DBH.2DBH.1DBH.0DBL.3DBL.2DBL.1DBL.0Ri.7Ri.6Ri.5Ri.4Ri.3Ri.2Ri.1Ri.0

Zugriff auf die Programm- und Datenspeicher des Debuggers :

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
"1""1""1""1""1""1""1""1"MM8PC.7PC.6PC.5PC.4PC.3PC.2PC.1PC.0

Ausführen des Befehls in Adresse 0x1FE09 im Debug-Programmspeicher :

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
"1""1""1""1""1""1""1""1"MM8"0""0""0""0""1""0""0""1"

SignalFunktion
MM8A8 Ausgang der MMU
MM16A16 Ausgang der MMU
PBRProgram Bank Register
DBLData Bank Register Low Nibble (A8 ... A11)
DBHData Bank Register High Nibble (A12 ... A15)
PCProgram Counter
RiInhalt des zur Adressierung in "MOVX"-Befehlen genutzten Indexregisters (R0, R1)
"1"Wird vom Debug-µC auf logisch "1" (+5V = H) gehalten
"0"Wird vom Debug-µC auf logisch "0" (0V = L) gehalten

Adressraum der Grundkonfiguration (siehe auch "5.0.0 Speicherkonfigurationen"):

Start (h)Ende (h)Bezeichnung
000000FFFF8049 Programmspeicher
100001FCFF8049 Datenspeicher (extern)
1FD001FDFFMemory Mapped I/O
1FE001FEFFDebug Programmspeicher
1FF001FFFFDebug Datenspeicher

3.6.8 Memory-Mapped-I/O mit 8049

Mit Hilfe eines "8-Bit Equality Comparator" 74HC688 wird aus den Adress-Signalen A8 bis A16 das Signal IO/*M gewonnen, das das Einblenden von 256 I/O-Adressen in den Adressraum für den externen Datenspeicher erlaubt. Externe I/O-Einheiten können damit wie ein externes RAM über die "MOVX"-Befehle des MCS48-Befehlssatzes angesprochen werden, so dass 256 * 8 Bit (=2048 Bit) zur Ausgabe und 256 * 8 Bit (=2048 Bit) zur Eingabe adressierbar sind. Das Signal IO/*M führt H-Pegel im I/O-Adressbereich und L-Pegel, wenn das externe RAM angesprochen wird.

IO/*M Erzeugung
Bild: Erzeugung des IO/*M-Signals für memory-mapped-I/O mit DeMon48

Informationen zu den gezeigten Jumpern : siehe "5.0.0 Speicherkonfigurationen".

3.7.0 Hardware-Breakpoint

Das DeMon48-System bietet derzeit die Möglichkeit, einen Hardware-Breakpoint (evtl. kommt später ein Breakpoint-RAM) zu aktivieren, was die Ausführung von Code bei aktiviertem Breakpoint ohne Geschwindigkeitsverlust ermöglicht.

Aufgrund des Multiplexings von DBR und PBR über das *PSEN-Signal ist es notwendig, mit Hilfe zweier RC-Netzwerke das *X_PSEN-Signal zu verzögern und zu verkürzen, so dass der Zeitpunkt an dem der Adressbus abgefragt wird in etwa in die Mitte des ursprünglichen *PSEN-Signals verschoben wird. Andernfalls könnte es zu Glitches kommen, die zur Erkennung des Hardware-Breakpoints führen.

Wichtig: Im zweiten Zyklus des 1Byte "RET"-Befehls wird die auf das Befehls-Byte (0x83) folgende Adresse durch den µC ausgegeben und *PSEN aktiviert (siehe Kapitel 2.5.0). Darum darf als Hardware-Breakpoint die auf den "RET"-Befehl folgende Adresse nicht angegeben werden, weil die Breakpoint-Adresse dann auch bei der Ausführung des "RET"-Befehls erreicht wird !
Wenn ein Hardware-Breakpoint beim auf "RET" folgenden Befehl ausgelöst werden muss, kann man einen "NOP"-Befehl nach "RET" einfügen.

Hardware Breakpoint Schematic
Bild: Hardware-Breakpoint Schaltung

Hardware Breakpoint Gate Signal
Bild: Verzögertes und verkürztes Gate-Signal für die "74HC688" 8Bit-Komparatoren