ctr

DeMon48_128k

intel 8049 / MCS-48 Hardware-Debugger & Monitor

Dokumentation - Kapitel 3


3.0.0 MCS-48 im 21. Jahrhundert

Vor einigen Monaten fiel mir wieder einmal die Stange mit den 8049-µCs in die Hand und im Lauf der Zeit - unterstützt durch gelegentliche Lektüre im "intel 8-Bit Embedded Controllers"-Handbuch von 1990 - wuchs doch eine gewisse Neugier, ob diesem wirklich archaischen Mikrocontroller überhaupt noch Lebenszeichen zu entlocken sein würden.

Zum ersten Ziel - und es war zu diesem Zeitpunkt kaum absehbar, ob es nicht auch das letzte Ziel werden würde - wurde eine Möglichkeit, irgendwie ein RAM mit einem Programm vom steuernden Personal Computer aus zu laden, das dann der 8049 eigenständig, ohne Möglichkeit von Aussen einzugreifen, ausführt. Wenigstens keine (E)EPROMs mehr (wo ist eigentlich das vor vielen Jahren zuletzt genutzte EPROM-Programmiergerät?).

3.1.0 Bescheidene Anfänge : Oszillator-Inbetriebnahme

Erstmal wurde nur eine Stromversorgung angeschlossen und als einzige On-Chip-Hardware der Oszillator bei *RESET=L und EA=H in Betrieb genommen. Der 8049 µC generiert sogar im Reset-Zustand (*RESET=L) Signale an seinen ALE- und *PSEN-Anschlüssen und gibt kontinuierlich die Adresse 0x000 auf BUS und P2 aus : leider sehr ungünstig, um ein RAM vom 8049 isoliert während des Reset-Zustands mit Daten zu laden - wie ich mir das ursprünglich als einfachste Möglichkeit vorgestellt hatte (später entdeckte ich, dass dieses Verhalten in einem leicht zu übersehenden Satz u.a. im "intel 8-Bit Embedded Controllers"-Handbuch von 1990 beschrieben ist : "[...]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).

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

3.1.1 NOP-Schleife, erste Hardware und Erweiterungen

Die einfachste Möglichkeit für einen weiteren Test ist eine "NOP-Schleife" ("No OPeration") : die CPU findet nur "NOP"-Anweisungen 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 da 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=low 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, da sich so ein wesentlich 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 ebenfalls vom 8049 getrennt werden können 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 größeren 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)

3.2.0 Modifizierte Harvard-Architektur

Nachdem diese Trockenübungen ohne echten Programmspeicher zur vollen Zufriedenheit verliefen, kam der Gedanke auf, das zukünftig als externen Programmspeicher eingesetzte RAM auch als externen Datenspeicher, der über die "MOVX"-Befehle und die "*RD"- und "*WR"-Steuerleitungen des Mikrocontrollers beschrieben und gelesen werden kann, zu nutzen. Dafür werden mit Hilfe eines UND-Gatters die beiden Signale "*PSEN" und "*RD" zusammengeführt (es entsteht eine ODER-Verknüpfung der beiden Low-aktiven Signale zum ebenfalls Low-aktiven "*OE"-Signal des RAMs) und das "*PSEN"-Signal zur Bildung des Adress-Signals A16 herangezogen. Bei einem Zugriff auf den Programmspeicher ist dieses "L", bei einem Zugriff auf den Datenspeicher ist es "H", sodass sich der externe Programmspeicher von der physikalischen RAM-Adresse 0x00000 bis Adresse 0x0FFFF und der externe Datenspeicher von der physikalischen RAM-Adresse 0x10000 bis Adresse 0x1FFFF (mit Debugger bis 0x1FDFF) erstreckt - wenn nicht der später beschriebene "memory mapped I/O"-Bereich eingeblendet wird. Der 8049 kann mit den maximal 12 unter seiner Kontrolle stehenden Adressleitungen 4kByte adressieren, die fehlenden Adress-Bits werden durch zwei zusätzliche 4Bit-D-FlipFlops bereitgestellt, die das Ansprechen von jeweils bis zu 16 4kB-Bänken - unabhängig voneinander für Programm- und Datenspeicherbereich unter Softwarekontrolle auszuwählen - ermöglicht. Die Hardware zur Bereitstellung der zusätzlichen Adress-Bits wird mit Hilfe des in der Regel ungenutzten "PROG"-Signals und der entsprechenden, im Befehlssatz bereits vorhandenen Befehle vom 8049 angesteuert (siehe "3.10.0 DeMon48_128k Speicherorganisation / MMU").

