Der gläserne VC 20 — Teil 6

Als Abschluß unseres Kurses beschäftigen wir uns mit Interruptmechanismus und Betriebssystem des Volkscomputers. Damit haben wir in den sechs Teilen den VC 20 aus jeder Perspektive durchleuchtet und wirklich zum »gläsernen« Computer gemacht.

Ein Betriebssystem, so sagt ja bereits der Name, ist für die elementaren Funktionen des Computers wie zum Beispiel Bildschirmverwaltung, Tastaturabfrage, laden und speichern von Daten und vielem mehr verantwortlich. Natürlich ist auch im VC 20 solch ein Systemprogramm eingebaut: Es befindet sich im ROM zwischen Adresse $E429 und $FFFF, der restliche ROM-Bereich (von $C000 bis $E428) wird vom Basic-Interpreter beansprucht. Diese zwei Programmblöcke sind bei diesem Computer so miteinander verzahnt, daß kein Teil für sich alleine arbeiten kann.

Was beim Einschalten alles geschieht

Als erstes möchte ich nun die Vorgänge beschreiben, die sich beim Einschalten des Systems abspielen.

Zunächst wird die Resetleitung der CPU von der Elektronik des Computers auf Low gezogen (genau das Gleiche geschieht übrigens beim Betätigen eines eventuell eingebauten Reset-Tasters), wodurch die wichtigsten Systemkomponenten (CPU, VIC und die Ein-/ Ausgabebausteine) in einen definierten Betriebszustand versetzt werden.

Danach sucht sich die 6502-Zentraleinheit aus den Speicherstellen $FFFC und $FFFD die Startadresse des Betriebssystems heraus und beginnt die Programmausführung an der dort abgespeicherten Stelle (beim VC 20 $FD22). Diese Vektoren sind hardwaremäßig in der CPU festgelegt worden und können daher nicht durch ein Programm geändert werden.

Die Einschaltroutine hat nun die verschiedensten Aufgaben zu bewältigen. Am wichtigsten ist zunächst die Initialisierung des Prozessorstacks, also des Stapelspeichers, der die Rücksprungadressen beim Befehl JSR verwaltet (damit der Computer aus den Unterprogrammen wieder ins Hauptprogramm zurückfindet).

Der nächste Schritt in der Reset-Routine ist die Modulabfrage, in der die Modulidentifikation abgefragt wird. Findet der VC 20 im Bereich $A004 bis $A009 die Zeichenfolge »aOCBM« (die den Bytes 41 30 C3 C2 und CD entspricht), so gibt das Betriebssystem die Kontrolle an das Modulprogramm ab. Auch hierbei spielen wieder Vektoren ein Rolle, denn die Startadresse des Moduls muß in den beiden Speicherstellen $A000 und $A001 abgelegt sein.

Mit Hilfe eines solchen Modulprogramms, das automatisch gestartet wird, kann man nun den Computer ganz nach seinen Wünschen gestalten. Dies kann zum Beispiel in Form einer Basic-Erweiterung geschehen, wie sie an dieser Stelle ja schon besprochen worden ist.

Findet die Einschaltroutine — um wieder zum Reset zurückzukommen — keine Modulmarkierung, so wird die Initialisierung ganz normal fortgesetzt. Diese besteht im wesentlichen aus vier Unterprogrammen, die auch in einem Autostartprogramm nacheinander aufgerufen werden sollten. Da ist als erstes eine Routine ($FD8D), die die Aufgabe hat, in der Zeropage bestimmte Startwerte einzutragen und den verfügbaren Speicher festzustellen. Dieser RAM-Test ist für den VC 20 besonders wichtig, denn auf diese Weise kann er erstens die Funktionsweise des Speichers überprüfen und zweitens die momentane Ausbauversion feststellen. Wird tatsächlich einmal ein Defekt in einer oder mehreren Speicherstellen des Grundversions-RAMs festgestellt, dann »hängt« sich das Betriebssystem in einer Endlosschleife auf, und der Bildschirm bleibt dunkel.

Nach dieser Prüfung, die bei voll ausgebautem Speicher einige Sekunden in Anspruch nimmt, werden in einer weiteren Unterroutine ($FD52) die Kernal-Vektoren im Bereich zwischen $0314 und $0355 installiert (einige dieser Zeiger werden wir später noch genauer kennenlernen). Zum Schluß müssen natürlich auch in den Registern der Peripheriebausteine die benötigten Werte abgespeichert werden. Dafür sind die zwei letzten Unterprogramme der Einschaltroutine zuständig. Das eine ($FDF9) initialisiert die I/O-Register, damit Interrupts und Tastaturoperationen stattfinden können, und das andere ($E518) kümmert sich um die Grundeinstellung des VIC.

Dies waren im wesentlichen die Schritte, die nach dem Einschalten des Computers ablaufen; damit ist nun das Betriebssystem einsatzbereit und gibt nun die Kontrolle an den Basic-Interpreter ($E378) ab. Der wiederum setzt als erstes die in Folge 3 angesprochenen Basic-Vektoren, damit die essentiellen Operationen (wie zum Beispiel die Umwandlung einer Zeile in Interpretertoken) über diese abgewickelt werden können.

