Ein erster NMI

Den NMI hält keiner auf

C64 Studio, AMCE & TASM

Wie bereits bei der Einleitung zu den Interrupts (ich gehe davon aus, dass ihr den Beitrag bereits gelesen habt) erwähnt, handelt es sich beim NMI (None Maskable Interrupt) um einen Interrupt, der nicht mit SEI/ CLI unterdrückt werden kann. Wie ihr wisst, sind die einzigen Quellen für einen NMI die RESTORE-Taste und der CIA-2.
Die beiden CIAs sind übrigens fast identisch und unterscheiden sich nur durch wenige Register und durch das Auslösen von IRQ (CIA-1) und NMI (CIA-2).

Bevor wir jetzt einen NMI programmieren, sollten wir mal wieder einen Blick auf den ROM-Vector werfen. Der NMI springt zur Adresse $fe43, die wir im ROM-Vector unter $fffa finden. Schauen wir mit WinVICE mal nach, was unter $fe43 genau gemacht wird, so stossen wir auf nur zwei Befehle.

Es werden also erstmal weitere IRQs gesperrt (warum es hier einen SEI gibt ist mir allerdings nicht ganz klar, das I-Flag wurde ja bereits gesetzt, aber doppelt hält wohl einfach besser 😉 ) und dann wird über den RAM-Vector an $0318 zur dort hinterlegten Adresse gesprungen. Wie ihr seht, werden hier, im Gegensatz zum normalen IRQ, weder der Akku, noch das X-/Y-Register gesichert. Darum müssen wir uns bei Bedarf selbst kümmern. Wir können also unter $0318/19 wieder die Adresse für unsere eigene NMI-Routine eintragen.

NMI durch den Timer-A vom CIA-2

Unser kleines Testprogramm wird einen NMI und einen IRQ verwenden. Damit könnt ihr dann kontrollieren, dass der NMI tatsächlich nicht durch ein SEI verhindert werden kann. Der NMI wird über den Timer des CIA-2 den Rahmen blinken lassen, der IRQ verändert die Hintergrundfarbe.

Wir beginnen wieder mit der BASIC-Zeile 2018 SYS 2064:NEW.

Wir beginnen mit der Einrichtung unseres NMIs. Da wir NMIs nicht mit SEI verhindern können, müssen wir einen anderen Weg für die sichere Einrichtung finden. Sonst droht, wie im vorherigen Beitrag erwähnt, wieder ein Absturz, wenn wir bereits das LSB in $0318 geändert haben und ausgerechnet dann ein NMI auftritt. NMIs lassen sich über das Register 13 des CIA-2 unterbinden. Ihr findet es an der Adresse $dd0d. Wer jetzt denkt, es würde reichen dort einfach eine 0 einzutragen, der irrt. Das Register ist (so wie auch das Gegenstück, an der Adresse $dc0d, vom CIA-1) sehr speziell. Beim Schreiben bestimmt ihr über Bit-7, ob ihr die Bits auf 0 oder 1 setzen möchtet. Dann setzt ihr die Bits 4-0 auf 1 (die BITs 6 & 5 sind unbenutzt), die ihr auf den Wert aus BIT-7 ändern möchtet. Wir merken uns die aktuellen Interrupts des CIA-2 im X-Register und deaktivieren dann alle. Jetzt können wir die Adresse unserer NMI-Routine im RAM-Vector für den NMI ab $0318 eintragen. Wir möchten den Timer-A des CIA-2 verwenden, also legen wir an den Adressen $dd04 / $dd05 das LO- & HI-Byte für unseren Timer ab. Hier könnt ihr später mit anderen Intervallen testen, wie die sich auf die Rahmenfarbe auswirken. Jetzt müssen wir noch den Timer starten, das erreichen wir durchs setzten von BIT-0 im 14. Register $dd0e des CIA-2. Die anderen Bits setzen wir auf 0, somit beginnt unser Timer immer wieder von vorne. Jetzt sind wir fertig und müssen die NMIs wieder erlauben. Also holen wir die gemerkten Interrupts aus dem X-Register in den Akku und setzen BIT-0, damit wird es dem Timer-A erlaubt Interrupts auszulösen (hier NMIs). Denkt dran, dass wir wieder das Register 13 beschreiben und daher auch Bit-7 auf 1 setzen müssen!

Um die Auswirkung von SEI/CLI zu beobachten, richten wir noch einen eigenen IRQ fest.

Wir sperren die Interrupts mit SEI und verbiegen dann den RAM-Vector. Die Schleife ab loop dient nun dazu, zu demonstrieren, dass der NMI läuft (der Rahmen blinkt), der IRQ aber nicht. Was kein Wunder ist, da wir die Interrupts ja noch unterbunden haben. Sobald ihr die SPACE-Taste betätigt, erlauben wir die Interrupts duch CLI wieder und kehren zum BASIC zurück. Hier könnt ihr nun (obwohl es durchs Blinken eigentlich kaum möglich ist) weiterarbeiten und z. B. etwas programmieren.

Wir benötigen noch unsere beiden Interrupt-Routinen, fangen wir mit dem irq an, der besteht nur aus zwei Zeilen.

Das solltet nichts Neues für euch sein. Wichtig ist aber wieder, dass wir die Interrupt-Routinen erst ab $1000 oder später ablegen, damit wir etwas Platz für ein BASIC-Programm haben, schaut euch ggf. nochmal den letzten Beitrag an. Wenn ihr wollt, könnt ihr später auch ; jmp irq einkommentieren, um nochmal zu sehen, dass NMIs auch auftreten, während ein IRQ läuft.

Abschließend brauchen wir noch unseren NMI.

Da die ROM-Routine den Akku und die Register nicht sichert, kümmern wir uns zu Beginn darum und legen diese auf den Stack. Dann müssen wir den Interrupt bestätigen, dies geschieht einfach durchs lesen von Register 13 $dd0d. Wir prüfen anschließend, ob es sich um den Timer-Interrupt gehandet hat, falls nicht verlassen wir den NMI, sonst erhöhen wir die Rahmenfarbe. Beim Verlassen holen wir den Akku und die Register vom Stack und kehren per RTI zurück zum unterbrochenen Programm.
Da wir hier nicht zur ROM-Routine springen, werden SYSTEM-NMIs erfolglosbleiben. Beachtet das beim Testen bitte.

Vorsicht beim Turbo Assembler, da wir hier den NMI umbiegen, kommt ihr mit RESTORE nicht mehr zurück zum Sourcecode!

Wenn ihr das Programm nun startet, dann sollte zunächst nur der Rahmenblinken. Sobald ihr SPACE betätigt werden die IRQs erlaubt und auch die Hintergrundfarbe ändert sich. Außerdem kehren wir dann auch zurück ins BASIC.

NMI und IRQ in Aktion.
NMI und IRQ in Aktion.

So, dass soll es zu den NMIs gewesen sein. Ihr könnt jetzt mit IRQs und NMIs etwas experimentieren, bevor wir als nächstes endlich zu den Rasterzeileninterrupts kommen.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (5 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


ZurückWeiter

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Protected by WP Anti Spam