C 64
Listing des Monats

Weg mit dem Müll

Der Feind aller Datenverarbeitungsprogramme ist die Beseitigung alter, überflüssiger Stringinhalte. Sie zwingt dem Anwender oft unzumutbare Wartezeiten auf. Dieses Programm befreit Sie davon endgültig.

Anfänger kennen die Müllabfuhr, die Garbage Collection, meistens noch nicht, da Ihre Programme noch kurz, die Anzahl der verwendeten Strings gering und der freie Speicherplatz groß ist.

Fortgeschrittene lernen sie spätestens dann kennen, wenn Sie ein umfangreiches und vielseitiges Programm geschrieben haben (mit vielen Strings und vor allem Stringarrays) und dann erstaunt feststellen, daß der Computer plötzlich alle Arbeiten unterbricht, um nach Sekunden oder Minuten weiter zu machen, als ob nichts geschehen wäre.

Das Problem mit dem Müll

In dieser Zeit, die im Extremfall eine Stunde übersteigen kann, war die besagte Garbage Collection am Werk. Dabei läuft etwa Folgendes ab (ausführlich beschrieben im 64’er, Ausgabe 1/85 und 2/85):

Da der Computer jedesmal, wenn eine Stringvariable neu definiert wird, den Text des Strings (wenn er nicht im Programm steht oder die Länge Null hat) in freie Speicherplätze schreibt, den Zeiger auf diesen Text ändert und den alten Text einfach stehenläßt, ist irgendwann kein Speicherplatz mehr frei. Dann muß das Betriebssystem die alten, ungültig gewordenen Texte entfernen, beziehungsweise die Aktuellen wieder zusammenschieben, bevor es weitergeht.

Dieses »Müllaufsammeln« (englisch: garbage collection) macht nun der C 64 nach einer relativ umständlichen Methode. Deren Zeitbedarf wächst ungefähr quadratisch mit der Anzahl der definierten Stringvariablen. Das heißt, bei einem Programm mit 1 000 definierten Strings braucht die Garbage Collection etwa 10 000mal so lang wie bei zehn definierten Strings.

Die Lösung: eine neue Routine

Eine Grundregel des Programmierens lautet: Geschwindigkeit ist umgekehrt proportional zum verwendeten Speicherplatz. Ist man also knapp mit Speicherplatz, dann braucht man komplizierte und langsame Verfahren. Und so kann man mit einem einzigen Durchlauf die Überreste der Variablen beseitigen: Zuerst werden alle Stringvariablen und -arrays untersucht, und die gültigen Texte, sofern sie nicht im Programm stehen oder die Länge Null haben, hintereinander in einen freien Speicherplatz geschrieben. Dieser befindet sich natürlich im RAM unterhalb des ROMs. Gleichzeitig korrigiert man die Textzeiger auf den späteren, richtigen Speicherbereich. Zuletzt überträgt man den gesamten Block gültiger Stringinhalte in den normalen Basic-Bereich und korrigiert die Basic-Zeiger auf die aktuellen Adressen.

(W. Meierhofer/og)

Superschnelle Garbage Collection auf dem C 64. Keine überflüssigen Wartezeiten mehr bei umfangreichen String- und Arrayoperationen.

Die hier vorliegende neue Garbage-Collection hat folgende Vorteile:

  1. Ausführen der Garbage Collection im 10tel Sekundenbereich
  2. Aufruf von Basic aus möglich
  3. Einbindung ins Betriebssystem möglich
  4. Obwohl, wie viele bereits erkannt haben werden, der Bereich unter dem Betriebssystem- und Interpreter-ROM benutzt wird, kann der Anwender das Betriebssystem nach seinen Erfordernissen ändern (zum Beispiel Tastaturtabellen-Änderungen) und unter bestimmten Voraussetzungen sogar hochauflösende Grafik in diesem RAM benutzen.

Einschränkungen:

  1. Da als Hilfsspeicherbereich das RAM unter dem ROM (2 x 8 KByte) verwendet wird, sollte die Zeichenzahl aller definierten, aktuellen Stringtexte (ohne Mülltexte) nicht größer als zirka 16 000 Byte sein. Bei einer größeren Zahl kann es zu Textfehlern kommen, da dann der Speicherbereich von $FFFF bis $E000 und $BFFF bis $A000 nicht mehr ausreicht (Bereich von oben nach unten benutzt) und damit der normale Stringtextbereich von $A000 nach unten zu mit überschrieben wird. In der Regel sind aber Texte, die in diesem überschriebenen Bereich stehen, längst im Hilfsbereich als eine der ersten gerettet worden, so daß man auch mehr als zirka 16 000 Zeichen verwenden kann.

    Auch in großen Programmen werden meist wesentlich weniger Strings benutzt. Wird der Bereich unterhalb von $A000 vor Basic geschützt und mit Maschinenprogrammen belegt, darf die Zahl von zirka 16 000 nicht überschritten werden.

  2. Das RAM unter dem Interpreter-ROM ($A000 bis $BFFF) kann frei benutzt werden, wenn die Gesamtzeichenzahl aller aktuellen Strings nicht über zirka 8 000 liegt und die Garbage Collection nicht ins Betriebssystem eingebunden wird.

Tippen Sie zuerst Listing 1 mit dem MSE ein, und speichern Sie es.

  1. Anwendung in »normalen« Basic-Programmen

    Einzige Voraussetzung ist, daß sich das Programm mit der schnellen Routine im Speicher befindet. Es kann jederzeit durch SYS 50944 (zum Beispiel in regelmäßig durchlaufenden Programmteilen oder während des Einlesens von sequentiellen Dateien) aufgerufen werden.

  2. Einbindung ins Betriebssystem

    Die Garbage Collection kann auch fest ins Betriebssystem eingebunden werden, das heißt wenn kein Speicherplatz mehr vorhanden ist, wird sie automatisch anstelle der alten ausgeführt. Selbstverständlich ist sie auch noch einzeln mit SYS 50944 aufrufbar.

    Die Einbindung veranlaßt der Befehl SYS 51400.

    Damit läuft das Betriebssystem und der Interpreter im RAM (Inhalt der Speicherstelle 1 ist 53 anstatt der üblichen 55).

Die Anwendung