Bevor die bekannte Einschaltmeldung (CBM BASIC..) auf dem Bildschirm ausgegeben wird, initialisiert der Interpreter den Basic-Teil der Zeropage und des Speichers nach seinen Bedürfnissen. Dabei werden zum Beispiel verschiedene Zeiger (Adresse 43, 44, 45, …) gesetzt und der erste Verbindungszeiger im Programmspeicher gelöscht (daher kann ein Basic-Programm nach einem Reset nicht mehr gelistet werden).

Unterbrechung? Ja, bitte!

Nachdem die Systemmeldung auf dem Bildschirm ausgegeben wurde, verzweigt die Interpreterroutine in die sogenannte Eingabewarteschleife ($C474), die wirja bereits in Folge 3 näher beleuchtet haben. Damit ist der Computer bereit, Anweisungen des Benutzers zu empfangen.

Soweit die Beschreibung der Resetroutine, die durch ein Signal an einem Prozessoreingang gestartet wurde. Auch die Interrupts, auf die ich genauer eingehen möchte, werden über die Hardware ausgelöst. Der Plural bei Interrupts verrät bereits, daß es mehrere — genauer gesagt sogar 3 — Unterbrechungen von dieser Sorte gibt.

Da ist zunächst der Systeminterrupt, der vom VC 20 alle 60steF Sekunde durchlaufen wird (ein Timer in dem 6522-Ausgabebaustein ist für dessen Auslösung verantwortlich). Durch diese Interruptanfrage — sie wird auch IRQ (Interrupt Request) genannt — wird die Zentraleinheit veranlaßt, das gerade laufende Programm zu unterbrechen, um in eine Abarbeitungsroutine zu verzweigen. Auch hierbei besorgt sich die CPU über den Hardwarevektor ($FFFE und $FFFF) die entsprechende Startadresse ($FF72).

Dort werden als erstes alle CPU-Register auf den Stack gerettet, denn die dort gespeicherten Werte werden ja später bei der Wiederaufnahme des regulären Programmablaufs benötigt. Danach verzweigt die Routine über einen eigenen Vektor (Interrupt-Vektor $0314,$0315) in das eigentliche Abarbeitungsunterprogramm (Adresse $EABF).

Hier wird zunächst die interne Uhr (TI) um eins hochgezählt und bei dieser Gelegenheit auch gleich der Zustand der STOP-Taste abgefragt. Ist diese gedrückt, dann setzt die Unterroutine ein Flag in der Zeropage, und der Interpreter unterbricht — nachdem der Interrupt zu Ende bearbeitet worden ist — das laufende Basic-Programm. Durch »verbiegen« des erwähnten Sprungvektors kann dieses Unterprogramm in der Abarbeitungsroutine so übersprungen werden, daß keine Abfrage der STOPTaste mehr stattfindet, womit bewirkt wird, daß man ein Basic-Programm nicht mehr auf normalem Wege anhalten kann (dieses nur als eine Programmschutzvariante).

Nun aber wieder zurück zu unserer Abarbeitungsroutine. Hier wird als nächstes der sogenannte Cursorblinkzähler, der bei jedem Interrupt um eins dekrementiert wird, abgefragt. Ist dieser auf Null, so wird der momentane Zustand des Cursors geändert; das heißt aus revers wird normal oder umgekehrt (auf diese Weise wird ein Blinken des Zeigers erreicht).

Abschließend wird — und das ist das Wichtigste an dem ganzen Interrupt — die Tastatur des Computers abgefragt. Drückt der Benutzer irgendeine Taste, so wandelt die Routine diesen Tastencode in den entsprechenden ASCII-Wert und speichert das Ergebnis im Tastaturpuffer ab. Dieser bleibt solange dort, bis ein Zeichen von der Tastatur angefordert wird. Das ist entweder im Direktmodus der Fall oder bei den Basic-Befehlen INPUT und GET (oder den entsprechenden Unterprogrammen im Betriebssystem).

Damit hat die Interrupt-Routine ihren Zweck erfüllt, und der VC 20 kann die Arbeit an dem eigentlichen (Basic-) Programm wieder aufnehmen. Vorher werden allerdings noch die CPU-Register wiederhergestellt (deren Inhalte befinden sich ja immer noch auf dem Stack) und dann gibt das Interrupt-Unterprogramm über den Befehl RTI (Return From Interrupt) die Kontrolle wieder an das eigentliche Hauptprogramm ab. Diese Einschnitte mittels Interrupt haben für den Benutzer den Vorteil, daß er sich nicht um die, in einem gewissen Zeitabstand immer wiederkehrenden, Tastaturabfragen zu kümmern braucht. Außerdem kann kein noch so kurzer Tastendruck verloren gehen, wie es der Fall wäre, wenn die Tastatur in unregelmäßigen, längeren Abständen abgefragt werden würde.

Eingriff ins Eingemachte

