Hier die Erklärung:
Nachdem wir die Initialisierungsroutine
von vorhin aufgerufen haben, zeigt der
IRQ-Vektor jetzt also auf die Routine
" IRQ" . Die CIA1 signalisiert nun einen
Timerunterlauf in Form eines Signals an
den Prozessor. Dieser springt daraufhin
auf die Jobroutine ab $ FF47, wo die Prozessorregister auf den Stapel gerettet
werden und über den IRQ-Vektor unsere
Routine angesprungen wird.
Diese erniedrigt nun also den COUNTER
und prüft, ob er schon 0 ist. Das ist
der Fall, da wir den COUNTER ja mit 1 initialisiert hatten, und er soeben auf
0 abgezählt wurde. Das Programm verzweigt deshalb also auf das Label " L1" .
Dort wird jetzt geprüft, welcher Ausgabemodus eingestellt ist. Da MSGMODE auf
1 steht gehts jetzt also gleich weiter
zu " L2", wo zunächst einmal MSGMODE auf
0 gezählt wird. Durch einen Aufruf von
" BLANK" wird die MSG-Zeile gelöscht.
Dies heißt für uns auch, daß einmal geblinkt wurde. Also müssen wir jetzt den
Zähler für die Anzahl der Blinks um 1 erniedrigen. Gehen wir einmal davon aus, daß wir die Initialisierungsroutine mit
einer 10 im Akku aufgerufen hatten. Somit ist der Inhalt von PULSE jetzt, nach
dem Herunterzählen 9 . Das heißt, daß die
0 noch nicht unterschritten wurde und
deshalb wird beim folgenden Branch-Befehl auch gleich auf das Label PRP
verzweigt. Dort steht eine kleine Jobroutine, die unseren IRQ wieder beendet.
Der COUNTER wird hier mit 30 neu geladen
und das Programm verzweigt anschließend
auf den System-IRQ, der nun regulär abgearbeitet wird, und der den Interrupt
wieder beendet, indem er die alten Prozessorregister zurückholt und den Prozessor mittels RTI wieder in das alte
Programm, das bearbeitet wurde, als der
Interrupt auftrat, zurückschickt.
Das Label SYSIRQ beinhaltet also die
Sprungadresse des System-IRQs, wie Sie anhand des Source-Codes erkennen können.
Ich habe dort nämlich wieder mittels
" . EQ" eine Zuweisung an diesen Labelnamen gemacht.
Bei dem folgenden IRQ, zählt unsere Routine wieder den COUNTER um 1 herunter.
Diesmal jedoch, ist diese Speicherzelle
noch nicht 0, weshalb die Routine auch
nicht in die Ausgaberoutine ab " L1" verzweigt, sondern gleich auf den System- IRQ springt. Dies geht nun 30 Interrupts lang so weiter, erst dann gibt
es wieder eine 0 im COUNTER. Unsere Routine verzweigt jetzt wieder in die Ausgaberoutine. Dort wird wieder der Ausgabemodus geprüft, der diesmal jedoch 0, also " Text ausgeben" ist. Dort müssen
wir jetzt MSGMODE dann auf 1 hochzählen
und dann mittels DOMSG unsere Mitteilung
auf dem Bildschirm ausgeben. Anschließend können wir den Interrupt wieder
über den System-IRQ verlassen.
Diese Vorgänge werden nun solange wiederholt, bis PULSE die 0 unterschreitet.
Dann nämlich wird nicht auf PRP verzweigt, sondern es werden gleich die
Befehle hinter dem BPL-Befehl abgearbeitet. Sie setzen den IRQ-Vektor wieder
auf den System-IRQ zurück, so daß also
unsere eigene Routine nicht mehr angesprungen wird. Ihre Aufgabe ist nun
erfüllt.
Diesmal brauchen wir das Interrupt-Flag
übrigens nicht zu setzen, da innnerhalb
eines Interrupts dieses Flag ja schon
durch den Prozessor gesetzt wurde ( letzten Monat hatte ich das ja genauer
erklärt) .
Auch jetzt verzweigen wir wieder auf den
System-IRQ um unseren Interrupt zu beenden.
Das wäre nun also eine Routine, die in
den System-IRQ eingebunden ist. Das Betriebssystem springt sie direkt an, und
sie selbst fährt nach ihrer eigenen Arbeit gleich mit dem System-IRQ fort. So
daß dieser also auch weiterhin arbeitet.
Der Vorteil ist schnell ersichtlich.
Laden Sie doch einfach einmal das Programm " MSGOUT. CODE" auf der Vorderseite
dieser MD. Es ist ein Assembler-Programm, das mit " SYS 4096*8", oder
" SYS 32768" aufgerufen wird. Der MSG-Text " DAS IST EIN IRQ UEBERS BETRIEBS-SYSTEM!" blinkt nun in der untersten
Bildschirmzeile. Währenddessen haben wir
aber immer noch den Cursor auf dem Bildschirm, den wir auch weiterhin benutzen
können. Würden wir unsere IRQ-Routine
nicht über den System-IRQ wieder verlassen, wäre das auch nicht der Fall. Dadurch können Sie also während Ihren eigenen IRQs die Tastatur weiterhin verwenden!
Kommen wir nun zu einem weiteren Problem. Angenommen, Sie wollten eine Mitteilung ausgeben, wärend zum Beispiel
gerade eine Routine damit beschäftigt
ist, im RAM unter dem ROM Daten zu verschieben. In dem Fall können Sie ja nicht mehr über den System-IRQ springen, da das Betriebssystem-ROM ja abgeschaltet wäre. Man kann dies tun, indem man
einige Bits im Prozessorport verändert.
Dieser wird durch die Speicherzelle
$0001 repräsentiert. Dort steht normalerweise der Wert 55(=$37), was für den
Prozessor die Speicherkonfiguration:
* BASIC-ROM bei $ A000-$ BFFF eingeschaltet.
* I/ O-Bereich bei $ D000-$ DFFF ( wo auch
die Register der beiden CIAs liegen) eingeschaltet.
* Betriebssystem-ROM bei $ E000-$ FFFF
eingeschaltet.
Wollen wir nun auf das RAM unter dem
BASICund dem Betriebssystem-ROM zugreifen, so kann man letztere mit dem
Schreiben des Wertes 53(=$35) in den
Prozessorport abschalten. Das I/ O-ROM, das wir ja noch brauchen ( wegen der
CIA1), bleibt dabei eingeschaltet.
Der System-IRQ ist somit nicht mehr für uns vorhanden und ebenso auch nicht die
Jobroutine, die über den IRQ-Vektor
$0314/$0315 auf entsprechende IRQ-Routinen springt.
In dem Fall müssen wir die Steuerung des
Interrupts selbst bewältigen. Das heißt
zunächst einmal, daß wir diesmal die
Prozessorregister selbst retten müssen
( was ja normalerweise die Jobroutine bei
$ FF47 macht - siehe Teil 2 des CIA-Kurses), und sie auch entsprechend wieder zurückholen müssen. Als IRQ-Vektor
zählt jetzt auch nicht mehr der bei
$0314/$0315, sondern wir benutzen den
Hardware-Vektor direkt. Da das ROM dort
ja abgeschaltet ist, können wir also
problemlos die Speicherzellen
$ FFFE/$ FFFF mit einem Vektor auf unsere
IRQ-Routine beschreiben.
Zur Demonstration habe ich Ihnen unsere
MSGOUT-Routine einmal umgeschrieben, so
daß sie auch ohne Betriebssystem auskommt. Der Source-Code hierzu ist ebenfalls auf dieser MD zu finden, unter dem Namen " MSGOUT-RAM. SRC" . Im Prinzip brauchen wir nur ein paar Befehle zu der
ROM-Version von MSGOUT hinzuzufügen, um
die RAM-Version zu erhalten. Das wichtigste ist hierbei die Initialisierungsroutine, die ich Ihnen hier nun aufführen möchte:
------------ SEI Interrupts wie immer speren.
STA PULSE Blinkzähler merken.
STX LOOP1+1 Anfangsadresse des. . .
STY LOOP1+2 . . . Textes merken.
------------
LDX #<( IRQ) Anfangsadresse der neuen. . .
LDY #>( IRQ) . . . IRQ-Routine laden.
STX $ FFFE Und den Hardware-Vektor. . .
STY $ FFFF . . . darauf ausrichten.
------------
LDA #$35 Wert für " ROM aus" laden. . .
STA $01 . . . und ab in Prozessorport.
------------
LDA #01 Initialisierungswert laden.
STA COUNTER Zähler initialisieren.
STA MSGMODE Modus initialisieren.
CLI IRQs wieder freigeben.
------------
LOOP3 :
LDA $01 Prozessorport laden. CMP #$37 Vergleiche mit "ROM an". BNE LOOP3 Ungleich, also weiter prü- fen. RTS Ansonsten Tschüß! ------------
Viel hat sich hier ja nicht geändert.
Den ersten Abschnitt kennen wir ja noch
von der alten MSGOUT-Routine. Diesmal
müssen wir jedoch noch aus einem zweiten
Grund die Interrupts sperren. Indem wir
nämlich später noch das Betriebssystem-ROM abschalten, nehmen wir dem Prozessor
die Grundlage für IRQs. Zum Einen verschwindet somit nämlich der Hardware-Vektor des Betriebssystems, zum Anderen
auch alle Jobroutinen für den System-IRQ. Der Prozessor springt dann irgendwo
im undefinierten RAM rum und hängt sich dann unweigerlich auf. Also jetzt geht
nix mehr ab mit IRQs!
Der zweite Abschnitt ist uns auch nicht
so unbekannt. Diesmal setzen wir jedoch
nicht den IRQ-Vektor $0314/$0315, sondern den Hardware-Vektor für IRQs bei
$ FFFE/$ FFFF. Das können wir getrost auch
bei eingeschaltetem ROM tun ( wie das
hier der Fall ist), denn die geschriebenen Daten landen auf jedem Fall im RAM, da der Prozessor ins ROM ja nicht
schreiben kann. Weil er aber irgendwo
hin muß mit seinen Daten, schickt er sie
automatisch ins RAM. Nur der Lesezugriff
kommt aus dem ROM!
Um auch dies zu ändern, verändern wir im
dritten Abschnitt der Initialisierungsroutine dann auch noch den Prozessorport
so, daß BASICund Betriebssystem-ROM
abgeschaltet werden.
Im vierten Abschnitt werden jetzt noch
die variablen Register unserer IRQ-Routine initialisiert. Hier hat sich
nichts geändert.
Wichtig ist nun noch der letzte Abschnitt. Wir können nämlich unsere Initialisierungsroutine nicht einfach so
verlassen - zumindest nicht in diesem
Beispiel. Denn normalerweise, wenn Sie
sich im Eingabemodus des 64 ers befinden, wird eine Eingabeschleife des BASICs
durchlaufen, die ständig auf Eingaben
prüft und dann bei entsprechenden BA-SIC- Befehlen, diese aufruft. Wenn Sie
also mit SYS unsere IRQ-Routine starten, dann wird die Initialisierngsroutine
nach ihrer Arbeit wieder in die BASIC-Eingabeschleife zurückkehren wollen. Die
ist jetzt jedoch nicht mehr verfügbar, weil wir ja das BASIC-ROM abgeschaltet
haben. Auch hier springt der Prozessor
dann mitten ins leere RAM, verläuft sich
dort und stürzt vor lauter Kummer einfach ab. Da ich die IRQ-Routine nun aber
so programmiert habe, daß sie automatisch, wenn sie genug geblinkt hat, BA-SIC und Betriebssystem wieder einschaltet, können wir dies als Kennzeichen dafür nehmen, daß die Grundvoraussetzungen für ein Verlassen der Initialisierungsroutine wieder gegeben sind. Deshalb also, habe ich eine Warteschleife
hier eingebaut, die immer nur prüft, ob
die ROMs mittlerweile wieder da sind.
Erst wenn dieser Fall eintritt, wird
zurückgesprungen!
Soviel zur Initialisierung für eine Arbeit unter dem ROM. Kommen wir nun zur
Interrupt-Routine selbst. Auch sie muß
leicht modifiziert werden. Auch hier
will ich einen kurzen Abriß der hinzugefügten Befehle geben:
------------ IRQ PHA Akku retten. TXA X-Reg. in Akku schieben... PHA ...und retten. TYA Y-Reg. in Akku schieben... PHA ...und retten. ------------ (etc...)
So fängt nun die neue IRQ-Routine an.
Anschließend folgen genau die Befehle, die auch in MSGOUT-ROM verwedet wurden.
Bis auf einen Unterschied: wenn es nämlich darum geht, den Interrupt wieder
abzuschalten, weil wir oft genug geblinkt haben, lautet die Abschaltroutine
folgendermaßen:
------------ LDA #$37 Alte Speicherkonfiguration STA $01 wieder einschalten.
JMP SYSIRQ Und IRQ beenden,------------
Hier wird einfach das ROM wieder eingeschaltet. Ein Zurückbiegen von Vektoren
entfällt, da das ROM ja nun wieder da
ist, und von nun an der System-IRQ wieder treu seine Dienste leistet, so, als
wäre nichts geschehen.
Nach dieser Änderung des Prozessorports
ist auch die Bedingung der Warteschleife der Initialisierungsroutine erfüllt, womit diese sogleich wieder zum guten
alten BASIC zurückspringt.
Eins muß ich jedoch noch hinzufügen. Wie
sie ja noch wissen, verzweigt die ganze
Routine ja noch öfter auf den System-IRQ, der dann ja gar nicht da ist! Demnach hätte ich diese Verzweigungen, die
ich vorhin so leichtfertig übersprungen
habe, ja erwähnen müssen!
Nun, ich habe dieses Problem anders
gelöst. Ich habe nämlich den " . EQ"- Pseudo-Opcode von " HYPRA-ASS", mit dem
ich dem Label " SYSIRQ" die Adresse
"$ EA31" zuwies aus dem Source-Code entfernt, und dafür eine eigene SYSIRQ-Routine geschrieben. Der Name entspricht
zwar nicht mehr dem, was vorher die Bedeutung war ( SYStem-IRQ), aber so ging
es halt am einfachsten.
Diese neue Routine tut nun nichts anderes, als den Interrupt ordnungsgemäß zu beenden. Wie wir ja noch aus dem letzten
CIA-Kurs wissen, tut dies der System-IRQ
am Ende auch. Die entsprechenden Befehle
hierzu stehen ab Adresse $ EA7 E. Genau
die habe ich nun in die neue " IRQ-Beenden"- Routine übernommen:
----------------- SYSIRQ LDA $DC0D ICR von CIA1 löschen. PLA Altes Y-Reg. vom Sta- pel in Akku holen... TAY ...und zurück in Y- Reg. schieben. PLA Altes X-Reg. vom Sta- pel in Akku holen... TAX ...und zurück in X- Reg. schieben. PLA Alten Akkuinhalt vom Stapel holen. RTI Und Interrupt beenden. -----------------
Die Bedeutung dieser Befehle sollte Ihnen ja noch bekannt sein. Zunächst müs- sen wir weitere IRQs durch Löschen des
ICR-Registers der CIA1 wider ermöglichen
( dadurch werden ja die Interrupt-Quellen- Flags gelöscht, wie wir aus Teil
1 dieses Kurses noch wissen) . Dann holen
wir uns in umgekehrter Reihenfolge die
Prozessorregister wieder vom Stapel runter, bevor wir den Interrupt mit RTI
beenden.
So. Das war' s dann mal wieder für diesen
Monat. Noch einen Hinweis zu den Programmen bezüglich dieses Kurses:
* Die beiden Source-Codes der MSGOUT-Routine können Sie übrigens auch lesen, wenn sie nicht den HYPRA-ASS besitzen. Laden Sie hierzu ein Source-Code- File einfach an den BASIC-Anfang
( also mit ",8" am Ende) und geben Sie
LIST ein. Jetzt wird der Text zwar
nicht automatisch formatiert, so wie
HYPRA-ASS das normalerweise tut, aber
lesen kann man das ganze schon. Zur
Anschauung genügt es zumindest.
* Das File " MSGOUT-CODE" beinhaltet beide Versionen von MSGOUT. Laden Sie es
bitte absolut ( also mit ",8,1") und
starten Sie die einzelnen Routinen
mit:
- SYS 32768 für MSGOUT-ROM - SYS 32777 für MSGOUT-RAM
Ich will mich jetzt von Ihnen verabschieden. Nächsten Monat wollen wir uns
dann einmal um die Kupplung von Timer A
und Timer B einer CIA kümmern und auch
noch den BRK-Interrupt behandeln. Bis
dahin noch viel Spaß beim Herumprobieren
mit IRQs.
( ub)