Der gläserne VC 20 – Teil 3
In der letzten Folge befaßten wir uns schwerpunktmäßig mit der Zeropage. Diesmal wird der sich daran anschließende Adreßbereich von $0100 bis $03FF unter die Lupe genommen.
Dieser Bereich ist so interessant, daß sich die Betrachtungen darüber bis in die 4. Folge erstrecken werden. Übrigens ist dieser Teil auch auf den C 64 anwendbar, denn Betriebssytem und Basic-Interpreter dieser beiden Computer sind ja nahezu identisch.
Wenn wir einen Basic-Befehl im Direktmodus (LIST, RUN, PRINT) oder eine Programmzeile mit Zeilennummer eingeben, werden die Informationen — wie bekannt — auf den Bildschirm geschrieben und gelangen gleichzeitig in den Basic-Eingabepuffer (Adresse 512-600/ $0200-$0258). Dort werden Programmzeilen oder direkte Befehle zunächst einmal im ASCII-Format gesammelt (Bild 1a). Dies geschieht solange, bis man die RETURN-Taste betätigt.
![](126-1a.jpg)
Dieser Puffer hat eine Kapazität von 88 Zeichen — also den bekannten vier Bildschirmzeilen.
Der Weg einer Eingabezeile
Drückt man die RETURN-Taste, so beginnt der Interpreter mit der Auswertung der Kommandos. Eingaben ohne Zeilennummer werden auf ihre Syntax hin überprüft und danach ausgeführt.
Verfolgen wir nun einmal genauer den Weg einer Befehlszeile. Die einzelnen ASCII-Zeichen werden von der inzwischen hinreichend bekannten CHRGET-Routine aus dem Puffer gelesen und mit den Befehlswörtern aus dem ROM verglichen. War die Überprüfung positiv — ist der Befehl als identifiziert worden —, so verkürzt der Computer die Kommandozeile, indem er die Befehle in Token (siehe Teil 1, Tabelle 1) umwandelt. Diese Prozedur durchlaufen sowohl die Programmzeilen (mit Zeilennummer) als auch die direkten Kommandos (Bild 1b). An dieser Stelle trennen sich nun aber die Wege dieser beiden Zeilentypen.
![](126-1b.jpg)
Dezimal | Hexadezimal | Bemerkung |
---|---|---|
255-266 | 00FF-010A | Arbeitsspeicher für Fließkomma nach ASCII |
256-318 | 0100-013E | Korrekturpuffer für Bandbetrieb |
256-511 | 0100-01FF | Prozessor Stack |
512-600 | 0200-0258 | Basic-Eingabepuffer |
601-610 | 0259-0262 | Tabelle der logischen Filenummern... |
611-620 | 0263-026C | ....sowie der dazugehörigen Gerätenummern.... |
621-630 | 026D-0276 | ...und der entsprechenden Sekundäradressen |
631-640 | 0277-0280 | Tastaturpuffer |
641-642 | 0281-0282 | Start des verfügbaren RAM-Bereichs |
643-644 | 0283-0284 | Ende des verfügbaren RAM-Bereichs |
645 | 0285 | Timeout-Flag für den seriellen Port |
646 | 0286 | Aktueller Farbcode |
647 | 0287 | Farbe unter dem Cursor |
648 | 0288 | High-Byte des Bildschirmspeichers (Information für das Betriebssystem) |
649 | 0289 | Größe des Tastaturpuffers (Maximum 10) |
650 | 028A | Repeat-Flag (0: Cursor + Space/ 64: Keine Taste/ 128: Alle Tasten mit Repeat) |
651 | 028B | Repeat-Zähler (bestimmt die Wartezeit bis die Taste wiederholt wird) |
652 | 028C | Repeat-Verzögerung (bestimmt die Zeit, bis die Taste das erste mal wiederholt wird) |
653 | 028D | Kontrolltasten-Flag (1: SHIFT/ 2: CBM/ 4: CTRL. Es können auch 2 Tasten erkannt werden zum Beispiel 5: SHIFT + CTRL) |
654 | 028E | Letzte Kontrolltaste (Identisch mit 653) |
655-656 | 028F-0290 | Vektor für Tastaturdecodierung |
657 | 0291 | Flag für SHIFT + CBM gesperrt (keine Groß-Kleinschrift Umschaltung. 0: Normal/ 128: Sperre) |
658 | 0292 | Flag für Scrolling |
659-670 | 0293-029E | RS 232 Register, Zeiger, ect. |
671-672 | 029F-02A0 | Zwischenspeicher für IRQ bei Bandbetrieb |
673-676 | 02Al-02A4 | Diverse VIA-Zeiger |
677-767 | 02A5-02FF | Zwischenspeicher einer Bildschirmzeile |
786-769 | 0300-0301 | Vektor für Fehlermeldung (C43A) |
770-771 | 0302-0303 | Vektor für Basic-Warmstart (C483) |
772-773 | 0304-0305 | Vektor für Umwandlung von ASCII in Token (C579) |
774-775 | 0306-0307 | Vektor für Umwandlung von Token in ASCII (C717) |
776-777 | 0308-0309 | Vektor für Basic-Befehlsadresse (C7E1) |
778-779 | 030A-030B | Vektor für arithmetisches Element (CE83) |
780 | 030C | Akku für SYS-Befehl (Bei SYS wird 780 in den Akku geladen) |
781 | 030D | X-Reg für SYS-Befehl |
782 | 030E | Y-Reg für SYS-Befehl |
783 | 030F | Speicher für Status Register für SYS-Befehl |
788-789 | 0314-0315 | IRQ-Vektor (EABF) *** Kernal-Vektoren |
790-791 | 0316-0317 | BRK-Vektor (FED2) |
792-793 | 0318-0319 | NMI-Vektor (FEAD) |
794-795 | 031A-031B | OPEN-Vektor (F40A) |
796-797 | 031C-031D | CLOSE-Vektor (F34A) |
798-799 | 031E-031F | Kanal für Eingabe (CHKIN, F2C7) |
800-801 | 0320-0321 | Kanal für Ausgabe (CHOUT, F309) |
802-803 | 0322-0323 | Kanäle initialisieren (CLRCH, F3F3) |
804-805 | 0324-0325 | Eingabe-Vektor (F20E) |
806-807 | 0326-0327 | Ausgabe-Vektor (F27A) |
808-809 | 0328-0329 | Vektor für STOP-Taste prüfen (F770) |
810-811 | 032A-032B | GET-Vektor (F1F5) |
812-813 | 032C-032D | Alle Files schließen ((F3EF) |
814-815 | 032E-032F | User-Vektor (FED2) |
816-817 | 0330-0331 | LOAD-Vektor (F549) |
818-819 | 0332-0333 | SAVE-Vektor (F685) |
828-1019 | 033C-03FB | Kassettenpuffer |
Zunächst zu dem weiteren Weg einer Programmzeile. Die inzwischen komplett übersetzte Zeile im Basic-Puffer wird nun in einem zweiten Durchlauf vom Zwischenspeicher in den Programmspeicher übertragen, wobei sie auch gleich richtig eingeordnet wird (damit die Reihenfolge der Zeilennummern stimmt). Weil sich dadurch der Programmbereich im Basic-Speicher vergrößert, muß der Variablenbereich weiter oben angesiedelt werden. Die bis dahin gespeicherten Variablen werden dadurch natürlich überschrieben. Nach dem Übertragen der Programmzeile springt das Interpreterprogramm wieder in die Warteschleife zurück, damit weitere Befehle entgegen genommen werden können.
Nun möchte ich den weiteren Weg einer Direktmoduszeile beschreiben, denn der verläuft anders. Nachdem die Zeile mit Hilfe der CHRGET-Routine in Interpretercode (also Token) umgewandelt worden ist, wird der 2-Byte-Zeiger ($7A-$7B) auf den Pufferanfang zurückgestellt und der Inhalt wie eine Programmzeile behandelt und abgearbeitet (wieder mit Hilfe der CHRGET-Routine).
Verbotenes
Die Befehle INPUT und GET dürfen im Direktmodus nicht verwendet werden. Der Grund liegt darin, daß diese Eingabebefehle ebenfalls den Basic-Eingabepuffer zur Zwischenspeicherung der Daten verwenden. Das »Direktmodusprogramm«, was sich zu dieser Zeit im Puffer befindet, würde dann überschrieben. Um das zu verhindern, sind diese Kommandos im Direktmodus verboten (Fehlermeldung »ILLEGAL DIRECT«).
Die Tastaturverwaltung
Die Schlüsselfunktionen für Basic wie beispielsweise die Umwandlung der ASCII-Zeichen in Token (wie eben beschrieben), Umwandlung der Token in Klartext (die Befehlswörter werden bei LIST wieder als ASCII-Zeichen sichtbar gemacht), werden durch indirekte Sprünge über Vektoren abgewickelt. Durch Zusatzroutinen besteht so die Möglichkeit, Klartextbefehle wie SOUND, KEY, OLD ect. in den Interpreter mit einzubinden. Dieses Verfahren besprechen wir später, zunächst aber schreiten wir in der Betrachtung des Adreßbereiches von $0277-$03FF fort (dieser ist in Tabelle 1 genauer aufgelistet).
Der nächste, größere Komplex, den wir hier behandeln wollen, ist die Tastaturverwaltung. Wie sie bestimmt schon öfter bemerkt oder gelesen haben, wickelt der VC 20 (der C 64 übrigens auch) seine Tastaturoperationen ebenfalls über einen Puffer ab. Dies wird beim LISTen von Basic-Programmen deutlich, denn während der Computer ein Programm ausgibt, können sie alle Tasten (mit Ausnahme des STOP-Keys) drücken — diese erscheinen aber nicht auf dem Bildschirm. Erst wenn das Programm zu Ende gelistet ist, sieht man, daß keine Taste »vergessen« wurde, denn alle Eingaben wurden im Tastaturpuffer (Adresse 631-640/ $0277-$0280) zwischengespeichert .
Dieser Puffer hat eine Kapazität von maximal zehn Zeichen. Wem dies zuviel ist, der kann die Länge des Puffers durch Adresse 649 einstellen. Ratsam kann dies bei relativ langsamen Basic-Spielen sein, wo es empfehlenswert ist, nur ein Zeichen im Tastaturpuffer zwischenzuspeichern (POKE 649,1). Anderenfalls »hinkt« die Tastatur immer den Ereignissen hinterher.
Mit Hilfe des Tastaturpuffers kann aber auch — und das ist das interessante an diesen zehn Bytes — eine relativ unkonventionelle Art der Programmierung praktiziert werden.
Der »DATA-Erzeuger«
Um dies verständlich zu machen, habe ich ein Programm (Listing 1) geschrieben, welches DATA-Zeilen aus Maschinenprogrammen oder Sonderzeichen erzeugt. Die Problematik dabei ist folgende: Wenn ich DATA-Zeilen ins Programm schreiben möchte, so ist dies nur im Direktmodus möglich. Um dies automatisch zu tun, benötigen wir ein Programm. Also was tun? — Man bedient sich des Tastaturpuffers!
Dazu nochmals die Durchleuchtung der Funktionsweise. Während des Systeminterrupts (was dort geschieht klären wir in einer der nächsten Folgen), der alle 60stel Sekunde durchlaufen wird, fragt der Computer die Tastatur ab. Ein eingegebenes Zeichen wird dabei im Tastaturpuffer abgelegt, wo es so lange verbleibt, bis ein Zeichen von der Tastatur benötigt wird. Dies ist zum Beispiel im Direktmodus der Fall (wenn der Cursor blinkt) oder im Programm — bei INPUT oder GET. Die Zeichen werden dann wieder aus dem Tastaturpuffer hervorgeholt und zwar nach dem Prinzip »First in, First out«. Bei unserem Programm werden zunächst einmal sechs Speicherzellen initialisiert. Die ersten zwei Adressen enthalten die Anfangsadresse der abzuspeichernden Daten aus dem Speicher. Dieser Zeiger wird solange inkrementiert, bis er den Wert der Endadresse — der in den zwei folgenden Bytes abgelegt ist — erreicht hat. In den letzten zwei Speicherstellen (Adresse 252, 253) steht die Zeilennummer in der Reihenfolge Low-/ High-Byte. Bequemer währe es natürlich, wenn man normale Variablen verwenden könnte. Dies ist jedoch nicht möglich, weil diese beim Einfügen einer DATA-Zeile gelöscht werden. Darum bleibt nur der Umweg über Speicherstellen, deren Inhalte durch Basic nicht überschrieben werden können.
Als nächstes erzeugt das Programm — mit Hilfe der in 252/253 gespeicherten Zwei-Byte-Zahl — eine Zeilennummer, welche auf den Bildschirm gePRINTet wird. Dann schreibt es das Befehlswort »DATA« und druckt acht dreistellige Zahlen (die Daten aus dem zu verarbeitenden Maschinenprogramm) aus. Nun haben wir eine fertige Programmzeile auf dem Bildschirm stehen, die sich allerdings noch nicht im Speicher befindet. Dies erledigt jetzt unser Tastaturpuffer.
Vorher müssen wir und jedoch genau überlegen, wie unser Bildschirm aussieht, denn dementsprechend muß der Cursor programmiert werden.
Cursorprogrammierung einmal anders
Bild 2 zeigt ein Bildschirmfoto dieser Situation. Zuerst wird der Cursor mit HOME (= CHR$ (19)) an die linke obere Ecke befördert. Dort wird durch einen Druck auf die RETURN-Taste (=CHR$(13)) die Basic-Zeile in den Speicher übernommen. Nun befindet sich der Cursor in der dritten Zeile. Durch ein »Cursor down« (=CHR$(17)) bewegt er sich eine Zeile nach unten und steht nun auf dem Befehlswort RUN 60. Ein weiteres RETURN bewirkt den erneuten Start des Hilfsprogramms.
![](126-2.jpg)
Wir benötigen für die Cursorbewegung also vier Werte. Diese werden vor dem Ende des Programms mit »POKE 631,19: POKE 632, 13: POKE 633,17: POKE 634, 13: POKE 198,4« in den Tastaturpuffer geschrieben. Hierdurch simulieren wir eine gedrückte Tastenfolge: denn nachdem sich der Computer über den Befehl END wieder im Direktmodus befindet, wird zuerst der Tastaturpuffer geleert, wodurch der Cursor den vorbestimmten Weg nimmt. POKE 198,4 gibt an (das noch als Nachtrag) wieviele Zeichen sich momentan im Puffer befinden. Dieser POKE-Befehl darf bei der Manipulation des Tastaturspeichers nie vergessen werden. Ferner ist die Bereitschaftsmeldung »READY«, die beim Übergang in den Direktmodus ausgegeben wird, zu berücksichtigen. Man muß beachten, daß dadurch nicht die bereits auf dem Bildschirm befindlichen Zeichen überschrieben werden.
Die Bedienung des Programms an sich ist ganz einfach. Nach Eingabe der Anfangs- und Endadresse werden die DATA-Zeilen mit jeweils acht Elementen in den Programmspeicher generiert; begonnen wird mit Zeilennummer 300, die in 10er-Schritten erhöht wird. Da sich das Listing selbst kommentiert, erübrigt sich alles Weitere. Der Vollständigkeit halber ist noch zu erwähnen, daß die REM-Zeilen nicht mit eingegeben werden müssen.
Benutzt wurde der Tastaturpuffer bereits in einem Programm, das im ersten Teil dieser Serie abgedruckt wurde. Die Rede ist von der Autostartroutine, welche einen Basic-Programmstart (aus einem Maschinenprogramm heraus) durch Füllen des Puffers mit dem Kommando RUN (+ CHR$ (13)) realisierte.
Die Eckadressen
Als nächstes besprechen wir die Adressen 641-644/ $0281-$0284. Sie enthalten die Anfangs- beziehungsweise Endadresse des verfügbaren Speicherbereiches.
Bei dem Systemreset ($FD22/ 64802) werden verschiedene Routinen wie beispielsweise Initialisierung der Zeropage, Setzen der Vektoren, RAM-Test ect. durchlaufen. Dabei wird unter anderem auch der verfügbare Speicherplatz festgestellt und die Eckadressen den obengenannten Registern übergeben. Diese Daten sind die Grundlage für alle weiteren Grundeinstellungen des Systems, also Basic-Beginn und -Ende, Beginn des Video- und Farbspeichers ect.
Wird es nun nötig, eine Speicherkonfiguration zu simulieren, beispielsweise Grundversion bei eingesteckter 8-KByte-Erweiterung, so kann das über diese Zeiger geschehen:
POKE 642, 16: POKE 644, 30: SYS 64970: SYS 64821 bewirkt diese Simulation. Das Unterprogramm im Betriebssystem (Adresse 64970), das zur Reset-Routine gehört, hat die Aufgabe, die Seiten 0 (Zeropage), 2 und 3 zu löschen. Danach prüft der Computer seine Speicherzellen. Dieser Test hat die Aufgabe, das RAM auf seine Funktionsfähigkeit hin zu überprüfen, damit es nicht zu Fehlfunktionen durch einen beschädigten Speicher kommt. Bei dieser Gelegenheit wird die Ausbaustufe des Speichers festgestellt; das Ergebnis findet sich dann in den Registern für die Anfangs- beziehungsweise Endadresse. Hier steigen wir nun mit den entsprechend manipulierten Registern (642 auf 16 und 644 auf 30) in die ROM-Routine ein. Mit Hilfe dieser Werte richtet das Unterprogramm nun den Video- und Farbspeicher ein. Mit dem zweiten SYS-Befehl (SYS 64821) wird die Initialisierung fortgesetzt. Dabei richtet er den Speicher für Basic ein und gibt die Kaltstartmeldung
CBM BASIC V2
3583 BYTES FREE
aus.
Soweit ein kleiner Einblick ins Betriebssystem, eine ausführlichere Erläuterung erfolgt in einer der nächsten Folgen.
Die Speicherorganisation
Jetzt möchte ich noch einen kleinen Einblick in die Speicherorganisation geben, was auch im Hinblick auf Grafik von Bedeutung ist. Wie hinreichend bekannt sein dürfte, gibt es drei verschiedene Speichererweiterungen zu kaufen, nämlich 3 KByte, 8 KByte und 16 KByte. Die Sammelerweiterungen (also 27/32/64 KByte-Erweiterungen sollen hier gedanklich ebenfalls in diese drei verschiedenen Module zerlegt werden.
Bei Erweiterungen von mehr als 8 KByte verändert sich die Lage des Bildschirm- und Farbspeichers (die Einstellung nimmt die Reset-Routine vor). Anhand von Bild 3 soll erläutert werden, warum eine Verschiebung notwendig ist.
![](126-3.jpg)
Der Video-Interface-Chip VIC (daher auch der Englische Name des VC 20), der vor allem für die Erzeugung des Fernsehsignals und den Aufbau des Bildschirmes verantwortlich ist, kann hardwaremäßig nur Videospeicherplätze zwischen 4096 und 8192 adressieren. Folglich muß das Bildschirm-RAM in diesem Bereich angesiedelt werden.
In der Grundversion liegt es zwischen Adresse 7680 und 8191 — also am Ende des verfügbaren Speichers, damit der Speicherbereich für Basic auch bei eingesteckter 3 KByte-Erweiterung durchgängig ist (läge der Bildschirmspeicher sowie bei einer 8-KByte-Erweiterung, währe dies nicht der Fall).
Ist ein Speichermodul von mehr als 8 KByte eingesteckt (egal ob der 3-KByte-Bereich zugeschaltet ist oder nicht), so legt das System den Videospeicher an die unterste adressierbare Stelle für den VIC, also Adresse 4096. Aus diesem Grund kann die eingesteckte 3-KByte-Erweiterung nicht mehr für Basic benutzt werden, denn sonst wäre der Speicher nicht mehr durchgängig.
Programme sollten immer auf allen Erweiterungsversionen lauffähig sein. Wer also in Routinen mit dem Bildschirm- oder Farbspeicher arbeitet, kann sich mit Hilfe der Register 36866 und 86869 im VIC die momentanen Adressen beschaffen.
- Bildschirm: 4*(PEEK(36866)AND128) + 64*(PEEK(36869)AND120)
- Farbspeicher: 4*(PEEK(36866)AND128) + 37888
Gewußt wo — Die Bildschirmadressen
Mit Hilfe bestimmter Bits aus diesen Registern bildet der VIC die Adressen, die er benötigt, um — unabhängig vom Prozessor — Bild- und Farbspeicherstellen auszulesen, damit er mit diesen Informationen das Fernsehbild erzeugen kann.
Das Betriebssystem hingegen bezieht seine Informationen über die Lage des Videospeichers nicht aus diesen VIC-internen Registern, sondern aus Adresse 648. Gibt man beispielsweise »POKE 648,28« ein, so liegt der Bildschirmspeicher zwischen 7168 und 7679. In Wirklickkeit stellt der Video-Interface-Chip — der, wie gesagt, unabhängig arbeitet — weiterhin den Speicherausschnitt zwischen Adresse 7680 und 8191 auf dem Bildschirm dar. Der Cursor schreibt also in einem ganz anderen Speicherabschnitt. Erst durch einen Warmstart (durch die Tastenkombination RUN/ STOP — RESTORE) werden die VIC-Register angepaßt.
Vom Bildschirm nun wieder zur Tastatur. In unseren systematischen Betrachtungen der Seite 1 bis 3 im Speicher, kommen wir nun zu den Adressen $0280-$0291/649-656, die der Tastatur zugeordnet sind. Sie enthalten lediglich Parameter für die Arbeit mit der Tastatur wie zum Beispiel Repeat Flag, das Flag für Kontrolltasten ect. Näheres entnehmen sie bitte Tabelle 1.
Praktisches: Die Befehlseingabe über Funktionstasten
Zwei weithin unbekannte Adressen ($028F, $0290/ 655,656) sind vorzüglich dazu geeignet, eine Funktionstastenabfrage auf Interruptbasis zu realisieren. Besagte Adressen bilden einen Vektor für die Tastaturdecodierung, der meines Erachtens nur für den Zweck der Funktionstastenabfrage geschaffen wurde.
Was sind überhaupt Vektoren? ROM, so sagt ja bereits der Name (Read Only Memory), ist grundsätzlich nicht überschreibbar. Wer dennoch das System ergänzen will (beispielsweise durch neue Basic-Befehle), ist gezwungen, das gesamte ROM auszutauschen, es sei denn, die Schöpfer des Computers haben mögliche Optionen — so wie beim VC 20 (oder C 64) — bereits eingeplant. Dies geschieht, indem aus dem ROM heraus ins RAM verzweigt wird.
Normalerweise steht an der entsprechenden RAM-Adresse nur ein Zeiger auf eine ROM-Adresse, eben ein Vektor. Durch Änderung eines solchen Vektors kann man elegant bestehende Routinen umgehen und sie durch eigene ersetzen beziehungsweise ergänzen. Soweit, so gut.
Wie im Handbuch zu lesen, gibt es Unterschiede zwischen den Bildschirmcodes eines Zeichens (»A« beispielsweise hat den Wert 1) und dem allgemein verbreiteten ASCII-Code (»A« hat hier den Wert 65). Gleiches gilt für die Tastatur. Hier unterscheidet man ebenfalls zwischen dem ASCII- und dem sogenannten Tastatur-Matrixcode. Diese VC 20-interne Codierung wird durch eine Betriebssystemroutine — die über einen Vektor verfügt (den oben erwähnten für die Tastaturdecodierung) — in ASCII-Zeichen umgewandelt. Das Maschinenprogramm in Listing 2 und 3 wird durch »verbiegen« des Vektors 655/656 in die Tastaturroutine mit eingebaut. Es fragt die Codes der Funktionstasten ab und druckt die Zeichen aus, mit denen sie belegt wurden.
Das recht komfortable Programm liegt als Basic-Lader in Listing 2 vor. Nach dem Starten mit RUN wird das Maschinenprogramm automatisch ans Ende des verfügbaren Speichers geladen. Mit der SPACE-Taste aktiviert man diese Routine. Als erstes werden die Befehle gelistet, mit denen die Funktionstasten belegt sind. (Tabelle 2).
Fl: LIST | - LIST-Befehl mit CHR$(13) |
F2: RUN | - wie bei LIST |
F3: INPUT | |
F4: GOSUB | |
F5: SYS0 | |
F6: RETURN | |
F7: RESTORE | |
F8: LOAD’ | — ’ entspricht dem Hochkomma (”) |
Auffällig ist bei den Kommandos LIST und RUN der Pfeil nach oben (↑). Er bewirkt ein sofortiges Ausführen des Befehls — entspricht also »LIST« und RETURN-Taste beziehungsweise »RUN« und RETURN-Taste. Das andere auffällige Zeichen ist der Apostroph (‘) bei dem Kommando LOAD, der dem Hochkomma (”) entspricht. Das echte Gänsefüßchen ist bereits für die Syntax des Änderungsbefehls (siehe unten) vergeben.
Auf den Befehl π wie er beispielsweise beim Programm »Basic-Switch« (Folge 2) verwendet wurde, habe ich dieses Mal aus Platzgründen verzichtet, damit Lader und Maschinenprogramm in der Grundversion Platz finden. Die Kommandos werden statt dessen über den Befehl »SYS0« (oder F5) eingegeben. Dabei machen wir uns den USR-Vektor (Adresse 0-2) als Sprungzeiger zu Nutze. Damit zur Syntax bei der Funktionstastenprogrammierung:
- SYS0 L – Listen der Funktionstastenbelegungen
- SYS0 O – (Off) schaltet die Funktionstasten ab
- SYS0 R – (Restart) schaltet die Funktionstasten wieder ein.
- SYS0,X, »befehl« – belegt die X-te Funktionstaste mit einem Befehl.
Zum Schluß kommen wir noch zu einem Thema, mit dem ich mich mehr an den fortgeschrittenen Maschinensprachenprogrammierer wenden möchte. Im ersten Teil dieser Serie wurde beschrieben, wie der Basic-Befehlssatz mit Hilfe der CHRGET-Routine und des Befehles »π« im beschränktem Umfang erweitert werden kann. Diese Methode hat allerdings viele Unzulässigkeiten; es sind beispielsweise keine verkürzten Befehle wie beim normalen Basic (LIST = L SHIFT I) möglich.
Nun soll beschrieben werden, wie der Basic-Befehlssatz um richtige Klartextkommandos erweitert werden kann. Auch hierfür müssen wir bestehende Interpreterroutinen, die über Vektoren angesprungen werden, umgehen beziehungsweise ergänzen.
Für das Verarbeiten von Basic-Programmen sind drei Schlüsselroutinen zu substituieren. Da ist zunächst das Unterprogramm »ASCII in Token wandeln«. ($C57C) welches — wie der Name bereits sagt — die Aufgabe hat, Eingabezeilen im Basic-Puffer (wie zu Anfang beschrieben) in Interpretercode zu wandeln. Das Gegenstück dazu ist die Unterroutine »Interpretercode in Klartext wandeln.« Will man Basic-Zeilen sichtbar machen, so benutzt man das Kommando LIST. Dazu wird eben diese ROM-Routine benötigt, die die Rückumwandlung der Token in ASCII-Zeichen vornimmt.
Sämtliche Befehle sind in Form von ASCII-Zeichen im Basic-ROM enthalten, lediglich zum letzten Buchstaben jedes Befehlswortes wurde 128 ($80) addiert. Läßt man sich die Befehle durch
FOR T= 49310 TO 47565: PRINT CHR$(PEEK(T)):NEXT
ausdrucken, so sehen die Kommandos im Groß-/Kleinschrift-Modus folgendermaßen aus:
END = enD
FOR = foR ect.
Ferner ist jedem Basic-Befehl eine Adresse zugeordnet, bei der eine Abarbeitung vorgenommen wird. Diese Adressen sind in einer Tabelle (von $C00C $C07F) zusammengefaßt. Eine spezielle Routine hat wiederum die Aufgabe, die Befehlsadresse für ein entsprechendes Token aus der Tabelle zu lesen, und einen Sprung nach dorthin durchzuführen. Auch dieses Unterprogramm kann man mittels eines Vektors umgehen.
Damit haben wir das nötige Rüstzeug, um selbst Basic-Kommandos in den Interpreter mit aufzunehmen. In Listing 4 ist ein dafür geeignetes Programm abgedruckt, welches ich jetzt näher erläutern möchte. Es ist der Kopf für ein Utility, in das nach Belieben Befehlswörter und Sprungadressen eingesetzt werden können.
Die Routine gliedert sich in vier Teile: Der erste Teil (von $2000 - $203A) ist eine Kopie der Reset-Routine. Dabei wird der Computer neu initialisiert und eine neue Kaltstartmeldung
**** 64’ER BASIC ****
ausgedruckt. Hier ist genügend Platz vorgesehen, damit der Text nach eigenen Wünschen gestaltet werden kann. Ferner wurden die ersten neun Bytes mit NOPs versehen, damit dort — falls das Programm im Modulbereich abgelegt wird — die obligate Autostartinformation (a0CBM) eingesetzt werden kann. Anderenfalls — wenn das Maschinenprogramm per SYS gestartet wird — muß der erste Mnemonic-Befehl ein SEI sein.
Die sich jetzt anschließenden Programmteile sind die oben erwähnten Ergänzungen zu den Interpreterroutinen. Dies sind teilweise Kopien aus den alten ROM-Routinen mit Ergänzungen für die neuen Basic-Kommandos. Die Befehlswörter müssen jetzt nur noch eingetragen werden. Das geschieht folgendermaßen:
Die Befehle werden mit Hilfe des Programms »Tokenerzeuger« (Listing 5) in den entsprechenden Adreßbereich geschrieben. Bei nachträglichen Eintragungen ist zu beachten, daß zum letzten Buchstaben jedes Befehlswortes der Wert $80 (=128) zu addieren ist. Die Ergänzungsbefehle beginnen mit Token 204. Das erste Befehlswort beginnt mit dieser Nummer, der zweite erhält automatisch die 205 und so weiter.
Weiterhin ist für jedes Kommando die Sprungadresse in der Tabelle ($2208-$2268) zu vermerken. Dabei muß die Reihenfolge Low-/High Byte beachtet werden. Außerdem muß das LOW-Byte vor dem Eintrag um eines dekrementiert werden (LOW Byte -1). Auch diese Arbeit erledigt das Programm in Listing 5.
Ein Befehl wurde bereits eingetragen. Der Befehl KILL führt einen Reset durch und damit ist das Programm abgeschaltet. Wer also seine Maschinenprogramme ins Basic einbinden möchte, kann dies mit der beschriebenen Methode tun. Beispielsweise könnte man die in Listing 2 abgedruckte Funktionstastenroutine mit dem KEY-Befehl belegen. Auch im Hinblick auf Grafik, Tonerzeugung oder Joystickabfrage bietet sich hier die Möglichkeit, die Unzulänglichkeiten des Basics zu überwinden. Auch die Besitzer eines C 64 können die beschriebene Routine benutzen, da die Basic-ROMs nahezu identisch sind (sie haben lediglich eine andere Adresse). Außerdem habe ich in Tabelle 3 eine Liste der wichtigsten Unterprogramme zusammengestellt, mit denen man unter anderem Parameter, Strings, Kommas oder ähnliches abfragen kann. Auf jeden Fall sollte man sich für diese Arbeit ein ROM-Listing (zum Beispiel »VC 20 Intern« von Data Becker) zulegen.
Adresse | Funktionsbeschreibung | Bemerkung | Übergaberegister |
C613 | Startadresse einer Programmzeile berechnen | Eingabe: Zeile in $14,15 Ausgabe: Adresse in $5F, 60 | |
C807 | Prüft auf Doppelpunkt | Zur Syntaxkontrolle | |
CD8A | Ausdruck holen (z.B. Zeichen) | $D7FD wandelt Fließk. in Int. | |
CAA0 | PRINT-Befehl: Ausdruck oder String holen und ausdrucken | ||
CEF7 | Prüft auf Klammer zu »)« | Syntaxkontrolle | |
CEFA | Prüft auf Klammer auf »(« | Syntaxkontrolle | |
CEFD | Prüft auf Komma | Syntaxkontrolle | |
CF04 | »SYNTAX ERROR« ausgeben | ||
D113 | Prüft auf Buchstabe (A-Z) | ||
D248 | »ILLEGAL QUANTITY« ausgeben | ||
D79B | Byte-Wert (0-255) holen | Bei der Argumentabfrage |
Damit möchte ich für heute schließen. Das nächste Mal werden wir sehen, was man mit den Kernal-Vektoren anfangen kann und betrachten die Grafikmöglichkeiten beim VC 20.
(Christoph Sauer/ev)1 rem ************************* 2 rem **** data-erzeuger **** 3 rem **** **** 4 rem **** by c. sauer **** 5 rem **** muenchen **** 6 rem ************************* 10 input"{clr}{down}{down}startadresse ";sa 20 input"{down}endadresse ";ea 30 a=sa:gosub250:poke248,a1:poke249,a2 40 a=ea:gosub250:poke250,a1:poke251,a2 50 poke252,44:poke253,1 55 rem *** startzeilennummer = 300 60 def fnz(x)=peek(x)+peek(x+1)*256 65 rem *** doppelpeek funktion 70 ad=fnz(252) 75 rem *** ad = aktuelle zeilennummer 80 print"{clr}"ad"dA "; 90 fort=0to7 95 rem *** data zeile mit 8 elementen 96 rem *** erzeugen 100 b=peek(fnz(248)+t) 110 b$=str$(b):l=len(b$) 120 b$=right$(b$,l-1) 130 ifl=4then150 140 fory=l+1to4:b$="0"+b$:next 145 rem *** jedes element ist 3 stellig 150 b$=","+b$ 160 ift=0thenb$=right$(b$,3) 170 printb$;:iffnz(248)+t>=fnz(250) then230 175 rem *** lfd. adresse > end adresse ? 180 next 190 a=ad+10:gosub250:poke252,a1: poke253,a2 200 a=fnz(248)+8:gosub250:poke248,a1: poke249,a2 210 print:print"{down}run 60" 213 rem *** tastaturpuffer mit <home>, 215 rem *** <return>, <crsr down> und 217 rem *** <reutrn> fuellen 220 poke631,19:poke632,13:poke633,17: poke634,13:poke198,4 230 end 250 a2=int(a/256):a1=a-a2*256 260 return
100 dims(2) 110 s(0)=18372:s(1)=16820:s(2)=7987 120 print"{clr}{down}funktionstasten:" 130 fory=0to2:ps=0 140 fort=0to151:readd:ps=ps+d:next 150 ifps<>s(y)thenprint"{down}{rvon}fehler in zeile":goto170 160 goto180 170 print"{rvon}"y*190+310"{left}-"(y+1)*190+310:end 180 next 190 poke55,56:poke56,peek(56)-2:clr 200 ad=peek(56):pa=ad*256+57:restore 210 fort=patopa+454 220 readd 230 ifd=-1thend=ad 240 ifd=-2thend=ad+1 250 poket,d:next 260 print"{pur}{down}start mit sys{blu}"pa 270 print"{down}aktivierung mit {rvon}space{rvof}" 280 geta$:ifa$<>" "then280 290 syspa 300 sys0l 310 data169,239,141,143,002,169,-01,141 320 data144,002,169,205,141,024,003,169 330 data-02,141,025,003,169,076,133,000 340 data169,090,133,001,169,-01,133,002 350 data096,032,121,000,201,076,208,052 360 data169,049,133,250,162,000,169,016 370 data133,251,169,013,032,210,255,169 380 data070,032,210,255,165,250,032,210 390 data255,169,032,032,210,255,189,077 400 data-02,032,210,255,232,198,251,208 410 data245,230,250,165,250,201,057,208 420 data213,076,115,000,032,121,000,201 430 data079,208,003,076,210,254,201,082 440 data208,006,032,057,-01,076,115,000 450 data201,044,240,003,076,008,207,032 460 data155,215,224,009,144,003,076,072 470 data210,202,134,250,032,121,000,201 480 data044,208,233,032,115,000,201,034 490 data208,226,138,010,010,010,010,170 500 data160,016,032,115,000,201,034,240 510 data009,157,077,-02,232,136,208,242 520 data240,009,169,000,157,077,-02,232 530 data136,208,249,076,115,000,165,157 540 data240,012,165,203,162,005,221,001 550 data-02,240,010,202,208,248,076,220 560 data235,039,047,055,063,197,197,240 570 data245,133,197,138,024,010,133,250 580 data173,141,002,201,001,240,002,198 590 data250,165,250,170,202,138,010,010 600 data010,010,133,251,168,185,077,-02 610 data240,016,201,094,240,015,201,039 620 data208,002,169,034,032,210,255,200 630 data208,235,169,000,133,207,240,009 640 data169,013,141,119,002,169,001,133 650 data198,076,214,235,076,073,083,084 660 data094,000,000,000,000,000,000,000 670 data000,000,000,000,082,085,078,094 680 data000,000,000,000,000,000,000,000 690 data000,000,000,000,073,078,080,085 700 data084,000,000,000,000,000,000,000 710 data000,000,000,000,071,079,083,085 720 data066,000,000,000,000,000,000,000 730 data000,000,000,000,083,089,083,048 740 data000,000,000,000,000,000,000,000 750 data000,000,000,000,082,069,084,085 760 data082,078,000,000,000,000,000,000 770 data000,000,000,000,082,069,083,084 780 data079,082,069,000,000,000,000,000 790 data000,000,000,000,076,079,065,068 800 data039,000,000,000,000,000,000,000 810 data000,000,000,000,120,072,138,072 820 data152,072,173,029,145,016,037,045 830 data030,145,170,041,002,240,026,044 840 data017,145,032,052,247,032,225,255 850 data208,018,032,082,253,032,249,253 860 data032,024,229,032,057,-01,108,002 870 data192,076,222,254,076,086,255,000
>> FUNKTIONSTASTEN << ****************** INITIALSIERUNG 1C39 LDA #$EF 1C3B STA $028F 1C3E LDA #$1C ; AENDERUNG DES 1C40 STA $0290 ; TASTATUR-VEKTORS 1C43 LDA #$CD 1C45 STA $0318 1C48 LDA #$1D 1C4A STA $0319 ; NEUER NMI VEKTOR 1C4D LDA #$4C 1C4F STA $00 1C51 LDA #$5A 1C53 STA $01 1C55 LDA #$1C ; SPRUNGZEIGER 1C57 STA $02 ; FUER 'SYS0' 1C59 RTS ****************** BEFEHLSAUSWERTUNG 1C5A JSR $0079 ; CHRGOT, LFD. ZEICHEN 1C5D CMP #$4C ; 'SYS0 L'? 1C5F BNE $1C95 ; NEIN, DANN WEITER 1C61 LDA #$31 ; SONST F1-F8 AUSLISTEN 1C63 STA $FA ; ZAEHLER VORBEREITEN 1C65 LDX #$00 1C67 LDA #$10 1C69 STA $FB 1C6B LDA #$0D ; ZEILENVORSCHUB 1C6D JSR $FFD2 ; AUSGEBEN 1C70 LDA #$46 ; 'F' 1C72 JSR $FFD2 ; AUSGEBEN 1C75 LDA $FA ; LFD. NUMMER 1C77 JSR $FFD2 ; AUSGEBEN 1C7A LDA #$20 ; ' ' 1C7C JSR $FFD2 ; AUSGEBEN 1C7F LDA $1D4D,X; BEFEHLSSTRING 1C82 JSR $FFD2 ; AUSGEBEN 1C85 INX 1C86 DEC $FB 1C88 BNE $1C7F 1C8A INC $FA 1C8C LDA $FA 1C8E CMP #$39 ; LETZTER STRING ? 1C90 BNE $1C67 ; NEIN, DANN WEITER 1C92 JMP $0073 ; SONST INS BASIC 1C95 JSR $0079 ; LFD. BEFEHL HOLEN 1C98 CMP #$4F ; 'O' FUER 'SYS0 O' ? 1C9A BNE $1C9F ; NEIN, DANN WEITER 1C9C JMP $FED2 ; SONST WARMSTART 1C9F CMP #$52 ; 'R' FUER 'SYS0 R' ? 1CA1 BNE $1CA9 ; NEIN, DANN WEITER 1CA3 JSR $1C39 ; SONST PROGRAMMRESTART 1CA6 JMP $0073 ; UND ZURUECK ZU BASIC 1CA9 CMP #$2C ; ',' FUER AENDERUNG ? 1CAB BEQ $1CB0 ; JA, DANN WEITER 1CAD JMP $CF08 ; SONST 'SYNTAX ERROR' 1CB0 JSR $D79B ; TASTENNR. INS X-REG. 1CB3 CPX #$09 ; >8 ? 1CB5 BCC $1CBA ; NEIN, DANN WEITER 1CB7 JMP $D248 ; SONST FEHLERMELDUNG 1CBA DEX 1CBB STX $FA 1CBD JSR $0079 ; CHRGOT, LFD. BEFEHL 1CC0 CMP #$2C ; ',' KOMMA ? 1CC2 BNE $1CAD ; NEIN, DANN FEHLER 1CC4 JSR $0073 ; NAECHSTES ZEICHEN ? 1CC7 CMP #$22 ; '"' HOCHKOMMA ? 1CC9 BNE $1CAD ; NEIN, DANN FEHLER 1CCB TXA 1CCC ASL 1CCD ASL 1CCE ASL 1CCF ASL 1CD0 TAX ; X-REG. * 16 1CD1 LDY #$10 ; BEFEHLSSTRING HOLEN 1CD3 JSR $0073 ; NAECHSTES ZEICHEN 1CD6 CMP #$22 ; '"' ENDE DES STRINGS 1CD8 BEQ $1CE3 ; REST MIT 0 FUELLEN 1CDA STA $1D4D,X; SONST STRING AB- 1CDD INX ; SPEICHERN 1CDE DEY 1CDF BNE $1CD3 ; WEITER 1CE1 BEQ $1CEC ; ENDE: JMP $0073 1CE3 LDA #$00 ; REST MIT 0 FUELLEN 1CE5 STA $1D4D, 1CE8 INX 1CE9 DEY 1CEA BNE $1CE5 1CEC JMP $0073 ; ZURUECK INS BASIC ****************** FUNKTIONSTASTEN ABF. 1CEF LDA $9D ; RUN ODER DIREKTMODUS 1CF1 BEQ $1CFF ; BEI RUN KEINE ABFR. 1CF3 LDA $CB ; GEDRUECKTE TASTE 1CF5 LDX #$05 1CF7 CMP $1D01,X; MIT TASTATURCODE DER 1CFA BEQ $1D06 ; FUNKTIONSTAS. VERGL. 1CFC DEX 1CFD BNE $1CF7 ; WEITER,TASTE ERKANNT 1CFF JMP $EBDC ; TEST NEGATIV 1D02 .BYTE $27 ; < F1 > 1D03 .BYTE $2F ; < F3 > 1D04 .BYTE $37 ; < F5 > 1D05 .BYTE $3F ; < F7 > 1D06 CMP $C5 ; ENTPRELLUNG 1D08 BEQ $1CFF 1D0A STA $C5 1D0C TXA 1D0D CLC 1D0E ASL 1D0F STA $FA 1D11 LDA $028D ; KONTROLLTASTE 1D14 CMP #$01 ; <SHIFT> GEDRUECKT ? 1D16 BEQ $1D1A ; JA, DANN WEITER 1D18 DEC $FA ; WERT UM 1 ERNIEDR. 1D1A LDA $FA 1D1C TAX 1D1D DEX 1D1E TXA 1D1F ASL 1D20 ASL 1D21 ASL 1D22 ASL 1D23 STA $FB ; ACCU * 16 1D25 TAY 1D26 LDA $1D4D,Y; BEFEHLSSTRING LADEN 1D29 BEQ $1D3B ; ENDE ? 1D2B CMP #$5E ; '^' DIREKT AUSFUEHREN 1D2D BEQ $1D3E ; JA, DANN VERZWEIGEN 1D2F CMP #$27 ; >'< HOCHKOMMAERSATZ? 1D31 BNE $1D35 ; NEIN, DANN WEITER 1D33 LDA #$22 ; HOCHKOMMA LADEN 1D35 JSR $FFD2 ; UND AUSGEBEN 1D38 INY 1D39 BNE $1D26 1D3B LDA #$00 ; CURSOR ANSCHALTEN 1D3D STA $CF 1D3F BEQ $1D4A ; UNBEDINGTER SPRUNG 1D41 LDA #$0D ; <RETURN> 1D43 STA $0277 ; IN DEN PUFFER 1D46 LDA #$01 ; EIN ZEICHEN IM 1D48 STA $C6 ; TASTATURPUFFER 1D4A JMP $EBD6 ; ZURUECK ZUM URSPRUNG ****************** BEFEHLSSPEICHER 1D4D LIST^........... 1D5D RUN^............ 1D6D INPUT........... 1D7D GOSUB........... 1D8D SYS0 ........... 1D9D RETURN.......... 1DAD RESTORE......... 1DBD LOAD'........... ****************** NEUE NMI ROUTINE 1DCD SEI ; KOPIE DER ALTEN 1DCE PHA 1DCF TXA 1DD0 PHA 1DD1 TYA 1DD2 PHA 1DD3 LDA $911D 1DD6 BPL $1DFD 1DD8 AND $911E 1DDB TAX 1DDC AND #$02 ; RS 232 AKTIV ? 1DDE BEQ $1DFA ; JA, DANN VERZWEIGEN 1DE0 BIT $9111 1DE3 JSR $F734 ; STOPTASTE ABFRAGEN 1DE6 JSR $FFE1 ; STOPTASTE GEDRUECKT ? 1DE9 BNE $1DFD ; NEIN, DANN RTI 1DEA JSR $FD52 ; VEKTOREN SETZEN 1DEE JSR $FDF9 ; I/O REGISTER SETZEN 1DF1 JSR $E518 ; CLR SCREEN 1DF4 JSR $1C39 ; REGISTER AENDERN 1DF7 JMP ($C002); BASIC WARMSTART 1DFA JMP $FEDE ; NMI FUER RS 232 1DFD JMP $FF56 ; RTI
1 rem befehlskopf 2 rem 100 dims(5) 110 rem *** pruefsummen 120 s(0)=10538:s(1)=0:s(2)=10372 130 s(3)=14363:s(4)=12024:s(5)=733 140 fory=0to5:s=0 150 fort=0to103:readd:s=s+d:next 160 if s<>s(y)thenprint"{clr}{down}{down}{down}{rvon}fehler in teil"y+1:end 170 next 180 restore 190 print"{clr}{down}{down}{down}{rvon}m{rvof}odulbereich oder" 200 print"{down}{down}{rvon}e{rvof}nde des speichers ?" 210 get a$:ifa$=""then210 220 ifa$="m"thenprint"{home}{down}{down}{down}m":goto380 230 ifa$<>"e"then210 240 print"{home}{down}{down}{down}{down}{down}{down}e" 245 rem *** programm ans speicherende 250 poke55,0:poke56,peek(56)-3:clr 260 ad=peek(56) 270 hd=ad*256:n=623 275 rem *** leseschleife 280 fort=hdtohd+n 290 readd 300 ifd=-1thend=ad 310 ifd=-2thend=ad+1 320 ifd=-3thend=ad+2 330 poket,d:next 340 print"{clr}{down}{down}start des programms" 350 iffl=0thenprint"{down}mit sys"hd"." 360 iffl=1thenprint"{down}mit reset (=sys 64802)" 370 end 375 rem *** programm in den modulbereich 376 rem *** mit identifikation a0cbm 380 v$="009160199254065048195194205" 390 fore=0to8 400 x=val(mid$(v$,e*3+1,3)):readd 410 poke40960+e,x:next 420 hd=40969:n=614:ad=160:fl=1:goto 280 430 rem teil 1 440 data120,234,234,234,234,234,234,234 450 data234,032,141,253,032,082,253,032 460 data249,253,032,024,229,162,011,189 470 data059,-01,157,000,003,202,016,247 480 data088,234,234,234,032,164,227,165 490 data043,164,044,032,008,196,169,071 500 data160,-01,032,030,203,032,018,228 510 data076,129,227,058,196,131,196,229 520 data-01,152,-02,226,-02,134,206,042 530 data042,042,042,032,054,052,039,069 540 data082,032,032,066,065,083,073,067 550 data032,042,042,042,042,013,000,000 560 data000,000,000,000,000,000,000,000 570 rem teil 2 580 data000,000,000,000,000,000,000,000 590 data000,000,000,000,000,000,000,000 600 data000,000,000,000,000,000,000,000 610 data000,000,000,000,000,000,000,000 620 data000,000,000,000,000,000,000,000 630 data000,000,000,000,000,000,000,000 640 data000,000,000,000,000,000,000,000 650 data000,000,000,000,000,000,000,000 660 data000,000,000,000,000,000,000,000 670 data000,000,000,000,000,000,000,000 680 data000,000,000,000,000,000,000,000 690 data000,000,000,000,000,000,000,000 700 data000,000,000,000,000,000,000,000 710 rem teil 3 720 data000,000,000,000,000,000,000,000 730 data000,000,000,000,000,000,000,000 740 data000,000,000,000,000,166,122,160 750 data004,132,015,189,000,002,016,007 760 data201,255,240,062,232,208,244,201 770 data032,240,055,133,008,201,034,240 780 data086,036,015,112,045,201,063,208 790 data004,169,153,208,037,201,048,144 800 data004,201,060,144,029,132,113,160 810 data000,132,011,136,134,122,202,200 820 data232,189,000,002,056,249,158,192 830 data240,245,201,128,208,048,005,011 840 data164,113,232,200,153,251,001,185 850 rem teil 4 860 data251,001,240,089,056,233,058,240 870 data004,201,073,208,002,133,015,056 880 data233,085,208,159,133,008,189,000 890 data002,240,223,197,008,240,219,200 900 data153,251,001,232,208,240,166,122 910 data230,011,200,185,157,192,016,250 920 data185,158,192,208,180,160,255,202 930 data200,232,189,000,002,056,249,105 940 data-03,240,245,201,128,208,002,240 950 data173,166,122,230,011,200,185,104 960 data-03,016,250,185,105,-03,208,226 970 data189,000,002,016,155,076,009,198 980 data016,066,201,255,240,062,036,015 990 rem teil 5 1000 data048,058,170,132,073,201,204,176 1010 data010,160,192,132,035,160,158,132 1020 data034,208,011,233,076,170,160,-03 1030 data132,035,160,105,132,034,160,000 1040 data010,240,016,202,016,012,230,034 1050 data208,002,230,035,177,034,016,246 1060 data048,241,200,177,034,048,008,032 1070 data071,203,208,246,076,243,198,076 1080 data239,198,032,115,000,201,204,144 1090 data025,201,252,176,021,032,243,-02 1100 data076,174,199,233,203,010,168,185 1110 data009,-03,072,185,008,-03,072,076 1120 data115,000,032,121,000,076,231,199 1130 rem teil 6 1140 data052,253,000,000,000,000,000,000 1150 data000,000,000,000,000,000,000,000 1160 data000,000,000,000,000,000,000,000 1170 data000,000,000,000,000,000,000,000 1180 data000,000,000,000,000,000,000,000 1190 data000,000,000,000,000,000,000,000 1200 data000,000,000,000,000,000,000,000 1210 data000,000,000,000,000,000,000,000 1220 data000,000,000,000,000,000,000,000 1230 data000,000,000,000,000,000,000,000 1240 data000,000,000,000,000,000,000,000 1250 data000,000,000,000,000,000,000,000 1260 data000,075,073,076,204,000,000,000
100 print"{clr}{down}{down}{rvon}tokenerzeuger" 110 input"{down}{down}startadresse";sa 115 rem *** programmueberpruefung 120 fort=1to3:readd:ifpeek(sa+8+t)<>dthenprint"{clr}{down}{down}{rvon}programm nicht geladen":end 130 print"{down}{down}{rvon}1{rvof} beginn bei 204 (kill wird ueberscrieben) 140 print"{down}{rvon}2{rvof} oder bei token 205 ?" 150 geta$:ifa$=""then150 160 ifa$="1"thenf=617:goto190 170 ifa$<>"2"then150 180 f=621 190 input"{down}{down}wieviele token";at 200 ifat>47thenprint"{rvon}{down}nicht moeglich":goto190 205 rem *** befehlswort abspeichern 210 z=sa+f:w=sa+520 220 fort=1toat 230 print"{down}token "t+204", adresse" 240 inputa$,b:b=b-1 250 fory=1tolen(a$) 260 x=asc(mid$(a$,y,1)):ifmid$(a$,y+1,1)=""thenx=x+128 270 pokez,x:z=z+1:next 280 b2=int(b/256):b1=b-b2*256 290 pokew,b1:pokew+1,b2:w=w+2 300 next 310 data32,141,253