Wie wir bereits gesehen haben, springt der Computer über einen Vektor ($0314-$0315) in die Bearbeitungsroutine ein. Dadurch können wirjetzt in den Ablauf eingreifen, indem wir dort zusätzliche Unterprogramme einbetten. Als Beispiel für solch einen Eingriff möchte ich nun eine Routine besprechen, die den regelmäßigen Interruptzyklus benützt, um die aktuelle Uhrzeit auf dem Bildschirm anzuzeigen. Dazu bedienen wir uns einer speziellen Eigenschaft des VIC, die es erlaubt, mehr als 23 Zeilen auf dem Bildschirm anzuzeigen. Mit Hilfe des Registers # 4 (Adresse 36867 beziehungsweise $9003) kann man die anzeigbare Zeilenzahl vergrößern oder verkleinern. Für unseren Zweck genügt es, die verfügbaren Zeilen auf 25 zu erhöhen. In diesen Zeilen wird dann später die Uhrzeit angezeigt werden. Mittels POKE 36867,50 wird diese Umschaltung realisiert. Meine Routine kann mit dem Lader (Listing 1) irgendwo im Speicher plaziert werden (das Programm gibt die günstigste Adresse vor). Den wichtigsten Teil der Initialisierungsroutine — also dem Unterprogramm, das den Interrupt-Vektor »verbiegt« — möchte ich kurz anhand von Listing 2 beschreiben. Dort wird gleich als erstes eine Unterroutine aufgerufen, die ein eventuell vorhandenes Basic-Programm im Speicher verschieben soll. Das hat seinen guten Grund, denn die zwei zusätzlichen Bildschirmzeilen nehmen, um ihre Zeichen ablegen zu können, den Anfang des Basic-Speichers in Beschlag, so daß ein dort stehendes Programm teilweise überschrieben werden würde. Diese Verschiebe-Routine ist so universell, daß sie auch für andere Anwendungen nützlich sein kann (um beispielsweise ein Basic-Programm zu verschieben, damit Platz für einen Grafikspeicher ist). Die Anzahl der zu verschiebenden Bytes ist, bei einer anderweitigen Verwendung, in die Additionsroutine in $210E einzusetzen.

Nachdem die 24. und 25. Zeile zugeschaltet worden ist, wird der Interruptvektor auf den Anfang unserer Uhrenroutine gestellt, damit sie beim nächsten Aufruf mit abgearbeitet wird. Bevor man diesen Zeiger jedoch verändert, muß man mittels des Befehls SEI dafür sorgen, daß zeitweise keine Interrupts mehr akzeptiert werden. Sonst könnte es passieren, daß die — zufällig gerade zu dieser Zeit ausgelöste — Interruptroutine ihren Vektor sucht, und wir sind gerade dabei, ihn zu verändern, wodurch der Computer eine falsche Adresse finden und damit abstürzen würde. Das ist auch der Grund, warum man niemals versuchen darf, Interrupt-Vektoren von Basic aus mit POKE-Befehlen zu verändern.

Der Maschinenbefehl SEI (Set Interrupt-disable Flag) setzt das entsprechende Flag im Statusregister der CPU. Wird nun ein IRQ ausgelöst, so testet die Zentraleinheit zuerst dieses Flag; ist es gesetzt, dann wird die Interruptanfrage ignoriert. Das Gegenstück dazu ist der Befehl CLI, der dieses Flag löscht, damit Interrupts wieder möglich werden.

Interrupt auf Tastendruck

Ich habe ja eingangs bereits erwähnt, daß es auch andere Interrupts gibt — diesen wollen wir uns jetzt zuwenden. Wer wird es glauben, auch die RESTORE-Taste löst einen Interrupt aus. Über den Hardwarevektor in $FFFA und $FFFB bezieht die CPU die Anfangsadresse der NMI-Abarbeitungsroutine (NMI bedeutet »nicht maskierbarer Interrupt«). Dieser Interrupt unterscheidet sich von dem IRQ in der Weise, daß er nicht per Spezialbefehl abschaltbar ist. Die Abarbeitungsroutine ($FEAD), die wiederum über einen Vektor in $0318, $0319 angesprungen wird, prüft nun als erstes, ob sie etwa ein Modulprogramm unterbrochen hat. Ist dies der Fall, so verzweigt die Routine in die NMI-Bearbeitung des Modulprogramms (dazu ist in $A002, $A003 die entsprechende Startadresse anzugeben). Dadurch ist es für den Programmierer eines solchen Moduls möglich, alle Unterbrechungen, die durch das Drücken der RESTORE-Taste entstehen, abzublocken, womit natürlich auch ein gewisser Programmschutz gewährleistet ist.

Sollte ein solches Modulprogramm nicht vorhanden sein, so wird die NMI-Routine normal fortgesetzt. Als erstes wird dann die STOP-Taste des Computers abgefragt, denn ein Programmabbruch soll ja nur durch das Drücken der Tastenkombination RUN/STOP-RESTORE möglich sein. Wenn auch diese Klippe genommen ist, beginnt der eigentliche Warmstart des Computers, der die Vektoren neu setzt und die Zeropage ordnet (daher wird auch unser Uhrenprogramm nach dem Drücken dieser Tastenkombination abgeschaltet).

