ctr

DeMon48_128k

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

Dokumentation - Kapitel 4


4.0.0 Die Monitor-Software : der "God Mode" für den Entwickler

Durch kleine MCS-48 Programme, die von der Debug-µC Firmware in das externe RAM geladen und unter Kontrolle des Debug-µC vom MCS-48 µC ausgeführt werden, wurde es möglich ein Monitor- und Debug-System zu realisieren, das keine On-Chip-Ressource des MCS-48 µCs blockiert oder nach Verlassen der Monitorsoftware in einem geänderten Zustand zurücklässt.

Grundsätzlich verläuft die Ausführung einzelner Befehle des MCS-48 Anwender-Programms unter Kontrolle des Debuggers mit Visualisierung im GUI in drei Phasen :

I. Monitor Aufruf
  1. Lesen des Zustands der MMU und Retten der internen Register des MCS-48 µCs
    (DBR, PBR, PC, A, T, PSW, R0)
  2. Lesen des Zustands der Ports, des User-Flags1, der Test- und des Interrupt-Eingangs
    (P1, P2, F1, T0, T1, *INT)
  3. Internes RAM des MCS-48 µCs lesen
  4. Upload aller gelesenen Werte in das Windows-GUI
II. Meta-Zustand
  • Der MCS-48 µC ist gestoppt, im Windows-GUI können durch den Anwender Inhalte von PC, A, PSW, internen Registern, internes/externes RAM, Ports etc. geändert werden.
  • Wird durch den Anwender das Ausführen des nächsten Befehls durch Klick auf die entsprechende Schaltfläche im GUI ausgelöst, folgt das Beenden des Monitors und die Ausführung des nächsten Befehls im MCS-48 Anwender-Programm :
III. Monitor-Ausgang
  1. Download von im GUI geänderten Speicherbereichen im externen RAM
  2. Download von im GUI geänderten Speicherbereichen im internen RAM
  3. Download von im GUI geänderten Registerinhalten
  4. Wiederherstellen der Register im MCS-48 µC
  5. Wiederherstellen des Programmzählers (PC) im MCS-48 µC
  6. Ausführen des nächsten Befehls
  7. Sprung zu : I. Monitor-Aufruf

Während der Monitor aktiv ist und Monitor-Routinen ausgeführt werden, hat der MCS-48 µC maximal über die unteren 8 Adressleitungen A0 bis A7 Kontrolle.

Die Adressleitung A8, die bei aktivem Monitor zum Umschalten zwischen 256 Byte Debug-Programmspeicher und 256 Byte Debug-Datenspeicher durch den MCS-48 µC dient, wird über die MMU mit dem *PSEN-Signal verbunden. Die übrigen Adress-Signale A9 bis A16 werden vom Debug-µC bereitgestellt.

4.1.0 Monitor-Aufruf

Beim Aufruf der Monitor-Software wird zunächst geprüft, ob die Ausführung des MCS-48 Anwender-Programms durch den MCS-48 µC bereits durch die Single-Step-Hardware angehalten wurde. Falls nicht, wird die Single-Step-Hardware aktiviert und gewartet, bis der MCS-48 µC alle zum gerade ausgeführten Befehl gehörenden Befehlszyklen abgearbeitet hat.

4.1.1 PC, PBR, DBR retten

Es wird der durch den MCS-48 µC an BUS und P2.0 bis P2.3 ausgebene Stand des Programmzählers gelesen und als DBG_ReturnAddress gespeichert.

Im gleichen Schritt werden die Inhalte des Program Bank Registers (PBR, bestehend aus PBR1 und PBR2) und Data Bank Registers (DBR) gelesen und gespeichert.

4.1.2 Monitor-Start

  1. Laden und Ausführen (ohne Prüfung auf Hardware-Interrupt (*)) :
    $   04 JMP 009
    $+1 09
    ($ = untere 8 Bit des aktuellen Inhalts des PC (Program Counter))
  2. Laden :
    09 00/65 NOP / STOP TCNT
    0A 04    JMP 009
    0B 09

    Wenn im GUI die Monitor-Option "STOP TCNT" (Stop Timer / Counter) ausgewählt ist, wird durch die Firmware des Debug-µC an Adresse 0x09 der entsprechende Opcode (0x65) eingetragen, andernfalls der für "NOP" (0x00).
  3. Befehle in Adressen 0x09 bis 0x0B ausführen
  4. Wenn vor der Ausführung eines der Befehle in Adressen 0x09 bis 0x0B ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehle in Adressen 0x09 bis 0x0B erneut ausführen

