String-Descriptor
Ein String-Descriptor ist eine Datenstruktur, die in allen CBM-BASIC-Varianten eine Zeichenkette beschreibt.
Ein solcher Descriptor enthält die wesentlichen Informationen zu einer Zeichenkette, nämlich die Länge und einen Zeiger mit der Adresse der eigentlichen Zeichenkettendaten, welche am String-Heap oder als Konstanten im BASIC-Text selbst liegen. Mit dieser Struktur bleibt die Zeichenkettenverwaltung flexibel und Zeichenketten sind nicht etwa an feste Längen gebunden.
Der String-Descriptor ist in einfachen Variablen und Arrays vom Typ String abgelegt. Zudem sind die Elemente des String-Descriptor-Stacks String-Descriptoren, die temporäre im Zuge der Auswertung von String-Ausdrücken gebraucht werden.
Aufbau[Bearbeiten | Quelltext bearbeiten]
Bei BASIC V2 ist der String-Descriptor eine Struktur bestehend aus 3 Bytes:
String-Heap (oder direkt im Programmcode) ▲ │ Offset │ ┌─────────────────────┐ │ 2 │ Adresse High-Byte ││ │ ├─────────────────────┤├─┘ Wert 0 - 65535 ($0000 - $FFFF) 1 │ Adresse Low-Byte ││ ├─────────────────────┤ 0 │ Länge │ Wert 0 - 255 ($00 - $FF) └─────────────────────┘
Andere BASIC-Varianten
Andere CBM-BASIC-Varianten verwenden im Vergleich zu BASIC V2 die gleiche Struktur. Lediglich bei der CBM-II-Serie gibt es deutliche organisatorische Änderungen, wegen der universelleren Speicherarchitektur, wo die Adresse auch noch die Speicherbank aufnehmen muss.
BASIC-Variante | Descriptor-Länge (Bytes) |
---|---|
BASIC 2.0 | 3 |
BASIC 4.0 | 3 |
BASIC 4.0+ 128K | 4 |
BASIC 4.0+ 256K | 4 |
BASIC 3.5 | 3 |
BASIC 7.0 | 3 |
Erweiterte Struktur bei BASIC 4.0+ bestehend auf 4 Bytes:
String-Heap ▲ │ Offset │ ┌─────────────────────┐ │ 3 │ Speicher-Bank ││ │ Wert 0 - 15 ($00 - $0F) ├─────────────────────┤│ │ 2 │ Adresse High-Byte │├─┘ ├─────────────────────┤│ Wert 0 - 65535 ($0000 - $FFFF) 1 │ Adresse Low-Byte ││ ├─────────────────────┤ 0 │ Länge │ Wert 0 - 255 ($00 - $FF) └─────────────────────┘
Programmierung[Bearbeiten | Quelltext bearbeiten]
- Hat die Länge einen Wert von 0, so ist die Adresse nicht definiert.
- Eine Adresse wird in den diversen Implementierungen stets als größer oder gleich $0100 angenommen, sodass in der Assemblerprogrammierung beim Hantieren mit dem High-Byte der Adresse immer das Zero-Flag als gelöscht betrachtet werden kann, um mit einem BNE-Befehl den Programmfluss in kompakter Weise fortzuführen.
- Der Wert für die Speicher-Bank verwendet zwar ein Byte, ist aber üblicherweise auf Werte kleiner oder gleich 15 beschränkt, was der Speicherkonzeption der CBM-II-Rechnerserie geschuldet ist (Speicherraum von 1 MByte).
Bei der Assemblerprogrammierung sind die indizierten Adressierungsarten mit X-Register und Y-Register äußerst nützlich, da der Offset im jeweiligen Register gehalten werden kann und mittels INX/INY bzw. DEX/DEY in der Struktur navigiert werden kann.
- Zeropage X-indizierte Adressierung: wenn ein Descriptor in der Zeropage liegt, typischerweise am String-Descriptor-Stack.
- Indirekte Y-nachindizierte Zeropage-Adressierung: in den üblichen Fällen, wenn ein Zeiger auf den Descriptor in der Zeropage vorliegt.
BASIC[Bearbeiten | Quelltext bearbeiten]
Um den Zugriff auf einen String zu erhalten, muss der String-Descriptor lokalisiert werden. Manche CBM-BASIC-Varianten oder BASIC-Erweiterungen bieten an, die Adresse einer Variablen oder eines Arrays mittels einer Funktion im Speicher zu ermitteln. Damit kann dann auf die String-Descriptor-Struktur zugegriffen werden. Beispiele:
- BASIC 7.0: POINTER
für einfache Variablen und Array-Elemente - Exbasic Level II: VARPTR
nur für einfache Variablen
Ohne entsprechende BASIC-Funktion können Routinen des BASIC-ROMs bemüht werden, die aus BASIC mittels SYS erreichbar sind.
BASIC-V2-Beispiel[Bearbeiten | Quelltext bearbeiten]
Folgendes Beispiel ermittelt für eine einfache Variable B$ die Daten des String-Descriptors und gibt den String aus:
120 B$="ABCD" 130 DIM A, L, H 150 SYS 45195 B$:A=256*PEEK(781)+PEEK(780) 160 PRINT "VARPTR:";A 170 L=PEEK(A): H=PEEK(A+1)+256*PEEK(A+2) 180 PRINT "LAENGE:";L 190 PRINT "ADRESSE: ";H 200 PRINT "STRING: ";CHR$(34); 210 FOR I=H TO H+L-1: PRINTCHR$(PEEK(I));: NEXT 220 PRINTCHR$(34)
Beim C64 ist BASIC-ROM-Routine $B08B/45195[1] nützlich. Das Ergebnis, die Adresse der Variable, befindet sich in den CPU-Registern A und Y befinden, die aus den Übergabespeicherstellen des SYS-Befehls entnommen werden können.
Ausgabe:
ARPTR: 2271 LAENGE: 4 ADRESSE: 2057 STRING: "ABCD" |
BASIC-7.0-Beispiel[Bearbeiten | Quelltext bearbeiten]
Dieses Programm ermittelt ebenso wie beim BASIC-V2-Beispiel für eine Array-Element A$(1) bzw. eine einfache Variable B$ (REM in Zeile 155 entfernen) die Daten des String-Descriptors und gibt den String aus:
110 A$(1)="123" 120 B$="ABCD" 130 DIM A, L, H 140 BANK 1 150 A=POINTER(A$(1)) 155 REM A=POINTER(B$) 160 PRINT "VARPTR:";A;"($";HEX$(A);")" 170 L=PEEK(A): H=PEEK(A+1)+256*PEEK(A+2) 180 PRINT "LAENGE:";L 190 PRINT "ADRESSE: $";HEX$(H) 200 PRINT "STRING: ";CHR$(34); 210 FOR I=H TO H+L-1: PRINTCHR$(PEEK(I));: NEXT 220 PRINTCHR$(34)
Mittels DIM werden alle verwendeten Variablen angelegt, da sich sonst im Laufe des Programms mit neu angelegten Variablen die Array-Adresse verschiebt und der Wert in Variable A dann falsch wäre.
Aufgerufen mit RUN ergibt sich folgende Ausgabe:
VARPTR: 1062 ($0426) LAENGE: 3 ADRESSE: $FEFB STRING: "123" |
Interpreter-Manipulationen[Bearbeiten | Quelltext bearbeiten]
Im BASIC-Interpreter wird ein Descriptor beim Erstellen eines Strings zusammengestellt und bleibt dann unverändert, solange bis er durch einen Descriptor für einen String überschrieben wird. Lediglich im Zuge der Garbage Collection (GC) wird beim Verschieben der Strings am String-Heap das Anpassen der im Descriptor befindlichen String-Adresse notwendig.
Sogenannte "In-Place" Manipulationen von Zeichenketten sind bei CBM-BASIC-Varianten nicht vorgesehen, daher bleibt die Länge auch unverändert.
Manche BASIC-Erweiterungen bieten ein Kommando für das Vertauschen der Werte zweier String-Variablen an (typischweise bei Sortieralgorithmen). Sie nutzen dabei die Datenorganisation geschickt aus, indem nur die Descriptoren getauscht werden müssen, ohne dass die eigentlichen String-Daten tatsächlich bewegt werden müssen.
Auch das Kürzen von Strings, eine sogenannte Truncate-Funktion, wäre einfach zu realisieren, indem lediglich die Länge im Descriptor verkleinert wird (zumindest im Fall der einfachen String-Heap-Organisation, wie sie bei BASIC V2 zu finden ist).