Natürlich kann auch der NMI für unsere Programme mißbraucht werden. Dazu muß nur wieder (wie soll es auch anders sein) der entsprechende Vektor geändert werden, so daß er auf ein eigenes Maschinenprogramm zeigt. In Folge 3 ist eine sehr kurze und effiziente Anwendung der RESTORE-Taste erläutert worden. Damals ging es darum, sich innerhalb von Hochkommas (“) von den lästigen Cursor-Steuerzeichen zu befreien. Durch das Drücken der RESTORE-Taste, die dann quasi als zusätzliche Funktionstaste arbeitet, kann man also auch Aktionen auslösen.

Aller guten Dinge sind drei

Der Vollständigkeit halber möchte ich jetzt auch noch den dritten Interrupttyp anführen, der allerdings nicht durch irgendwelche Signale in der Hardware ausgelöst wird. Ich spreche von dem Maschinenbefehl BRK. Dieser wird in der CPU wie ein Interrupt-Aufruf (IRQ) behandelt, und deshalb benutzt er auch den gleichen Hardwarevektor, nämlich $FFFE. Das Betriebssystem im VC 20 hingegen trifft eine Unterscheidung zwischen diesen beiden Interrupttypen, denn der Befehl BRK soll beim VC 20 einen Warmstart bewirken. Auch diese Abarbeitung wird (ich traue mich es schon nicht mehr zu sagen) über einen Vektor ($0316, $0317) geleitet. Damit bietet sich die Möglichkeit, dieses Kommando dazu zu benutzen, ein Unterprogramm über einen Ein-Byte-Befehl (!) aufzurufen. Durch die Angabe der Startadresse in diesem Vektor kann man jedes beliebige Unterprogramm adressieren und mit BRK aufrufen.

Soweit zu den Interrupts. Ich möchte Ihnen zum Schluß noch, da wir gerade beim Betriebssystem sind, einige sehr nützliche Unterprogramme aus dem Interpreter vorstellen. Diese Routinen können nämlich sehr gut in eigenen Maschinenprogrammen Verwendung finden, und dies spart enormen Platz- und Programmieraufwand.

Nützliche Routinen im Betriebssystem

Verschieberoutine ($C3B8): Genau diese wurde in meinem Beispielprogramm verwendet, um das eventuell im Speicher vorhandene Basic-Programm zu verschieben. Die Benutzung dieser Routine ist verhältnismäßig einfach. Die Anfangsadresse des zu verschiebenden Speicherbereichs muß in den beiden Zeropagespeicherstellen $5F und $60 abgelegt werden. Um das Ende zu markieren, vermerkt man die entsprechende Adresse plus 1 (!) in den Speicherstellen $5A und $5B. Schließlich ist auch noch die neue Anfangsadresse in $58 und $59 abzulegen. Auch hierbei ist darauf zu achten, daß eins dazuaddiert wird, denn die Routine ist dementsprechend konzipiert. Der Aufruf der Routine geschieht schließlich mit »JSR $C3B8«.

Fehlermeldung ausgeben ($C437): Oftmals ist es nötig, eine Basic-Fehlermeldung auszugeben (zum Beispiel in einer Befehlserweiterung, in der ein Komma abzufragen ist). Wenn dieses nicht vorhanden ist oder sonst ein Fehler vorliegt, dann soll eine Fehlermeldung ausgedruckt werden. Alles, was man bei dieser Routine zu tun hat, ist die Eingabe der Fehlernummer ins X-Register. Alle möglichen Meldungen und die dazugehörigen Nummern sind in Tabelle 1 zu finden. Dann wird die Abarbeitungsroutine mit JMP $C437 angesprungen. Zu beachten ist, daß ein Maschinenprogramm auf diese Weise beendet wird (der Interpreter springt nämlich in die Eingabewarteschleife zurück). Möchte man nur eine Meldung ausgeben (ohne das Programm anzuhalten), so ist die Nachricht mit einer anderen Routine, die noch besprochen wird, zu erzeugen. Zwei oft benötigte Fehlermeldungen (Syntax- und Illegal Quantity Error) sind noch einfacher zu bekommen. Um den Syntax Error zu erzeugen springt man mit JMP $CF08 in eine Unterroutine ein, die bereits die Fehlernummer enthält (auf diese Weise spart der Interpreter Speicherplatz). Auch für den Illegal Quantity Error gibt es solch ein Unterprogramm: JMP $D248. Für beide Abkürzungen gilt im übrigen — dies noch als Ergänzung — das oben zum Programmabbruch gesagte.

Nummer Fehlermeldung Adresse
01 TOO MANY FILES OPEN $C19E
02 FILE OPEN $ClAD
03 FILE NOT OPEN $ClB5
04 FILE NOT FOUND $ClC2
05 DEVICE NOT PRESENT $ClD0
06 NOT INPUT FILE $ClE2
07 NOT OUTPUT FILE $ClF0
08 MISSING FILE NAME $ClFF
09 ILLEGAL DEVICE NUMBER $C210
0A NEXT WITHOUT FOR $C225
0B SYNTAX $C235
0C RETURN WITHOUT GOSUB $C23B
0D OUT OF DATA $C24F
0E ILLEGAL QUANTITY $C25A
0F OVERFLOW $C26A
10 OUT OF MEMORY $C272
11 UNDEF’D STATEMENT $C27F
12 BAD SUBSCRIPT $C290
13 REDIM’D ARRAY $C29D
14 DIVISION BY ZERO $C2AA
15 ILLEGAL DIRECT $C2BA
16 TYPE MISMATCH $C2C8
17 STRING TOO LONG $C2D5
18 FILE DATA $C2E4
19 FORMULA TOO COMPLEX $C2ED
lA CAN’T CONTINUE $C300
lB UNDEF’D FUNCTION $C30E
lC VERIFY $C31E
lD LOAD $C324
Tabelle 1. Sämtliche Fehlermeldungen und ihre Adressen