3.3.0 Debug-µC, serielle Hardware

Als Mikrocontroller zur Steuerung des DeMon48_128k-Systems und als Interface zum Personal Computer fiel die Wahl auf einen betagten, ebenfalls noch als Restbestände hier vorhandenen PIC16F874. Die Hardware wurde zunächst, um Signale am PIC-Mikrocontroller und Verbindungen beim Aufbau auf dem Steckbrett ("Solderless Breadboard") zu sparen, auf Schieberegistern zur Parallel- zu Seriell- bzw. Seriell- zu Parallel-Wandlung basierend (74HC165, 74HC595) geplant.

3.3.1 Erster Test mit Debug-µC (NOP)

Die erste Test-Firmware für den PIC16F874 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 sollten sich nach Start also die gleichen Signale auf den Adress- und Datenleitungen des 8049 messen lassen, wie beim Test mit dem 74HC541 als "Programmspeicher", nur dass jetzt wirklich von unterschiedlichen Adressen des RAMs gelesen wird.

3.3.2 Zweiter Test mit Debug-µC (256Byte-Page Write)

Da sich das gewünschte Resultat auf Anhieb einstellte, kam ein weiteres Testprogramm zur Ausführung durch den 8049 µC zum Einsatz. Es beschreibt 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

Es sollte damit vor allem die Trennung zwischen Programm- und Datenbereichen im RAM durch Heranziehen von *PSEN als zusätzliche Adressleitung getestet werden. Sollte diese Trennung nicht funktionieren, würde sich das Programm selbst überschreiben, was u.a. durch das Ausbleiben der Low-Impulse auf der *WR-Leitung nachweisbar wäre, sobald der "MOVX @R0,A" Befehl überschrieben ist. Auch dieser Test wurde unspektakulär bestanden.

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.4.0 Single-Step Hardware

Im nächsten Schritt wurde die Hardware durch die von intel beschriebene Einzelschritt-Schaltung um das 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, 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.5.0 Zwischenstand

Zu diesem Zeitpunkt war erreicht, dass ein Programm in das RAM geladen und vom 8049 entweder mit der durch die Oszillatorfrequenz vorgegebenen Geschwindigkeit oder Befehl für Befehl mit Hilfe des Einzelschritt-Modus der vom Anwender-PC via Debug-Mikrocontroller gesteuert wird. Wurde ein Schritt ausgeführt, signalisiert der Debug-µC an den steuernden Personal Computer die auf den entsprechenden 8049-BUS- und -P2-Leitungen anliegende Adresse des nächsten, noch nicht ausgeführten Befehls, so dass die Ausführung des Programms durch den Anwender über die sich ändernde Adressinformation beobachtet werden konnte.

An ein Überschreiten der internen 2kB-Programmspeicherbank-Grenze durch "SEL MBx"-Befehle, an eine Nutzung des Timers-/Counters oder an Interrupts war zu diesem Zeitpunkt allerdings noch überhaupt nicht zu denken.

3.6.0 Debug-µC, parallele Hardware

Um die Codeausführung bei Nutzung der Soft-Breakpoints zu beschleunigen, wurde die gesamte Hardware neugestaltet und von seriellem auf parallelen Austausch zwischen PIC-Mikrocontroller und restlicher Hardware umgestellt. Die Einzelschritt-Ausführung mit aktivierten Soft-Breakpoints funktionierte nun etwa 100% schneller als mit der seriellen Lösung.

3.7.0 Das "SEL MBx"-Problem (Speichermanagement à la intel in 1976)