Soll das Betriebssystem (Tastaturtabelle oder ähnlich) noch mehr geändert werden, sind die entsprechenden POKEs in Maschinensprache ab Speicherstelle $C9B2 anzufügen und mit RTS abzuschließen (siehe Listing 2, Source-Code).

(An diese Routine, die immer wieder die geänderten Bytes für den automatischen Aufruf der neuen Garbage Collection ins Betriebssystem schreibt, hängt man dadurch die eigenen Änderungen an.)

Dies macht man am besten durch ein Monitorprogramm, mit dem man dann auch die gesamte Neufassung seiner »persönlichen« Garbage Collection/Betriebssystemversion speichern kann.

Normaler InhaltBeispiel für Änderungen
$C9B2$60RTS$C9B2$A9LDA #$FF
$FF
$C9B4$8DSTA $A000
$00
$A0
$C9B7$60RTS
Speichert den Wert 255 ($FF) an die Speicher Stelle 40960 ($A000).
Dieses Beispiel ergibt natürlich keinen Sinn.

Mit Grundkenntnissen im Maschinensprache ist es kein Problem, die Erweiterung herzustellen, zumal ja meist mehr als Grundkenntnisse notwendig sind, um das Betriebssystem zu ändern.

Übersicht:

Keine EinbindungEinbindung ins Betriebssystem
AufrufSYS 50944Automatisch und mit SYS 50944 zusätzlich möglich.
SpeicherkonfigurationNormal (Inhalt von 1 ist 55)
  • Inhalt von 1 ist 53
  • ROM läuft im RAM
  • Einbindung ins Betriebssystem mit SYS 51398
  • Betriebssystemänderungen mit aufnehmbar
Verwendbarkeit RAM von $A000-$BFFF (40960-49151)ja, wenn Gesamtzeichenzahl aller aktuellen Strings geringer als zirka 8000 (8 KByte)nein
Verwendbarkeit RAM von $E000-$FFFF (57344-65535)neinnein
Speicherplatz unterhalb $A000 für Maschinenprogrammefrei verwendbar, wenn Gesamtzeichenzahl aller aktuellen Strings kleiner als 16 KByte ist

Eine Besonderheit am Schluß: Um eine Kontrolle zu haben, wann und wie lange die Garbage Collection läuft, wird während der Ausführung ganz unten rechts am Bildschirm ein kleiner, brauner Punkt angezeigt.

Will man dieses Zeichen ändern, so POKEt man nach 50956 den Bildschirmcode des gewünschten Zeichens, nach 50961 die Farbe.

POKE 50956,1: POKE 50961,7 bewirkt, daß dort während des Ablaufs ein gelbes A steht.

Mit POKE 50956,32 wird die Anzeige abgestellt.

Programmbeschreibung für Maschinisten

Aufruf

Prinzipiell kann das Programm, wie oben erwähnt, jederzeit mit SYS 50944 (beziehungsweise mit JSR $C700) aufgerufen werden.

Der Trick, es ins Betriebssystem einzubinden (SYS 51400), besteht darin, das ROM (Betriebssystem und Interpreter) ins RAM zu kopieren und anstelle der normalen Garbage Collection einen Sprung in »Garbage 64« unterzubringen. Dies muß natürlich nach jeder Garbage Collection immer wieder automatisch geschehen, da »Garbage 64« das RAM unter dem ROM beschreibt, um die gültigen Stringtexte zwischenzuspeichern (siehe weiter unten).

An die Routine, die diese Änderung vornimmt (am Ende von »Garbage 64« ab $C9A0), kann man eigene, zusätzliche Befehle anfügen, die zum Beispiel die Tastaturtabellen beeinflussen, so daß der Interpreter beziehungsweise das Betriebssystem bei der Einbindung von »Garbage 64« durch SYS 51400 anwenderspezifisch mit geändert und diese Änderung nach jeder Garbage Collection erhalten wird.

Programmteil Vorbereitung

»Garbage 64« schaltet zuerst Interrupts ab, rettet die Speicherkonfiguration und die Prozessorregister, und schreibt in die rechte untere Ecke des Bildschirms eine Kontrollanzeige. Dieses Zeichen kann, wie schon erwähnt, von Basic aus mittels POKE geändert werden.

Ferner werden die benötigten Speicherplätze in der Zeropage ebenfalls gerettet und anschließend die benötigten Zeiger und Bytes vorbelegt.

Programmteil Variablen

Nun durchsucht »Garbage 64« den Bereich der Variablen mittels des Zeigers »LAZEI« und überprüft in dieser Schleife laufend, ob das Ende des Variablenbereichs bereits erreicht wurde.

Wird eine Stringvariable gefunden, deren Länge nicht 0 ist und deren Text nicht außerhalb des Stringtextbereichs am Basic-Ende steht (zum Teil weisen die Zeiger auf Texte im Basic-Programm), so wird der dazugehörige Text im Unterprogramm ABSPEI (ab $C925) anhand des Stringzeigers im RAM unter dem ROM, von $FFFF beginnend abwärts, abgelegt.

Dabei wird der Textzeiger im Deskriptor so korrigiert, als ob der Stringtext im normalen Bereich (ab Basic-Ende abwärts) stehen würde.

Es wird überprüft, ob der zwischenzuspeichernde Stringtext den Bereich von $E000 abwärts überschreiben würde, was natürlich einen Totalabsturz zur Folge hätte. In diesem Fall wird die letzte benutzte Position im »oberen« RAM in »MEZEI« gemerkt und ab $BFFF abwärts weitergemacht. Sind mehr als zirka 8 KByte + 8 KByte gültige Stringtexte vorhanden, was so gut wie nie der Fall ist, wird der Bereich unterhalb $A000 ebenfalls noch beschrieben, was aber keine Rolle spielt, solange in diesem Bereich keine Maschinenprogramme (die vor Basic geschützt wurden) stehen.

Die Möglichkeit, daß hier noch nicht gerettete, gültige Stringtexte stehen, ist vernachlässigbar und wäre von Basic aus nur durch absichtliche und äußerst umständliche Definitionsreihenfolgen erreichbar.

Programmteil Arraybereich

Ist der Variablenbereich durchsucht, wird mit dem Arraybereich genauso vorgegangen. Jedes Array wird überprüft, ob es ein Stringarray ist und jeder Stringdeskriptor wird untersucht, ob der Stringtext die Länge größer 0 hat und der Text im richtigen Bereich steht.

