Der gläserne VC 20 – Teil 2
In der ersten Folge haben wir uns hauptsächlich mit der Basic-Verwaltung befaßt. Dabei tauchte immer wieder der Begriff Zeropage auf, mit dem wir uns heute beschäftigen wollen.
Die Zeropage — oder zu Deutsch die Seite Null — ist in Maschinensprache besonders einfach zu handhaben. Als Seite bezeichnet man im übrigen immer ein Paket von jeweils 256 Byte. So entspricht Seite 0 den Adressen 0-255, Seite 1 den Adressen 256-511 und so weiter.
Eine Zeropageadressierung, zum Beispiel LDA $42, benötigt nur zwei Byte, eine absolute Adressierung, zum Beispiel LDA $1234, hingegen drei Bytes im Speicher. Damit verbunden ist auch die Bearbeitungsgeschwindigkeit eines Maschinenprogramms. Denn die Zeropageadressierung ist schneller als die entsprechende Drei-Byte-Methode. Bei kleineren Programmen in Assembler fällt dieser Aspekt zwar nicht so sehr ins Gewicht, bei sehr umfangreichen Routinen, (wie zum Beispiel beim Basic-Interpreter) spielt die Adressierungsart jedoch eine größere Rolle.
In der Seite 0 legt der Computer also insbesondere die Daten ab, die er oft benötigt, wie zum Beispiel Vektoren, Parameter etc. Die komplette Liste der Adreßbelegung zeigt Tabelle 1.
Wir wollen es jedoch nicht nur mit der Aufstellung alleine bewenden lassen. Die interessantesten Lokationen möchte ich hier herausgreifen und besprechen. Beginnen wir also mit den ersten drei Bytes im Speicher:
Der USR-Vektor
Adresse 0,1,2: Über die USR-Funktion kann der Benutzer eigene mathematische Routinen, die nicht im Basic implementiert sind, in seine Programme einbinden. Dieses Kommando ruft ein Maschinenprogramm auf, dessen Startadresse vorher in den Speicherzellen 1 und 2 abgelegt wurde. Wer sich dieses Befehls nicht bedient, hat drei Zeropagespeicherzellen zur freien Verfügung, die er für eigene Maschinenprogramme nutzen kann. Die Syntax der USR-Funktion entspricht derjenigen normaler Basic-Funktionen wie zum Beispiel FRE, POS, SIN, COS, TAN etc.: A= USR (B) oder PRINT USR (B).
Der Vorteil gegenüber dem SYS-Befehl liegt in der Möglichkeit Parameter zu übergeben. In unserem Beispiel wird der Benutzerroutine die Variable B übergeben. Der errechnete Zahlenwert kann dann seinerseits wieder einer Variablen zugewiesen werden.
Wie kann man sich in einem Maschinenprogramm die eingegebene Zahl beschaffen? Nun, alle Basic-Variablen werden im Fließkommaformat abgespeichert, eine Ausnahme bilden nur die Integer-Variablen. Dabei werden die Zahlen vom Computer nach einem bestimmten Verfahren binär verschlüsselt, so daß eine Zahl mit acht Nachkommastellen und einem Exponenten in nur fünf Bytes Platz findet.
Zwischengespeichert wird das Ergebnis in besonderen Fließkomma-Akkumulatoren (Adresse 97-104). Mit Hilfe eines Unterprogramms aus dem Basic-Interpreter kann es von dort aus abgerufen werden, wobei es in eine Integerzahl (also ein Wert zwischen 0 und 65536) umgewandelt wird.
Aufgerufen wird die Routine mit JSR $D7F7. Der 2-Byte-Wert steht dann in den Zeropagestellen $14 und $15 (Hexadezimal) zur Verfügung. Da diese Adressen vom Interpreter oft benutzt werden, ist es ratsam die Zahlen in andere Register zu übertragen (in welche, darauf kommen wir später noch zu sprechen).
Das Gegenstück zu dem eben gezeigten Unterprogramm stellt die Routine »2-Byte in Fließkomma« dar. Das Low-Byte wird in das y-Register geladen, das höherwertige Byte muß in den Akku. Dann wird die Routine mit JSR $D391 gestartet, wobei die USR-Variable (in unserem Beispiel A) die Daten erhält.
Speicher ausgedeutet — die Basic-Zeiger
Adresse 43 — 56: Diese Adressen spielen, wie wir schon im ersten Teil gesehen haben, bei der Verwaltung von Basic-Programmen und -Variablen eine zentrale Rolle. Über sie erfolgt die Trennung zwischen Programm und den einzelnen Variablentypen. Auch für das Speichern und Laden von Programmen liefern sie die Basisdaten. Die einzelnen Funktionen können sie Tabelle 1 entnehmen.
Dezimal | Hexadezimal | Bemerkung |
0-2 | 00 - 02 | USR-Sprungvektor (Normal: JMP $D248) |
3-4 | 03 - 04 | Vektor für Unterprogramm ’Fließkomma nach Integer’ |
5-6 | 05 - 06 | Vektor für Unterprogramm ’Integer nach Fließkomma’ |
7 | 07 | Suchzeichen (sucht ’:’ oder Zeilenende) |
8 | 08 | Hochkommaflag |
9 | 09 | Spaltenspeicher beim TAB-Befehl |
10 | 0A | LOAD/VERIFY Flag (0 = LOAD/1 = VERIFY) |
11 | OB | Eingabepufferzeiger (Anzahl der Elemente) |
12 | 0C | Flag für Dim (enthält die laufende Variable) |
13 | 0D | Variablentyp (0 = Numerisch/128 = String) |
14 | 0E | Numerische Variable (0 = Fließkomma/128 Integer) |
15 | 0F | Flag bei DATA und LIST |
16 | 10 | Flag für FN |
17 | 11 | Eingabeflag (0 = INPUT/64 = GET/152 = READ) |
18 | 12 | Vorzeichen bei ATN |
19 | 13 | Aktuelles Ein-/Ausgabegerät |
20-21 | 14 - 15 | Integerwert (zum Beispiel Zeilennummer oder Zwischenerg.) |
22 | 16 | Zeiger im Stringstapel |
23-24 | 17 - 18 | Zeiger auf den zuletzt verwendeten String |
25-33 | 19 - 21 | Stringstapel |
34-37 | 22 - 25 | Speicher für div. Hilfszeiger |
38-42 | 26 - 2A | Speicherbereich bei einer Multiplikation |
43-44 | 2B - 2C | Zeiger auf Beginn des Basic-Speicherbereichs |
45-46 | 2D- 2E | Beginn der Variablen (= Ende des Programms) |
47-48 | 2F - 30 | Beginn der Arrays (= Ende der Variablen) |
49-50 | 31 - 32 | Zeiger auf Ende der Arrays |
51-52 | 33 - 34 | Zeiger auf Beginn der Strings |
53-54 | 35 - 36 | Hilfszeiger für Strings |
55-56 | 37 - 38 | Zeiger auf Basic-Ende |
57-58 | 39 - 3A | Momentane Basic-Zeilennummer |
59-60 | 3B - 3C | Vorherige Basic-Zeilennummer |
61-62 | 3D- 3E | Zeiger auf lfd. Basic-Befehl (für CONT) |
63-64 | 3F - 40 | Momentane Zeilennummer für DATA |
65-66 | 41 - 42 | Momentane Adresse einer DATA-Zeile |
67-68 | 43 - 44 | Zeiger auf Element in DATA-Zeile und INPUT-Vektor |
69-70 | 45 - 46 | Momentaner Variablenname |
71-72 | 47 - 48 | Adresse der momentanen Variablen |
73-74 | 49 - 4A | Zeiger für FOR/NEXT Variable |
75-76 | 4B - 4C | Zwischenspeicher für Programmzeiger (bei SAVE) |
77 | 4D | Speicher für Vergleichssymbole |
78-79 | 4E - 4F | Zeiger für FN |
80-83 | 50 - 53 | Verschieden genutzter Speicherbereich (Hauptsächlich für Strings) |
84-86 | 54 - 56 | FN-Sprungvektor (ähnlich USR) |
87-96 | 57 - 60 | Feld für diverse arithmetische Zwecke (Arithmetik Akku #3 und #4) |
97 | 61 | Fließkommaakku #1 : Exponent |
98 - 101 | 62 - 65 | Fließkommaakku #1 : Matisse |
102 | 66 | Fließkommaakku #1 : Vorzeichen |
103 | 67 | Zeiger für Polynomauswertung |
104 | 68 | Überlauf von Akku #1 |
105 | 69 | Fließkommaakku #2 : Exponent |
106 - 109 | 6A - 6D | Fließkommaakku #2 : Mantisse |
110 | 6E | Fließkommaakku #2 : Vorzeichen |
111 | 6F | Vergleichsbyte der Vorzeichen von Akku #1 und Akku #2 |
112 | 70 | Rundung für Akku #1 |
113 - 114 | 71 - 72 | Länge des Kassettenpuffers |
115 - 138 | 73 - 8A | CHRGET-Routine |
139 - 143 | 8B - 8F | RND-Wert als Fließkommazahl |
144 | 90 | Statusflag ST |
145 | 9) | |
150 | 96 | Kassetten EOT (End of Tape) erhalten |
151 | 97 | Zwischenspeicher für Register |
152 | 98 | Anzahl der offenen Dateien |
153 | 99 | Eingabegerät (Normal = 0 : Tastatur) |
154 | 9A | Ausgabegerät (Normal = 3 : Bildschirm) |
155 | 9B | Paritätsbyte) |
150 | 96 | Kassetten EOT (End of Tape) erhalten |
151 | 97 | Zwischenspeicher für Register |
152 | 98 | Anzahl der offenen Dateien |
153 | 99 | Eingabegerät (Normal = 0 : Tastatur) |
154 | 9A | Ausgabegerät (Normal = 3 : Bildschirm) |
155 | 9B | Paritätsbyte vom Band (Prüfsumme) |
156 | 9C | Flag für Byte erhalten (von Band) |
157 | 9D | Flag für Direktmodus (= 128) oder Programm (= 0) |
158 | 9E | Band: erster Teil - Prüfsumme |
159 | 9F | Band: zweiter Durchlauf - Prüfsumme |
160 - 162 | A0- A2 | Interne Uhr (Stunde, Minute, Sekunde) |
163 | A3 | Bitzähler für serielle Ausgabe |
164 | A4 | Zähler für Band |
165 | A5 | Startsynchronisation bei Kassetten |
166 | A6 | Zeiger im Bandpuffer |
167 - 171 | A7- AB | Flags für Schreiben/Lesen bei Band |
172 - 173 | AC - AD | Zeiger auf Kassettenpuffer und für scrolling |
174 - 175 | Ae- AF | Zeiger auf Programmende bei LOAD |
176 - 177 | B0 - Bl | Bandzeitkonstanten |
178 - 179 | B2 - B3 | Startadresse des Bandpuffers |
180 | B4 | Bitzähler für Band |
181 | B5 | Band oder RS232: nächstes zu sendendes Bit |
182 | B6 | Übertragungsspeicher für RS232 |
183 | B7 | Länge des Filenamens |
184 | B8 | Logische Filenummer |
185 | B9 | Sekundäradresse |
186 | Ba | Gerätenummer |
187 - 188 | BB- BC | Zeiger auf Filenamen |
189 | BD | Ein-/Ausgabespeicher (für seriell) |
190 | BE | Blockzähler für Band |
191 | BF | Puffer für serielle Ausgabe |
192 | CO | !Bandmotor Flag |
193 - 194 | Cl - C2 | Startadresse für Ein-/Ausgabe |
195 - 196 | C3- C4 | Endadresse für Ein-/Ausgabe |
197 | C5 | gedrückte Taste (momentaner Wert im Tastaturmatrixcode) |
198 | C6 | Anzahl der gedrückten Tasten (im Tastaturpuffer) |
199 | C7 | Bildschirm: negative Anzeige |
200 | C8 | Zeiger auf Zeilenende bei Eingabe |
201 | C9 | Cursorzeile |
202 | CA | Cursorspalte |
203 | CB | Welche Taste? (64 = keine) |
204 | CC | Cursorblinken |
205 | CD | Cursor Blinkzähler |
206 | CE | Zeichen unter dem Cursor |
207 | CF | Flag für Cursorblinken (0 = An/1 = Aus) |
208 | DO | Eingabe von Bildschirm/oder Tastatur |
209 - 210 | D1 - D2 | Start der aktuellen Bildschirmzeile |
211 | D3 | Position des Cursors in der aktuellen Bildschirmzeile |
212 | D4 | Flag für Cursor (0 = Direkt, sonst programmiert) |
213 | D5 | Länge der Bildschirmzeile (21 od. 42 od. 63 od. 84) |
214 | D6 | Cursorzeile |
215 | D7 | Zeiger für diverse Zwecke |
216 | D8 | Anzahl der Inserts |
217 - 242 | D9- F2 | High Bytes der Bildschirmzeilenanfänge |
243 - 244 | F3 - F4 | Zeiger im Farbspeicher |
245 - 246 | F5 - F6 | Zeiger in der Tastaturdekodiertabelle |
247 - 248 | F7 - F8 | Zeiger auf RS232 Eingabepuffer |
249 - 250 | F9 - FA | Zeiger auf RS232 Ausgabepuffer |
251 - 254 | FA - FE | Freie Zeropageadressen |
255 (- 266) | FF - 10A | Zwischenspeicher für Fließkomma nach ASCII |
Eine interessante Gebrauchsmöglichkeit ergibt sich durch die Memoryroutine. Dies ist ein kurzes Basic-Programm, welches die Aufgabe hat, den von Programmen, Variablen, Strings und Arrays belegten Speicherplatz festzustellen (Listing 1):
Wie man nach dem Starten des Programms sehen kann, erhält man die Werte, indem man die Zeiger — nachdem sie in Dezimalzahlen umgewandelt worden sind — voneinander subtrahiert. Den Speicherplatz, der durch Variablen belegt ist, erhält man beispielsweise durch Subtraktion des Hilfszeigers, »Beginn der Variablen« (45/46) von dem Zeigerpaar »Beginn der Arrays« (47/ 48).
Gerade bei stark limitiertem Speicherplatz (wie zum Beispiel in der Grundversion) kann dieses Hilfsprogramm Aufschluß über die momentane Speicherverteilung geben.
Eine weitere Verwendung ergibt sich durch geschicktes Manipulieren der Speicherzellen, so daß es möglich wird, mehrere Programme gleichzeitig um Speicher unterzubringen.
Diese Aufgabe erfüllt das recht komfortable Programm »Basic-Switch« (Listing 2); zunächst aber die Grundlagen: Die besagten Zeiger stecken, wie bereits im ersten Teil unseres Kurses beschrieben, den Adreßbereich für Basic-Programme ab. Die ersten vier Bytes aus dem Zeropagekomplex (Adresse 43 bis 46) grenzen das Programm nach oben und unten ab. Die daran folgenden Variablen sind in ihrer Ausdehung ebenfalls durch ein Zeigerpaar (Adresse 55/56) limitiert. Der Bereich jenseits dieser Markierung ist für Basic tabu. Dort ist also Platz für Maschinenprogramme oder ähnliches, denn ein Überschreiben durch Basic-Variable ist nicht möglich.
Will man nun mehrere Basic-Programme im Speicher versammeln, so muß man sie nur durch verstellen der Zeiger eindeutig voneinander trennen.
Wie in Bild 1 zu sehen ist, grenzen die Programmblöcke direkt aneinander. Unter Programmblock ist hier die Einheit von Programm und einem sich daran anschließenden Variablenbereich zu verstehen. Durch Umschalten der Zeiger wird dann immer der gewünschte Programmblock eingeblendet.
Nützlich kann diese Art der Speicheraufteilung sein, wenn man umfangreiche Programme erstellen möchte und es nötig wird, andauernd Hilfsprogramme (zum Beispiel Grafikeditor oder ähnliches) nachzuladen. Durch Basic-Switch kann hier viel Arbeit eingespart werden.
Diese Routine liegt wieder in zweierlei Form ausgedruckt vor. Zum einen als Assemblerlisting für solche, die tiefer in das Programm einsteigen möchten (Listing 3). Und zum anderen als Basic-Lader. Das Programm ist grundsätzlich auf jeder Ausbaustufe lauffähig. Sinnvoll wird es jedoch erst bei größerem Speicher. Es ist in bewährter Weise über das Befehlswort PI in den Interpreter eingebunden.
Nachdem die Routine per SYS gestartet worden ist, (der Lader übernimmt auch diese Arbeit), muß der erste Programmbereich initialisiert werden, das heißt der Benutzer nimmt die größenmäßige Einteilung des Speichers vor. Man muß sich jedoch schon im voraus darüber im klaren sein, wie lang das Programm inklusive Variablen sein soll, denn eine nachträgliche Änderung ist nicht möglich.
Die Syntax für die Initialisierung lautet: π E »Länge«. Danach steht der Speicherbereich abzüglich zwei Bytes für Basic zur Verfügung. Der erste so eingerichtete Programmblock hat die Nummer 1, der zweite 2 und so fort. Nachdem ein zweiter Programmbereich eingerichtet wurde, kann mit π S »Nummer« zwischen den Blöcken umgeschaltet werden, wobei die Routine jedoch jedesmal die Variable löscht.
Um dem Benutzer die Orientierung zu erleichtern, druckt das Maschinenprogramm nach jedem Umschalten die ersten REM-Zeilen aus. Daher ist es ratsam, jedem Programm eine Kopfzeile mit den wichtigsten Informationen zu geben.
Nach dieser etwas umfangreichen Erläuterung kehren wir nun zur Beschreibung der Zeropage zurück.
Adresse 59,60: Diese Speicherzellen enthalten die laufende Zeilennummer eines Basic-Programms. Sollte es im Programmlauf unterbrochen werden, hat man hier die Möglichkeit, die Zeilennummer nachzulesen.
Adresse 63 — 66: Mit Hilfe dieser Zeiger läßt sich ein zeilennummerngesteuertes RESTORE realisieren. Die ersten zwei Bytes enthalten die momentane Zeilennummer für die DATA-Abfrage. Dies ist sehr praktisch, denn damit kann man jederzeit feststellen, in welcher DATA-Zeile man sich befindet. Das zweite Doppelbyte bildet die Adresse für die nächste DATA-Zeile. Man hat also zwischen Zeilennummer und der absoluten Adresse einer DATA-Zeile zu unterscheiden.
Das nun folgende kurze Maschinenprogramm (Listing 4 und 5) ermöglicht ein Zeilennummern-RESTORE. Man kann damit zwar nicht gezielt auf ein Element in einer DATA-Zeile zugreifen, aber zumindest auf eine bestimmte Zeile.
Was auch im Zusammenhang mit dem Maschinensprachkurs von Interesse sein kann, ist der Aufbau dieser kurzen Routine. Denn durch geschicktes Nutzen von Unterprogrammen aus dem ROM kann man nämlich viel Speicherplatz sparen. Aus diesem Grunde werde ich in einer späteren Folge Interpreter- und Betriebssystemunterroutinen näher beleuchten.
Das vorliegende Maschinenprogramm benötigt genau 43 Byte und liegt am Ende des verfügbaren Basic-Speichers. Die Start- und Endadresse wird vom Lader angegeben. Aufgerufen wird der Zeilenrestore mit: SYS »Startadresse«, »Zeilennumer«.
Die DATA-Zeiger werden dann auf die angegebene Zeile zurückgestellt, was sowohl im Direktmodus, als auch vom Programm aus erfolgen kann.
Zustandsbeschreibung: Das Statusflag
Adresse 144: Dieses Statusflag ist auch von Basic aus über die STATUS beziehungsweise ST-Anweisung abfragbar. Es liefert das Computerstatus-Byte, dessen Inhalt aufgrund der letzten Input-Output-Operation gesetzt wurde. Bezogen auf den Kassettenport liefert es nach Bild 2 bestimmte Meldungen. Die Informationen über die geladenen Programme werden binär wiedergegeben. Die Null signalisiert ein ordnungsgemäß geladenes Programm. 32 hingegen bedeutet, daß ein Prüfsummenfehler vorliegt. Es ist aber auch möglich, daß der Computer mehrere Meldungen in dieses Byte packt, beispielsweise 52 = 32 + 16 + 4 : Hier wurde ein kurzer Block geladen, jedoch ist die Prüfsumme falsch und ein fataler Ladefehler liegt vor.
An dieser Stelle ist es angebracht, sich näher mit dem Aufzeichnungsverfahren zu beschäftigen (Bild 3).
So kommen Programme aufs Band
Jeder Abspeichervorgang beginnt mit dem Header. Dieser Kopf besteht aus dem Vorspann (das ist ein etwa acht Sekunden langer Pfeifton) und dem eigentlichen Programmkopf. Dieser enthält vier wichtige Informationen, nämlich über Programmtyp, Startadresse, Endadresse und Programmname. Diese Daten sind ebenfalls im Bandpuffer zu finden und können von dort abgerufen werden.
Das erste Byte (Adresse 828) gibt Auskunft über den Headertyp. Eine 1 zeigt an, daß es sich um ein Programm handelt, das verschoben geladen werden kann, also auch an eine andere Stelle, als die, von der aus es abgespeichert wurde. Das Gegenstück dazu ist die absolute Lademethode (Headertyp 3), die bereits in der ersten Folge meiner Abhandlung vorgestellt wurde. Gemeint ist LOAD”..”,1,1. Die Sekundäradresse 1 signalisiert dem Computer, daß er das Programm (unabhängig von den Zeigern 43, 44) wieder in den gleichen Adreßbereich laden soll. Um Verwechslungen vorzubeugen, ist es wichtig, Headertyp und Sekundäradresse zu unterscheiden. Für den Headertyp gibt es drei Möglichkeiten:
1: Laden mit Verschiebelader (die Anfangsadresse wird durch den Zeiger 43 und 44 bestimmt).
2: Ein File — also Daten aus Variablen — wurde abgespeichert. Die Unterscheidung ist wichtig, denn Daten können nicht mit LOAD geladen werden.
3: Ein Programm ist absolut zu laden.
Die nächsten vier Bytes geben die Anfangs- und Endadresse des geladenen Files an. Mit der Endadresse hat es eine besondere Bewandtnis. Sie wird nämlich nach korrektem Laden der Zeropage (Adresse 45, 46) übergeben. Bei Load Error geschieht dies nicht; PRINT FRE(0) zeigt dann die volle Bytezahl an, obwohl sich ein Programm im Speicher befindet. Man darf das Programm — das vielleicht nur einen geringfügigen Fehler hat —, dann nicht starten, weil die Variablen dieses überschreiben würden. Abhilfe schafft in diesem Fall
POKE 45,PEEK(831):POKE 46,PEEK (832):CLR.
Die Werte werden damit von »Hand« übertragen und ein normaler Programmablauf ist in den meisten Fällen wieder möglich.
Jetzt aber wieder zurück zu Bild 3: Nachdem der Vorspann und der Programmkopf vor einem Trennzeichen (dies ist ein ganz kurzer Pieps) auf Band geschrieben worden ist, wird der Header gleich noch einmal abgespeichert.
Dies ist eine Eigenheit des Commodore-Systems, das der Datensicherheit dient. Denn nachdem der Programmkopf 1 geladen hat, vergleicht er in einem zweiten Durchgang das bisher geladene (welches sich ja schon im Speicher befindet) mit Programmkopf 2. Eine Abweichung veranlaßt den Computer eine Fehlermeldung auszugeben, in ganz schweren Fällen wird der Ladevorgang gleich ganz unterbrochen. Diese Verfahrensweise gilt nicht nur für den Programmkopf, sondern auch für Programme und Daten — sie alle werden doppelt abgespeichert.
Nachdem also der Programmkopf erkannt worden ist, zeigt der Computer den Filenamen an, und das eigentliche Programm (oder die Daten) wird geladen. Ihnen ist wiederum ein kurzer Vorspann vorangestellt. Der Endblock besteht aus Endmarkierung und einem Nachspann. Er zeigt dem VC 20 das Ende des geladenen Programms an, womit der Ladevorgang beendet ist.
Nach diesem Exkurs zum Kassettenaufzeichnungsformat nun wieder zur Zeropage.
Die STOP-Taste mit Sonderfunktion
Adresse 145: Mit Hilfe dieser Speicherstelle kann man den Zustand der STOP- und der linken SHIFT-Taste abfragen:
Wert =
253: Linke SHIFT-Taste gedrückt
254: STOP-Taste gedrückt
255: Keine der beiden gedrückt
Wenn man das Low-Byte des STOP-Vektors (Adresse 808) auf 114 — statt ursprünglich 112 — setzt, so ist man in der Lage, diese Taste von Basic aus abzufragen, ohne das laufende Programm anzuhalten. Ihr kann dann in der Routine eine besondere Funktion zugewiesen werden, beispielsweise Anhalten des Programmablaufs für eine bestimmte Zeit.
Programmiert oder direkt? Das ist hier die Frage
Adresse 157: Dieses Flag kann nur zwei Zustände annehmen: Entweder 0 oder 128. Ist der Inhalt der Speicherstelle Null, dann arbeitet der Computer gerade ein Programm ab; bei 128 befindet er sich im Direktmodus.
Diese Zeropageadresse kann dann von Bedeutung sein, wenn man eigene Basic-Kommandos definieren will (vergleiche Teil 1).
Zum Beenden einer solchen Routine gibt es zwei Möglichkeiten: Entweder kam der Programmbefehl aus dem Direktmodus. In diesem Fall springt man in die Interpreterschleife (JMP $C474) zurück, damit der Computer READY meldet. Kam das Kommando aber aus einem Basic-Programm, so muß man mit JMP $0079 in die CHRGOT-Routine zurückspringen, damit kein Bereitschaftszeichen ausgegeben wird. Mit Hilfe dieses Flags trifft man hier die Unterscheidung.
Nun wenden wir uns einem anderen Kapitel zu, der Cursorverwaltung. Über die Zeropage kann jederzeit die Cursorposition festgestellt werden. Die Speicherstelle 211 zeigt die Spalte, 214 die Zeile, in der sich der Blinker derzeit befindet. Korrekte Werte liefern die zwei Adressen aber nur innerhalb eines Programms.
Interessanter ist die umgekehrte Verfahrensweise, nämlich das Setzen des Cursors an eine beliebige Bildschirmposition. Dazu müssen allerdings vier Adressen geändert werden. Glücklicherweise nimmt uns eine Unterroutine aus dem Betriebssystem diese Arbeit ab.
Gewußt wo — der Cursor
In Maschinensprache wird das X-Register mit dem Zeilenwert, das Y-Register mit dem Spaltenwert geladen. Danach ruft man mit JSR $E50C das Unterprogramm auf.
Von Basic aus werden die zwei CPU-Register über die Adressen 781 und 782 geladen (warum das so ist, sehen wir in der nächsten Folge): POKE 781,»Zeile«: POKE 782,»Spalte«: SYS 58636.
Diese Verfahrensweise ist, wie man sieht, wesentlich komfortabler als das Hantieren mit den Steuerzeichen.
Eine andere Zeropageadresse kann uns zu einer weiteren Erleichterung verhelfen. Jeder kennt das Problem, den Cursor innerhalb von Anführungszeichen (»Gänsefüßchen«) zu bewegen. Es werden nur Steuerzeichen ausgedruckt, eine Bewegung des Zeigers findet nicht statt.
Dieses Manko läßt sich durch eine kurze Maschinenroutine beheben:
LDA #$00 ; lösche Hochkommaflag
STA $D4 ; speichere es ab
JMP $FEAD ; springe in die NMI-Routine
Ausgelöst wird diese Routine durch Drücken der RESTORE-Taste. Dazu muß vorher der NMI-Vektor (eine nähere Beschreibung nächstes Mal) auf den Beginn unserer Routine gestellt werden. Da die sehr kurz ist, legen wir sie im Kassettenpuffer ab. Dort ist sie solange sicher verwahrt, bis man etwas laden oder abspeichern will. Hier zunächst der Einzeiler, der die Routine in den Bandpuffer generiert:
10 FOR T=828 TO 834: READD: POKE T,D:NEXT: DATA 169, 0, 133, 212, 76, 173, 254: POKE 792, 60: POKE 793, 3
Arbeitet man im Hochkommamodus, so kann man durch Drücken der RESTORE-Taste diesen Betriebszustand abschalten, der Cursor kann dann wieder ganz normal bewegt werden.
Abschließend möchte ich noch sagen, welche Zeropageadressen ohne Komplikationen von eigenen Maschinenroutinen genutzt werden können, was im hohen Maße vom Verwendungszweck abhängt.
Nicht verändert werden dürfen solche Speicherstellen, die vom Betriebssystem gebraucht werden, also die Adressen 192 bis 244 und 160 bis 162. Hat das Maschinenprogramm keine Verbindung zu Basic, so können alle Speicherstellen zwischen 0 und 138 überschrieben werden, weil sich hier nur Basic-Parameter befinden. Eng wird der Platz für die sogenannten Utilities — also für Basic-Ergänzungen oder Hilfsprogramme. Will man Daten nur zwischenspeichem, kann man sie in den Bereich zwischen Adresse 147 bis 159 packen. Dort sind sie bis zum nächsten Lade- oder Abspeichervorgang sicher.
Dauerhaft können Daten nur am Anfang und am Ende der Zeropage abgelegt werden. Dazu gehören Adresse 0 bis 2 und 245 bis 254.
Genau diese werden aber auch von käuflichen Basic-Erweiterungen gebraucht, wodurch es zu Komplikationen kommen kann. Also Vorsicht, erst durch vorheriges Probieren testen, ob man sie benutzen darf!
Damit möchte ich für heute schließen. In der nächsten Folge werden schwerpunktmäßig der Tastaturpuffer, die Basic-Vektoren und die Kernalvektoren behandelt.
(Christoph Sauer/ev)