Parallel dazu lauerte noch eine Besonderheit des 8049 : Das "SEL MBx"-Problem.

Die Mitglieder der MCS48-Familie unterstützen einen bis zu 12 Bit breiten Adressbus (A0 bis A11), so dass maximal 4kByte adressiert werden können. Allerdings sind diese 4 kByte in 2 Bänke à 2 kByte aufgeteilt, und nur innerhalb dieser 2 kB großen Speicherbänke kann durch "JMP"- und "CALL"-Befehle linear, also "am Stück", adressiert werden.
Zum Wechsel zwischen diesen Speicherbänken gibt es im MCS48-Befehlssatz die Befehle "SEL MB0" (Auswahl der Speicherbank 0 : Adressbereich 0x000 bis 0x7FF (0 bis 2047 dezimal)) und "SEL MB1" (Auswahl der Speicherbank 1 : Adressbereich 0x800 bis 0xFFF (2048 bis 4095 dezimal)). Wird einer dieser Befehle ausgeführt, wird im 8049 ein Flipflop, dessen offizielle Bezeichnung "DBF : Memory Bank Flip-Flop" lautet (intel 8-Bit Embedded Controllers Handbook 1990; Kap. "MCS(R)-48 Instruction Set, Symbols and Abbreviations Used"; S.3-7), für das 12. Bit (A11) des Programmzählers (PC) durch "SEL MB1" gesetzt oder durch "SEL MB0" zurückgesetzt. Sobald ein unbedingter Sprung per "JMP"- oder "CALL"-Befehl ausgeführt wird, werden die unteren 11 Bits (A0 bis A10) des Programmzählers mit der im Sprungbefehl angegebenen Zieladresse geladen und als 12. Bit des Programmzählers (A11) wird der Zustand des oben genannten Vorbereitungsflipflops ("DBF"/"Memory Bank FlipFlop"/"MB-FlipFlop"/"MB-FF") übernommen.

Nach Ausführen des folgenden Codes hat der Programmzähler ("PC") den Wert 0x800 (Bit11 = 1) :

000 F5 SEL MB1 ;MB-Flipflop = 1
001 04 JMP 000 ;PC = 0x000 + 0x800
002 00

Wird danach Folgendes ausgeführt, wird der Programmzähler auf 0x000 (Bit11 = 0) gesetzt :

800 E5 SEL MB0 ;MB-Flipflop = 0
801 04 JMP 000 ;PC = 0x800 - 0x800
802 00

Es kann natürlich sein, dass der Aufruf der DeMon48-Monitor-Software nach einem SEL MBx-Befehl, aber vor dem Ausführen eines "JMP" oder "CALL"-Befehls erfolgt. Da in der Monitor-Software auch unbedingte Sprünge ausgeführt werden, würde ohne besondere Maßnahmen nach dem Verlassen der Monitor-Software Chaos herrschen, da die Speicherbank bereits durch das Ausführen der Monitor-Software gewechselt wurde und das MB-Flipflop nicht wie gewünscht beim nächsten "JMP"- oder "CALL"-Befehl wirksam ist. Verschärft wird das Problem noch durch die Tatsache, dass zwischen "SEL MB0"/"SEL MB1" und "JMP" oder "CALL" mehrere Befehle stehen können, wie z.B.:

000 F5 SEL MB1
001 00 NOP
002 04 JMP 00A
003 0A

Es ist also nicht ausreichend, nur dafür zu sorgen, dass die Programmausführung in der vor dem Aufruf der Monitor-Software gewählten Speicherbank fortgesetzt wird, da dann in o.g. Beispiel der Befehl "JMP 00A" zur Adresse 0x00A und nicht, wie beabsichtigt und ohne Monitor-Software vom 8049 auch ausgeführt, zur Adresse 0x80A springt. Es muss beim Beenden der Monitor-Software unbedingt neben Bit0 bis Bit11 des Programmzählers (PC) auch der Zustand des MB-Flipflops im Moment des Aufrufs der Monitor-Software wiederhergestellt werden.

Internes MB-FlipFlop (A12)
Bild: Prinzipdarstellung der internen Logik zur Erzeugung des 12. Adress-Bits, PC.11