(*) Wenn während der Ausführung des vorherigen MCS-48 Befehls ein Interrupt ausgelöst wurde, ist die Rücksprungadresse in das MCS-48 Anwender-Programm bereits korrekt als einer der Interrupt-Vektoren gespeichert.

4.1.3 Interrupt-Erkennung

Während der Ausführung der Monitor-Programme durch den MCS-48 µC müssen Timer/Counter-Interrupts und durch L-Pegel am *INT-Eingang ausgelöste Interrupts erkannt werden, weil bei einer Interrupt-Anforderung eine Rücksprungadresse innerhalb eines Monitor-Programms auf dem Stack des MCS-48 µCs abgelegt wird und die Rücksprungadresse in das MCS-48 Anwenderprogramm nicht auf den entsprechenden Interrupt-Vektor zeigt.

Der Debug-µC erkennt Interrupt-Anforderungen, indem vor der Ausführung jedes auf die erste "JMP 009"-Anweisung (siehe "4.1.2 Monitor-Start") folgenden MCS-48 Befehls der Adressbus des MCS-48 µCs auf die Interrupt-Vektoren 0x003 und 0x007 geprüft wird.

Wurde ein Interrupt erkannt, wird nach der Rettung des PSW der Stack des MCS-48 µCs so manipuliert, dass die dort abgelegte Rücksprungadresse innerhalb des Monitor-Programms durch die bisherige Rücksprungadresse in das MCS-48 Anwender-Programm ausgetauscht und als Rücksprungadresse in das MCS-48 Anwender-Programm je nach Quelle der Interrupt-Anforderung der korrekte Interrupt-Vektor im Debug-µC gespeichert wird.

Ausserdem werden die unteren 8Bit des Programmzählers (PCL) durch einen "JMP 009"-Befehl wieder auf die Startadresse des aktuellen Monitor-Programms gesetzt.

4.1.4 "Pending Bank Switch"-Erkennung

Es muss durch den Debug-µC ermittelt werden, ob ein Bankwechsel per "SEL MB0" oder "SEL MB1" im MCS-48 Anwenderprogramm vorbereitet, aber noch nicht durch einen JMP- oder CALL-Befehl als Bit11 des PC wirksam geworden ist ("Pending Bank Switch").

Dies geschieht nachdem der MCS-48 µC den ersten "JMP 009"-Befehl ausgeführt hat. Ein Wechsel zur unteren Programmspeicherbank war vorbereitet, wenn beim erstmaligen Lesen des MCS-48 Adressbusses das Bit11 gesetzt war und nach dem unbedingten Sprungbefehl zurückgesetzt ist, ein Wechsel zur oberen Programmspeicherbank war vorbereitet, wenn beim erstmaligen Lesen des Adressbusses das Bit11 zurückgesetzt war und nach dem unbedingten Sprungbefehl gesetzt ist.

Die Erkennung eines "Pending Bank Switch" und die korrekte Wiederherstellung durch die Debugger-Firmware funktioniert auch im Zusammenhang mit Interrupts, da keine Überprüfung auf einen "Pending Bank Switch" erfolgt, wenn nach der Ausführung des ersten "JMP 009"-Befehls und vor der Ausführung des darauf folgenden Befehls ein Interrupt erkannt wurde. In diesem Fall wird der Zustand des MB-FFs beim Monitor-Ausgang nicht manipuliert, was nur notwendig ist, wenn Bit 11 des Programmzählers wieder in den Zustand vor den Monitor-Routinen versetzt werden muss. Alle 12 Bits des Programmzählers des MCS-48 µCs werden durch den "RETR"-Befehl am Ende der Interrupt-Service-Routine im MCS-48 Anwenderprogramm wieder korrekt gesetzt und das MB-FF wurde durch die Monitor-Software nicht verändert.

Wird ein "Pending Bank Switch" erkannt, wird beim Monitor-Ausgang die im MCS-48 Anwenderprogramm aktive Speicherbank ausgewählt und auch das Vorbereitungs-FlipFlop (MB-FF) in den Zustand vor Aufruf der Monitor-Software versetzt.

4.1.5 A, T, PSW, R0 retten

