CIA-Kurs: "Die Geheimnisse des Secret-Service" (Teil 4)
Herzlich Willkommen zum 4 . Teil unseres
CIA-Kurses. Diesen Monat möchte ich dann
doch vorgreifen und Ihnen zunächst einmal die NMI-Interrupts erklären. Dann
können wir nämlich anhand eines einfachen Beispiels auch eine sehr nützliche
Anwendungsweise von Timerkopplung behandeln.
Mit dem IRQ kennen Sie sich mittlerweile
ja gut aus. Wir haben diese Interruptart
in Zusammenhang mit dem Systeminterrupt
ja schon eingehendst kennengelernt und
auch schon eigene IRQ-Routinen geschrieben, die sowohl eigenständig, als auch
im System-IRQ eingebunden arbeiteten.
Kommen wir nun also auch zu der anderen
für uns wichtigen Interruptart, dem NMI.
Zunächst: Was ist der Unterschied zwischen einem IRQ und einem NMI? Da haben
wir zum einen schon einmal den Unterschied, daß beide Interruptarten von
jeweils einem CIA angesteuert werden.
Das hatte ich Ihnen ja schon zu einem
früheren Zeitpunkt erläutert. CIA2 löst
also NMIs aus, CIA1 IRQs.
Doch es gibt da noch einen weitgehendst
wichtigeren Punkt, in dem sich der NMI
vom IRQ unterscheidet. Ich hatte Ihnen
damals bei der Erklärung der Hardwareverbindungen der CIAs und des Prozessors
untereinander ja schon erklärt, daß jede
der CIAs nicht nur verschiedenartige
Interrupts auslöst, sondern daß vielmehr
der Prozessor über zwei verschiedene
Eingänge verfügt, an denen der jeweilige
Interrupt ausgelöst werden kann. Das
bedeutet aber auch, daß er einen Unterschied zwischen beiden Interruptarten
macht, und das ist ganz wichtig für uns
zu wissen!
IRQ ist die Abkürzung für " Interrupt-ReQuest", was soviel bedeutet, wie " Anfrage auf eine Unterbrechung" . Das Wort
" Anfrage" möchte ich hier ganz deutlich
herausstellen, denn wie Sie mittlerweile
ja ebenfalls wissen sollten, können wir
den Prozessor durch den SEI-Befehl dahingehend manipulieren, daß er Signale
am IRQ-Eingang ignoriert. Im Fachjargon
sagt man auch, man kann einen Interrupt
" maskieren"- durch Setzen des Interruptflags können wir also softwaremäßig
IRQs sperren und das ist dann auch der
Punkt, bei dem der Unterschied zum NMI
in Erscheinung tritt." NMI" ist nämlich
ebenfalls eine Abkürzung und steht für
" Non-Maskable- Interrupt", was mit
" Nichtmaskierbare- Unterbrechung" den
Nagel auf den Kopf trifft. Und schon
hätten wir das Kind im Brunnen. NMIs
sind softwaremäßig nicht sperrbar und
das kann enorme Vorteile gegenüber dem
IRQ haben!
Hier einmal ein einfaches Beispiel: die
Routinen des Betriebssystems müssen in
der Regel aus dem einen oder anderen
Grund von Zeit zu Zeit IRQs verhindern.
Zum Einen aus Zeitersparnis und somit
zur Geschwindigkeitssteigerung, zum Anderen bei komplizierten Synchronisationsvorgängen mit der Peripherie des
Computers, wobei auftretende Interrupts
Zeitwerte verfälschen und somit stören
könnten, benutzt das Betriebssystem nun
ebenfalls den SEI-Befehl. Die Folge des
Ganzen wird schnell klar: soll der IRQ
nun ganz zeitkritische Arbeiten erldedigen, so kommt er schnell aus dem Takt
und ist somit oft viel zu ungenau. Glänzendes Beispiel ist die BASIC-Uhr TI$ .
Sie wird nämlich über den System-IRQ
gesteuert, der ja normalerweise 60 Mal
pro Sekunde auftritt. Rein theoretisch
braucht die Routine für TI$ also nur bis
60 zu zählen, um zu wissen, daß jetzt
eine Sekunde verstrichen ist. Praktisch
sieht es aber so aus, daß zum Beispiel die Ein-/ Ausgaberoutinen des Betriebssystems oft den IRQ unterbinden. Es genügt
also, ein längeres Programm von Diskette
zu laden um die TI$- Uhr extrem zu bremsen, so daß sie die eine oder andere
Sekunde nachgeht. Aus den Sekunden werden Minuten, je mehr man lädt und irgendwann kann man die Zeitwerte der Uhr
komplett vergessen: dadurch, daß IRQs
zwischendurch nicht mehr auftreten können, aber die Zeit weiterhin unerbittlich verstreicht, zählt die TI$- Routine
zwar weiterhin 60 IRQs, diese jedoch
dauerten länger als eine Sekunde.
NMIs hingegen werden IMMER bearbeitet, sobald sie auftreten. Sogar dann, wenn
sich der Prozessor gerade innerhalb eines IRQs befindet. Er rettet dann einfach die Daten des IRQs ( Programmzeiger, Prozessorstatus etc.) und bearbeitet den
NMI. Umgekehrt jedoch, kann kein IRQ
während eines NMIs auftreten, da der
Prozessor ja dann das Interruptflag ja schon von selbst gesetzt hat ( sie erinnern sich. . .) . Es sei denn wir lassen
dies ausdrücklich zu, indem wir innerhalb der NMI-Routine das Flag durch CLI
wieder löschen.
Sie sehen also, man muß immer Unterschiede machen, wofür ein Interrupt benötigt wird. Einfache Probleme lassen
sich schnell mit dem IRQ bewältigen ( und
das ist bei den meisten der Fall), da er
bei Bedarf auch sehr einfach abgeschaltet werden kann. Bei zeitkritischen Problemen benutzt man besser einen NMI. Er
funktioniert genau und zuverlässig, wobei man allerdings in Kauf nehmen muß, daß man diesen nicht so einfach wieder
verhindern kann.
Das ist nämlich der Grund warum man bei
der Programmierung eines NMIs mehr Aufwand hat. Für ihn existiert, ebenso wie
für den IRQ, auch ein Vektor, der verändert werden muß, wenn man die NMIs auf
eigene Interruptroutinen umleiten will.
Wir hatten ja letzten Monat schon gelernt, daß man dabei sichergehen muß, daß während dieser Veränderung in gar
keinem Fall ein Interrupt ausgelöst werden darf, da so schon während das LO-Byte, jedoch noch nicht das HI-Byte des
Vektors verändert ist, der Rechner unkontrolliert in die Pampas springen
könnte, was so unangenehme Folgen hätte, wie zum Beispiel einen Rechnerabsturz.
Aus diesem Grund müssen wir zusehen, daß
alle eventuell in Frage kommenden NMI-Quellen so geschaltet sind, daß sie keinen Interrupt auslösen, während wir den
NMI-Vektor verändern.
Im Normalfall ist dieses Problem eigentlich relativ einfach zu handhaben, denn
das Betriebssystem benutzt den Timer-NMI
ausschließlich nur bei Betrieb der
RS232- Schnittstelle, also bei der seriellen Datenübertragung per Modem. In
aller Regel können wir diesen Fall jedoch ausklammern und davon ausgehen, daß
alle Funktionen der CIA2, die den NMI betreffen, funktionslos ihr Dasein fristen. Nur im Falle einer eigenen Benutzung sollten wir uns immer im Klaren
darüber sein, was für eine Aufgabe der
NMI gerade behandelt und wie sie gesteuert wird. Im Regelfall genügt es
jedoch, einfach alle Bits des ICR-Registers der CIA2 zu löschen, so daß
von dort keine Interrupts mehr an den
Prozessor gelangen. Dies geschieht durch
ein Schreiben des Wertes 127(=$7 F) in
selbiges Register ($ DD0 D = dez.56589) .
Eine weitere Besonderheit des NMIs ist, daß die RESTORE-Taste hardwaremäßig DI-REKT an die NMI-Leitung des Prozessors
angeschlossen ist, daß also auch von
dort Interrupts ausgelöst werden können.
Dieses macht sich das Betriebssystem
zunutze, denn bei einem Druck auf RUN/- STOP-RESTORE, was den C64 ja wieder in
einen einigermaßen definierten Zustand
zurückbringt, wird immer ein NMI ausgelöst. Was nun allerdings wirklich da- bei geschieht und wie es mit Sprungvektoren für NMIs aussieht, wollen wir uns
jetzt einmal näher anschauen.
Dazu ist wieder einmal eine kleine Reise
in die tieferen Gefilde des Betriebssystems angesagt. Beginnen wir mit den
elementaren Grundvoraussetzungen:
* Zunächst also wird ein NMI ausgelöst, indem der Benutzer auf die RESTORE-Taste drückt.
* Der Prozessor hält seine momentane
Arbeit jetzt unverzüglich an, rettet
wie bei jedem Interrupt die wichtigsten Daten auf den Stapel ( das hatten
wir ja schon), setzt das Interruptflag, so daß ihn keine IRQs mehr stören können und macht sich daran, wieder in einem eigenen Vektor für NMIs, am Ende seines Adressbereichs nachzusachauen, wo er jetzt weiterfahren
soll. Dieser Vektor liegt bei
$ FFFA/$ FFFB und zeigt auf eine Jobrou- tine des Betriebssystems bei $ FE43 .
Schauen wir uns einmal an, was dort so
läuft:
---------------- NMI-Anspringen
FE43 SEI IRQs sperren.
FE44 JMP ($0318) Öber NMI-Vektor springen.
----------------
Da hätten wir auch schon den angesprochenen NMI-Vektor, der für uns veränderbar ist. Er belegt die Speicherstellen
$0318/$0319( dez.792/793) und zeigt
normalerweise auf die Adresse gleich
hinter der soeben aufgelisteten Routine, auf $ FE47 .
Was übrigens anzumerken ist, ist die
Tatsache, daß wir beim Einbinden von
eigenen NMIs in den System-NMI darauf
achten müssen, daß wir auch die Prozessorregister quasi " von Hand" auf den Stapel retten müssen. Die IRQ-Vorbereitungsroutine hatte dies ja noch
VOR dem Sprung über den RAM-Vektor gemacht, weshalb wir uns nicht mehr darum
kümmern mußten. Beim NMI macht das Betriebssystem das erst NACH dem Sprung
über den Vektor, in der nun folgenden
Routine:
---------------- NMI vorbereiten. FE47 PHA Akku auf Stapel. FE48 TXA X nach Akku... FE49 PHA ...und auf Stapel. FE4A TYA Y nach Akku... FE4B PHA ...und auf Stapel. FE4C LDY #$7F Wert laden... FE4E STA $DD0D ...und damit alle NMI-Quellen von der CIA2 kommend sperren. ---------------- Auf RS232-Betrieb prü- fen. FE51 LDY $DD0D Interruptquellen- Anzeige aus ICR lesen und somit löschen um weitere NMIs freizuge- ben. FE54 BMI $FE72 Wenn die RS232- Schnittstelle aktiv ist, verzweigen. ----------------
Anmerkung: Mit dem letzten Befehl wurde
abgefragt, ob eines der Interruptquellenbits des ICR gesetzt ist. Da das Betriebssystem ja CIA2- gesteuerte NMIs nur
dann benutzt, wenn die RS232 Schnittstelle läuft, genügt es, nur zu prüfen, ob der NMI überhaupt von der CIA2 kommt.
In diesem Fall ist Bit 7 des ICR auf 1 .
Wenn das nicht der Fall ist, dann kann
der Auslöser nur die RESTORE-Taste gewesen sein, und es wird wiefolgt fortgefahren:
---------------- Auf ROM-Modul prüfen FE56 JSR FD02 Prüft ob ein ROM-Modul im Expansions-Port steckt. FE59 BNE $FE5E Wenn nein, dann ist das Zero-Flag gelöscht und wir überspringen den folgenden Befehl. FE5B JMP ($8002) Ja, wir haben ein Mo- dul, also springen wir auf den Modul-NMI (siehe unten). ---------------- Prüfen, ob R-S/RESTORE FE5E JSR $F6BC Flag für STOP-Taste in der Zeropage ($91 = dez. 145) berechnen und setzen. FE61 JSR $FFE1 STOP-Taste abfragen. FE64 BNE $FE72 Wenn nicht gedrückt verzweigen, um den NMI zu beenden. ---------------- R-S/RESTORE ausführen FE66 JSR $FD15 Standard-Vektoren für Interrupts und Ein-/ Ausgabevektoren ini- tialisieren. FE69 JSR FDA3 Ein-/Ausgabebausteine initialisieren. FE6C JSR E518 Bildschirm löschen. FE6F JMP ($A002) BASIC-Warmstart ausführen. ----------------
Der Teil von $ FE47-$ FE59 rettet nun
zunächst einmal die drei Prozessorregister auf den Stapel. Desweiteren werden alle Interruptquellen die der CIA2 geben könnte, gesperrt.
Dann, von $ FE51-$ FE55 wird das ICR der
CIA2 ausgelesen und somit für neue Interrupts freigeben ( ist im Moment zwar
nicht möglich, da wir ja die Interrupts vorher sperrten, wird aber für die
RS232- Behandlung gebraucht!) . Gleichzeitig wird geprüft, ob der Befehl von der
CIA2 kam, und wenn ja zur RS232- Unterroutine verzweigt.
Im nun folgenden Teil von $ FE56-$ FE5 D
wird geprüft, ob ein ROM-Modul im Expansionsport steckt. Wie so etwas funktioniert, will ich Ihnen nächsten Monat
erklären. Wenn ein Modul da ist, dann
wird auf einen moduleigenen NMI verzweigt, andernfalls wissen wir nun
endgültig, daß der Benutzer wahrscheinlich den Computer zurücksetzen will, und
wir können in den folgenden Teil verweigen.
Dieser geht von $ FE5 E-$ FE65 und prüft
nach, ob die RUN/ STOP-Taste gleichzeitig
auch noch gedrückt ist. Hierzu wird eine
Unterroutine ab $ F6 BC benutzt, die direkt die Tastatur abfragt und in Speicherzelle $91 der Zeropage anzeigt, ob
die RUN/ STOP-Taste gedrückt ist. Steht dort eine 0, so war dies der Fall. Andernfalls verzweigt das Programm nun
doch in die RS232- Routine. Ehrlich gesagt, weiß ich nicht warum dies so ist, denn es läge näher, den NMI direkt zu
beenden, aber die Wege des C64- Betriebssystems sind manchmal halt auch
unergründlich. . .
Jetzt sind wir aber endlich im letzten
Teil angelangt. R/ S-RESTORE wurde
gedrückt, was heißt, daß wir einen " Mini- Reset" ausführen sollen. Es werden
nun drei Unterroutinen aufgerufen, die
die wichtigsten Voreinstellungen im System vornehmen, nämlich das Zurücksetzen
der Sprungvektoren von ( inclusive)$0314-$333( Routine ab $ FD15), das Rücksetzen des Grafikchips ( VIC) und des
Soundchips ( SID), sowie der CIAs ( Routine ab $ FDA3) und das Löschen des Bildschirms ( Routine ab $ E518) . Zum Schluß
wird dann mit einem indirekten Sprung
auf den NMI-BASIC- Warmstart in den" READY"- Modus des BASICs verzweigt.
Hierbei wird der NMI nicht wie üblich
mit RTI beendet, sondern die Warmstartroutine stetzt einfach den Stackpointer
wieder zurück und springt dann in die
BASIC-Eingabe- Warteschleife.
Soviel zum System-NMI. Wir werden in den
nächsten Kursteilen auch noch auf die
RS232- Schnittstelle und deren Bedienung, sowie auf die ROM-Modul- Behandlung näher
eingehen, weshalb ich diese Themen diesmal aussparen möchte.
Kommen wir nun zu der Programmierung von
NMIs. Die Anwendungsbereiche für diese
Interruptart sind vielfältig. Sie läßt
sich gut bei zeitkritischen Problemen
einsetzen, wie zum Beispiel das zyklische Lesen von Daten, in einer fest vorgeschriebenen Geschwindigkeit ( Digitizer
und Scanner arbeiten oft mit NMIs) . Am
eindrucksvollsten ist aber bestimmt das
Abspielen von Musik über den NMI. Viele Sound-Editoren benutzen ja schon häufig
die Möglichkeit, ein Musikstück via Interrupt spielen zu lassen, jedoch gehen
diese meist über den IRQ.
Ich habe Ihnen einmal ein Beispielprogramm auf dieser MD mit abgespeichert.
Es heißt " NMI/ IRQ-DEMO" und beinhaltet
drei kleine Unterprogramme. Das Programm
tut nichts anderes, als einen Interrupt
zu initialisieren, der ständig einen Ton
über Stimme 1 des SID spielt. Hierbei
wird bei jedem Aufruf das HI-Byte der
Tonfrequenz um 1 erhöht. Das Ergebnis
ist ein ganz lustiger Soundeffekt, der
nicht unähnlich dem Geräusch ist, das
entsteht, wenn Scotty die Besatzung der
" Enterprise" rumbeamt.
Eigentliche Aufgabe dieses Beispielprogramms ist nun aber, Ihnen den Unterschied zwischen IRQ und NMI zu verdeutlichen. Deshalb gibt es zwei Möglichkeiten, es aufzurufen. Zum Einen können Sie es mit " SYS 4096*9" starten. Dann initialisieren Sie einen NMI. Der Ton wird
ständig über den NMI ausgegeben. Nun
bietet sich zusätzlich noch die Möglichkeit, daß Sie durch " SYS 4096*9+3" eine
kleine Unterroutine aufrufen, die alle
IRQs sperrt. Zu erkennen ist dies daran, daß nach dem Aufruf der Cursor nicht
mehr blinkt. Trotzdem aber hören Sie
weiterhin den Soundeffekt - dies also
als Beweis, daß der NMI unabhängig vom
IRQ arbeitet.
Die zweite Möglichkeit den Effekt zu
starten ist die mit " SYS 4096*9+6" . Sie
initialisieren dann einen IRQ, der jedoch genau dasselbe tut wie der NMI zuvor. Sie können nun nocheinmal mit
" SYS 4096*9+3" die IRQs sperren, und
schon hören Sie nichts mehr.
Als Beispiel zu den Problematiken, diesich mit dem IRQ und dem Betriebssystem
ergeben, empfehle ich Ihnen, während der IRQ läuft einmal ein Programm von Diskette zu laden. Sie werden merken, daß
die Tonausgabe zwischenzeitlich desöfteren stockt. Wenn das passiert, dann hat
gerade wieder einmal eine Routine des
Betriebssystems den IRQ mittels SEI abgeschaltet. Leider können Sie dieses
Problem nicht mit einem laufenden NMI
untersuchen. Der stört nämlich dann die
anfangs schon erwähnten Synchronisationsvorgänge, die beim Laden benötigt
werden, wobei der 64 er nur Mist anstellt. Probieren können Sie es einmal.
Manchmal hat man Glück, machmal nicht.
Bitte laden Sie nun den zweiten Teil des
IRQ-Kurses aus dem Kurs-Menü.