Programmzeilen neu ordnen ($C533): Dies ist eine der praktischsten Routinen, die mir im gesamten Basic-Interpreter untergekommen ist. Das kleine unscheinbare Unterprogramm kann mehr, als zunächst zu vermuten ist. Es ist wahrscheinlich jedem schon einmal passiert, daß plötzlich aus irgendeinem Grund ein verstümmeltes Basic-Programm im Speicher steht. Das kann durch falsches Laden vom Band oder auch durch unvorsichtiges POKEn im Basic-Speicher geschehen. Der Grund für diesen »Zeilensalat« ist eine Veränderung der Verbindungszeiger (Koppeladressen), die am Beginn jeder (Basic-) Programmzeile stehen, und die im Normalfall auf die Adresse der jeweils folgenden Zeile zeigen (siehe auch Folge 1 dieses Kurses). Das Unterprogramm ab $C533 hat nun die nützliche Eigenschaft, fast alle auf diese Weise beschädigten Programme wieder zu restaurieren. Man ruft es einfach von Basic aus mit SYS 53483 auf, und schon ist das Programm zumindest wieder LISTbar (jedenfalls in den meisten Fällen). Damit aber noch nicht genug. Bei dieser Gelegenheit stellt die Routine auch noch die Länge des Programmes fest und übergibt die Endadresse den Speicherstellen $22 und $23. Damit ist es möglich, die durch einen Reset oder durch NEW scheinbar verlorenen Programme völlig wiederherzustellen. Dazu ist lediglich der erste Verbindungszeiger zu rekonstruieren (das macht unsere Unterroutine), und das Programmende muß in den Speicherstellen $2D und $2E vermerkt werden. Die nachfolgende Programmzeile ist das kürzeste Rekonstruktionsprogramm, das mir bekannt ist. Ich habe es bereits in Folge 1 besprochen und möchte es in diesem Zusammenhang noch einmal erwähnen.
SYS 50483:POKE 46,PEEK(35): POKE 45,PEEK(781) + 2:CLR

Bevor diese Zeile eingegeben wird, muß man den Anfang des Basic-Programms im Speicher markieren, denn sonst wird es von der Routine nicht gefunden: POKE (Basic-Anfangsadresse) + 1,1.

Natürlich ist diese Routine auch im C 64 zu finden. Dort ist die Startadresse $A533.

String ausgeben ($CB1E): Mit diesem Unterprogramm ist es in einfacher Weise möglich, Texte in einem Maschinenprogramm auszugeben. Dadurch entfallen die sonst nötigen langwierigen Druckschleifen, mit denen man sich sonst herumschlagen muß. Auch bei dieser Unterroutine ist die Handhabung wieder ganz einfach. Der auszugebende Text, der natürlich auch Sonderund Steuerzeichen enthalten darf, muß im ASCII-Format irgendwo im Speicher stehen. Das Textende muß mit einer Null markiert sein. Ist diese Voraussetzung erfüllt, braucht man nur noch den Akku mit dem Low-Byte, das Y-Register mit dem High-Byte der Anfangsadresse zu laden und das Unterprogramm mit JSR $CBlE aufzurufen: Schon steht der Text auf dem Bildschirm. Auch Fehlermeldungen können auf diese einfache Weise ausgegeben werden (die Möglichkeit hatte ich ja oben schon angekündigt). Die Adressen der einzelnen Fehlernachrichten sind Tabelle 1 zu entnehmen.

Es soll nicht verschwiegen werden, daß es in seltenen Fällen zu einem Problem kommen kann: Weil dieses Unterprogramm auf String-Basis arbeitet, wird der Text als Variable zwischengespeichert. Wurden die Zeiger $37 und $38 (dezimal 55, 56) verändert, so kommt es manchmal dazu, daß irgendwelche unsinnigen Zeichen, aber nicht der gewünschte Text ausgegeben werden. In solch einem Fall müssen die Zeichen konventionell, das heißt über die Kernalroutine $FFD2 ausgegeben werden.

Nachlese

Zum Schluß möchte ich nochmals ein Thema aufgreifen, das ich schon einmal angesprochen hatte — ich spreche von dem Autostartprogramm aus Folge 1. Damals wurde ein Programm abgedruckt, das mir viele Leserzuschriften eingebracht hat. Einige berichteten mir, daß es bei ihnen nicht möglich gewesen sei, diese Routine ordnungsgemäß laufen zu lassen. Da dieses Programm bei mir tadellos arbeitet, kann ich mir die geschilderten Fehler nicht erklären. Dennoch möchte ich diese Thematik nochmals aufgreifen, denn es gab eine Reihe von Anfragen nach einer Version für andere Commodore-Computer.

