Magic Disk 64

home to index to html: MD9102-KURSE-CIA-KURS_TEIL_4-1_:_DIE_GEHEIMNISSE_DES_SECRET-SERVICE_(TEIL_4).html
                CIA-Kurs:               
  "Die Geheimnisse des Secret-Service"  
                (Teil 4)                
----------------------------------------
Herzlich Willkommen zum 4. Teil  unseres
CIA-Kurses. Diesen Monat möchte ich dann
doch  vorgreifen und Ihnen zunächst ein-
mal die  NMI-Interrupts  erklären.  Dann
können  wir  nämlich anhand eines einfa-
chen Beispiels auch eine sehr  nützliche
Anwendungsweise von Timerkopplung behan-
deln.                                   
Mit dem IRQ kennen Sie sich mittlerweile
ja gut aus. Wir haben diese Interruptart
in  Zusammenhang mit dem Systeminterrupt
ja schon eingehendst  kennengelernt  und
auch schon eigene IRQ-Routinen geschrie-
ben, die sowohl eigenständig,  als  auch
im  System-IRQ  eingebunden  arbeiteten.
Kommen wir nun also auch zu der  anderen
für uns wichtigen Interruptart, dem NMI.
Zunächst:  Was  ist der Unterschied zwi-
schen einem IRQ und einem NMI? Da  haben
wir  zum  einen  schon einmal den Unter-
schied,  daß  beide  Interruptarten  von
jeweils  einem  CIA  angesteuert werden.
Das hatte ich Ihnen ja  schon  zu  einem
früheren  Zeitpunkt erläutert. CIA2 löst
also NMIs aus, CIA1 IRQs.               
Doch  es gibt da noch einen weitgehendst
wichtigeren Punkt, in dem sich  der  NMI
vom  IRQ  unterscheidet. Ich hatte Ihnen
damals bei der Erklärung  der  Hardware-
verbindungen der CIAs und des Prozessors
untereinander ja schon erklärt, daß jede
der  CIAs  nicht  nur  verschiedenartige
Interrupts auslöst, sondern daß vielmehr
der  Prozessor  über  zwei  verschiedene
Eingänge verfügt, an denen der jeweilige
Interrupt  ausgelöst  werden  kann.  Das
bedeutet aber auch, daß er einen  Unter-
schied  zwischen  beiden  Interruptarten
macht, und das ist ganz wichtig für  uns
zu wissen!                              
IRQ ist die  Abkürzung  für  "Interrupt-
ReQuest",  was soviel bedeutet, wie "An-
frage auf eine Unterbrechung". Das  Wort
"Anfrage"  möchte ich hier ganz deutlich
herausstellen, denn wie Sie mittlerweile
ja ebenfalls wissen sollten, können  wir
den  Prozessor  durch den SEI-Befehl da-
hingehend manipulieren, daß  er  Signale
am  IRQ-Eingang ignoriert. Im Fachjargon
sagt man auch, man kann einen  Interrupt
"maskieren"  -  durch  Setzen des Inter-
ruptflags können wir also  softwaremäßig
IRQs  sperren  und das ist dann auch der
Punkt, bei dem der Unterschied  zum  NMI
in  Erscheinung tritt. "NMI" ist nämlich
ebenfalls eine Abkürzung und  steht  für
"Non-Maskable-Interrupt",     was    mit
"Nicht-maskierbare-Unterbrechung"    den
Nagel  auf  den  Kopf  trifft. Und schon
hätten wir das  Kind  im  Brunnen.  NMIs
sind  softwaremäßig  nicht  sperrbar und
das kann enorme Vorteile  gegenüber  dem
IRQ haben!                              
Hier einmal ein einfaches Beispiel:  die
Routinen  des  Betriebssystems müssen in
der Regel aus  dem  einen  oder  anderen
Grund  von Zeit zu Zeit IRQs verhindern.
Zum Einen aus  Zeitersparnis  und  somit
zur  Geschwindigkeitssteigerung, zum An-
deren  bei  komplizierten   Synchronisa-
tionsvorgängen  mit  der  Peripherie des
Computers, wobei auftretende  Interrupts
Zeitwerte  verfälschen  und somit stören
könnten, benutzt das Betriebssystem  nun
ebenfalls  den SEI-Befehl. Die Folge des
Ganzen wird schnell klar: soll  der  IRQ
nun ganz zeitkritische Arbeiten erldedi-
gen, so kommt er schnell  aus  dem  Takt
und ist somit oft viel zu ungenau. Glän-
zendes Beispiel ist die  BASIC-Uhr  TI$.
Sie  wird  nämlich  über  den System-IRQ
gesteuert, der ja normalerweise  60  Mal
pro  Sekunde  auftritt. Rein theoretisch
braucht die Routine für TI$ also nur bis
60 zu zählen, um zu  wissen,  daß  jetzt
eine  Sekunde verstrichen ist. Praktisch
sieht es aber so aus, daß  zum  Beispiel
die Ein-/Ausgaberoutinen des Betriebssy-
stems oft den IRQ unterbinden. Es genügt
also, ein längeres Programm von Diskette
zu laden um die TI$-Uhr extrem zu  brem-
sen,  so  daß  sie  die eine oder andere
Sekunde nachgeht. Aus den Sekunden  wer-
den  Minuten,  je  mehr man lädt und ir-
gendwann kann man die Zeitwerte der  Uhr
komplett  vergessen:  dadurch,  daß IRQs
zwischendurch nicht mehr auftreten  kön-
nen,  aber  die Zeit weiterhin unerbitt-
lich verstreicht, zählt die  TI$-Routine
zwar  weiterhin  60  IRQs,  diese jedoch
dauerten länger als eine Sekunde.       
NMIs hingegen werden  IMMER  bearbeitet,
sobald  sie  auftreten. Sogar dann, wenn
sich der Prozessor gerade innerhalb  ei-
nes  IRQs  befindet. Er rettet dann ein-
fach die Daten des IRQs (Programmzeiger,
Prozessorstatus etc.) und bearbeitet den
NMI. Umgekehrt  jedoch,  kann  kein  IRQ
während  eines  NMIs  auftreten,  da der
Prozessor ja dann das  Interruptflag  ja
schon  von selbst gesetzt hat (sie erin-
nern sich...). Es sei  denn  wir  lassen
dies  ausdrücklich  zu, indem wir inner-
halb der NMI-Routine das Flag durch  CLI
wieder löschen.                         
Sie sehen also,  man  muß  immer  Unter-
schiede machen, wofür ein Interrupt  be-
nötigt wird.  Einfache  Probleme  lassen
sich schnell mit dem IRQ bewältigen (und
das ist bei den meisten der Fall), da er
bei Bedarf auch sehr einfach  abgeschal-
tet werden kann. Bei zeitkritischen Pro-
blemen benutzt man besser einen NMI.  Er
funktioniert  genau   und   zuverlässig,
wobei man allerdings in Kauf nehmen muß,
daß man diesen nicht so  einfach  wieder
verhindern kann.                        
Das ist nämlich der Grund warum man  bei
der Programmierung eines NMIs mehr  Auf-
wand hat. Für ihn existiert, ebenso  wie
für den IRQ, auch ein Vektor, der verän-
dert werden muß, wenn man die  NMIs  auf
eigene Interruptroutinen umleiten  will.
Wir hatten ja letzten  Monat  schon  ge-
lernt, daß man  dabei  sichergehen  muß,
daß während dieser  Veränderung  in  gar
keinem Fall ein Interrupt ausgelöst wer-
den darf, da so schon  während  das  LO-
Byte, jedoch noch nicht das HI-Byte  des
Vektors verändert ist, der  Rechner  un-
kontrolliert  in  die  Pampas   springen
könnte, was so unangenehme Folgen hätte,
wie zum Beispiel einen Rechnerabsturz.  
Aus diesem Grund müssen wir zusehen, daß
alle eventuell in Frage  kommenden  NMI-
Quellen so geschaltet sind, daß sie kei-
nen Interrupt auslösen, während wir  den
NMI-Vektor verändern.                   
Im Normalfall ist dieses Problem eigent-
lich relativ einfach zu handhaben,  denn
das Betriebssystem benutzt den Timer-NMI
ausschließlich  nur  bei   Betrieb   der
RS232-Schnittstelle, also  bei  der  se-
riellen Datenübertragung per  Modem.  In
aller Regel können wir diesen  Fall  je-
doch ausklammern und davon ausgehen, daß
alle Funktionen der CIA2,  die  den  NMI
betreffen, funktionslos ihr Dasein  fri-
sten. Nur im Falle einer eigenen  Benut-
zung sollten wir  uns  immer  im  Klaren
darüber sein, was für eine  Aufgabe  der
NMI gerade behandelt  und  wie  sie  ge-
steuert wird.  Im  Regelfall  genügt  es
jedoch,  einfach  alle  Bits  des   ICR-
Registers der CIA2 zu  löschen,  so  daß
von dort keine Interrupts  mehr  an  den
Prozessor gelangen. Dies geschieht durch
ein Schreiben des Wertes 127  (=$7F)  in
selbiges Register ($DD0D = dez. 56589). 
Eine weitere Besonderheit des NMIs  ist,
daß die RESTORE-Taste hardwaremäßig  DI-
REKT an die NMI-Leitung  des  Prozessors
angeschlossen ist,  daß  also  auch  von
dort Interrupts ausgelöst werden können.
Dieses  macht  sich  das  Betriebssystem
zunutze, denn bei einem Druck auf  RUN/-
STOP-RESTORE, was den C64 ja  wieder  in
einen einigermaßen  definierten  Zustand
zurückbringt, wird immer  ein  NMI  aus-
gelöst. Was nun allerdings wirklich  da-
bei geschieht und wie es mit  Sprungvek-
toren für NMIs aussieht, wollen wir  uns
jetzt einmal näher anschauen.           
Dazu ist wieder einmal eine kleine Reise
in die tieferen Gefilde des  Betriebssy-
stems  angesagt.  Beginnen  wir  mit den
elementaren Grundvoraussetzungen:       
* Zunächst also wird ein NMI  ausgelöst,
  indem  der  Benutzer  auf die RESTORE-
  Taste drückt.                         