Es werden nacheinander die Inhalte des Akkumulators (A), des Timer-/Counter-Registers (T) und des "Program Status Word" (PSW) gesichert, indem während der Ausführung der an die Adresse 0x09 des Debug-Programmspeichers geladenen "MOVX @R0,A"-Anweisung durch den Debug-µC die Adressleitungen A0 bis A7 und A9 bis A16 auf dem Wert 0x1FE09 gehalten werden, so dass, unabhängig vom bisher unbekannten Inhalt des R0-(R0'-)Registers, der Akkumulatorinhalt des MCS-48 µCs in Adresse 0x09 im Debug-Datenspeicher geschrieben wird. A8 bleibt über die MMU unter Kontrolle des MCS-48 µCs, da sie zur Umschaltung zwischen Debug-Programmspeicher und Debug-Datenspeicher benötigt wird. Auf dieselbe Art wird anschließend der Inhalt von R0 in der Registerbank 0 gesichert und am Ende dieser Routine steht endlich ein Indexregister zur Verfügung, dessen Inhalt schadlos zerstört werden kann - beispielsweise für die Verwendung in einer "MOVX @Ri,A"-Anweisung.

Laden :

09 90 MOVX @R0,A ;Put A in 0x1FF09
0A 42 MOV A,T
0B 00 NOP
0C 04 JMP 009
0D 09

Akkumulator retten

  1. D0 bis D7, A8, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
    A0 bis A7, A9 bis A16 unter Kontrolle von Debugger
    A0 bis A7, A9 bis A16 = 0x1FE09
  2. Befehl in Adresse 0x09 (MOVX @R0,A) ausführen
    Der Akkumulatorinhalt wird in Adresse 0x1FF09 des Debugger-RAM geschrieben
  3. Wenn vor der Ausführung des Befehls in Adresse 0x09 ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehl in Adresse 0x09 ausführen
  4. Adresse 0x1FF09 (Akkumulatorinhalt) lesen und als DBG_A sichern

T-Register retten

  1. A0 bis A7, D0 bis D7, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
  2. Befehle in Adressen 0x0A (MOV A,T) bis 0x0D (JMP 009) ausführen :
    Danach steht der Inhalt des T-Registers in A
  3. Wenn vor der Ausführung eines der Befehle in Adressen 0x0A bis 0x0D ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehle in Adressen 0x09 bis 0x0D erneut ausführen
  4. D0 bis D7, A8, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
    A0 bis A7, A9 bis A16 unter Kontrolle von Debugger
    A0 bis A7, A9 bis A16 = 0x1FE09
  5. Befehl in Adresse 0x09 (MOVX @R0,A) ausführen
    Der Akkumulatorinhalt (=T-Register) wird in Adresse 0x1FF09 des Debug-RAM geschrieben
  6. Adresse 0x1FF09 (T-Register) lesen und als DBG_T sichern
  7. Wenn vor der Ausführung des Befehls in Adresse 0x09 ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehl in Adresse 0x09 ausführen
    3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren

PSW retten

  1. Opcode für "MOV A,PSW" (0xC7) in Adresse 0x0A schreiben :
    09 90 MOVX @R0,A ;Put A in 0x1FF09
    0A C7 MOV A,PSW
    0B 00 NOP
    0C 04 JMP 009
    0D 09
  2. A0 bis A7, D0 bis D7, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
  3. Befehle in Adressen 0x0A (MOV A,PSW) bis 0x0D (JMP 009) ausführen :
    Danach steht der Inhalt des PSW in A
  4. Wenn vor der Ausführung eines der Befehle in Adressen 0x0A bis 0x0D ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
    3. Befehle in Adressen 0x09 bis 0x0D erneut ausführen
  5. D0 bis D7, A8, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
    A0 bis A7, A9 bis A16 unter Kontrolle von Debugger
    A0 bis A7, A9 bis A16 = 0x1FE09
  6. Befehl in Adresse 0x09 (MOVX @R0,A) ausführen
    Der Akkumulatorinhalt (=PSW) wird in Adresse 0x1FF09 des Debugger-RAM geschrieben
  7. Adresse 0x1FF09 (PSW) lesen und als DBG_PSW sichern
  8. Wenn vor der Ausführung des Befehls in Adresse 0x09 ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehl in Adresse 0x09 ausführen
    3. StackPointer in DBG_PSW inkrementieren
    4. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren

R0RB0 (R0 in Registerbank0) retten

  1. Da jetzt der Zustand des PSW, das das Bit zur Auswahl der aktiven Registerbank "BS" enthält, gesichert ist, darf sein Inhalt durch explizite Auswahl der Registerbank 0 zerstört werden.
    Opcode für "SEL RB0" (0xC5) in Adresse 0x0A und Opcode für "MOV A,R0" (0xF8) in Adresse 0x0B schreiben :
    09 90 MOVX @R0,A ;Put A in 0x1FF09
    0A C5 SEL RB0
    0B F8 MOV A,R0
    0C 04 JMP 009
    0D 09
  2. A0 bis A7, D0 bis D7, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
  3. Adressen 0x0A (SEL RB0) bis 0x0D (JMP 009) ausführen :
    Danach steht der Inhalt des R0-Registers, Registerbank 0 in A
  4. Wenn vor der Ausführung eines der Befehle in Adressen 0x0A bis 0x0D ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. StackPointer in DBG_PSW inkrementieren
    3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
    4. Befehle in Adressen 0x09 bis 0x0D erneut ausführen
  5. D0 bis D7, A8, *RAM_OE, *RAM_WE unter Kontrolle von MCS-48 µC
    A0 bis A7, A9 bis A16 unter Kontrolle von Debugger
    A0 bis A7, A9 bis A16 = 0x1FE09
  6. Befehl in Adresse 0x09 (MOVX @R0,A) ausführen
    Der Akkumulatorinhalt (=R0, RB0) wird in Adresse 0x1FF09 des Debug-RAM geschrieben
  7. Adresse 0x1FF09 (R0, RB0) lesen und als DBG_R0RB0 sichern
  8. Wenn vor der Ausführung des Befehls in Adresse 0x09 ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. Befehl in Adresse 0x09 ausführen
    3. StackPointer in DBG_PSW inkrementieren
    4. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren

PCL auf 0x09 setzen

  1. Adressen 0x0A bis 0x0D ausführen, um die unteren 8Bit des Programmzählers (PCL) wieder auf 0x09 zu setzen
  2. Wenn vor der Ausführung eines der Befehle in Adressen 0x0A bis 0x0D ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. StackPointer in DBG_PSW inkrementieren
    3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren

4.1.6 P1, P2, T0, T1, *INT, F1 lesen

Der Zustand von P1, P2, F1 und der T0-, T1- und *INT-Eingänge werden gelesen und im Debug-Datenbereich gespeichert.

Laden :

;(Read Ports And Bits)
09 B8 MOV R0,#00
0A 00
0B 09 IN A,P1
0C 90 MOVX @R0,A ;Put A in 0x1FF00
0D 18 INC R0
0E 0A IN A,P2
0F 90 MOVX @R0,A ;Put A in 0x1FF01
10 18 INC R0
11 27 CLR A
12 97 CLR C      ;Bit 3 = *INT
13 86 JNI 16
14 16
15 A7 CPL C
16 F7 RLC A
17 97 CLR C      ;Bit 2 = F1
18 A7 CPL C
19 76 JF1 1C
1A 1C
1B A7 CPL C
1C F7 RLC A
1D 97 CLR C      ;Bit 1 = T1
1E 46 JNT1 21
1F 21
20 A7 CPL C
21 F7 RLC A
22 97 CLR C      ;Bit 0 = T0
23 26 JNT0 26
24 26
25 A7 CPL C
26 F7 RLC A
27 90 MOVX @R0,A ;Put A in 0x1FF02
28 04 JMP 009
29 09

Nach Ausführen der Befehle in den Adressen 0x09 bis 0x29 haben die unteren 8Bit des Programmzählers (PCL) den Inhalt 0x09 und der Zustand von P1 kann aus RAM-Adresse 0x1FF00, der Zustand von P2 aus RAM-Adresse 0x1FF01 und der Zustand von *INT (Bit3), F1 (User Flag 1) (Bit2), T1 (Bit1) und T0 (Bit0) aus RAM-Adresse 0x1FF02 gelesen werden.

Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x29 ein neuer IRQ erkannt wurde :

  1. PCL = 0x09
  2. StackPointer in DBG_PSW inkrementieren
  3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
  4. Adressen 0x09 bis 0x29 erneut ausführen

4.1.7 Internes RAM lesen

Es wird der MCS-48 Code zum Kopieren des internen RAMs (Adresse 0x01 bis 0x7F, Adresse 0x00 wurde bereits mit R0, RB0 als DBG_R0RB0 gesichert) in das externe Debug-RAM geladen.

Laden :

;(Copy internal RAM locations 01h to 7Fh to external RAM locations 01h to 7Fh)
09 B8 MOV R0,#7F
0A 7F
0B F0 MOV A,@R0
0C 90 MOVX @R0,A
0D E8 DJNZ R0,0B
0E 0B
0F 04 JMP 009
10 09

Nach Ausführen der Befehle in den Adressen 0x09 bis 0x10 haben die unteren 8Bit des Programmzählers (PCL) den Inhalt 0x09 und die Inhalte des internen RAMs von Adresse 0x01 bis Adresse 0x7F können aus den Adressen 0x1FF01 bis 0x1FF7F des Debug-Datenbereichs im externen RAM gelesen werden.

Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x10 ein neuer IRQ erkannt wurde :

  1. PCL = 0x09
  2. StackPointer in DBG_PSW inkrementieren
  3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
  4. Adressen 0x09 bis 0x10 erneut ausführen

4.1.8 Stack modifizieren

Falls während der Ausführung der vorangegangenen MCS-48 Programme ein Hardware-Interrupt (Timer-/Counter-Interrupt oder L-Pegel am *INT-Anschluss) abgefangen und der Stack noch nicht modifiziert wurde, wird diese Routine ausgeführt.

Laden :

;(Modify Stack locations)
09 B8 MOV R0,#xx
0A xx             ;Load R0 with stack-index
0B 23 MOV A,#xx
0C xx             ;new PCL
0D A0 MOV @R0,A
0E 18 INC R0
0F 23 MOV A,#xx
10 xx             ;new PSW/PCH
11 A0 MOV @R0,A
12 B8 MOV R0,#xx
13 xx             ;Load R0 with previously saved value for R0RB0
14 23 MOV A,#xx
15 xx             ;Load A with with previously saved value for A
16 04 JMP 009
17 09

Nach Laden des Programmcodes wird durch den Debug-µC an Adresse 0x0A die berechnete Adresse (StackIndex = (StackPointer - 1) * 2 + 8) des unteren Bytes der zuletzt vom MCS-48 µC beschriebenen Stack-Speicherstellen, an Adresse 0x0C das niederwertige Byte der Rücksprungadresse in das MCS-48 Anwenderprogramm und an Adresse 0x10 die restlichen 4Bit der Rücksprungadresse in das MCS-48 Anwenderprogramm zusammen mit den höherwertigen 4Bit des durch den Debug-µC gespeicherten PSW eingetragen. Schließlich werden noch die vom Debug-µC gesicherten Werte für R0RB0 in Adresse 0x13 und für den Akkumulator in Adresse 0x15 geladen, da die Inhalte dieser beiden durch das Programm zerstört werden.

Nach Ausführen der Befehle in den Adressen 0x09 bis 0x17 ist auf dem Stack die korrekte Rücksprungadresse inklusive PSW-Bits abgelegt und die unteren 8Bit des Programmzählers (PCL) haben den Inhalt 0x09.

Während der Ausführung des Codes findet keine Überprüfung auf einen neuen IRQ statt - der wurde schließlich bereits erkannt und dadurch die Modifikation des Stacks angestoßen.

4.2.0 Monitor-Metazustand

Es ist jetzt eine Art "Meta-Zustand" erreicht, in dem der Zustand des MCS-48 µCs nach dem zuletzt ausgeführten Befehl des MCS-48 Anwenderprogramms vollständig bekannt ist und im GUI vor dem Fortsetzen des MCS-48 Anwender-Programms beliebig geändert werden kann.

4.3.0 Monitor-Ausgang

4.3.1 Gerettete und geänderte Inhalte zurückschreiben

Das Zurückschreiben der beim Monitor-Aufruf geretteten Inhalte (Register, Flags, Bits) erfolgt in der umgekehrten Reihenfolge der Rettung. PBR, DBR, P1, P2, F1 und internes RAM werden nur beschrieben, wenn sie durch den Anwender im GUI geändert wurden oder der Stackinhalt durch die Debugger-Firmware im Debug-Datenbereich manipuliert werden musste. R0, PSW, A, PC, die Auswahl der aktiven 2kB-Speicherbank und der Zustand des MB-FlipFlops sind durch die Ausführung der Monitor-Programme stets als "zerstört" anzusehen, so dass sie, im Fall einer Änderung durch den Nutzer im GUI gemeinsam mit dem T-Register, beim Verlassen der Monitor-Software zurück in den MCS-48 µC geschrieben werden.

4.3.2 Internes RAM beschreiben

Laden :

;(Copy external RAM locations 01h to 7Fh to internal RAM locations 01h to 7Fh)
09 B8 MOV R0,#7F
0A 7F
0B 80 MOVX A,@R0
0C A0 MOV @R0,A
0D E8 DJNZ R0,0B
0E 0B
0F 04 JMP 009
10 09

Nachdem der Debug-µC den Programmcode in den Debug-Programmspeicher geladen, die Adressen 0x1FF01 bis 0x1FF7F des Debug-Datenspeichers im externen RAM mit den gewünschten Inhalten des internen RAM des MCS-48 µCs beschrieben und der MCS-48 µC die Befehle in den Adressen 0x09 bis 0x10 ausgeführt hat, haben die unteren 8Bit des Programmzählers (PCL) den Inhalt 0x09 und die Adressen 0x01 bis 0x07 des internen RAM sind mit den neuen Inhalten beschrieben.

Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x10 ein neuer IRQ erkannt wurde :

  1. PCL = 0x09
  2. StackPointer in DBG_PSW inkrementieren
  3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
  4. Befehle in Adressen 0x09 bis 0x10 erneut ausführen

4.3.3 PBR, DBR, P1, P2 und F1 beschreiben

Laden :

;(Load Port2, Port1, User Flag F1)
09 23    MOV A,#xx ;P2
0A xx
0B 3A    OUTL P2,A
0C 23    MOV A,#xx ;P1
0D xx
0E 39    OUTL P1,A
0F A5    CLR F1
10 00/B5 NOP/CPL F1
11 04    JMP 009
12 09

Nach dem Laden des Programmcodes werden vom Debug-µC in Adresse 0x0A der neue Wert für P2, in Adresse 0x0D der neue Wert für P1 und für ein zurückgesetztes User-Flag1 (F1) in Adresse 0x10 der MCS-48 Opcode für "NOP" (0x00) oder für ein gesetztes User-Flag1 (F1) "CPL F1" (0xB5) eingetragen und anschließend die Befehle in den Adressen 0x09 bis 0x12 ausgeführt.

Nach der Ausführung haben die unteren 8Bit des Programmzählers (PCL) den Inhalt 0x09 und Port1 (P1), Port2 (P2) und User-Flag1 (F1) haben die neuen Werte übernommen.

Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x12 ein neuer IRQ erkannt wurde :

  1. PCL = 0x09
  2. StackPointer in DBG_PSW inkrementieren
  3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
  4. Befehle in Adressen 0x09 bis 0x12 erneut ausführen

In das PBR und DBR der MMU können vom Debug-µC ohne Beteiligung des MCS-48 µCs neue Werte geschrieben werden.

4.3.4 R0, PSW, T und A wiederherstellen

Laden :

;(Restore R0, PSW, T, A)
09 B8    MOV R0,#xx   ;R0, RB0
0A xx
0B 23    MOV A,#xx    ;PSW
0C xx
0D D7    MOV PSW,A
0E 23    MOV A,#xx    ;T
0F xx
10 00/62 NOP / MOV T,A
11 23    MOV A,#xx    ;A
12 xx
13 04    JMP 009
14 09

Nachdem der Programmcode und der Inhalt des Registers R0 in Registerbank 0 (R0, RB0) aus DBG_R0RB0 in Adresse 0x0A, der Inhalt des Prozessor-Status-Wortes (PSW) aus DBG_PSW in Adresse 0x0C, der Inhalt des Timer-/Counter-Registers (T) aus DBG_T in Adresse 0x0F und der Inhalt des Akkumulators (A) aus DBG_A in Adresse 0x12 des Debug-Programmspeichers durch den Debug-µC geladen und die Befehle in den Adressen 0x09 bis 0x14 durch den MCS-48 µC ausgeführt wurden, sind R0, PSW, T und A mit den im Debug-µC gespeicherten Werten beschrieben und die unteren 8Bit des Programmzählers (PCL) haben den Inhalt 0x09. Durch Einfügen des MCS-48 Opcodes für "NOP" (0x00) an Adresse 0x10 kann das Beschreiben des T-Registers unterdrückt werden, wenn keine Änderung des Inhalts des T-Registers durch den Nutzer im GUI vorliegt.

Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x14 ein neuer IRQ erkannt wurde :

  1. PCL = 0x09
  2. StackPointer in DBG_PSW inkrementieren
  3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
  4. Befehle in Adressen 0x09 bis 0x14 erneut ausführen

4.3.5 Stack modifizieren

Siehe "4.1.8 Stack modifizieren".

4.3.6 PC, Timer-/Counter-Konfiguration und MB-FF wiederherstellen

Laden :

;(Restore active MB, TC-Config, PC, pending MB)
09 xx NOP / SEL MBx (restore active memory bank)
0A xx NOP / STRT T / STRT CNT
0B xx JMP ReturnAddress-1 (PC=PC+1 by NOP/SEL MBx in 0D)
0C xx
0D xx NOP / SEL MBx (restore pending SEL MBx)
  1. Wiederherstellung von Bit11 des PC (aktive Speicherbank) vorbereiten :
    Wurde beim Monitor-Start ein "Pending SEL MB0" festgestellt (Bit11 des PC war beim Monitor-Aufruf gesetzt und nach Ausführung eines unbedingten Sprungbefehls zurückgesetzt), wird an Adresse 0x09 ein "SEL MB1"-Befehl (Opcode 0xF5) eingetragen.
    Wurde beim Monitor-Start ein "Pending SEL MB1" festgestellt (Bit11 des PC war beim Monitor-Aufruf zurückgesetzt und nach Ausführung eines unbedingten Sprungbefehls gesetzt), wird an Adresse 0x09 ein "SEL MB0"-Befehl (Opcode 0xE5) eingetragen.
    Wurde weder ein "Pending SEL MB0" noch ein "Pending SEL MB1" beim Start der Monitor-Software festgestellt, wird an Adresse 0x09 ein "NOP"-Befehl (Opcode 0x00) eingetragen.
  2. Wiederherstellung der Timer-/Counter-Konfiguration vorbereiten :
    In Adresse 0x0A wird je nach Auswahl der Monitor-Optionen im GUI den Opcode für "NOP" (0x00), "STRT T" (Opcode 0x55 : Timer/Counter als Zeitgeber starten) oder "STRT CNT" (Opcode 0x45 : Timer/Counter als Zähler starten) gespeichert.
  3. Wiederherstellung von Bit0 bis Bit10 des PC vorbereiten :
    Adressen 0x0B und 0x0C werden mit einem unbedingten Sprungbefehl ("JMP") auf die um 1 verminderte Monitor-Rückspungadresse geladen, um Bit0 bis Bit10 des Programmzählers (PC) damit wiederherzustellen. Die dort eingetragene Zieladresse muss wegen der Ausführung des in Adresse 0x0D zu hinterlegenden Befehls um 1 kleiner als die tatsächliche Zieladresse im Anwender-Programm sein (s.u. "Zustand des MB-FlipFlops wiederherstellen") !
  4. Wiederherstellung des MB-FlipFlops vorbereiten :
    Wurde beim Monitor-Start ein "Pending SEL MB0" festgestellt (Bit11 des PC war beim Monitor-Aufruf gesetzt und nach Ausführung eines unbedingten Sprungbefehls zurückgesetzt), wird an Adresse 0x0D ein "SEL MB0"-Befehl (Opcode 0xE5) eingetragen.
    Wurde beim Monitor-Start ein "Pending SEL MB1" festgestellt (Bit11 des PC war beim Monitor-Aufruf zurückgesetzt und nach Ausführung eines unbedingten Sprungbefehls gesetzt), wird an Adresse 0x0D ein "SEL MB1"-Befehl (Opcode 0xF5) eingetragen.
    Wurde weder ein "Pending SEL MB0" noch ein "Pending SEL MB1" beim Start der Monitor-Software festgestellt, wird an Adresse 0x0D ein "NOP"-Befehl (Opcode 0x00) eingetragen.

Nachdem damit die Vorbereitungen abgeschlossen sind, kann mit der Ausführung des geladenen Programms begonnen werden. Der MCS-48 µC hat dabei die Kontrolle über A0 bis A7, D0 bis D7, *RAM_OE, *RAM_WE. A8 bis A16 werden vom Debug-µC auf 0x1FE00 gehalten.

  1. Bit0 bis Bit11 des PC , Timer-/Counter-Konfiguration wiederherstellen :
    Durch Ausführen des Programms von Adresse 0x09 bis zum Befehl in Adressen 0x0B und 0x0C werden Bit11 des PC (aktive Speicherbank), die Timer-/Counter-Konfiguration und Bit0 bis Bit10 des PC wiederhergestellt.
  2. Wenn vor der Ausführung eines der Befehle in den Adressen 0x09 bis 0x0C ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. StackPointer in DBG_PSW inkrementieren
    3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
    4. "R0, PSW, T und A wiederherstellen" ausführen (siehe 4.3.4)
    5. "Stack modifizieren" ausführen (siehe 4.1.8)
    6. Befehle in Adressen 0x09 bis 0x0C erneut ausführen
  3. Zustand des MB-FlipFlops wiederherstellen :
    Da keine weiteren unbedingten Sprungbefehle in dem MCS-48 Programmcode vorkommen, kann jetzt der Zustand des MB-FlipFlops wiederhergestellt werden (andernfalls käme das MB-FlipFlop als Bit11 des PC schon zur Wirkung), wenn beim Monitor-Start ein "Pending Bank Switch" erkannt wurde. Zur Ausführung des Befehls an Adresse 0x0D wird der Adress-Bus des MCS-48 µCs vom externen RAM getrennt (Bit0 bis Bit10 des PC zeigen bereits auf die um 1 verminderte Monitor-Rückspungadresse) und der Debug-µC als Quelle aller Adress-Signale genutzt und der Befehl an Adresse 0x0D (vollständig 0x1FE0D) ausgeführt.
  4. Beim Ausführen des Befehls an Adresse 0x0D wird nicht auf externen Interrupt (L-Pegel an *INT) reagiert, wenn ReturnAddress-1 = 0x003, und es wird nicht auf einen Timer-/Counter-Interrupt reagiert, wenn ReturnAddress-1 = 0x007 (*). Dies ist der einzige Zeitpunkt während der Ausführung der Monitor-Software, in der die IRQ-Erkennung scheinbar(!) nur eingeschränkt funktioniert (siehe (*)). Wurde ein zu unterdrückender Interrupt erkannt, wird das entsprechende Status-Bit im Debug-µC durch dessen Firmware wieder gelöscht, um einen eventuellen SingleStep-Befehl vom steuernden PC nicht zu blockieren.
    Wenn vor der Ausführung des Befehls in Adresse 0x0D ein neuer IRQ erkannt wurde :
    1. PCL = 0x09
    2. StackPointer in DBG_PSW inkrementieren
    3. Wenn der neu erkannte IRQ ein Timer-/Counter-Interrupt ist, dann DBG_T inkrementieren
    4. "R0, PSW, T und A wiederherstellen" ausführen (siehe 4.3.4)
    5. "Stack modifizieren" ausführen (siehe 4.1.8)
    6. Befehle in Adressen 0x09 bis 0x0C erneut ausführen
    7. Befehl in Adresse 0x0D erneut ausführen
  5. Der PC hat durch Ausführung des Befehls in Adresse 0x0D den richtigen Wert (nämlich die Adresse des nächsten Befehls im MCS-48 Anwenderprogramm) angenommen.

(*) : Wenn ReturnAddress-1 = 0x003, dann ist die Adresse des nächsten auszuführenden Befehls im Anwenderprogramm 0x004 - das zweite Byte einer ISR für externe Interrupts (L-Pegel an *INT). Es ist höchst unwahrscheinlich, dass an dieser Stelle ein externer Interrupt auch bei minimalistischster ISR ausgelöst werden soll (ausgenommen, die ISR besteht nur aus einem "RETR" - aber dann ergibt der ganze Interrupt keinen Sinn (s.u.)) :

;ISR für externen Interrupt (L-Pegel an *INT)
003 xx Irgendein Einzelbyte-Befehl
004 93 RETR (darauf zeigt der PC als nächsten auszuführenden Befehl)

Versagen würde die Erkennung externer Interrupts (L-Pegel an *INT) nur bei folgender Konstruktion und ReturnAddress=0x004 - und sogar in diesem Fall nur dann, wenn zwischen der Ausführung des "JMP ReturnAddress-1" und des letzten "SEL MBx"-Befehls in der Monitor-Software eine externe Interrupt-Anforderung erzeugt wird und nur sehr kurz anliegt :

;ISR für externen Interrupt (L-Pegel an *INT)
003 93 RETR      ;die denkbar sinnloseste ISR
;Code ausserhalb der "ISR"
004 xx
[...]

Analog dazu beim Timer-/Counter-Interrupt :

Wenn ReturnAddress-1 = 0x007, dann ist die Adresse des nächsten auszuführenden Befehls im Anwenderprogramm 0x008 - das zweite Byte einer ISR für Timer-/Counter-Interrupts. Es ist höchst unwahrscheinlich, dass an dieser Stelle ein Timer-/Counter-Interrupt auch bei minimalistischster ISR ausgelöst werden soll (ausgenommen, die ISR besteht nur aus einem "RETR" - aber dann ergibt der ganze Interrupt keinen Sinn (s.u.)) :

;ISR für Timer-/Counter-Interrupt
007 xx Irgendein Einzelbyte-Befehl
008 93 RETR (darauf zeigt der PC als nächsten auszuführenden Befehl)

Versagen würde die Erkennung von Timer-/Counter-Interrupts nur bei folgender Konstruktion und ReturnAddress=0x008 - und sogar in diesem Fall nur dann, wenn zwischen der Ausführung des "JMP ReturnAddress-1" und des letzten "SEL MBx"-Befehls in der Monitor-Software eine Timer-/Counter-Interrupt-Anforderung erzeugt wird und nur sehr kurz anliegt :

;ISR für externen Interrupt (L-Pegel an *INT)
007 93 RETR      ;die denkbar sinnloseste ISR
;Code ausserhalb der "ISR"
008 xx
[...]