Um möglichst allen Wünschen gerechtzu werden, erläutere ich nun eine neuartige, kürzere Methode, in der auf sämtliche Programmschutzmaßnahmen, wie zum Beispiel das Abschalten der RESTORE-Taste, verzichtet wurde.

Für diese selbststartenden Programme nutzen wir eine besondere Eigenschaft des Kassettenpuffers aus. Der ist nämlich in der Lage, einen Programmnamen mit einer Länge von bis zu 187 Zeichen zu speichern. Davon werden allerdings nur die bekannten 16 Zeichen auf dem Bildschirm angezeigt; der Rest bleibt im Verborgenen. Da der gesamte Inhalt des Kassettenpuffers als Vorspann zu jedem Programm abgespeichert wird, kann man aber tatsächlich einen sehr viel längeren Filenamen mit dem Programm übertragen. Der Trick ist nun folgender: Neben dem Programmnamen mit einer Länge von 16 Zeichen wird auch ein kleines Maschinenprogramm im Bandpuffer generiert. Dieses wird nach der Beendigung des Ladevorganges von dem geänderten Kernal INPUT-Vektor angesprungen (siehe dazu auch Folge 1). Die kleine Maschinenroutine macht nun nichts anderes, als diesen Zeiger auf seinen Ursprungswert zurückzustellen und den Tastaturpuffer mit den Zeichen LOAD (CR) und RUN (CR) zu füllen. Das ist im Prinzip nichts anderes, als wenn man über die Tastatur SHIFT RUN/STOP eingeben würde, nur mit dem Unterschied, daß dies hier automatisch geschieht.

Im einzelnen wird also Folgendes gemacht: Man generiert einen kurzen Vorspann (ohne Programm) auf Band. Dieser enthält lediglich den präparierten Filenamen. Nachdem dieser Vorspann geladen worden ist, verzweigt der Computer über den INPUT-Vektor in das sich im Bandpuffer befindliche Maschinenprogramm, das wiederum einen neuen Ladebefehl erzeugt, mit dem das eigentliche Programm nachgeladen und automatisch gestartet wird.

Dazu jetzt die einzelnen Schritte, mit denen man einen solchen Vorspann generieren kann:

  1. Eingabe des Ladeprogramms (Listing 3)
  2. Programm testen und abspeichern
  3. Lader starten
  4. Programmnamen eingeben
  5. Die auf dem Bildschirm ausgedruckten Zeilen nacheinander mit RETURN ausführen
  6. Vorspann abspeichern
  7. Wenn auf dem Bildschirm »SEARCHING« erscheint, muß der beginnende Ladevorgang mit der STOP-Taste abgebrochen werden
  8. Hauptprogramm nachladen und abspeichern

Listing 4 zeigt das kleine Maschinenprogramm aus den DATA-Zeilen 360 bis 380 des Basic-Laders im Assembler-Format. Wer den C 64 dazu bringen will, das Gleiche zu tun, den möchte ich auf die REM-Zeilen am Ende des Programms aufmerksam machen, in denen die nötigen Änderungen beschrieben sind.

Zwei Anmerkungen wären zu dieser Methode noch zu machen:

Erstens: Die demonstrierte Autostartroutine funktioniert nur im Kassettenbetrieb. Die Floppystation nutzt diesen Puffer nicht, folglich funktioniert auch diese Methode nicht.

Zweitens: Da das Maschinenprogramm an einen festen Speicherplatz im Kassettenpuffer gebunden ist, muß auch der Filename eine feste Länge haben, denn an diesen schließt sich die Maschinenroutine an. Daher ergänzt der Lader den Programmnamen auf 16 Zeichen.

Mit diesem Ausflug in Betriebssystem und Interruptmechanismus ist der VC 20, so meine ich, genug durchleuchtet worden. Ich hoffe, daß Sie in den sechs Folgen dieses Kurses einen Eindruck von den Geschehnissen im Inneren Ihres Computers bekommen haben, so daß dieser Kurs seinen Namen auch wirklich verdient hat.