SEL MB1
Bild: Ausführen des "SEL MB1"-Befehls (0xF5) und Wirkung auf P2.3 nach "JMP 000" (0x04,0x00)

3.7.1 "SEL MBx" und Interrupts

Eine weitere Besonderheit ergibt sich durch die Bankumschaltung per "SEL MBx"-Befehlen im Zusammenhang mit Interrupts. Durch das Auslösen eines Hardware-Interrupts werden zwar alle Programmspeicher-Zugriffe auf die Speicherbank 0 (0x000...0x7FF) begrenzt, der Zustand des MB-FlipFlops bleibt jedoch erhalten.

3.7.2 "SEL MBx" und "CALL"

Besondere Vorsicht ist übrigens bei µCs der MCS-48-Familie geboten, wenn ein Bankwechsel im Zusammenhang mit einem Unterprogramm-Aufruf per "CALL" stattfindet. Da alle 12 Adress-Bits auf dem Stack gespeichert und durch den Rücksprungbefehl "RET" wiederhergestellt werden, ist der Inhalt des Programmzählers (PC) zwar danach wieder korrekt, jedoch wird das MB-Flipflop durch "RET" nicht beeinflusst. Das kann zu unerwarteten Ergebnissen führen :

000 F5 SEL MB1
001 14 CALL 000 ;Sprung zu 0x800 (Adresse 0x000 in Bank 1)
002 00
003 04 JMP 010  ;Sprung zu 0x010 (oder doch 0x810 ?)
004 10
800 83 RET      ;Rücksprung zur Adresse 0x003

Tatsächlich wird der letzte Sprungbefehl "JMP 010" den Programmzähler auf 0x810 setzen, da das MB-Flipflop noch gesetzt ist !

("[...]Since all twelve bits of the program counter including bit (11) are stored in the stack when a Call is executed, the user may jump to subroutines across the 2K boundary and the proper bank will be restored upon return. However, the bank switch flipflop will not be altered on return." Quelle: "intel MCS-48 Family of Single Chip Microcomputers User's Manual" (1980); Kap. "3.1.2 Extended Program Memory Addressing (Beyond 2K)" / "Program Memory Bank Switch"; S. 3-2).

Siehe auch "6.6.0 Schaltfläche 'AS' " zur Bankwechsel-Automatik des AS-Macroassemblers.

3.8.0 Bitte ein Bit ...

Eine weitere Auswirkung des eigentlich nur 11 Bit breiten Programmzählers (PC) ist, dass, im Fall des Überlaufs des Programmzählers, auf Adresse 0x7FF Adresse 0x000 folgt und entsprechend auf Adresse 0xFFF Adresse 0x800 folgt. Bedingte Sprungbefehle speichern nur eine 8-Bit Adresse im entsprechenden Befehl und in der Regel muss das Ziel innerhalb derselben 256-Byte-Page liegen. Es gibt, wie könnte es anders sein, einen Sonderfall, wenn der Sprungbefehl in der höchsten Adresse einer 256-Byte-Page liegt : dann liegt das Ziel durch das Inkrementieren des Programmzählers während S2 (1. Befehls-Zyklus) oder S3 (2. Befehls-Zyklus) in der nächsten 256-Byte-Page.

3.9.0 Timer-/Counter-Interrupt

Der Timer-/Counter-Interrupt ist eine weitere "liebenswerten Eigenheit" der daran nicht armen MCS48-Mikrocontroller (ursprünglich wollte ich mich sogar auf den Ratschlag beschränken, Programme, die Hardware-Interrupts des 8049 nutzen, nicht im Einzelschritt-Modus auszuführen - aber das wäre doch sehr unbefriedigend).

