ASDIS intern
ASDIS ist ein (DER!) Assembler/Disassembler für den ZX81. Wir haben nix besseres, und wenn man viel mit ASDIS arbeitet, kommt man oft an dessen Grenzen. Zum einen wird bei langen Listings der Speicher knapp, daher sollte man schon sehr sparsam mit Labels und Marken umgehen. Das Drucken von langen Listings wird ebenfalls zur Qual, da ASDIS immer nur eine Seite druckt. Und könnte man dann auch noch mit Makros arbeiten...
Leider fehlen uns für das Programm selbst Unterlagen, wie ASDIS intern arbeitet. Aus diesem Grund habe ich einen Anfang gemacht, und das Gedächtnis von ASDIS analysiert: die Variable A$, in der der gesamte Quelltext gespeichert wird. Mit diesen Erkenntnissen sollten schon einige Arbeitserleichterungen machbar sein, dazu abschließend.
Die Variable A$ wird von ASDIS dynamisch verwaltet, d.h. ihre Länge ist veränderlich. Der Speicherknappheit kann man leicht Abhilfe schaffen, indem man RAMTOP einfach auf 65535 hochsetzt (durchgängig RAM vorausgesetzt). Lange Listings sind dann kein Problem mehr, sofern man sie auf einen Datenträger auch sichern kann.
In A$ steht nun alles drin, was ASDIS an Informationen zum Quelltext braucht. ASDIS hat zwar auch noch ein paar Systemvariablen, aber die brauchen uns vorerst noch nicht zu interessieren.
Der Aufbau von A$ ist nun recht einfach. Zu Anfang steht ein Header mit folgendem Inhalt:
Position |
1/2 |
3/4 |
5/6 |
7/8 |
Inhalt |
Anzahl d. Marken |
Anzahl d. Labels |
Ablegeadresse |
Laufadresse |
Zum Nachmachen: PRINT (CODE A$(5)-28)+256*(CODE A$(6)-28) zeigt uns ganz einfach die Ablegeadresse des Programmes an. Die Einträge sind also allesamt 16bit-Zahlen im gewohnten Format. Als nächstes folgt eine Tabelle mit allen Marken im Klartext und ihren Adressen. Jede Marke belegt damit 7 Bytes (5 Bytes Name, 2 Bytes Adresse) im Header. Ein simples PRINT A$ läßt diese Einträge schon sichtbar werden, da die Codierung im ZX-Format erfolgt. Nach den Marken folgt im selben Muster die Tabelle mit allen Labels.
ASDIS speichert Marken und Labels nur einmal als Klartext im Header. Im Quelltext wird dann nur noch die relative Position im Header, bezogen auf den Anfang der jeweiligen Tabelle, eingetragen. Jedes Label und jede Marke hat also seine Nummer, bei 0 beginnend. Ist aber der Opcode eines Mnemonics ein Label oder eine Marke, steht diese auch im Klartext in der jeweiligen Zeile.
Nach dem Header folgt nun der eigentliche Quelltext, wobei jede Zeile ebenfalls im Klartext abgelegt wird. Der Zeilenaufbau ist sehr unterschiedlich, aber einfach zu lesen:
Position |
1 |
2 |
3/4 |
>5 |
Inhalt |
Code |
Zeilenlänge |
Labelnummer |
Zeileninhalt |
Eine Labelnummer wird nur eingetragen, wenn ein Label vorhanden ist, logisch. Wenn nicht, entfällt der Eintrag und nach der Zeilenlänge schließt sich sofort der Zeileninhalt an. Eine Zeilenendekennung wie im ZX (119=Newline) gibt es nicht, ASDIS muß also über die Zeilenlängen die nächste Anfangsadresse berechnen. Als Zeilenlänge wird immer nur die Länge des eigentlichen Codes eingetragen!
Am ersten Byte (Code) erkennt ASDIS, um welche Art einer Zeile es sich im folgenden handelt. Jede Zeile hat entsprechend ihrem Inhalt einen speziellen, eigenen Aufbau, der nachfolgend für alle möglichen Fälle dargestellt wird. Die Beispiele werden so geschrieben, wie sie auch ASDIS selbst anzeigt.
eine einfache Mnemoniczeile ohne Labels/Marken
Länge (Bytes) |
1 |
1 |
n |
Eintrag (dez. / hex.) |
24 / $18 |
Zeilenlänge (n) |
Code |
Bsp.: LD A,B sieht so aus: 24 - 1 - 120
eine einfache Mnemoniczeile mit Label
Länge |
1 |
1 |
2 |
n |
Eintrag |
152 / $98 |
Zeilenlänge (n) |
Position d. Labels |
Code |
Bsp.: LABEL LD A,B sieht so aus: 152 - 1 - 0 - 0 - 120 (wenn erstes Label im Header)
eine Kommentarzeile:
Länge |
1 |
1 |
n |
Eintrag |
52 / $34 |
Zeilenlänge (n) |
ZX-Klartext |
Bsp.: ;ANFANG sieht so aus: 52 - 6 - 38 - 51 - 43 - 38 - 51 - 44
ein Freiraum (Platzhalter)
Länge |
1 |
1 |
Eintrag |
40 / $28 |
n (Größe des Freiraumes) |
Damit ist klar, warum man nur maximal 256 Bytes Freiraum in einer Zeile angeben kann!
eine Datenzeile (Tabelle):
Länge |
1 |
1 |
n |
Eintrag |
56 / $38 |
Zeilenlänge (n) |
Tabelleninhalt |
eine Textzeile:
Länge |
1 |
1 |
n |
Eintrag |
60 / $3C |
Zeilenlänge (n) |
ZX-Klartext |
ein Freiraum mit Label (Platzhalter):
Länge |
1 |
1 |
2 |
n |
Eintrag |
168 / $A8 |
Zeilenlänge (n) |
Position d. Labels |
Tabelleninhalt |
eine Datenzeile mit Label:
Länge |
1 |
1 |
2 |
n |
Eintrag |
184 / $B8 |
Zeilenlänge (n) |
Position d. Labels |
Tabelleninhalt |
der Operand ist ein Label:
Länge |
1 |
1 |
5 |
n |
2 |
Eintrag |
88 / $58 |
Zeilenlänge (n) |
Labelname im Klartext |
Opcode |
Adresse (assembliert) |
Bsp.: LD A,(LABEL) : 88 - 3 - 49 - 38 - 39 - 42 - 49 - 58 - (2 Bytes Adresse)
der Operand ist eine Marke:
Länge |
1 |
1 |
2 |
5 |
n |
2 |
Eintrag |
216 / $D8 |
Zeilenlänge (n) |
Position d. Marke |
Name der Marke im Klartext |
Opcode |
Adresse (assembliert) |
Bsp.: LD HL,(VARS) : 216 - 3 - 0 - 0 - 59 - 38 - 55 - 56 - 0 - 42 - 16 - 64
eine Textzeile mit Label:
Länge |
1 |
1 |
2 |
n |
Eintrag |
188 / $BC |
Zeilenlänge (n) |
Position d. Labels |
Klartext |
Es wird klar, daß bestimmte Konstruktionen viel Platz fressen, diese machen aber gerade den Vorteil eines Assemblers aus. Hier stehen sich also wieder einmal Komfort und Speicherbedarf gegenüber.
Als erste Übung habe ich mir einen Kommentarkiller geschrieben. In einem Rutsch entfernt dieser alle Kommentarzeilen aus einem Listing und schafft wieder etwas Platz. Obs sinnvoll ist, wenn keine Kommentare mehr drin stehen? Es läßt sich ja erweitern, indem man entbehrliche Kommentare invers schreibt und nur noch diese entfernt würden.
Die nächste Anwendung wird eine Druckroutine sein, die das Listing übersichtlich formatiert. Später will ich dann zur Makroverwaltung kommen, mit der definierte Blöcke gelöscht, verschoben, kopiert oder eingefügt werden können. Die Routinen zur A$-Behandlung habe ich bereits fertig.
Kai Fischer, email: kai@zx81.de