Zu beachten ist, daß der Arraykopf abhängig von der Anzahl der Dimensionen verschieden lang ist, und so der Beginn der Deskriptoren errechnet werden muß.

Wenn ein Array kein Stringarray ist, wird es anhand der Längenangabe im Arraykopf (siehe dazu auch 64’er, Ausgabe 1/85 und 2/85) übersprungen.

Programmteil RAM unter ROM nach Variable

Schließlich wird der Bereich der im RAM unter dem ROM abgelegten gültigen Stringtexte in den Bereich verlegt, in dem Stringtexte zu stehen haben, nämlich am Basic-Ende. Dabei wird zuerst der Bereich ab »ROMZEI« (der durch das RAM unter dem ROM »mitgelaufene« Zeiger) bis $C000, falls dieser Bereich benutzt wurde und dann der Bereich von »MEZEI« bis $FFFF (beziehungsweise von »ROMZEI« bis $FFFF, wenn nur das obere RAM beschrieben worden war), in den Bereich von »NEUZEI« aufwärts übertragen. »NEUZEI« ist der Zeiger, der im »echten« Stringtextbereich mitgelaufen war und der zur Aktualisierung der neuen Zeiger in den Stringdeskriptoren diente. Er bezeichnet ebenfalls den neuen Beginn des Stringtextbereichs.

Das Unterprogramm »SPEISTRI« (ab $0891) führt die eigentliche Umspeicherung durch, nachdem die oben erwähnten zwei Fälle festgestellt und die verwendeten Hilfszeiger (die jetzt allerdings eine neue Funktion haben) vorgesetzt wurden.

Programmteil ROM neu ins RAM kopieren

Nun wird, wenn der Interpreter und das Betriebssystem vorher im ROM »gelaufen« waren und »Garbage 64« nicht eingebunden war, sofort zum Programmteil »ENDE« gesprungen.

Ansonsten wird das Betriebssystem wieder ins RAM kopiert, und, wenn das RAM unter dem Interpreter-ROM benutzt worden war (mehr als 8 KByte Stringtexte), der Interpreter ebenfalls.

In der Subroutine »EINBIND« wird »Garbage 64« wieder eingebunden und die vom Anwender ergänzten Änderungen wieder durchgeführt.

Programmteil Ende

Hier sind die am Anfang geretteten Speicher wieder zu holen und die Kontrollanzeigezu löschen. Die vorher eingestellte Speicherkonfiguration wird aktiviert und mit RTS »Garbage 64« beendet.

Testen Sie doch mal die »Müllabfuhr« im C 64

Um Ihnen den Geschwindigkeitsvorteil einmal vorzuführen, haben wir noch ein Demonstrationsprogramm (Listing 3) für Sie. Tippen Sie es zuerst ohne die Zeilen 1 bis 5 ein und starten Sie das Demo. Die Zahl hinter dem Kommentar »test ok« ist zunächst die normale Zeit der Schleifen. Wenn die Garbage Collection zuschlägt, sehen Sie das an der wesentlich längeren Zeit. Tippen Sie dann die fehlenden fünf Zeilen ein, und starten Sie das Programm erneut. Dazu muß »GARBAGE64« (Listing 1) auf Diskette verfügbar sein. Für Kassette ändern Sie bitte Zeile 2 in »… ,1,1«. Von der Garbage Collection merken Sie nun nichts mehr. Noch krasser ist der Vorgang beim folgenden Beispiel zu sehen:

DIM A$(9000):FOR I=0 TO 9000 : A$(I)="A" : NEXT : PRINT FRE(0)

Mit der schnellen Routine ist das kein Problem, aber ohne sie …