(Christoph Sauer/ev)
100 rem ************************
110 rem * irq-clock by c.sauer *
120 rem ************************
130 rem
140 rem
150 rem #######################
160 rem ## nur fuer speicher ##
170 rem ## groesser 8 kbyte  ##
180 rem #######################
190 fort=0to279:readd:s=s+d:next
200 ifs<>32965thenprint"fehler in datas":end
210 print"{clr}{down}{down}wo soll das programm"
220 print"{down}abgespeichert werden?"
230 v=peek(56)*256-512
240 print"{down}vorschlag: "v;
250 input"{left}{left}{left}{left}{left}{left}{left}{left}";v
260 w2=int(v/256):w1=v-w2*256
270 poke55,w1:poke56,w2
280 restore
290 fort=vtov+279
300 readd
310 if d<0thengosub330
320 poket,d:next:sysv:end
330 onabs(d)goto340,350,360,370,380
340 d=w2:return
350 d=w2+1:return
360 d=w1+208:return
370 d=w1+60:return
380 d=w1+10:return
390 data032,-03,-01,173,003
400 data144,041,128,009,050
410 data141,003,144,162,044
420 data169,160,157,249,017
430 data173,015,144,041,007
440 data157,249,149,202,208
450 data240,162,008,173,134
460 data002,157,022,150,202
470 data208,250,120,169,-04
480 data141,020,003,169,-01
490 data141,021,003,088,032
500 data089,198,076,116,196
510 data162,005,181,097,157
520 data073,003,202,208,248
530 data162,011,189,254,000
540 data157,084,003,202,208
550 data247,165,113,141,097
560 data003,165,071,141,098
570 data003,165,093,141,099
580 data003,165,094,141,100
590 data003,165,162,166,161
600 data164,160,032,135,207
610 data132,094,136,132,113
620 data160,006,132,093,160
630 data036,032,104,222,169
640 data023,133,169,169,018
650 data133,170,160,000,162
660 data002,185,255,000,145
670 data169,200,192,006,240
680 data018,202,208,243,169
690 data058,145,169,136,230
700 data169,208,002,230,170
710 data162,003,208,233,162
720 data005,189,073,003,149
730 data097,202,208,248,162
740 data011,189,084,003,157
750 data254,000,202,208,247
760 data173,097,003,133,113
770 data173,098,003,133,071
780 data173,099,003,133,093
790 data173,100,003,133,094
800 data076,191,234,166,045
810 data134,090,164,046,132
820 data091,032,-05,-02,134
830 data088,134,045,132,089
840 data132,046,166,043,134
850 data095,164,044,132,096
860 data032,-05,-02,134,043
870 data134,166,132,044,132
880 data167,032,184,195,032
890 data051,197,165,166,208
900 data002,198,167,198,166
910 data169,000,168,145,166
920 data096,132,168,024,138
930 data105,049,170,165,168
940 data105,000,168,096,000
Listing 1. »IRQ-Clock« (Basic-Lader)
****************** INITIALISIERUNG
****************** DER UHRENROUTINE
2000  JSR $20D0  ; BASICPGM. VERSCIEBEN
2003  LDA $9003  ; BILDSCHIRMGROESSE
2006  AND #$80   ; LADEN UND 24,25.
2008  ORA #$32   ; ZEILE ZUSCHALTEN
200A  STA $9003  ; ABSPEICHERN
200D  LDX #$2C   ; ZEILEN LOESCHEN
200F  LDA #$A0   ; MIT INVERSEN SPACES
2011  STA $11F9,X; ABSPEICHERN
2014  LDA $900F  ; AKTUELLE FARBE
2017  AND #$07   ; WIRD ZUR
2019  STA $9FF9,X; ZEICHENFARBE
201C  DEX        ; SCHLEIFE
201D  BNE $200F
201F  LDX #$08   ; FARBE DER UHRZEIT
2021  LDA $0286  ; IST AKTUELLE ZEICH-
2024  STA $9616,X; CHENFARBE
2027  DEX
2028  BNE $2024
202A  SEI        ; INTERRUPTS VERHIND.
202B  LDA #$3C   ; VEKTOREN INITIAL.
202D  STA $0314
2030  LDA #$20
2032  STA $3015
2035  CLI        ; INTERRUPTS ZULASSEN
2036  JSR $C659  ; BASIC-BEFEHL CLR
2039  JMP $C474  ; INS BASIC ZURUECK
****************** UHREN ANZEIGE
203C  LDX #$05   ; DIVERSE ZEROPAGE-
203E  LDA $61,X  ; DATEN IN DEN BAND-
2040  STA $0349,X; PUFFER RETTEN
2043  DEX
2044  BNE $203E
2046  LDX #$0B
2048  LDA $00FE,X
204B  STA $0354,X
204E  DEX
204F  BNE $2048
2051  LDA $71
2053  STA $0361
2056  LDA $47
2058  STA $0362
205B  LDA $5D
205D  STA $0363
2060  LDA $5E
2062  STA $0364
2065  LDA $A2    ; UHRZEIT (A2,A1,A0)
2067  LDX $A1    ; HOLEN
2069  LDY $A0
206B  JSR $CF87  ; IN STRING VERWANDELN
206E  STY $5E    ; UNTERROUTINE VOR-
2070  DEY        ; BEREITEN
2071  STY $71
2073  LDY #$06   ; 6 ZIFFERN DARSTELLEN
2075  STY $5D
2077  LDY #$24
2079  JSR $DE68  ; ZIFFERN BERECHNEN
207C  LDA #$17  ; BILDSCHIRMSPEICHER-
207E  STA $A9    ; ADRESSE DER ZIFFERN
2080  LDA #$12
2082  STA $AA
2084  LDY #$00   ; ZAEHLER FUER ZIFFERN
2086  LDX #$02   ; ZAEHLER FUER ':'
2088  LDA $00FF,X; ZIFFERN HOLEN
208B  STA ($A9),X; IN BILDSCHIRMSPEICHER
208D  INY        ; SCHLEIFE 1
208E  CPY #$06   ; ALLE ZIFFERN ?
2090  BEQ $20A4  ; JA, DANN ENDE
2092  DEX
2093  BNE $2088
2095  LDA #$3A   ; DOPPELPUNKT LADEN
2097  STA ($A9),Y; UND ABSPEICHERN
2099  DEY        ; SCHLEIFE 2
209A  INC $A9    ; ZAHLER KORREKTUR
209C  BNE $20A0
209E  INC $AA    ; ZAHLER KORREKTUR
20A0  LDX #$03   ; NEUER ':' ZAHLER
20A2  BNE $208D
20A4  LDX #$05   ; ALLE GERETTETEN
20A6  LDA $0349,X; ZEROPAGE VARIABLEN
20A9  STA $61,X  ; WIEDER ZURUECK-
20AB  DEX        ; SPEICHERN.
20AC  BNE $20A6
20AE  LDX #$0B
20B0  LDA $0354,X
20BE  STA $00FE,X
20B6  DEX
20B7  BNE $20B0
20B9  LDA $0361
20BC  STA $71
20BE  LDA $0362
20C1  STA $47
20C3  LDA $0363
20C6  STA $5D
20C8  LDA $0364
20CB  STA $5E
20CD  JMP $EABF  ; ZUR INTERRUPTROUTINE
****************** BASICPROGRAMM VER-
****************** SCHIEBEN
20D0  LDX $2D    ; ENEADRESSE DES PGMS
20D2  STX $5A    ; HOLEN
20D4  LDY $2E
20D6  STY $5B
20D8  JSR $210A  ; 49 ADDIEREN
20DB  STX $58    ; UND ABSPEICHERN
20DD  STX $2D
20DF  STY $59
20E1  STY $2E
20E3  LDX $2B    ; PGM. ANFANGSADRESSE
20E5  STX $5F    ; HOLEN UND ABSP.
20E7  LDY $2C
20E9  STY $60
20EB  JSR $210A  ; 49 ADDIEREN
20EE  STX $2B    ; UND ABSPEICHERN
20F0  STX $A6
20F2  STY $2C
20F4  STY $A7
20F6  JSR $C3B8  ; PROGRAMM VERSCHIEBEN
20F9  JSR $C533  ; PGM.ZEILEN BINDEN
20FC  LDA $A6
20FE  BNE $2102
2100  DEC $A7    ; 0 AN DEN NEUEN PGM.-
2102  DEC $A6    ; ANFANG SPEICHERN
2104  LDA #$00
2106  TAY
2107  STA ($A6),
2109  RTS
****************** ADDITIONSROUTINE
210A  STY $A8
210C  CLC        ; 16-BIT ADDITION
210D  TXA        ; VORBEREITEN
210E  ADC #$31   ; DEZ 49 ADDIEREN
2110  TAX
2111  LDA $A8    ; HIGH-BYTE LADEN
2113  ADC #$00   ; UEBERTRAG ADDIEREN
2115  TAY
2116  RTS
Listing 2. »IRQ Clock« (Assembler-Listing)
100 print"{clr}**********************"
110 print"* autostart fur c 64 *"
120 print"* und vc 20 (version *"
130 print"* fuer vc 20).       *
140 print"*--------------------*"
150 print"* die anweisungen des*"
160 print"* programms muessen  *"
170 print"* genau befolgt wer- *"
180 print"* den !!"
190 print"**********************"
200 input"filename:";f$:fort=1to16-f:f$=f$+" ":next
210 fort=1to16:poke929+t,asc(mid$(f$,t,1)):next
220 fort=1to24:readd:poke945+t,d:next
230 print"{clr}{rvon}folgende zeilen nach-"
240 print"{rvon}einander mit <return>"
250 print"{rvon}durchgehen."
260 print"{down}poke43,24:poke44,3:poke45,60:poke46,3:clr"
270 print"{down}{down}fort=930to970:f$=f$+chr$(pE(t)):nE"
280 print"{rvon}{down}** achtung **"
290 print"{rvon}programmcassette be-"
300 print"{rvon}reit halten. unter-"
310 print"{rvon}brechen nach best-"
320 print"{rvon}aetigung der naech-"
330 print"{rvon}sten zeile nicht mit"
340 print"{rvon}<run/stop> !!!"
350 print"{down}pO804,81:pO805,3:savef$,1,1{home}{down}"
360 data162,014,142,036,003,162,242,142,037
370 data003,162,009,134,198,189,244,237,157
380 data119,002,202,016,247,096
1000 rem aenderungen fuer c 64:
1002 rem in zeile 360 sind die
1005 rem daten 014 und 242 durch
1007 rem 087 und 241 zu ersetzen
1010 rem in zeile 370 sind die
1020 rem daten 244,237 durch
1030 rem 231,236 zu ersetzen
Listing 3. »Autostart« für C 64 / VC 20
0351 LDX #$0E    ; INPUT-VEKTOR REKON-
0353 STX $0324   ; STRUIEREN.
0356 LDX #$F2
0358 STX $0325
035B LDX #$09    ; NEUN ZEICHEN
035D STX $C6     ; IN TATATURCOUNTER
035F LDA $EDF4,X ; ZEICHEN AUS ROM HOLEN
0362 STA $0277,X ; UND IN TASTATURPUFFER
0365 DEX
0366 BPL $035F   ; ALLE NEUNE ?
0368 RTS         ; JA, DANN ENDE
Listing 4. Die Maschinenroutine zum Autostartprogramm als Assembler-Listing
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →