Magic Disk 64

home to index to html: MD9311-KURSE-IRQ-KURS_1.1.html
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 1)                
----------------------------------------
Wer kennt sie nicht, die tollen Effekte,
die die großen Alchimisten der  Program-
mierkunst aus unserem guten alten "Brot-
kasten" herauszaubern: mehr als 8  Spri-
tes  gleichzeitig  auf  dem  Bildschirm,
Side-  und  Topborderroutinen,  die  die
Grenzen  des Bildschirmrahmens sprengen,
sich wellenförmig bewegende Logos,  Gra-
fikseiten,  die  sich in atemberaubender
Geschwindigkeit  zusammenfalten,   sowie
schillernd bunte 256-farbige Bilder. Man
könnte diese Liste noch bis ins Unendli-
che weiterführen und sie  würde  dennoch
die  vollständig  sein. In der Tat - was
manche Programmierer aus den Silizuimmo-
lekülen  der Hardware des C64 herauskit-
zeln hat schon so manch Einen zum  Stau-
nen  gebracht. Hätte Commodore zur Mark-
teinführung des C64 schon  gewusst,  was
für eine Wahnsinnsmaschine sie da gebaut
hatten, hätten sich die Entwickler wahr-
scheinlich selbst an den  Kopf  gefasst.
All  diese  Effekte sind nämlich absolut
unbeabsichtigt  unserer  "Dampfmaschine"
der  Computerwelt  eingepflanzt  worden,
sondern verdanken ihre  Existenz  einzig
und  allein  der  unermüdlichen Ausdauer
der Freaks und Coder, die in  nächtelan-
ger Herumtüftelei die Software dazu ent-
wickelten, die den  C64  weitaus  länger
leben ließ, als man je geglaubt hätte.  
"Rasterinterrupt"  heißt  das Zauberwort
das, einem "Sesam  öffne  Dich"  gleich,
dem  Programmierer das Tor zur wunderba-
ren Welt der Computer-Effekte  aufstößt.
Und  um genau diese Effekte soll sich in
diesem Kurs alles drehen.  Die  nächsten
Monate sollen Sie hier erfahren, wie man
sie  programmiert, und wir werden versu-
chen Ihnen das  Grundwissen  zu  vermit-
teln, neue Routinen selbst entwickeln zu
können.                                 
Im  ersten Teil dieses Kurses wollen wir
uns nun zunächst um die Grundlagen  küm-
mern, und den Fragen "Was ist ein Inter-
rupt?", "Wo kommt er her?" und "Was pas-
siert während eines Interrupts?" auf den
Grund  gehen. Leider müssen wir Sie auch
darauf hinweisen, daß zur Programmierung
von Interrupts  die  gute  Kenntnis  der
Maschinensprache sowie des Befehlssatzes
des  6510-Prozessors  (so wie er im 64er
seinen Dienst tut)  Grundbedingung  ist.
Desweiteren   sollten  Sie  einen  guten
Speichermonitor, bzw.  Disassembler  zur
Hand  haben, da wir der Einfachheit hal-
ber alle  Programmbeispiele  als  direkt
ausführbaren  Code der Magic Disk beifü-
gen werden. Ein gutes Hilfsmittel dieser
Art ist z.B. der, mittlerweise sehr weit
verbreitete, "SMON".                    
1) WAS SIND INTERRUPTS?                 
Das  Wort  "Interrupt" kommt aus dem En-
glischen und  bedeutet  "Unterbrechung".
Und  nichts  anderes  tut nun ein Inter-
rupt: er unterbricht den  Prozessor  bei
seiner  momentanen  Arbeit.  Das  tut er
natürlich  nicht  einfach  aus  heiterem
Himmel.  Vielmehr  hat der Programmierer
die  Möglichkeit  bestimmte  Bedingungen
anzugeben,  die einen Interrupt auslösen
sollen. Auf diese Weise  kann  man  ganz
genau  den  Zeitpunkt  bestimmen, zu dem
ein Interrupt auftreten soll.  Nun  wird
der  Prozessor  jedoch nicht nur einfach
angehalten, wenn er eine Interruptanfor-
derung  bekommt.  Da  das  Ganze ja auch
einen Nutzen haben soll, kann  natürlich
ein  ganz  bestimmtes  Programm  von ihm
abgearbeitet werden, das auf das  Inter-
ruptereignis reagieren soll.            
Bevor wir  uns  jedoch  in  diese  Dinge
stürzen,  wollen wir erst einmal klären,
welche Interrupts der 6510 versteht.  Er
kennt  insgesamt  vier Unterbrechungsty-
pen, die von ihm jeweils unterschiedlich
behandelt  werden.  Die   Unterscheidung
wird  ihm schon durch seinen hardwaremä-
ßigen Aufbau ermöglicht. Er verfügt näm-
lich  über  drei  Eingangsleitungen, die
die entsprechenden Interrupts bezeichnen
(der vierte Interrupt ist  ein  besonde-
rer,  den  wir  weiter  unten besprechen
werden). Die Chips um den Prozessor  he-
rum   sind  nun  mit  diesen  Interrupt-
Leitungen verbunden, und können  ihm  so
das Eintreten eines Unterbrechungsereig-
nisses mitteilen. Sie  sehen  also,  daß
Interrupts  hardwaremäßig ausgelöst wer-
den. Um nun eine Unterbrechung zu  einem
bestimmten Zeitpunkt auftreten lassen zu
können, sollte man sich ebenfalls in der
Programmierung  der  Hardware auskennen.
Und diese  wollen  wir  in  diesem  Kurs
natürlich auch ansprechen.              
Kommen wir nun jedoch zu den vier Inter-
rupttypen. Der Erste von ihnen ist  wohl
Einer  der  einfachsten. Es handelt sich
um den "RESET", der von keinem  externen
Chip, sondern vielmehr von einem "exter-
nen" Menschen ausgelöst wird.  Er  dient
dazu,  den Computer wieder in einen Nor-
malzustand zu versetzen. Da der  Prozes-
sor  selbst  nicht  merkt,  wann er Mist
gebaut hat und irgendwo hängt,  muß  ihm
der  Benutzer  das  durch  Drücken eines
RESET-Tasters   mitteilen.    Hieraufhin
springt  er dann automatisch in ein spe-
zielles Programm  im  Betriebssystem-ROM
und   stellt  den  Einschaltzustand  des
Rechners wieder  her.  Ein  Reset-Taster
ist  demnach  also direkt mit dem Reset-
Interrupteingang des Prozessors  verbun-
den.                                    
Der  zweite Interrupttyp heißt "NMI" und
kann ausschließlich von  CIA-B  des  C64
ausgelöst  werden. Dies ist ein speziel-
ler Chip, der die Kommunikation zwischen
externen Geräten  und  6510  ermöglicht.
Desweiteren  ist die 'RESTORE'-Taste di-
rekt mit dem NMI-Eingang des  Prozessors
verbunden. Drückt man sie, so wird eben-
falls  ein  NMI  ausgelöst.  Auch   hier
springt  der  6510  automatisch  in eine
spezielle Routine des ROMs und führt ein
NMI-Programm aus. Es prüft,  ob  zusätz-
lich   auch  noch  die  "RUN/STOP"-Taste
gedrückt wurde und verzweigt bei positi-
ver  Abfrage in die Warmstartroutine des
BASIC-Interpreters.                     
Der dritte Interrupt  ist  der  wohl  am
häufigsten  benutzte. Er heißt "IRQ" und
wird von CIA-A, dem Zwilling von  CIA-B,
sowie  dem  Videochip  (VIC)  ausgelöst.
Gerade weil  der  VIC  diesen  Interrupt
bedient, ist dies derjenige, mit dem wir
in  diesem Kurs am meisten arbeiten wer-
den.                                    
Der  vierte  und  letzte  Interrupt, ist
derjenige unter den  vieren,  der  keine
Leitung  zum  Prozessor  hat.  Das liegt
daran, daß er ein softwaremäßiger Inter-
rupt  ist,  mit  dem  sich der Prozessor
quasi selbst unterbricht. Er  wird  aus-
gelöst,   wenn   der   6510  die  "BRK"-
Anweisung ausführen soll. Demnach  heißt
er  auch  "BRK"-Interrupt.  Er  ist eine
sehr einfache Unterbrechung, die eigent-
lich  sehr selten verwendet wird, da sie
ja viel bequemer durch  ein  "JMP"  oder
"JSR"  ersetzt werden kann. Dennoch kann
er von Nutzen sein, z.B. wenn man  einen
Debugger  programmiert. Ebenso kann über
den BRK z.B.  in  einen  Speichermonitor
verzweigt  werden,  der  so die Register
oder einen bestimmten Speicherbereich zu
einem bestimmten Punkt im  Programm  an-
zeigen  kann.  Er unterscheidet sich von
den anderen Interrupts lediglich  darin,
daß er softwaremäßig ausgelöst wird.    
2) IRQ UND NMI - EIN UNGLEICHES PAAR    
Diese beiden Interrupttypen sind für uns
die Interresantesten, da mit  Ihnen  Un-
terbrechungsereignisse  der Hardware ab-
gefangen werden,  die  wir  ja  exzessiv
programmieren  werden.  Außer,  daß  sie
beide verschiende Quellen haben,  unter-
scheiden  Sie  sich  auch  noch in einem
weiteren Punkt: während  der  NMI  IMMER
ausgelöst  wird, wenn ein Unterbrechung-
sereignis eintritt, kann der  IRQ  "mas-
kiert", bzw. "abgeschaltet" werden. Dies
geschieht   über   den   Assemblerbefehl
"SEI", mit  dem  das  Interruptflag  des
Statusregisters gesetzt wird. Ist dieses
Flag  nun gesetzt, und es tritt ein IRQ-
Interruptereignis ein, so ignoriert  der
Prozessor schlichtweg das Auftreten die-
ser Unterbrechung. Dies tut er  solange,
bis  er  durch  den Asseblerbefehl "CLI"
die Instruktion erhält,  das  Interrupt-
Flag  wieder  zu löschen. Erst dann rea-
giert er wieder auf  eintretende  Unter-
brechungen.  Dies  ist ein wichtiger Um-
stand, den wir auch später  bei  unseren
IRQ-Routinen beachten müssen.           
3) INTERRUPTVEKTOREN - WEGWEISER FÖR DEN
   PROZESSOR                            
Was  geschieht  nun,  wenn der 6510 eine
der  oben  genannten  Interruptanforder-
ungen  erhält?  Er  soll  also seine mo-
mentane Arbeit unterbrechen, um  in  die
Interrupt-Routine  zu springen. Damit er
Nach Beendingung des  Interrupts  wieder
normal  fortfahren kann muß er jetzt ei-
nige Schitte durchführen:               
1) Ist  der  auftretende  Interrupt  ein
   BRK,  so  wird zunächst das dazugehö-
   rige Flag im Statusregister gesetzt. 
2) Anschließend werden High- und Lowbyte
   (in  dieser  Reihenfolge)  des   Pro-
   grammzählers, der die Speicheradresse
   des nächsten, abzuarbeitenden Befehls
   beinhaltet,  auf den Stack geschoben.
   Dadurch kann der 6510 beim Zurückkeh-
   ren  aus  dem  Interrupt  wieder  die
   ursprüngliche Programmadresse  ermit-
   teln.                                
3) Nun wird das Statusregister  auf  den
   Stack  geschoben,  damit es bei Been-
   dingung des  Interrupts  wiederherge-
   stellt werden kann, so daß es densel-
   ben Inhalt hat, als bei Auftreten der
   Unterbrechung.                       
4) Zuletzt holt sich der  Prozessor  aus
   den letzten sechs Bytes seines Adres-
   sierungsbereiches  (Adressen   $FFFA-
   $FFFF) einen von drei Sprungvektoren.
   Welchen  dieser drei Vektoren er aus-
   wählt hängt von der  Art  des  Inter-
   rupts ab, der ausgeführt werden soll.
   Hierzu eine Liste mit den Vektoren:  
   Adresse      Interrupt    Sprungadr. 
   ------------------------------------ 
   $FFFA/$FFFB  NMI          $FE43      
   $FFFC/$FFFD  RESET        $FCE2      
   $FFFE/$FFFF  IRQ/BRK      $FF48      
Da  diese  Vektoren  im ROM liegen, sind
sie schon mit bestimmten Adressen vorbe-
legt,  die  die  Betriebssystem-Routinen
zur Interruptverwaltung anspringen.  Wir
wollen  nun einmal einen Blick auf diese
Routinen werfen. Da der RESET keine  für
uns  nützliche  Unterbrechung darstellt,
wollen wir ihn jetzt  und  im  Folgenden
weglassen.  Kommen  wir  zunächst zu der
Routine für die IRQ/BRK-Interrupts.  Wie
Sie  sehen haben diese beiden Typen den-
selben Sprungvektor, werden also von der
selben Service-Routine bedient. Da diese
jedoch auch einen  Unterschied  zwischen
den  beiden  machen  soll  hat sie einen
speziellen Aufbau. Hier einmal ein  ROM-
Auszug ab der Adresse $FF48:            
FF48:  PHA          ;Akku auf Stapel    
FF49:  TXA          ;X in Akku und      
FF4A:  PHA          ; auf Stapel        
FF4B:  TYA          ;Y in Akku und      
FF4C:  PHA          ; auf Stapel        
FF4D:  TSX          ;Stackpointer nach  
FF4E:  LDA $0104,X  ;Statusreg. holen   
FF51:  AND #$10     ;BRK-Flag ausmas-   
                     kieren             
FF53:  BEQ $FF58    ;Nicht gesetzt also 
                     überspringen       
FF55:  JMP ($0316)  ;Gesetzt, also Öber 
                     Vektor $0316/$0317 
                     springen           
FF58:  JMP ($0314)  ;Öber Vektor $0314/ 
                     $0315 springen     
Dieses kleine Programm  rettet  zunächst
einmal alle drei Register, indem Sie sie
einzeln  in  den  Akku holt und von dort
auf  den  Stack  schiebt  ($FF48-$FF4C).
Dies  ist notwendig, da in der Interrup-
troutine die Register je  ebenfalls  be-
nutzt werden sollen, und so die ursprün-
glichen Inhalte beim Verlassen  des  In-
terrupts wiederhergestellt werden können
(durch   umgekehrtes   zurücklesen).  Ab
Adresse $FF4D wird nun eine  Unterschei-
dung  getroffen,  ob  ein BRK- oder IRQ-
Interrupt aufgetreten ist. Ist  ersteres
nämlich der Fall, so muß das BRK-Flag im
Statusregister,  das  bei  Auftreten der
Unterbrechung  vom  Prozessor  auf   den
Stack  geschoben  wurde,  gesetzt  sein.
Durch Zugriff auf  den  Stack,  mit  dem
Stackpointer  als  Index  in X, wird nun
das Statusregister in den  Akku  geholt.
Dies  ist  übrigens die Einzige Methode,
mit der das  Statusregister  als  Ganzes
abgefragt  werden  kann.  Natürlich geht
das immer nur aus  einem  Interrupt  he-
raus. Das BRK-Flag ist nun im 4. Bit des
Statusregisters   untergebracht.   Durch
eine UND-Verknüfung mit diesem Bit  kann
es  aus  dem Statusregisterwert isoliert
werden. Ist der  Akkuinhalt  nun  gleich
null,  so war das BRK-Flag nicht gesetzt
und es muß daher ein IRQ  vorliegen.  In
dem  Fall  wird  auf  den  JMP-Befehl an
Adresse $FF58 verzweigt. Im anderen Fall
wird der  JMP-Befehl  an  Adresse  $FF55
ausgeführt.                             
Wie  Sie sehen springen diese beiden Be-
fehle über einen indirekten Vektor,  der
beim  IRQ  in  den Adressen $0314/$0315,
beim BRK in $0316/$0317 liegt. Da  diese
Vektoren  im RAM liegen, können Sie auch
von uns verändert werden. Das  ist  also
auch der Punkt, an dem wir unsere Inter-
rupts  "Einklinken"  werden.  Durch  die
Belegung dieser Vektoren mit der Adresse
unser  eigenen  Interruptroutine  können
wir den Prozessor also zu dieser Routine
umleiten.                               
Werfen wir nun noch einen Blick auf  die
NMI-Routine,  die  durch  den Vektor bei
$FFFA/$FFFB angesprungen wird.  Sie  ist
noch  einfacher  aufgebaut  und  besteht
lediglich aus zwei Befehlen:            
FE43:  SEI          ;IRQs sperren       
FE44:  JMP  ($0318) ;Öber Vektor $0318/ 
                     $0319 springen     
Hier wird lediglich der IRQ geperrt  und
anschließend  über  den Vektor in $0318/
$0319 gesprungen. Die Akku,  X-  und  Y-
Regsiter  werden NICHT gerettet. Das muß
unsere Interruptroutine selbst tun.     
Zusammenfassend kann man also sagen, daß
beim Auslösen eines  Interrupts  jeweils
über  einen  der  drei  Vektoren, die im
Bereich von $0314-$0319 stehen, gesprun-
gen  wird.  Der angesprungene Vektor ist
dabei  interruptabhängig.  Hier  nochmal
eine  Öbersicht der Interruptvektoren im
genannten Bereich:                      
Adressen      Interrupt  Zieladr.       
---------------------------------       
$0314/$0315   IRQ        $EA31          
$0316/$0317   BRK        $FE66          
$0318/$0319   NMI        $FE47          
Diese  Vektoren  werden  bei einem Reset
mit  Standardwerten  vorbelegt.  Hierbei
wird  dann die jeweilige Standardroutine
des  Betriebssystems  angesprungen,  die
den  entsprechenden Interrupt bearbeiten
soll. Möchten wir eigene Interrupts ein-
binden,  so  müssen  wir  lediglich  die
Zieladresse der Vektoren auf den  Anfang
unserer Routine verbiegen.              
4) PROGRAMMBEISPIELE                    
Um  das Prinzip des Einbindens eines ei-
genen Interrupts kennenzulernen,  wollen
wir  nun  einmal einen eigenen Interrupt
programmieren. Es handelt sich dabei  um
den  einfachsten  von  allen,  den  BRK-
Interrupt. Hier einmal  ein  Programmli-
sting, daß Sie als ausführbares Programm
auch  auf  der Rückseite dieser MD unter
dem Namen "BRK-DEMO1" finden. Sie müssen
es absolut (mit ",8,1") in den  Speicher
laden, wo es dann ab Adresse $1000 (dez.
4096)  abgelegt  wird. Gestartet wird es
mit "SYS4096". Sie können es  sich  auch
mit Hilfe eines Disassemblers gerne ein-
mal ansehen und verändern:              
;*** Hauptprogramm                      
1000:  LDX #$1C  ;BRK-Vektor auf        
1002:  LDY #$10  ; $101C verbiegen.     
1004:  STX $0316                        
1007:  STY $0317                        
100A:  BRK       ;BRK auslösen          
100B:  NOP       ;Füll-NOP              
100C:  LDA #$0E  ;Rahmenfarbe auf dez.14
100E:  STA $D020 ; zurücksetzen         
1011:  LDX #$66  ;Normalen BRK-Vektor   
1013:  LDY #$FE  ; ($FE66) wieder       
1015:  STX $0316 ; einstellen           
1018:  STY $0317                        
101B:  RTS       ;Und ENDE!             
;*** Interruptroutine                   
101C:  INC $D020 ;Rahmenfarbe hochzählen
101F:  LDA $DC01 ;Port B lesen          
1022:  CMP #$EF  ;SPACE-Taste gedrückt? 
1024:  BNE LOOP  ;Nein, also Schleife   
1026:  PLA       ;Ja, also Y-Register   
1027:  TAY                              
1028:  PLA       ;X-Register            
1029:  TAX                              
102A:  PLA       ;u.Akku v.Stapel holen 
102B:  RTI       ;Interrupt beenden.    
In den Adressen $1000-$100A  setzen  wir
zunächst  einmal  Low-  und Highbyte des
BRK-Vektors auf Adresse $101C, wo unsere
BRK-Routine beginnt. Direkt danach  wird
mit  Hilfe des BRK-Befehls ein Interrupt
ausgelöst, der, wie wir ja  mittlerweile
wissen,  nach  Retten der Prozessorregi-
ster über den von  uns  geänderten  BRK-
Vektor auf die Routine ab $101C springt.
Selbige  tut nun nichts anderes, als die
Rahmenfarbe des Bildschirms um den  Wert
1 hochzuzählen, und anschließend zu ver-
gleichen, ob die 'SPACE'-Taste  gedrückt
wurde.  Ist dies nicht der Fall, so wird
wieder  zum  Anfang  verzweigt,  so  daß
ununterbrochen  die  Rahmenfarbe  erhöht
wird, was sich durch  ein  Farbschillern
bemerkbar  macht. Wird die 'SPACE'-Taste
nun endlich gedrückt, so kommen die fol-
genden  Befehle zum Zuge. Hier holen wir
die Prozessorregister, die von  der  Be-
triebssystem-Routine  in der Reihenfolge
Akku, X, Y auf dem Stack  abgelegt  wur-
den,  wieder  umgekehrt  zurück. Das ab-
schließende "RTI" beendet den Interrupt.
Diese Answeisung veranlasst den  Prozes-
sor  dazu, den alten Programmzähler, so-
wie das Statusregister wieder vom Stapel
zu holen, und an die Stelle im Hauptpro-
gramm  zurückzuspringen,  an der der BRK
ausgelöst wurde. Dies ist logischerweise
der Befehl direkt nach dem BRK-Kommando.
So sollte man normalerweise denken,  je-
doch nimmt BRK eine Sonderstellung dies-
bezüglich ein. Bei IRQs  und  NMIs  wird
tatsächlich  der Befehl nach dem zuletzt
bearbeiten  wieder  ausgeführt,   jedoch
wird  beim BRK der Offset 2 auf den Pro-
grammzähler hinzuaddiert,  weshalb  nach
Beendigung  des Interrupts ein Byte hin-
ter den BRK-Befehl verzweigt wird.  Dem-
nach  dient der NOP-Befehl, der nach dem
BRK kommt, lediglich dem  Angleichen  an
die  tatsächliche  Rücksprungadresse. Er
wird nie ausgeführt, da der Prozessor ja
an Adresse $100C weiterfährt.  Hier  nun
setzen  wir  die  Rahmenfarbe wieder auf
das  gewohnte  Hellblau  und  geben  dem
BRK-Vektor  wieder  seine  Ursprüngliche
Adresse zurück.  Würden  wir  das  nicht
tun,  so würde beim nächsten BRK-Befehl,
der irgendwo  ausgeführt  wird,  automa-
tisch  wieder zu unserem Bildschirmflak-
kern verzweigt werden. Hier jedoch würde
der Computer unter Umständen nicht  mehr
aus  dem  Interrupt zurückkehren können,
weil wir ja nicht wissen, welche Befehle
hinter dem auslösenden BRK standen (wenn
es  nicht  der  unseres  Programmes   an
Adresse $100A war).                     
 LADEN SIE NUN DEN 2. TEIL DES KURSES ! 



Valid HTML 4.0 Transitional Valid CSS!