IRQ-Kurs Teil 4.2 Wollen wir uns nun einmal anschauen, wie unser NMI-Programm aufgebaut ist. Die NMIs werden übrigens über Timer A der CIA2 ausgelöst, der denselben Wert wie der Timer des System-IRQs als Startwert bekommt (das wäre der Wert 16420=$4024). Hier also das Programm:
---------- NMI vorbereiten LDA $7F "NMI-Quellen sperren" laden, STA DD0D und in ICR von CIA2. LDX #$2B Zeiger auf eigene... LDY #$90 ...Routinen laden, STX 0318 und NMI-Vektor... STY 0319 ...setzen. LDX #$24 Timerwert LO-Byte. LDY #$40 Timerwert HI-Byte. STX DD04 In TALO und... STY DD05 ...in TAHI schreiben. LDA #$81 Wert laden... STA DD0D Timer A als Interruptquelle festlegen. STA DD0E Timer A starten. ---------- SID einstellen.
LDA #$0F Wert 15 für volle Lautstärke STA D418 ...ins Lautstärkeregister. LDA #$00 Zählregister für Tonfrequenz STA 02 initialisieren. RTS Und zurück.
---------- NMI-Routine CLI IRQs wieder freigeben. PHA Akku, TXA X-, PHA TYA und Y-Register auf Stapel PHA retten.
LDA #16 Wert für "Dreieckswelle aus" STA D404 ...in SID schreiben LDA 02 Zähler für Frequenz lesen... STA D401 und in Frequenz-HI schreiben INC 02 Zähler um 1 erhöhen. LDA #17 Wert für "Dreieckswelle an" STA D404 ...in SID schreiben.
LDA DD0D NMIs wieder freigeben PLA Akku, TAY X-, PLA TAX und Y-Register wieder vom PLA Stapel zurückholen. RTI Und Interrupt verlassen
---------- Im ersten Teil dieses Listings haben wir die Initialisierungsroutine für unseren NMI. Hier werden zunächst auf die schon beschriebene Art und Weise alle Inter- ruptquellen die von der CIA2 kommen, ge- sperrt. Anschließend wird der NMI-Vektor bei $0318/$0319 auf unsere eigene Routi- ne verbogen (die Routine beginnt bei $9000 im Speicher, weshalb die eigentli- che Interruptroutine bei $902B beginnt). Ist dies getan, müssen wir als nächstes den Timerwert in die Timerregister für Timer A laden (wie bei CIA1 sind dies die Register 4 und 5 - TALO und TAHI). Dies ist der wie oben schon beschriebene Wert $4024. Jetzt müssen wir nur noch den Timer A als NMI-Interruptquelle set- zen und ihn anschließend starten. Dies geschieht in den folgenden 3 Zeilen. Was der Wert $81 für Register 13 (ICR) und 14 (CRA) bedeutet wissen Sie ja schon aus Teil 1 dieses Kurses, als ich Ihnen die Funktionen dieser Register genauer erläutert habe. Zum Abschluß der Initialisierungsroutine müssen wir auch noch den SID darauf vor- bereiten, Sound auszugeben. Dazu haben wir auch noch genug Zeit, da der schon laufende Timer zum nächsten Interrupt noch lange genug zählen wird (ich hätte die SID-Initialisierung auch vorher an- bringen können). Also wird erst einmal die Lautstärke des Soundchips einge- schaltet, sowie den Anfangsfrequenzwert für unseren Soundeffekt in Adresse $02 in der Zeropage geschrieben. Diese Adresse wird als Zählregister be- nutzt, da man auf die Register des SID leider nicht zum Lesen zugreifen kann. Somit sind sie also auch nicht mittels INC hochzählbar. Nun sind alle Voreins- tellungen getätigt, und wir können wie- der zum aufrufenden Programm zurückver- zweigen. Im zweiten Teil des Listings sehen Sie nun die NMi-Routine selbst. Als erstes erlauben wir hier wieder das Auftreten von IRQs (sie erinnern sich, das Be- triebssystem hatte sie ja gesperrt). Nun werden nach der mittlerweile schon alt- bekannten Methode die Prozessorregister auf den Stapel gerettet, was wir bei NMIs ja IMMER von Hand machen müssen, da das Betriebssystem uns diese Arbeit leider nicht abnimmt. Nun kommt der Teil, in dem der nächste Ton gespielt wird. Hierzu wird erst ein- mal die Stimme 1 des SID abgeschaltet. Dies ist notwendig, weil wir keine Hüll- kurve vorher festgelegt hatten, die ei- nen Ton möglicherweise dauerhaft spielen würde. Deshalb befinden sich in den Hüllkurvenregistern die Werte 0, was bedeutet, daß ein Ton nur ganz kurz an- geschlagen wird und gleich wieder ver- stummt. Damit man aber die nächste Fre- quenz nun hört müssen wir die Stimme also erst noch ausschalten. Anschließend wird der Inhalt des Zählregisters $02 in das HI-Byte-Frequenzregister von Stimme 1 geschrieben ($D401), und der Zähler für den nächsten Interrupt um 1 erhöht. Nun schalten wir Stimme 1 wieder an, und zwar mit einer Dreieckswellenform - der Ton wird nun gespielt. Die Arbeit des NMIs ist getan, machen wir uns also daran, den Interrupt zu beenden. Ebenso altbekannt werden also das ICR wieder freigegeben, die Prozes- sorregister zurückgeholt und mittels RTI der NMI beendet. So. Nun wissen Sie also alles wissenwer- te über NMIs. Bis auf einige kleine Aus- nahmen, können Sie diese Interruptart genauso behandeln, wie einen IRQ. Da die CIAs ja baugleich sind, fällt die CIA- gesteuterte Programmierung von NMIs ja ebenso aus, wie beim IRQ. Ein ebenfalls ganz interessantes Anwen- dungsgebiet von NMIs ist die Steuerung von gewiseen Funktionen über einen Druck auf die RESTORE-Taste. Ich habe dies einmal bei einem Apfelmännchenprogramm benutzt. Diese Programme berechnen ja bekanntermaßen Grafiken aus der Mandel- brotmenge, die zwar ganz ansehlich sind, deren Berechnung jedoch oft Stunden, wenn nicht sogar Tage dauern kann. Ich wollte nun eben jenes Programm beschleu- nigen, indem ich den Bildschirm abschal- te. Wie Sie vielleicht wissen, kann durch diese Maßnahme eine Geschwingig- keitssteigerung von 5% erzielt werden, da der VIC bei abgeschaltetem Bildschirm micht mehr auf den Speicher des Compu- ters zugreifen muß, um die Daten für Grafiken, Zeichen, Sprites und ähnliches zu holen. Dadurch stört er den Prozessor nicht mehr beim Zugriff, wodurch dieser schneller arbeiten kann. Das Problem war nun jedoch, daß ich weiterhin sehen wollte, wie weit der Rechner nun mit der Grafikberechnung fortgefahren ist. Mit einer einfachen NMI-Routine war dies möglich. Ohne noch zeitraubend die Ta- statur abzufragen, habe ich einfach ei- nen neuen NMI "eingekoppelt", der nichts anderes tut, als den Bildschirm aus-, bzw. einzuschalten, wenn man die RESTO- RE-Taste drückt. Dieser Trick läßt sich vielfältig anwenden und ist einfach zu programmieren, hier das kleine Programm:
----------------- Initialisierung MAIN LDA #$7F CIA2-NMIs... STA CIA2+13 sperren. LDX #<(NMI) NMI-RAM-Vektor LDY #>(NMI) ...auf eigene STX $0318 ...NMI-Routine STY $0319 ...verbiegen. RTS Tschüß! ----------------- NMI-Routine NMI PHA Akku retten. LDA $D011 Register laden, EOR #16 Bildschirmbit inver- tieren. STA $D011 Und wieder speichern. PLA Akku zurückholen. RTI NMI-Ende. -----------------
Das ist tatsächlich alles! Das Programm ist im "Hypra-Ass"-Quellcode angegeben, dessen Sonderfunktionen ich Ihnen letzen Monat ja schon erklärte. Hier eine Doku- mentation: Im ersten Teil wird zunächst einmal die CIA2 als Interruptquelle gesperrt. Dies ist nicht unbedingt notwendig, da sie sowieso ausgeschaltet sein sollte, je- doch habe ich es hier zur Sicherheit einmal gemacht. Desweiteren wird der NMI-Vektor auf unseren eigenen NMI ver- bogen, und die Initialisierung ist been- det. Nun zum zweiten Teil: Zunächst einmal rette ich hier nur den Akku. X- und Y- Register werden in der NMI-Routine so- wieso nicht benutzt, weshalb wir sie nicht unbedingt auch noch retten müssen. Als nächstes laden wir den Inhalt von Register 17 des VIC ($D011=dez.53265) in den Akku, da mit dem 4. Bit dieses Regi- sters der Bildschirm ein- und ausge- schaltet wird. Der Inhalt dieses Regi- sters wird nun einfach mit dem Wert des 4. Bits geEORt. Dabei wird der Wert des Bits immer invertiert. Ist es 1 (=Bild- schirm an), so wird es nach dem EOR- Befehl 0 (=Bildschirm aus) sein und um- gekehrt. Der neue Wert muß nun nur noch wieder in $D011 zurückwandern, und wir können den NMI beenden. Das Programm finden Sie übrigens auch auf dieser MD unter dem Namen "NMI- SCREENOFF". Es muß absolut (",8,1") ge- laden werden und wird mit SYS 49152 ge- startet. Ab dann können Sie per Tasten- druck auf RESTORE den Bildschirm nach Belieben ein- und ausschalten. Das war es dann man wieder für diesen Monat. Ich wünsche Ihnen noch viel Spaß beim herumexperimentieren mit den NMIs und seien Sie nicht enttäuscht, wenns mal nicht auf Anhieb klappen sollte, denn: Wer noch nie einen Rechnerabsturz erlebt hat, ist kein wahrer Programmie- rer! In diesem Sinne bis nächsten Monat,
Ihr Uli Basters (ub).