* Der  Prozessor  hält  seine  momentane
  Arbeit jetzt unverzüglich  an,  rettet
  wie  bei  jedem Interrupt die wichtig-
  sten Daten auf den Stapel (das  hatten
  wir  ja  schon),  setzt das Interrupt-
  flag, so daß ihn keine IRQs mehr  stö-
  ren  können und macht sich daran, wie-
  der in einem eigenen Vektor für  NMIs,
  am  Ende seines Adressbereichs nachzu-
  sachauen,  wo  er  jetzt  weiterfahren
  soll.   Dieser    Vektor   liegt   bei
  $FFFA/$FFFB und zeigt auf eine Jobrou-
  tine  des  Betriebssystems  bei $FE43.
  Schauen wir uns einmal an, was dort so
  läuft:                                
----------------  NMI-Anspringen        
FE43 SEI          IRQs sperren.         
FE44 JMP ($0318)  Öber NMI-Vektor sprin-
                  gen.                  
----------------                        
Da hätten wir auch schon  den  angespro-
chenen NMI-Vektor, der für uns veränder-
bar ist. Er belegt  die  Speicherstellen
$0318/$0319  (dez.  792/793)  und  zeigt
normalerweise  auf  die  Adresse  gleich
hinter der soeben aufgelisteten Routine,
auf $FE47.                              
Was  übrigens  anzumerken  ist,  ist die
Tatsache, daß  wir  beim  Einbinden  von
eigenen  NMIs  in  den System-NMI darauf
achten müssen, daß wir auch die  Prozes-
sorregister  quasi  "von  Hand"  auf den
Stapel   retten   müssen.    Die    IRQ-
Vorbereitungsroutine  hatte dies ja noch
VOR dem Sprung über den  RAM-Vektor  ge-
macht,  weshalb wir uns nicht mehr darum
kümmern mußten. Beim NMI macht  das  Be-
triebssystem  das  erst  NACH dem Sprung
über den Vektor, in  der  nun  folgenden
Routine:                                
----------------  NMI vorbereiten.      
FE47 PHA          Akku auf Stapel.      
FE48 TXA          X nach Akku...        
FE49 PHA          ...und auf Stapel.    
FE4A TYA          Y nach Akku...        
FE4B PHA          ...und auf Stapel.    
FE4C LDY #$7F     Wert laden...         
FE4E STA $DD0D    ...und damit alle     
                  NMI-Quellen von der   
                  CIA2 kommend sperren. 
