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).