Zum Timer-/Counter-Interrupt in Mikrocontrollern der intel MCS-48-Familie schrieb 1979 John F. Wakerly in "0018-9162179/0020-0022$00.75 IEEE COMPUTER February 1979; 'The Intel MCS-48 Microcomputer Family : A Critique' " : "[...]At the time of this writing, the T1 interrupt input is generally useless for counting or timing asynchronous external events, because the current chip features a poorly designed synchronizer[...]". Zusätzlich zu dem dort beschriebenen Problem, dass zu zählende Ereignisse zum Teil nicht erkannt werden, existieren offenbar Schwierigkeiten bei durch den Timer/Counter ausgelösten Interrupts, da zum Teil anliegende Timer-/Counter-Interrupts erst nach einigen Maschinenzyklen verarbeitet werden und diese Latenz auch von der Art der Codeausführung (beispielsweise Soft-/Hardware-Breakpoint) abhängig ist.

3.10.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 2 74HC173 4fach D-FlipFlops unabhängig voneinander 4 Bit für Programmspeicherzugriffe und 4 Bit für Datenspeicherzugriffe als Adress-Signale bereitgestellt, die das sonst nur für den Betrieb der heute 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 2x 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 4kB-Bänke (=64kB) des Programm- und bis zu 16 4kB-Bänke (=64kB) des Daten-Speicherbereichs nutzen (der Debugger belegt 512 Byte im RAM).

Die Auswahl der aktiven Programmspeicher-Bank erfolgt über das "PBR" (Program Bank Register), die Auswahl der aktiven Datenspeicher-Bank erfolgt über das "DBR" (Data Bank Register).

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

Bei einem Wechsel der Programmspeicherbank können aufgrund der MMU-Hardware alle 16 Adressbits gleichzeitig beeinflusst werden, so dass zum Beispiel ein Sprung von 0x08E7 nach 0x53F0 durchgeführt wird :

08E3 SEL MB0   ;MB-FF (PC.11) = 0
08E4 MOV A,#05 ;A = 05
08E6 MOVD P4,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 optional, aber die Reihenfolge "MOVD/ORLD" gefolgt von einem Befehl zur Beeinflussung des Befehlszählers ist unbedingt einzuhalten, andere Befehle dürfen keinesfalls auf "MOVD/ORLD" folgen !

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

08E1 MOV R7,#00 ;R7 = 0 = aktuelle Programmspeicherbank
08E3 SEL MB0    ;MB-FF (PC.11) = 0
08E4 MOV A,#05  ;A = 05
08E6 MOVD P4,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 P4,A  ;PBR = A
542C RET        ;PC.0 ... PC.11 = Stack@SP

08E9 Nächster Befehl

MMU PBR Bankswitch Bank7 to Bank8
Bild: Wechsel von Programmspeicherbank 7 zu Bank 8

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.

Ü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.

Für zukünftige Hardwareerweiterungen können zusätzlich an die mit Hilfe des "PROG"-Signals realisierte Schnittstelle 2 4Bit "Expansion Registers", 2 zusätzliche 4Bit D-FlipFlops, angeschlossen werden.

3.10.1 Befehle zum Zugriff auf die MMU

DA16 = H, A16 = normal :

MOVD P4,ALoad Program Bank Register
MOVD P5,ALoad Data Bank Register
MOVD P6,ALoad Expansion Register #0
MOVD P7,ALoad Expansion Register #1

DA16 = L, A16 = forced low :

ORLD P4,ALoad Program Bank Register
ORLD P5,ALoad Data Bank Register
ORLD P6,ALoad Expansion Register #0
ORLD P7,ALoad Expansion Register #1

3.10.2 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
MM16DBR.3DBR.2DBR.1DBR.0P2.3P2.2P2.1P2.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
MM16A16 Ausgang der MMU
PBRProgram Bank Register
DBRData Bank Register
PCProgram Counter
P2Niederwertige Bits (0...3) der auf Port2 geschriebenen I/O Information
RiInhalt des zur Adressierung in "MOVX"-Befehlen genutzten Indexregisters (R0, R1)
MM8A8 Ausgang der MMU
"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.10.3 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.11.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.

Mit Hilfe zweier RC-Netzwerke wird das *X_PSEN-Signal verzögert und verkürzt, so dass der Zeitpunkt, an dem der Adressbus abgefragt wird, in etwa in die Mitte des ursprünglichen *PSEN-Signals verschoben wird.

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