Magic Disk 64

home to index to text: MD9102-KURSE-CIA-KURS_TEIL_4-1_:_DIE_GEHEIMNISSE_DES_SECRET-SERVICE_(TEIL_4).txt
                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ü.

Valid HTML 4.0 Transitional Valid CSS!