----------------  Auf RS232-Betrieb prü-
                  fen.                  
FE51 LDY $DD0D    Interruptquellen-     
                  Anzeige aus ICR lesen 
                  und somit löschen um  
                  weitere NMIs freizuge-
                  ben.                  
FE54 BMI $FE72    Wenn die RS232-       
                  Schnittstelle aktiv   
                  ist, verzweigen.      
----------------                        
Anmerkung: Mit dem letzten Befehl  wurde
abgefragt,  ob  eines der Interruptquel-
lenbits des ICR gesetzt ist. Da das  Be-
triebssystem ja CIA2-gesteuerte NMIs nur
dann  benutzt,  wenn  die RS232 Schnitt-
stelle läuft, genügt es, nur zu  prüfen,
ob der NMI überhaupt von der CIA2 kommt.
In  diesem Fall ist Bit 7 des ICR auf 1.
Wenn das nicht der Fall ist,  dann  kann
der Auslöser nur die RESTORE-Taste gewe-
sen sein, und es wird  wiefolgt  fortge-
fahren:                                 
----------------  Auf ROM-Modul prüfen  
FE56 JSR FD02     Prüft ob ein ROM-Modul
                  im Expansions-Port    
                  steckt.               
FE59 BNE $FE5E    Wenn nein, dann ist   
                  das Zero-Flag gelöscht
                  und wir überspringen  
                  den folgenden Befehl. 