(W. Meierhofer/og)
PROGRAMM : GARBAGE64      C700 C9B5
-----------------------------------
C700 : 78 48 98 48 8A 48 A5 01   4F
C708 : 8D 84 C9 A9 2E 8D E7 07   7C
C710 : A9 09 8D E7 DB A0 14 B9   25
C718 : 44 00 99 86 C9 88 D0 F7   A7
C720 : A9 37 85 01 A5 2D 85 45   4B
C728 : A5 2E 85 46 A9 00 85 47   4E
C730 : A9 00 85 48 A9 00 85 4B   8B
C738 : 85 4C 8D 85 C9 A5 37 85   A9
C740 : 4D A5 38 85 4E A5 45 C5   D1
C748 : 2F D0 06 A5 46 C5 30 F0   4B
C750 : 4E A0 00 B1 45 0A B0 39   FE
C758 : C8 B1 45 0A 90 33 18 A9   E2
C760 : 02 65 45 85 45 90 02 E6   C6
C768 : 46 A0 00 B1 45 F0 14 A0   A2
C770 : 02 B1 45 C5 34 90 0C D0   EE
C778 : 07 88 B1 45 C5 33 90 03   17
C780 : 20 25 C9 18 A9 05 65 45   8B
C788 : 85 45 90 02 E6 46 4C 45   71
C790 : C7 18 A9 07 65 45 85 45   D0
C798 : 90 02 E6 46 4C 45 C7 A5   05
C7A0 : 45 C5 31 D0 09 A5 46 C5   91
C7A8 : 32 D0 03 4C 32 C8 A0 00   79
C7B0 : B1 45 0A B0 68 C8 B1 45   BB
C7B8 : 0A 90 62 A0 03 B1 45 48   1A
C7C0 : 88 18 B1 45 65 45 85 4F   9F
C7C8 : 68 65 46 85 50 A0 04 B1   A3
C7D0 : 45 A8 A9 05 18 65 45 85   41
C7D8 : 45 90 02 E6 46 A9 02 18   AD
C7E0 : 65 45 85 45 90 02 E6 46   33
C7E8 : 88 D0 F2 A5 45 C5 4F D0   AB
C7F0 : 06 A5 46 C5 50 F0 A8 B1   A6
C7F8 : 45 F0 14 A0 02 B1 45 C5   1D
C800 : 34 90 0C D0 07 88 B1 45   9F
C808 : C5 33 90 03 20 25 C9 18   6E
C810 : A9 03 65 45 85 45 90 02   06
C818 : E6 46 4C EB C7 A0 03 B1   A3
C820 : 45 48 88 B1 45 18 65 45   17
C828 : 85 45 68 65 46 85 46 4C   59
C830 : 9F C7 A5 47 C9 00 D0 09   F7
C838 : A5 48 C9 00 D0 03 4C 09   DC
C840 : C9 A9 35 85 01 A5 4D 85   59
C848 : 33 A5 4E 85 34 AD 85 C9   EC
C850 : D0 16 A5 47 85 51 A5 48   88
C858 : 85 52 A9 FF 85 45 A9 FF   9A
C860 : 85 46 20 91 C8 4C BB C8   B2
C868 : A5 47 85 51 A5 48 85 52   94
C870 : A9 FF 85 45 A9 BF 85 46   5E
C878 : 20 91 C8 A5 4B 85 51 A5   B9
C880 : 4C 85 52 A9 FF 85 45 A9   ED
C888 : FF 85 46 20 91 C8 4C BB   E7
C890 : C8 A0 00 B1 51 91 4D A5   01
C898 : 45 C5 51 D0 08 A5 46 C5   81
C8A0 : 52 D0 02 F0 0F E6 4D D0   F8
C8A8 : 02 E6 4E E6 51 D0 E4 E6   8B
C8B0 : 52 4C 93 C8 E6 4D D0 02   46
C8B8 : E6 4E 60 AD 84 C9 C9 37   BF
C8C0 : F0 47 20 D5 C8 4C 09 C9   BD
C8C8 : A9 01 8D 85 C9 20 D5 C8   8C
C8D0 : A9 35 85 01 60 A9 37 85   D1
C8D8 : 01 A9 00 85 45 A9 E0 85   8F
C8E0 : 46 20 F8 C8 AD 85 C9 F0   9D
C8E8 : 0B A9 00 85 45 A9 A0 85   A8
C8F0 : 46 20 F8 C8 20 A0 C9 60   8C
C8F8 : A2 20 A0 00 B1 45 91 45   E9
C900 : C8 D0 F9 E6 46 CA D0 F4   73
C908 : 60 A0 14 B9 86 C9 99 44   9A
C910 : 00 88 D0 F7 A9 20 8D E7   29
C918 : 07 AD 84 C9 85 01 68 AA   A7
C920 : 68 A8 68 58 60 A0 02 B1   78
C928 : 45 85 4A 88 B1 45 85 49   C1
C930 : 88 B1 45 85 53 A8 A5 47   32
C938 : 38 E5 53 85 47 B0 26 C6   08
C940 : 48 A5 48 C9 DF D0 1E A9   F6
C948 : 01 8D 85 C9 98 18 65 47   19
C950 : 85 4B 90 02 E6 48 A5 48   B7
C958 : 85 4C A9 00 85 47 A9 C0   28
C960 : 85 48 4C 36 C9 88 B1 49   1D
C968 : 91 47 88 C0 FF D0 F7 A0   7F
C970 : 01 A5 4D 38 E5 53 85 4D   48
C978 : 91 45 B0 02 C6 4E C8 A5   65
C980 : 4E 91 45 60 EA EA EA 00   A6
C988 : 00 00 00 00 00 00 00 00   89
C990 : 00 00 00 00 00 00 00 00   91
C998 : 00 00 00 00 00 00 00 00   99
C9A0 : A9 20 8D 26 B5 A9 00 8D   45
C9A8 : 27 B5 A9 C7 8D 28 B5 A9   51
C9B0 : 60 8D 29 B5 60            DC
Listing 1. »Garbage 64« reduziert die Zeiten für die Stringmüllbeseitigung auf ein Minimum.
10 SYS9*4096
20 .OPT P,OO
30 *= $C700
100 ; **** GARBAGE COLLECTION ****
101 ;
102 ;DEFINITION DER HILFSZEIGER
103 LAZEI =$45;LAUFZEIGER D.DESKRIPTOREN
104 ROMZEI =$47;LAUFZEIGER UNTER ROM
105 STRIZEI =$49;HILFSZEIGER F. STRING
106 MEZEI =$4B;LETZTER PLATZ OBERES ROM
107 NEUZEI =$4D;STRINGBEREICHSLAUFZEIGER
108 FELDEND =$4F;HILFZEIGER ARRAYENDE
109 STRISPEI =$51;HILFSZEIGER
110 SUBTRAH =$53;STRINGLAENGEZWISCHENSP.
111 ;
112 ;
113 ;VORBELEGUNG DER SPEICHERPLAETZE
114 VROMZEI =$0000;START VON ROMZEI
116 VGRENZEI =$DF00;GRENZE+ FUER ROMZEI
117 VJUMPZEI =$C000
118 ;
119 ; 1. VORBEREITUNG ------------------
120 ;
121 ANFANG SEI;INTERRUPTS VERHINDERN
122 PHA;PROZESSORREGISTER RETTEN
123 TYA
124 PHA
125 TXA
126 PHA
127 LDA 1;SPEICHERKONFIGURATION RETTEN
128 STA ZAHL
129 LDA #46;KONTROLLANZEIGE BILDSCHIRM
130 STA 2023
131 LDA #9
132 STA 56295
133 LDY #20;BENOETIGTE SPEICHER RETTEN
134 RET LDA $44,Y
135 STA SAVE,Y
136 DEY
137 BNE RET
138 LDA #55;AUF ROM UMSCHALTEN
139 STA 1
140 LDA 45;VARIABLENSTART NACH LAZEI
141 STA LAZEI
142 LDA 46
143 STA LAZEI+1
144 ;
145 LDA #<VROMZEI;$FFFF+1 NACH ROMZEI
146 STA ROMZEI
147 LDA #>VROMZEI
148 STA ROMZEI+1
149 ;
150 LDA #0;MEZEI VORBELEGEN
151 STA MEZEI
152 STA MEZEI+1
153 STA ZWEI
154 ;
155 LDA 55;DURCHL.ZEIGER F.STRINGBEREICH
156 STA NEUZEI
157 LDA 56
158 STA NEUZEI+1
159 ;
160 ;
161 ;2. VARIABLENBEREICH ---------------
162 ;                                   600
163 VOVORN LDA LAZEI;SCHON ENDE VARIABLE
164 CMP 47
165 BNE WEI1
166 LDA LAZEI+1
167 CMP 48
168 BEQ FELDER
169 ;
170 WEI1 LDY #0;STRINGVARIABLE J/N
171 LDA (LAZEI),Y
172 ASL
173 BCS LA7;KEINE STRINGVARIABLE
174 INY
175 LDA (LAZEI),Y
176 ASL
177 BCC LA7;KEINE STRINGVARIABLE
178 ;
179 CLC;LAZEI=LAZEI+2;STRINGVARIABLE
180 LDA #2
181 ADC LAZEI
182 STA LAZEI
183 BCC WEI2
184 INC LAZEI+1
185 ;
186 ;
187 WEI2 LDY #0
188 LDA (LAZEI),Y;LEERSTRING J/N
189 BEQ LA5
190 ;
191 LDY #2;DESCRIPTOR IN STR.BEREICH J/N
192 LDA (LAZEI),Y
193 CMP 52
194 BCC LA5;HB 52 > ALS DESCRIPTOR HB
195 BNE WEI3;HB 52 <> HB DESKRIPTOR=OK
196 DEY
197 LDA (LAZEI),Y
198 CMP 51
199 BCC LA5;LB 51 > LB DES,HB 52 =HB DES
200 ;
201 WEI3 JSR ABSPEI
202 ;
203 LA5 CLC;LAZEI UM 5 ERHOEHEN
204 LDA #5
205 ADC LAZEI
206 STA LAZEI
207 BCC L1
208 INC LAZEI+1
209 L1 JMP VOVORN
210 ;
211 LA7 CLC;LAZEI UM 7 ERHOEHEN
212 LDA #7
213 ADC LAZEI
214 STA LAZEI
215 BCC L2
216 INC LAZEI+1
217 L2 JMP VOVORN
218 ;
219 ;
220 ;
221 ;3. ARRAYBEREICH -------------------
222 ;
223 FELDER LDA LAZEI;ARRAYBEREICHENDEJ/N
224 CMP 49
225 BNE WEI11
226 LDA LAZEI+1
227 CMP 50
228 BNE WEI11
229 JMP RAMUN
230 ;
231 WEI11 LDY #0;STRINGFELD J/N
232 LDA (LAZEI),Y
233 ASL
234 BCS LASTRL;KEIN STRINGFELD
235 INY
236 LDA (LAZEI),Y
237 ASL
238 BCC LASTRL;KEIN STRINGFELD
239 ;
240 ;
241 ;
242 ;STRINGFELD
243 LDY #3;ERRECHNEN FELDENDE
244 LDA (LAZEI),Y
245 PHA
246 DEY
247 CLC
248 LDA (LAZEI),Y
249 ADC LAZEI
250 STA FELDEND
251 PLA
252 ADC LAZEI+1
253 STA FELDEND+1
254 ;
255 LDY #4;ANZAHL DER DIMENSIONEN NACH Y
256 LDA (LAZEI),Y
257 TAY
258 ;
259 LDA #5;LAZEI AUF 1. DESCRIPTOR
260 CLC;LAZEI UM 5 ERHOEHEN
261 ADC LAZEI
262 STA LAZEI
263 BCC WEI14
264 INC LAZEI+1
265 ;
266 ;
267 WEI14 LDA #2;LAZEI + DIMENS.ANZAHL*2
268 CLC
269 ADC LAZEI
270 STA LAZEI
271 BCC WEI15
272 INC LAZEI+1
273 WEI15 DEY
274 BNE WEI14
275 ;
276 VUVURN LDA LAZEI;FELDENDE J/N
277 CMP FELDEND
278 BNE WEI16
279 LDA LAZEI+1
280 CMP FELDEND+1
281 BEQ FELDER
282 ;
283 WEI16 LDA (LAZEI),Y;STRING LEER J/N
284 BEQ LA3
285 ;
286 LDY #2;DESCRIPTOR IM STR.BEREICH J/N
287 LDA (LAZEI),Y
288 CMP 52
289 BCC LA3
290 BNE WEI17
291 DEY
292 LDA (LAZEI),Y
293 CMP 51
294 BCC LA3
295 WEI17 JSR ABSPEI
296 ;
297 LA3 CLC;LAZEI UM 3 ERHOEHEN
298 LDA #3
299 ADC LAZEI
300 STA LAZEI
301 BCC WEI18
302 INC LAZEI+1
303 WEI18 JMP VUVURN
304 ;
305 LASTRL LDY #3;LAZEI + ARRAYLAENGE
306 LDA (LAZEI),Y
307 PHA
308 DEY
309 LDA (LAZEI),Y
310 CLC
311 ADC LAZEI
312 STA LAZEI
313 PLA
314 ADC LAZEI+1
315 STA LAZEI+1
316 JMP FELDER
317 ;
318 ;
319 ;
320 ;
321 ;4. RAM UNTER ROM NACH VARIABLE ----
322 ;
323 RAMUN LDA ROMZEI;WAREN STRINGS DA
324 CMP #<VROMZEI
325 BNE RUMUN
326 LDA ROMZEI+1
327 CMP #>VROMZEI
328 BNE RUMUN
329 JMP ENDE
330 ;
331 RUMUN LDA #53;AUF ROM SCHALTEN
332 STA 1
333 LDA NEUZEI;STRINGBEGINNSZEIGER NEU
334 STA 51
335 LDA NEUZEI+1
336 STA 52
337 ;
338 LDA ZWEI;WAR UNTERES RAM IN USE J/N
339 BNE DOPP
340 ;
341 LDA ROMZEI;NUR 1.BEREICH ROMZEI-FFFF
342 STA STRISPEI;SUBROUTINEVORBELEGUNG
343 LDA ROMZEI+1
344 STA STRISPEI+1
345 LDA #<VROMZEI-1
346 STA LAZEI
347 LDA #>VROMZEI-1
348 STA LAZEI+1
349 ;
350 JSR SPEISTRI;UMSPEICHERUNGSROUTINE
351 ;
352 JMP ROMNEU
353 ;
354 ;
355 DOPP LDA ROMZEI;1.UND 2. BENUTZT
356 STA STRISPEI;SUBROUTINEVORBELEGUNG
357 LDA ROMZEI+1
358 STA STRISPEI+1
359 LDA #<VJUMPZEI-1
360 STA LAZEI
361 LDA #>VJUMPZEI-1
362 STA LAZEI+1
363 ;
364 JSR SPEISTRI;UMSPEICHERROUTINE
365 ;
366 LDA MEZEI;SUBROUTINEVORBELEGUNG
367 STA STRISPEI
368 LDA MEZEI+1
369 STA STRISPEI+1
370 LDA #<VROMZEI-1
371 STA LAZEI
372 LDA #>VROMZEI-1
373 STA LAZEI+1
374 ;
375 JSR SPEISTRI;UMSPEICHERROUTINE
376 JMP ROMNEU
377 ;
378 ;
379 ;
380 ;
381 SPEISTRI LDY #0;KOPIER V STRISPEI
382 ;- LAZEI NACH NEUZEI AUFWAERTS
383 WIDA LDA (STRISPEI),Y
384 STA (NEUZEI),Y
385 LDA LAZEI
386 CMP STRISPEI
387 BNE W21
388 LDA LAZEI+1
389 CMP STRISPEI+1
390 BNE W21
391 BEQ W24
392 W21 INC NEUZEI
393 BNE W22
394 INC NEUZEI+1
395 W22 INC STRISPEI
396 BNE WIDA
397 INC STRISPEI+1
398 W23 JMP WIDA
399 ;
400 W24 INC NEUZEI
401 BNE W25
402 INC NEUZEI+1
403 W25 RTS
404 ;
405 ;
406 ;
407 ;5. ROM NEU INS RAM KOPIEREN -------
408 ;
409 ROMNEU LDA ZAHL;EINGEBUNDEN J/N
410 CMP #55
411 BEQ ENDE;LAEUFT IM ROM
412 JSR RUMNEU
413 JMP ENDE
414 ;
415 LDA #1;HIER EXTRAEINSPRUNG VON BASIC
416 STA ZWEI
417 JSR RUMNEU
418 LDA #53
419 STA 1
420 RTS
421 ;
422 RUMNEU LDA #55;AUF ROM UMSCHALTEN
423 STA 1
424 ;
425 LDA #$00
426 STA LAZEI
427 LDA #$E0
428 STA LAZEI+1
429 JSR COPI
430 ;
431 LDA ZWEI;UNT. ROM AUCH ZU KOPIEREN
432 BEQ END
433 LDA #$00
434 STA LAZEI
435 LDA #$A0
436 STA LAZEI+1
437 JSR COPI
438 END JSR EINBIND
439 RTS
440 ;
441 ;
442 COPI LDX #32;8K ROM IN RAM KOPIEREN
443 LDY #0
444 AGEIN LDA (LAZEI),Y
445 STA (LAZEI),Y
446 INY
447 BNE AGEIN
448 INC LAZEI+1
449 DEX
450 BNE AGEIN
451 RTS
452 ;
453 ;6. ENDE ---------------------------
454 ;
455 ENDE LDY #20;SPEICHER RUECKRETTEN
456 ROT LDA SAVE,Y
457 STA $44,Y
458 DEY
459 BNE ROT
460 LDA #32;KONTROLLANZEIGE LOESCHEN
461 STA 2023
462 LDA ZAHL;ALTE SPEICHERKONFIGURATION
463 STA 1
464 PLA;PROZESSORINHALTE WIEDERHOLEN
465 TAX
466 PLA
467 TAY
468 PLA
469 CLI;INTERRUPTS WIEDER ERLAUBT
470 RTS
471 ;
472 ;
473 ;SUBROUTINEN -----------------------
474 ;
475 ;UNTERPROGRAMM, DAS DEN STRING, AUF
476 ;DESSEN DESKPRIPTOR LAZEI STEHT,UN-
477 ;TERHALB ROMZEI ABSPEICHERT UND DIE
478 ;POSITIONSANGABE DES DESKR. RELATIV
479 ;ZUM RICHT. STRINGBER. AKTUALISIERT
480 ;
481 ABSPEI LDY #2;STRINGADR. IN STRIZEI
482 LDA (LAZEI),Y;LAENGE IN Y UND STACK
483 STA STRIZEI+1
484 DEY
485 LDA (LAZEI),Y
486 STA STRIZEI
487 DEY
488 LDA (LAZEI),Y
489 STA SUBTRAH
490 TAY
491 ;
492 OGAIN LDA ROMZEI;ROMZEI-STRINGLAENGE
493 SEC
494 SBC SUBTRAH
495 STA ROMZEI
496 BCS ETZ
497 DEC ROMZEI+1
498 LDA ROMZEI+1
499 CMP #>VGRENZEI
500 BNE ETZ
501 ;
502 LDA #1;O.RAM VOLL,ROMZEI+YNACH MEZEI
503 STA ZWEI
504 TYA
505 CLC
506 ADC ROMZEI
507 STA MEZEI
508 BCC WTR
509 INC ROMZEI+1
510 WTR LDA ROMZEI+1
511 STA MEZEI+1
512 LDA #<VJUMPZEI;ROMZEI AUF  C000
513 STA ROMZEI
514 LDA #>VJUMPZEI
515 STA ROMZEI+1
516 JMP OGAIN
517 ;
518 ETZ DEY;STRING UNTERS ROM SPEICHERN
519 NOML LDA (STRIZEI),Y
520 STA (ROMZEI),Y
521 DEY
522 CPY #255
523 BNE NOML
524 ;
525 ;
526 LDY #1;NEUZEI-STR.LAENGE=DESKRIPTOR
527 LDA NEUZEI
528 SEC
529 SBC SUBTRAH
530 STA NEUZEI
531 STA (LAZEI),Y
532 BCS WAIDR
533 DEC NEUZEI+1
534 WAIDR INY
535 LDA NEUZEI+1
536 STA (LAZEI),Y
537 RTS
538 ;
539 ZAHL NOP;MERKER FUER KONFIGURATION
540 ZWEI NOP;=1 WENN MEHR O.RAM USED
541 SAVE NOP
542 *= *+25
543 ;
544 ;VERAENDERUNG DES GARBAGEEINSPRUNGS
545 ;UND EVENTUELL DES BETRIEBSSYSTEMS
546 GAR =$B526
547 EINBIND LDA #$20;JSR ANFANG U. RTS
548 STA GAR
549 LDA #<ANFANG
550 STA GAR+1
551 LDA #>ANFANG
552 STA GAR+2
553 LDA #$60
554 STA GAR+3
555 RTS

