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 Pro- zessorregister 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 verz- weigt deshalb also auf das Label "L1". Dort wird jetzt geprüft, welcher Ausga- bemodus 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 ge- blinkt 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. So- mit 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 Job- routine, 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 ab- gearbeitet wird, und der den Interrupt wieder beendet, indem er die alten Pro- zessorregister zurückholt und den Pro- zessor 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 Labelna- men gemacht. Bei dem folgenden IRQ, zählt unsere Rou- tine 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 Sy- stem-IRQ springt. Dies geht nun 30 In- terrupts lang so weiter, erst dann gibt es wieder eine 0 im COUNTER. Unsere Rou- tine verzweigt jetzt wieder in die Aus- gaberoutine. Dort wird wieder der Ausga- bemodus 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 wie- derholt, bis PULSE die 0 unterschreitet. Dann nämlich wird nicht auf PRP ver- zweigt, sondern es werden gleich die Befehle hinter dem BPL-Befehl abgearbei- tet. Sie setzen den IRQ-Vektor wieder auf den System-IRQ zurück, so daß also unsere eigene Routine nicht mehr ange- sprungen 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 (letz- ten Monat hatte ich das ja genauer erklärt). Auch jetzt verzweigen wir wieder auf den System-IRQ um unseren Interrupt zu been- den. Das wäre nun also eine Routine, die in den System-IRQ eingebunden ist. Das Be- triebssystem springt sie direkt an, und sie selbst fährt nach ihrer eigenen Ar- beit gleich mit dem System-IRQ fort. So daß dieser also auch weiterhin arbeitet. Der Vorteil ist schnell ersichtlich. Laden Sie doch einfach einmal das Pro- gramm "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 Bild- schirm, den wir auch weiterhin benutzen können. Würden wir unsere IRQ-Routine nicht über den System-IRQ wieder verlas- sen, wäre das auch nicht der Fall. Da- durch können Sie also während Ihren ei- genen IRQs die Tastatur weiterhin ver- wenden! Kommen wir nun zu einem weiteren Pro- blem. Angenommen, Sie wollten eine Mit- teilung ausgeben, wärend zum Beispiel gerade eine Routine damit beschäftigt ist, im RAM unter dem ROM Daten zu ver- schieben. In dem Fall können Sie ja nicht mehr über den System-IRQ springen, da das Betriebssystem-ROM ja abgeschal- tet wäre. Man kann dies tun, indem man einige Bits im Prozessorport verändert. Dieser wird durch die Speicherzelle $0001 repräsentiert. Dort steht norma- lerweise der Wert 55 (=$37), was für den Prozessor die Speicherkonfiguration: * BASIC-ROM bei $A000-$BFFF eingeschal- tet. * 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 BASIC- und dem Betriebssystem-ROM zu- greifen, 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 wie- der 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 aus- kommt. Der Source-Code hierzu ist eben- falls auf dieser MD zu finden, unter dem Namen "MSGOUT-RAM.SRC". Im Prinzip brau- chen wir nur ein paar Befehle zu der ROM-Version von MSGOUT hinzuzufügen, um die RAM-Version zu erhalten. Das wich- tigste ist hierbei die Initialisierungs- routine, die ich Ihnen hier nun auffüh- ren 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 ver- schwindet 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, son- dern 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 geschriebe- nen 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 Initialisierungs- routine dann auch noch den Prozessorport so, daß BASIC- und 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 Ab- schnitt. Wir können nämlich unsere Ini- tialisierungsroutine nicht einfach so verlassen - zumindest nicht in diesem Beispiel. Denn normalerweise, wenn Sie sich im Eingabemodus des 64ers 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 ein- fach ab. Da ich die IRQ-Routine nun aber so programmiert habe, daß sie automa- tisch, wenn sie genug geblinkt hat, BA- SIC und Betriebssystem wieder einschal- tet, können wir dies als Kennzeichen dafür nehmen, daß die Grundvoraussetzun- gen für ein Verlassen der Initialisie- rungsroutine wieder gegeben sind. Des- halb 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 Ar- beit unter dem ROM. Kommen wir nun zur Interrupt-Routine selbst. Auch sie muß leicht modifiziert werden. Auch hier will ich einen kurzen Abriß der hinzu- gefü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äm- lich darum geht, den Interrupt wieder abzuschalten, weil wir oft genug ge- blinkt 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 einge- schaltet. Ein Zurückbiegen von Vektoren entfällt, da das ROM ja nun wieder da ist, und von nun an der System-IRQ wie- der 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! Dem- nach 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 ent- fernt, und dafür eine eigene SYSIRQ- Routine geschrieben. Der Name entspricht zwar nicht mehr dem, was vorher die Be- deutung war (SYStem-IRQ), aber so ging es halt am einfachsten. Diese neue Routine tut nun nichts ande- res, 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 $EA7E. 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 Ih- nen 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 run- ter, bevor wir den Interrupt mit RTI beenden. So. Das war's dann mal wieder für diesen Monat. Noch einen Hinweis zu den Pro- grammen bezüglich dieses Kurses: * Die beiden Source-Codes der MSGOUT- Routine können Sie übrigens auch le- sen, wenn sie nicht den HYPRA-ASS be- sitzen. 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 bei- de 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 verab- schieden. 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)