FE5B JMP ($8002)  Ja, wir haben ein Mo- 
                  dul, also springen wir
                  auf den Modul-NMI     
                  (siehe unten).        
----------------  Prüfen, ob R-S/RESTORE
FE5E JSR $F6BC    Flag für STOP-Taste in
                  der Zeropage ($91 =   
                  dez. 145) berechnen   
                  und setzen.           
FE61 JSR $FFE1    STOP-Taste abfragen.  
FE64 BNE $FE72    Wenn nicht gedrückt   
                  verzweigen, um den NMI
                  zu beenden.           
----------------  R-S/RESTORE ausführen 
FE66 JSR $FD15    Standard-Vektoren für 
                  Interrupts und Ein-/  
                  Ausgabevektoren ini-  
                  tialisieren.          
FE69 JSR FDA3     Ein-/Ausgabebausteine 
                  initialisieren.       
FE6C JSR E518     Bildschirm löschen.   
FE6F JMP ($A002)  BASIC-Warmstart       
                  ausführen.            
----------------                        
Der  Teil  von  $FE47-$FE59  rettet  nun
zunächst einmal  die  drei  Prozessorre-
gister  auf den Stapel. Desweiteren wer-
den alle Interruptquellen die  der  CIA2
geben könnte, gesperrt.                 
Dann, von $FE51-$FE55 wird das  ICR  der
CIA2  ausgelesen  und somit für neue In-
terrupts freigeben (ist im  Moment  zwar
nicht  möglich, da wir ja die Interrupts
vorher  sperrten,  wird  aber  für   die
RS232-Behandlung gebraucht!). Gleichzei-
tig wird geprüft, ob der Befehl von  der
CIA2   kam,   und  wenn  ja  zur  RS232-
Unterroutine verzweigt.                 
Im  nun  folgenden  Teil von $FE56-$FE5D
wird geprüft, ob ein ROM-Modul im Expan-
sionsport  steckt. Wie so etwas funktio-
niert, will  ich  Ihnen  nächsten  Monat
erklären.  Wenn  ein  Modul da ist, dann
wird auf einen  moduleigenen  NMI  verz-
weigt,   andernfalls   wissen   wir  nun
endgültig, daß der Benutzer  wahrschein-
lich den Computer zurücksetzen will, und
wir können in den folgenden Teil verwei-
gen.                                    
Dieser geht von  $FE5E-$FE65  und  prüft
nach, ob die RUN/STOP-Taste gleichzeitig
auch noch gedrückt ist. Hierzu wird eine
Unterroutine ab $F6BC benutzt,  die  di-
rekt  die  Tastatur abfragt und in Spei-
cherzelle $91 der Zeropage  anzeigt,  ob
die  RUN/STOP-Taste  gedrückt ist. Steht
dort eine 0, so war dies der  Fall.  An-
dernfalls  verzweigt  das  Programm  nun
doch in die RS232-Routine.  Ehrlich  ge-
sagt,  weiß ich nicht warum dies so ist,
denn es läge näher, den  NMI  direkt  zu
beenden,   aber   die   Wege   des  C64-
Betriebssystems sind manchmal halt  auch
unergründlich...                        
Jetzt  sind  wir aber endlich im letzten
Teil   angelangt.   R/S-RESTORE    wurde
gedrückt,  was heißt, daß wir einen "Mi-
ni-Reset" ausführen  sollen.  Es  werden
nun  drei  Unterroutinen aufgerufen, die
die wichtigsten Voreinstellungen im  Sy-
stem vornehmen, nämlich das Zurücksetzen
der   Sprungvektoren   von   (inclusive)
$0314-$333 (Routine ab $FD15), das Rück-
setzen  des  Grafikchips  (VIC)  und des
Soundchips (SID), sowie der CIAs (Routi-
ne  ab  $FDA3) und das Löschen des Bild-
schirms (Routine ab $E518).  Zum  Schluß
wird  dann  mit  einem indirekten Sprung
auf  den  NMI-BASIC-Warmstart   in   den
"READY"-Modus   des   BASICs  verzweigt.
Hierbei wird der NMI  nicht  wie  üblich
mit  RTI beendet, sondern die Warmstart-
routine stetzt einfach den  Stackpointer
wieder  zurück  und  springt dann in die
BASIC-Eingabe-Warteschleife.            
Soviel zum System-NMI. Wir werden in den
nächsten  Kursteilen  auch  noch auf die
RS232-Schnittstelle und deren Bedienung,
sowie auf die ROM-Modul-Behandlung näher
eingehen, weshalb ich diese Themen dies-
mal aussparen möchte.                   
Kommen wir nun zu der Programmierung von
NMIs. Die Anwendungsbereiche  für  diese
Interruptart  sind  vielfältig. Sie läßt
sich gut  bei  zeitkritischen  Problemen
einsetzen,  wie  zum Beispiel das zykli-
sche Lesen von Daten, in einer fest vor-
geschriebenen Geschwindigkeit (Digitizer
und  Scanner  arbeiten oft mit NMIs). Am
eindrucksvollsten ist aber bestimmt  das
Abspielen  von Musik über den NMI. Viele
Sound-Editoren benutzen ja schon  häufig
die Möglichkeit,  ein Musikstück via In-
terrupt spielen zu lassen, jedoch  gehen
diese meist über den IRQ.               
Ich habe Ihnen einmal  ein  Beispielpro-
gramm  auf  dieser MD mit abgespeichert.
Es heißt "NMI/IRQ-DEMO"  und  beinhaltet
drei kleine Unterprogramme. Das Programm
tut  nichts anderes, als einen Interrupt
zu initialisieren, der ständig einen Ton
über Stimme 1 des  SID  spielt.  Hierbei
wird  bei  jedem  Aufruf das HI-Byte der
Tonfrequenz um 1  erhöht.  Das  Ergebnis
ist  ein  ganz lustiger Soundeffekt, der
nicht unähnlich dem  Geräusch  ist,  das
entsteht,  wenn Scotty die Besatzung der
"Enterprise" rumbeamt.                  
Eigentliche  Aufgabe dieses Beispielpro-
gramms ist nun aber,  Ihnen  den  Unter-
schied  zwischen IRQ und NMI zu verdeut-
lichen. Deshalb gibt es zwei Möglichkei-
ten, es aufzurufen. Zum Einen können Sie
es  mit  "SYS 4096*9" starten. Dann ini-
tialisieren Sie einen NMI. Der Ton  wird
ständig  über  den  NMI  ausgegeben. Nun
bietet sich zusätzlich noch die Möglich-
keit,  daß Sie durch "SYS 4096*9+3" eine
kleine Unterroutine aufrufen,  die  alle
IRQs sperrt. Zu erkennen ist dies daran,
daß  nach  dem  Aufruf  der Cursor nicht
mehr blinkt.  Trotzdem  aber  hören  Sie
weiterhin  den  Soundeffekt  - dies also
als Beweis, daß der NMI  unabhängig  vom
IRQ arbeitet.                           
Die zweite  Möglichkeit  den  Effekt  zu
starten  ist die mit "SYS 4096*9+6". Sie
initialisieren dann einen IRQ,  der  je-
doch  genau dasselbe tut wie der NMI zu-
vor.  Sie  können  nun  nocheinmal   mit
"SYS 4096*9+3"  die  IRQs  sperren,  und
schon hören Sie nichts mehr.            
Als  Beispiel zu den Problematiken, die-
sich mit dem IRQ und dem  Betriebssystem
ergeben, empfehle ich Ihnen, während der
IRQ läuft einmal ein Programm  von  Dis-
kette  zu  laden. Sie werden merken, daß
die Tonausgabe zwischenzeitlich desöfte-
ren  stockt. Wenn das passiert, dann hat
gerade wieder einmal  eine  Routine  des
Betriebssystems  den IRQ mittels SEI ab-
geschaltet.  Leider  können  Sie  dieses
Problem  nicht  mit  einem laufenden NMI
untersuchen. Der stört nämlich dann  die
anfangs   schon  erwähnten  Synchronisa-
tionsvorgänge, die beim  Laden  benötigt
werden,  wobei  der  64er  nur Mist  an-
stellt. Probieren können Sie es  einmal.
Manchmal hat man Glück, machmal nicht.  
Bitte laden Sie nun den zweiten Teil des
IRQ-Kurses aus dem Kurs-Menü.           
Valid HTML 4.0 Transitional Valid CSS!