READY.
Listing 2. Quellencode zu »Garbage 64«
10 sys9*4096
20 .opt p,oo
30 *= $c700
100 ; **** garbage collection ****
101 ;
102 ;definition der hilfszeiger
103 lazei =$45;laufzeiger d.deskriptoren
104 romzei =$47;laufzeiger unter rom
105 strizei =$49;hilfszeiger f. string
106 mezei =$4b;letzter platz oberes rom
107 neuzei =$4d;stringbereichslaufzeiger
108 feldend =$4f;hilfzeiger arrayende
109 strispei =$51;hilfszeiger
110 subtrah =$53;stringlaengezwischensp.
111 ;
112 ;
113 ;vorbelegung der speicherplaetze
114 vromzei =$0000;start von romzei
116 vgrenzei =$df00;grenze+ fuer romzei
117 vjumpzei =$c000
118 ;
119 ; 1. vorbereitung ------------------
120 ;
121 anfang sei;interrupts verhindern
122 pha;prozessorregister retten
123 tya
124 pha
125 txa
126 pha
127 lda 1;speicherkonfiguration retten
128 sta zahl
129 lda #46;kontrollanzeige bildschirm
130 sta 2023
131 lda #9
132 sta 56295
133 ldy #20;benoetigte speicher retten
134 ret lda $44,y
135 sta save,y
136 dey
137 bne ret
138 lda #55;auf rom umschalten
139 sta 1
140 lda 45;variablenstart nach lazei
141 sta lazei
142 lda 46
143 sta lazei+1
144 ;
145 lda #<vromzei;$ffff+1 nach romzei
146 sta romzei
147 lda #>vromzei
148 sta romzei+1
149 ;
150 lda #0;mezei vorbelegen
151 sta mezei
152 sta mezei+1
153 sta zwei
154 ;
155 lda 55;durchl.zeiger f.stringbereich
156 sta neuzei
157 lda 56
158 sta neuzei+1
159 ;
160 ;
161 ;2. variablenbereich ---------------
162 ;                                   600
163 vovorn lda lazei;schon ende variable
164 cmp 47
165 bne wei1
166 lda lazei+1
167 cmp 48
168 beq felder
169 ;
170 wei1 ldy #0;stringvariable j/n
171 lda (lazei),y
172 asl
173 bcs la7;keine stringvariable
174 iny
175 lda (lazei),y
176 asl
177 bcc la7;keine stringvariable
178 ;
179 clc;lazei=lazei+2;stringvariable
180 lda #2
181 adc lazei
182 sta lazei
183 bcc wei2
184 inc lazei+1
185 ;
186 ;
187 wei2 ldy #0
188 lda (lazei),y;leerstring j/n
189 beq la5
190 ;
191 ldy #2;descriptor in str.bereich j/n
192 lda (lazei),y
193 cmp 52
194 bcc la5;hb 52 > als descriptor hb
195 bne wei3;hb 52 <> hb deskriptor=ok
196 dey
197 lda (lazei),y
198 cmp 51
199 bcc la5;lb 51 > lb des,hb 52 =hb des
200 ;
201 wei3 jsr abspei
202 ;
203 la5 clc;lazei um 5 erhoehen
204 lda #5
205 adc lazei
206 sta lazei
207 bcc l1
208 inc lazei+1
209 l1 jmp vovorn
210 ;
211 la7 clc;lazei um 7 erhoehen
212 lda #7
213 adc lazei
214 sta lazei
215 bcc l2
216 inc lazei+1
217 l2 jmp vovorn
218 ;
219 ;
220 ;
221 ;3. arraybereich -------------------
222 ;
223 felder lda lazei;arraybereichendej/n
224 cmp 49
225 bne wei11
226 lda lazei+1
227 cmp 50
228 bne wei11
229 jmp ramun
230 ;
231 wei11 ldy #0;stringfeld j/n
232 lda (lazei),y
233 asl
234 bcs lastrl;kein stringfeld
235 iny
236 lda (lazei),y
237 asl
238 bcc lastrl;kein stringfeld
239 ;
240 ;
241 ;
242 ;stringfeld
243 ldy #3;errechnen feldende
244 lda (lazei),y
245 pha
246 dey
247 clc
248 lda (lazei),y
249 adc lazei
250 sta feldend
251 pla
252 adc lazei+1
253 sta feldend+1
254 ;
255 ldy #4;anzahl der dimensionen nach y
256 lda (lazei),y
257 tay
258 ;
259 lda #5;lazei auf 1. descriptor
260 clc;lazei um 5 erhoehen
261 adc lazei
262 sta lazei
263 bcc wei14
264 inc lazei+1
265 ;
266 ;
267 wei14 lda #2;lazei + dimens.anzahl*2
268 clc
269 adc lazei
270 sta lazei
271 bcc wei15
272 inc lazei+1
273 wei15 dey
274 bne wei14
275 ;
276 vuvurn lda lazei;feldende j/n
277 cmp feldend
278 bne wei16
279 lda lazei+1
280 cmp feldend+1
281 beq felder
282 ;
283 wei16 lda (lazei),y;string leer j/n
284 beq la3
285 ;
286 ldy #2;descriptor im str.bereich j/n
287 lda (lazei),y
288 cmp 52
289 bcc la3
290 bne wei17
291 dey
292 lda (lazei),y
293 cmp 51
294 bcc la3
295 wei17 jsr abspei
296 ;
297 la3 clc;lazei um 3 erhoehen
298 lda #3
299 adc lazei
300 sta lazei
301 bcc wei18
302 inc lazei+1
303 wei18 jmp vuvurn
304 ;
305 lastrl ldy #3;lazei + arraylaenge
306 lda (lazei),y
307 pha
308 dey
309 lda (lazei),y
310 clc
311 adc lazei
312 sta lazei
313 pla
314 adc lazei+1
315 sta lazei+1
316 jmp felder
317 ;
318 ;
319 ;
320 ;
321 ;4. ram unter rom nach variable ----
322 ;
323 ramun lda romzei;waren strings da
324 cmp #<vromzei
325 bne rumun
326 lda romzei+1
327 cmp #>vromzei
328 bne rumun
329 jmp ende
330 ;
331 rumun lda #53;auf rom schalten
332 sta 1
333 lda neuzei;stringbeginnszeiger neu
334 sta 51
335 lda neuzei+1
336 sta 52
337 ;
338 lda zwei;war unteres ram in use j/n
339 bne dopp
340 ;
341 lda romzei;nur 1.bereich romzei-ffff
342 sta strispei;subroutinevorbelegung
343 lda romzei+1
344 sta strispei+1
345 lda #<vromzei-1
346 sta lazei
347 lda #>vromzei-1
348 sta lazei+1
349 ;
350 jsr speistri;umspeicherungsroutine
351 ;
352 jmp romneu
353 ;
354 ;
355 dopp lda romzei;1.und 2. benutzt
356 sta strispei;subroutinevorbelegung
357 lda romzei+1
358 sta strispei+1
359 lda #<vjumpzei-1
360 sta lazei
361 lda #>vjumpzei-1
362 sta lazei+1
363 ;
364 jsr speistri;umspeicherroutine
365 ;
366 lda mezei;subroutinevorbelegung
367 sta strispei
368 lda mezei+1
369 sta strispei+1
370 lda #<vromzei-1
371 sta lazei
372 lda #>vromzei-1
373 sta lazei+1
374 ;
375 jsr speistri;umspeicherroutine
376 jmp romneu
377 ;
378 ;
379 ;
380 ;
381 speistri ldy #0;kopier v strispei
382 ;- lazei nach neuzei aufwaerts
383 wida lda (strispei),y
384 sta (neuzei),y
385 lda lazei
386 cmp strispei
387 bne w21
388 lda lazei+1
389 cmp strispei+1
390 bne w21
391 beq w24
392 w21 inc neuzei
393 bne w22
394 inc neuzei+1
395 w22 inc strispei
396 bne wida
397 inc strispei+1
398 w23 jmp wida
399 ;
400 w24 inc neuzei
401 bne w25
402 inc neuzei+1
403 w25 rts
404 ;
405 ;
406 ;
407 ;5. rom neu ins ram kopieren -------
408 ;
409 romneu lda zahl;eingebunden j/n
410 cmp #55
411 beq ende;laeuft im rom
412 jsr rumneu
413 jmp ende
414 ;
415 lda #1;hier extraeinsprung von basic
416 sta zwei
417 jsr rumneu
418 lda #53
419 sta 1
420 rts
421 ;
422 rumneu lda #55;auf rom umschalten
423 sta 1
424 ;
425 lda #$00
426 sta lazei
427 lda #$e0
428 sta lazei+1
429 jsr copi
430 ;
431 lda zwei;unt. rom auch zu kopieren
432 beq end
433 lda #$00
434 sta lazei
435 lda #$a0
436 sta lazei+1
437 jsr copi
438 end jsr einbind
439 rts
440 ;
441 ;
442 copi ldx #32;8k rom in ram kopieren
443 ldy #0
444 agein lda (lazei),y
445 sta (lazei),y
446 iny
447 bne agein
448 inc lazei+1
449 dex
450 bne agein
451 rts
452 ;
453 ;6. ende ---------------------------
454 ;
455 ende ldy #20;speicher rueckretten
456 rot lda save,y
457 sta $44,y
458 dey
459 bne rot
460 lda #32;kontrollanzeige loeschen
461 sta 2023
462 lda zahl;alte speicherkonfiguration
463 sta 1
464 pla;prozessorinhalte wiederholen
465 tax
466 pla
467 tay
468 pla
469 cli;interrupts wieder erlaubt
470 rts
471 ;
472 ;
473 ;subroutinen -----------------------
474 ;
475 ;unterprogramm, das den string, auf
476 ;dessen deskpriptor lazei steht,un-
477 ;terhalb romzei abspeichert und die
478 ;positionsangabe des deskr. relativ
479 ;zum richt. stringber. aktualisiert
480 ;
481 abspei ldy #2;stringadr. in strizei
482 lda (lazei),y;laenge in y und stack
483 sta strizei+1
484 dey
485 lda (lazei),y
486 sta strizei
487 dey
488 lda (lazei),y
489 sta subtrah
490 tay
491 ;
492 ogain lda romzei;romzei-stringlaenge
493 sec
494 sbc subtrah
495 sta romzei
496 bcs etz
497 dec romzei+1
498 lda romzei+1
499 cmp #>vgrenzei
500 bne etz
501 ;
502 lda #1;o.ram voll,romzei+ynach mezei
503 sta zwei
504 tya
505 clc
506 adc romzei
507 sta mezei
508 bcc wtr
509 inc romzei+1
510 wtr lda romzei+1
511 sta mezei+1
512 lda #<vjumpzei;romzei auf  c000
513 sta romzei
514 lda #>vjumpzei
515 sta romzei+1
516 jmp ogain
517 ;
518 etz dey;string unters rom speichern
519 noml lda (strizei),y
520 sta (romzei),y
521 dey
522 cpy #255
523 bne noml
524 ;
525 ;
526 ldy #1;neuzei-str.laenge=deskriptor
527 lda neuzei
528 sec
529 sbc subtrah
530 sta neuzei
531 sta (lazei),y
532 bcs waidr
533 dec neuzei+1
534 waidr iny
535 lda neuzei+1
536 sta (lazei),y
537 rts
538 ;
539 zahl nop;merker fuer konfiguration
540 zwei nop;=1 wenn mehr o.ram used
541 save nop
542 *= *+25
543 ;
544 ;veraenderung des garbageeinsprungs
545 ;und eventuell des betriebssystems
546 gar =$b526
547 einbind lda #$20;jsr anfang u. rts
548 sta gar
549 lda #<anfang
550 sta gar+1
551 lda #>anfang
552 sta gar+2
553 lda #$60
554 sta gar+3
555 rts
1 ifa=1then3
2 a=1:load"garbage64",8,1
3 poke53280,0:poke53281,0
4 sys51400
5 poke50956,1:poke50961,7
10 dimk(30)
12 diml%(100)
14 dime$(200,1):e$(200,1)="ok"
16 dimn$(20):n$(20)="test"
30 f%=56
32 a$="12345"
34 x=3
36 b$="abcdef"+"g"
38 c$="tes"+"t"
40 b$=""
62 forx=1to200:e$(x,0)="abcdefgh"+"9":next
63 forx=1to200:e$(x,0)="12345678"+"9":next
85 printa$:printb$:printc$:printe$(1,0):printe$(200,0):printn$(20);e$(200,1)
90 goto30
Listing 3. Demoprogramm für »Garbage 64«. Die Zeitunterschiede werden deutlich sichtbar.
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →