Von Basic zu Assembler (Teil 4)

Das Hauptaugenmerk wird in dieser Folge auf das Verschieben und Vertauschen von Speicherbereichen gerichtet. Des weiteren finden Sie noch Anwendungsvorschläge, um die Routinen richtig zu nutzen.

Endlich ist es soweit: Wie schon vor geraumer Zeit versprochen, lernen Sie nun die Blockverschieberoutine kennen, aber auch ihre Schwächen und einen Weg, Speicherbereiche fehlerfrei zu verschieben.

Eng mit dem Verschieben von Bereichen ist das andere Programm verwandt, das wir entwickeln. SWAP nennen wir es, und es soll Speicherbereiche miteinander vertauschen. Wie immer, so sind auch diesmal die Programme sowohl auf dem C 64 als auch dem C 128 einsetzbar.

Speicherblöcke verschieben

Häufiges Thema in Leseranfragen ist das Verschieben von Speicherbereichen. Das ist durchaus zu verstehen, denn mit einem Programminstrument, das beliebige Inhalte beliebig großer Speicherbereiche verschieben kann, läßt sich allerhand anstellen. So könnte man ein Basic-Programm vorübergehend beispielsweise nach $C000 legen, in der Zwischenzeit ein anderes laden und bearbeiten und dann das erste wieder herunterholen in den Basic-Speicher. Oder es wäre möglich, einen Hilfsbildschirm zu erstellen, diesen irgendwo im Speicher an einen sicheren Ort zu verlagern und ihn dann auf Tastendruck wieder hervorzuholen. Oder man könnte sich verschiedene Teile von Bildern erstellen, im Speicher ablegen und bei Bedarf in die aktuelle Bitmap blenden. Oder… Ihnen fallen bestimmt noch viele Anwendungen für ein solches Programminstrument ein.

Diejenigen unter Ihnen, vor denen nun ein C 64 steht, haben Glück: Im Betriebssystem des C 64 ist nämlich eine komplette und vor allem leicht ansteuerbare Blockverschieberoutine enthalten. C 128-Benutzer finden solch eine Routine zwar auch in ihrem Speicher vor (nämlich ab $F4EA8), die ist aber leider nicht zu verwenden, weil sie nicht einfach mit einem RTS endet, sondern noch allerlei unerwünschte Zeigeränderungen anstellt. Allerdings kann der C 128-Besitzer auch mit erheblichen Effekt auf den T-Befehl des eingebauten Monitors zugreifen. Auch von Basic aus ist das mit Hilfe des »programmierten Direktmodus« möglich. Wen näheres darüber interessiert, der sollte mal in folgendem Buch das Kapitel dazu nachlesen: Ponnath, »Grafikprogrammierung C 128«, Markt und Technik Verlag, MT857. Eine andere Möglichkeit für den C 128-Benutzer ist unser später noch vorzustellendes Programm BLOCK.

Sehen wir uns nun zunächst die im C 64-Interpreter enthaltene Blockverschieberoutine BLTUC an:

Das scheint also der Weg zur Benutzung dieser Routine zu sein: Man schreibt ein Basic-Programm, das die leidige Umrechnung der drei Adressen (Quellenstart, Quellenende +1 und Zielende +1) übernimmt und die errechneten LSB und MSB in die erforderlichen Abholspeicherstellen packt. Danach braucht man nur noch mittels eines SYS 41919 die BLTUC-Routine zu starten. Sollten Sie es mal probieren wollen, dann werden Sie einen Absturz des Programmes erleben. So geht es nicht, und zwar deshalb, weil der Basic-Interpreter die Speicherstellen $5F und $60 nach dem Belegen mit der Quellenstartadresse mit seinen Merkwerten überschreibt. Glücklicherweise enthält aber die Seite 3 eine Möglichkeit, Werte abholbereit für die Register so aufzubewahren, daß sie nach einem SYS-Befehl im Akku, dem X- und dem Y-Register zu finden sind. Die Zuordnung ist dann so:

Name Adresse Register
$ dez.
SAREG 30C 780 Akku
SXREG 30D 781 X-Register
SYREG 30E 782 Y-Register
SPREG 30F 783 Stapelzeiger

Wir schreiben nun die Quellenstartadresse statt nach $5F/60 zunächst nach 780 und 781. Der anschließende SYS-Befehl ruft zuerst ein kleines Maschinenprogramm auf, das die Werte in die richtigen Speicherzellen schreibt und dann BLTUC anspringt:
STA $5F
STX $60
JMP $A3BF

Beiliegend finden Sie ein kleines Basic-Programm, das all diese Aufgaben übernimmt: »BLTUC BAS« (Listing 1)

10 rem ***** bltuc - testprogramm *****
20 printchr$(147)
30 poke53280,0:poke53281,5:poke646,1
35 rem +++ farbram u. schirm belegen +++
40 for i = 0 to 79:poke55296+i,1:nexti
50 for i = 1 to 39:poke1024+i,i:nexti
52 rem +++ ml-prg.parameteruebergabe +++
54 for i=49152to49158:reada:pokei,a:nexti
56 data133,95,134,96,76,191,163
60 rem +++ aufwaerts verschieben +++
62 fori=0to300:nexti
64 printchr$(17)chr$(17)"wir verschieben jetzt!"
66 fori=0to300:nexti
70 qs=1025:qe=1064:rem quelle start und quelle ende+1
80 ze=1065:rem ziel ende+1
90 a=int(qs/256):poke781,a:poke780,qs-256*a
100 a=int(qe/256):poke91,a:poke90,qe-256*a
110 a=int(ze/256):poke89,a:poke88,ze-256*a
120 sys49152
130 printchr$(17)chr$(17)"das war um 1 aufwaerts":print"bitte taste druecken"
140 geta$:ifa$=""then140
150 rem +++ abwaerts verschieben +++
152 fori=0to300:nexti
154 printchr$(17)chr$(17)"jetzt verschieben wir abwaerts!"
156 fori=0to300:nexti
160 qs=1025:qe=1064:rem quelle start und quelle ende+1
170 ze=1063:rem ziel ende+1
180 a=int(qs/256):poke781,a:poke780,qs-256*a
190 a=int(qe/256):poke91,a:poke90,qe-256*a
200 a=int(ze/256):poke89,a:poke88,ze-256*a
210 sys49152
220 printchr$(17)chr$(17)"da sehen sie das problem":print"der bltuc-routine"
Listing 1. »BLTUC BAS« — Ein kleines Basic-Programm zum Testen der BLTUC-Routine

BLTUC BAS zeigt die Funktion von BLTUC anhand des Bildschirmspeichers. In den Zeilen 40 und 50 wird in die erste Bildschirmzeile — ab Position 1025 — eine fortlaufende Reihe von verschiedenen Zeichen geschrieben, die wir im nachfolgenden verschieben werden. Damit diese Zeichen sichtbar werden, müssen einige ältere Versionen des C 64 auch den Farbspeicher beschreiben. Das geschieht in der Zeile 40. Die Zeilen 54 und 56 erzeugen das kleine Maschinenprogramm, das die Belegung der Abrufzellen $5F, $60 und den Sprung in die BLTUC-Routine ausführt. Sie lesen den Dezimalcode des Maschinenprogrammes aus der DATA-Zeile in den Speicher ab 49152. Nun bereiten wir die erste Verschiebung vor: Hier soll einfach der ganze Bereich von 1025 bis 1063 um eine Zeile weiter geschoben werden, also nun bei 1065 beginnen. Damit das alles nicht ganz so schnell geht, sind noch einige kleine Warteschleifen ins Programm eingebaut worden. In den Zeilen 90 bis 110 trennen wir die in 70 und 80 benannten Start- und Endadressen auf in die MSB- und LSB-Werte und schreiben sie in die erforderlichen Speicherstellen 88 bis 91, beziehungsweise 780 und 781 ein. Zeile 120 vollführt nun mittels des SYS-Aufrufes die Verschiebung, was Sie auf dem Bildschirm erkennen können.

Auf Tastendruck gelangen Sie in den zweiten, den kritischen Teil des Programmes. Hier werden wir nun einen Fehler der BLTUC-Routine finden. Wir verschieben in diesem Teil den Inhalt des Speicherbereiches 1025 bis 1063 um eine Position abwärts, also in den Bereich 1024 bis 1062. Woran liegt es, daß hier plötzlich eine Fehlfunktion auftritt? Sehen wir uns dazu die BLTUC-Routine etwas genauer an. Als Programm BLTUC (Listing 2) finden Sie nachstehend ein Disassemblerlisting der BLTUC-Routine wie sie im C 64-Speicher ab $A3BF zu finden ist.

., a3bf 38       sec
., a3c0 a5 5a    lda $5a
., a3c2 e5 5f    sbc $5f
., a3c4 85 22    sta $22
., a3c6 a8       tay
., a3c7 a5 5b    lda $5b
., a3c9 e5 60    sbc $60
., a3cb aa       tax
., a3cc e8       inx
., a3cd 98       tya
., a3ce f0 23    beq $a3f3
., a3d0 a5 5a    lda $5a
., a3d2 38       sec
., a3d3 e5 22    sbc $22
., a3d5 85 5a    sta $5a
., a3d7 b0 03    bcs $a3dc
., a3d9 c6 5b    dec $5b
., a3db 38       sec
., a3dc a5 58    lda $58
., a3de e5 22    sbc $22
., a3e0 85 58    sta $58
., a3e2 b0 08    bcs $a3ec
., a3e4 c6 59    dec $59
., a3e6 90 04    bcc $a3ec
., a3e8 b1 5a    lda ($5a),y
., a3ea 91 58    sta ($58),y
., a3ec 88       dey
., a3ed d0 f9    bne $a3e8
., a3ef b1 5a    lda ($5a),y
., a3f1 91 58    sta ($58),y
., a3f3 c6 5b    dec $5b
., a3f5 c6 59    dec $59
., a3f7 ca       dex
., a3f8 d0 f2    bne $a3ec
., a3fa 60       rts
Listing 2. »BLTUC« - So steht die BLTUC-Routine im C 64-Speicher (Disassembler-Listing)

Die Anatomie der BLTUC-Routine

Das ganze Programm besteht aus zwei Teilen. Im ersten davon werden Berechnungen angestellt über die Länge des zu transportierenden Bereiches und zwei Transportzeiger eingerichtet. Im zweiten Teil findet dann die eigentliche Verschiebung statt. Die erste 16-Bit-Subtraktion (Quelle bis Ende +1 minus Quelle-Start) legt das MSB der Länge ins X-Register (das enthält dann die Anzahl der zu transportierenden Pages) und das LSB ins Y-Register und in die Speicherstelle $22 (dort liegt dann die restliche Länge, die weniger als eine ganze Page beträgt). Der BEQ-Befehl stellt fest, ob überhaupt ein solcher Rest vorhanden ist und verzweigt ansonsten direkt in den Transportteil. Zwei weitere Subtraktionen (Quelle-Ende +1 minus Länge des Restes und Ziel-Ende +1 minus Länge des Restes) richten die Zeiger $5A/5B und $58/59 auf die Adressen der nächstniedrigeren ganzen Page. Der Rest befindet sich noch im Y-Register. Das X-Register dient als Page-Zähler. Der BCC-Befehl bei $A3E6 führt immer zum Sprung nach $A3EC, weil an dieser Stelle das Carry-Bit immer frei ist.

Danach beginnt der Transportteil. Er besteht im wesentlichen aus zwei ineinander verschachtelten Schleifen, von denen die innere Schleife Byte für Byte aus dem Quell- in den Zielbereich kopiert (dabei beginnt sie mit dem Rest), die äußere zunächst ebenfalls ein Byte überträgt und dann die MSB-Werte der beiden Zeiger ($59 und $5B) herunterzählt. Dabei wird auch jedesmal der Pagezähler (X-Register) um 1 reduziert.

Kopieren von oben und von unten

Wir stellen also fest, daß ein Bereich durch BLTUC immer von der höheren zur niedrigeren Adresse hin durchgearbeitet wird. Sowohl der Index Y als auch der Page-Zähler X werden heruntergezählt. Was das zur Folge hat, werden wir nun bei einer genauen Betrachtung aller möglichen Verschiebungsfälle schnell erkennen. Insgesamt acht sind zu unterscheiden:

  1. Quell- und Zielbereich überschneiden sich nicht. Der Zielbereich liegt oberhalb des Quellbereiches. Das Kopieren erfolgt von unten (also von der niedrigsten Adresse an aufwärts. Die Register werden hochgezählt). Das nennen wir den Fall 1.
  2. Gleiche Bedingungen wie in Fall 1. Aber das Kopieren geschieht nun von oben (also von der höchsten Adresse an abwärts. Die Register zählen wir hier herunter). Dies ist Fall 2.
  3. Wieder liegt keine Überschneidung vor. Der Zielbereich liegt nun aber unterhalb des Quellbereiches. Das Kopieren erfolgt von unten. Fall 3 liegt vor.
  4. Die Bedingungen sind mit Fall 3 identisch, aber es wird wieder abwärts kopiert. Das ist Fall 4.
  5. Quell- und Zielbereich überschneiden sich. Ansonsten liegen die Verhältnisse wie bei Fall 1 vor. Das wäre dann Fall 5.
  6. Das ist der Fall 6, wo gleiche Bedingungen wie in Fall 2 vorliegen. Einziger Unterschied ist auch hier die Überschneidung von Quell- und Zielbereich.
  7. Fall 7 entspricht dem Fall 3 mit Überlappung der Bereiche.
  8. Das ist wieder der Fall 4 mit der Überschneidung von Quell- und Zielbereich.

Die Fälle 1 bis 4 bereiten keine Probleme. Hier bleibt es uns überlassen, wie wir eigene Verschiebungsprogramme organisieren wollen. Der BLTUC-Routinenanwendung entsprechen die Fälle 2 und 4. Sehen wir uns nun Fall 5 an (siehe dazu Bild 1).

Diagramm der Verschiebung bei Fall 5
Bild 1. Der Fall 5

In Bild 1a ist die Ausgangslage abgebildet, wobei der besseren Übersicht halber Quell- und Zielbereich untereinander gezeichnet sind. Natürlich handelt es sich bei den untereinanderliegenden Kästchen immer um ein- und dieselbe Speicherstelle. In Bild 1b wird das erste Byte des Quellbereiches in die erste Speicherstelle des Zielbereiches kopiert. Das ist aber gleichzeitig das zweite Byte des Quellbereiches. Was nun geschieht, zeigen die Teilbilder 1c und schließlich 1d: Der gesamte Zielbereich füllt sich mit dem Inhalt der ersten Quellbereichs-Speicherstelle. Hätten Sie das gedacht?

Bild 2 verdeutlicht uns den Fall 6.

Diagramm der Verschiebung bei Fall 6
Bild 2. Der Fall 6

Es ist nach dem gleichen Schema wie Bild 2 aufgebaut. Sie sehen, daß nun aber von oben herunter gearbeitet wird. Die erste Verschiebung packt das letzte Byte des Quellbereiches in die letzte Speicherstelle des Zielbereiches (Teilbild b). An den folgenden Teilbildern c und d ist deutlich, daß diese Methode fehlerfrei funktioniert. Nach diesem Schema arbeitet die BLTUC-Routine, weshalb wir beim Aufwärtsverschieben von Speicherinhalten auch bei Überlappungen keine Störungen erwarten brauchen.

Wenden wir uns nun dem Fall 7 zu. Bild 3 soll bei dieser Betrachtung wieder helfen:

Diagramm der Verschiebung bei Fall 7
Bild 3. Der Fall 7

In Fall 7 liegt ja der Zielbereich unterhalb des Quellbereiches und es wird von unten gearbeitet, also die Register aufwärts gezählt. Aus Bild 4 ist — gleiches Schema wie bisher — zu entnehmen, daß keine Probleme auftreten. Zu guter Letzt hilft uns nun das Bild 4 zum Verstehen des Falls 8:

Diagramm der Verschiebung bei Fall 8
Bild 4. Der Fall 8

Im Teilbild b erkennen Sie das Problem: Sobald das letzte Byte des Quellbereiches in die letzte Speicherstelle des Zielbereiches verschoben ist, haben wir das vorletzte Byte des Quellbereiches damit überschrieben, denn das ist ja gleichzeitig die letzte Speicherstelle des Zielbereiches. Jede weitere Verschiebung kopiert nun nur wieder diesen gleichen Inhalt, was Ihnen die Teilbilder c und d zeigen. Genau das macht die BLTUC-Routine, wie Sie im zweiten Teil des Programmes BLTUC BAS feststellen konnten. Man darf also diese Interpreter-Routine nicht anwenden, wenn der Zielbereich unterhalb des Quellbereiches liegt und beide sich überschneiden!

Wenn es daher unsicher ist, ob sich Quell- und Zielbereich überlappen oder wenn man davon ausgehen kann, daß das sicher der Fall sein wird, dann verfahre man beim Erstellen eigener Verschiebe-Routinen nach folgender Regel:

Fehlerfreies Verschieben mit BLOCK

Wie sollte ein Verschiebeprogramm aussehen, das allen Eventualitäten gerecht wird? Ganz einfach: Es müßte zunächst prüfen, ob eine Überlappung von Quell- und Zielbereich vorliegt und je nach Ergebnis dann zum entsprechenden Kopierteil verzweigen. Genau das tut das nachfolgend vorgestellte Programm BLOCK (Listing 3), welches sowohl auf dem C 64 als auch auf dem C 128 (dort aber nur innerhalb der gerade eingeschalteten Bank) arbeitet. L.A.Leventhal und W.Saville haben das Prinzip 1982 vorgestellt in »6502 Assembly Language Subroutines«. In Bild 5 finden Sie ein Flußdiagramm des Programmes BLOCK:

Flußdiagramm der BLOCK-Routine
Bild 5. Das Flußdiagramm des Programmes »BLOCK«
10  -;
20  -;
30  -.BASE $1300              ;IN HYPRA-ASS: .BA $C000
40  -;****************************************
50  -; BLOCKVERSCHIEBEROUTINE OHNE FEHLER
60  -;****************************************
70  -.DEFINE MVELEN   = $FA   ;IN HYPRA-ASS WIRD STATT DER
80  -.DEFINE MVDEST   = $FC   ; .DEFINE BEFEHLE JEWEILS DER BEFEHL
90  -.DEFINE MVSRCE   = $FE   ; .EQ = $... VERWENDET
100 -;
110 -;IN MVELEN WIRD DIE LAENGE DER ZU VERSCHIEBENDEN BEREICHES ANGEGEBEN
120 -;IN MVDEST DIE STARTADRESSE DES ZIELBEREICHES UND IN
130 -;MVSRCE DIE STARTADRESSE DES QUELLBEREICHES.
140 -;
150 -;------ PROGRAMM ---------------------------------------------
160 -;ALS ERSTES WIRD BESTIMMT, OB DER ZIELBEREICH OBERHALB DES
170 -; QUELLBEREICHES LIEGT UND OB SICH DIE BEIDEN BEREICHE UEBER-
180 -; LAPPEN. EINE UEBERLAPPUNG LIEGT DANN VOR, WENN DIE DIFFERENZ
190 -; VON ZIELADRESSE MINUS QUELLADRESSE KLEINER ALS DIE ANZAHL DER
200 -; ZU VERSCHIEBENDEN BYTES IST.
210 -;
220 -START     LDA MVDEST     ;BERECHNUNG ZIEL MINUS QUELLE
230 -          SEC
240 -          SBC MVSRCE
250 -          TAX
260 -          LDA MVDEST+1
270 -          SBC MVSRCE+1
280 -          TAY
290 -          TXA            ;VERGLEICH MIT LAENGE DES VERSCHIEBEBEREICHES
300 -          CMP MVELEN
310 -          TYA
320 -          SBC MVELEN+1
330 -          BCS DOLEFT     ;VERZWEIGEN, WENN KEINE UEBERLAPPUNG
340 -          JSR MVERHT     ;SONST ZUM UP FUER UEBERLAPPUNG
350 -          JMP EXIT
360 -DOLEFT    JSR MVELFT     ;ZUM UP OHNE UEBERLAPPUNG
370 -EXIT      RTS
380 -;
390 -;**** UP ZUM VERSCHIEBEN OHNE UEBERLAPPUNG: MVELFT ****
400 -;
410 -MVELFT    LDY #0         ;INDEX AUF NULL
420 -          LDX MVELEN+1   ;ANZAHL PAGES IN X
430 -          BEQ MLPART     ;FALLS KEINE GANZEN PAGES DANN REST
440 -MLPAGE    LDA (MVSRCE),Y ;EIN BYTE VERSCHIEBEN
450 -          STA (MVDEST),Y
460 -          INY            ;NAECHSTES BYTE
470 -          BNE MLPAGE     ;BIS 256 BYTES VERSCHOBEN SIND
480 -          INC MVSRCE+1   ;NAECHSTE PAGE DER QUELLE
490 -          INC MVDEST+1   ; UND DES ZIELBEREICHES
500 -          DEX            ;PAGEZAEHLER HERUNTERZAEHLEN
510 -          BNE MLPAGE     ;WEITERMACHEN BIS ALLE VOLLEN PAGES FERTIG
520 -MLPART    LDX MVELEN     ;LAENGE DES RESTBEREICHES IN X
530 -          BEQ MLEXIT     ;ZURUECK, WENN REST GLEICH NULL
540 -MLLAST    LDA (MVSRCE),Y ;EIN BYTE VERSCHIEBEN
550 -          STA (MVDEST),Y
560 -          INY            ;NAECHSTES BYTE
570 -          DEX            ;ZAEHLER HERUNTERZAEHLEN
580 -          BNE MLLAST     ;WEITER BIS REST DURCHGEARBEITET IST
590 -MLEXIT    RTS            ;ZURUECK ZUM HAUPTPROGRAMM
600 -;
610 -;**** UP ZUM VERSCHIEBEN MIT UEBERLAPPUNG : MVERHT ****
620 -;
630 -MVERHT    LDA MVELEN+1   ;ZEIGER AUF LETZTE QUELLPAGE RICHTEN
640 -          CLC
650 -          ADC MVSRCE+1
660 -          STA MVSRCE+1   ;FUER DAS MSB DER MAX. QUELLADRESSE
670 -          LDA MVELEN+1   ;ZEIGER AUF LETZTE ZIELPAGE RICHTEN
680 -          CLC
690 -          ADC MVDEST+1
700 -          STA MVDEST+1   ;FUER DAS MSB DER MAX. ZIELADRESSE
710 -          LDY MVELEN     ;LAENGE DES RESTES
720 -          BEQ MRPAGE     ;WENN NULL, DANN NUR GANZE PAGES
730 -MR0       DEY            ;ZAEHLER MINUS EINS
740 -          LDA (MVSRCE),Y ;EIN BYTE VERSCHIEBEN
750 -          STA (MVDEST),Y
760 -          CPY #0         ;REST SCHON FERTIG ?
770 -          BNE MR0        ;WEITER BIS DER GANZE REST VERSCHOBEN IST
780 -MRPAGE    LDX MVELEN+1   ;ANZAHL PAGES ALS ZAEHLER NACH X
790 -          BEQ MREXIT     ;WENN KEINE GANZEN PAGES DANN ENDE
800 -MR1       DEC MVSRCE+1   ;MSB QUELLADRESSE HERUNTERZAEHLEN
810 -          DEC MVDEST+1   ;EBENSO DAS MSB DER ZIELADRESSE
820 -MR2       DEY            ;INDEX Y HERUNTERZAEHLEN
830 -          LDA (MVSRCE),Y ;EIN BYTE VERSCHIEBEN
840 -          STA (MVDEST),Y
850 -          CPY #0         ;ZAEHLER SCHON NULL ?
860 -          BNE MR2        ;WEITER BIS GANZE PAGE VERSCHOBEN IST
870 -          DEX            ;PAGEZAEHLER HERUNTERZAEHLEN
880 -          BNE MR1        ;WEITER BIS ALLE PAGES VERSCHOBEN SIND
890 -MREXIT    RTS            ;ZURUECK ZUM HAUPTPROGRAMM
900 -;
Listing 3. »BLOCK« - Programm zum fehlerfreien Verschieben von Speicherinhalten

Zum Ansteuern des Programmes werden drei Vektoren benötigt:
MVELEN $FA/B enthält die Länge des zu verschiebenden Bereiches;
MVDEST $FC/D enthält die Startadresse des Zielbereiches;
MVSRCE $FE/F enthält die Startadresse des Quellbereiches.

Im Hauptprogramm wird zunächst der Abstand der Startadressen von Quell- und Zielbereich berechnet und dieser dann mit der angegebenen Länge des zu verschiebenden Bereiches verglichen. Ist der Abstand kürzer als diese Länge, dann liegt eine Überlappung vor. Es mag Ihnen vielleicht seltsam anmuten, daß das sowohl dann, wenn die Quelle unterhalb, als auch dann, wenn sie oberhalb des Zielbereiches liegt, funktioniert. Das — scheinbare — Geheimnis liegt im Carry-Bit verborgen: Die Routine rechnet automatisch mit Modulo(64K). Ein unter dem Quellbereich liegender Zielbereich erfährt die gleiche Behandlung, als läge er 64K höher. Rechnen Sie diesen Teil mal mit fiktiven Adressen bitweise nach, wenn Sie zu den »Fortgeschrittenen« zu zählen sind.

Der Vergleich des so berechneten Abstandes mit der angegebenen Länge folgt dem gleichen Prinzip, das wir auch schon in der zweiten Folge für Doppelschleifen beliebiger Länge zur Steuerung angewendet haben. Dort haben wir auf diese Weise festgestellt, ob schon die Endadresse erreicht ist. Hier verwenden wir das Verfahren, um herauszubekommen, ob eine Überlappung von Quell- und Zielbereich vorliegt. Ist nämlich die Länge in MVELEN kleiner als der berechnete Abstand, dann finden wir ein gesetztes Carry-Bit vor. Wir haben dann keine Überlappung und verzweigen zur Kopierroutine, die von unten nach oben arbeitet. Durch die Eigenart des vorherigen Umganges mit dem Carry-Bit wird der gleiche Weg auch dann eingeschlagen, wenn eine Überlappung zwar vorliegt, aber der Quell- oberhalb des Zielbereiches liegt.

Den Rest des Programmes bilden die beiden Transportschleifen. MVELFT kopiert aufwärts arbeitend, indem zunächst die ganzen Pages, danach dann der Rest übertragen wird. MVERHT berechnet zuerst aus der Länge und den beiden MSB der Startadressen (von Quelle und Ziel) die Adresse der letzten Page. Indem ins Y-Register das LSB der Länge gepackt und mittels der indirekt indizierten Adressierung gearbeitet wird, findet hier — abwärts zählend — als erstes die Übertragung des Restes und danach die der ganzen Pages statt.

Damit Sie BLOCK auf Herz und Nieren prüfen können, finden Sie beiliegend noch ein kleines Basic-Aufrufprogramm namens »BLOCK BAS« (Listing 4).

10 rem ******* verschieben mit dem block-programm *******
20 printchr$(147):rem c 128 = wait0,1
30 poke53280,0:poke53281,5
40 poke241,1:rem c64 = poke646,1
50 rem ----- farbram belegen -----
60 for i=0 to 1000:poke55296+i,1:nexti:rem dauert ein wenig!
70 rem ----- bildschirm belegen -----
80 s=1504:rem startadresse quelle
90 for i=0 to 39:pokes+i,i:nexti
100 rem ----- parameter abfragen -----
110 input"wieviele bytes (sinnvoll 0 bis 40)";n
120 input"zielort (sinnvoll 1024 bis 1984)";z
130 rem ----- berechnen und uebergabe -----
140 a=int(s/256):poke255,a:poke254,s-256*a
150 a=int(z/256):poke253,a:poke252,z-256*a
160 a=int(n/256):poke251,a:poke250,n-256*a
170 rem ----- verschieben -----
180 sys4864:rem c64 = sys 49152
190 end
Listing 4. »BLOCK BAS« — Basic-Programm zum Testen von BLOCK

Ähnlich wie beim Programm BLTUC BAS finden alle Operationen der besseren Verfolgbarkeit wegen im Bildschirmspeicher statt. Auch hier ist wieder — weil ältere C 64-Modelle das benötigen — eine Zeile zum Belegen des Farb-RAM eingefügt worden (Zeile 60), die Sie dann weglassen können, wenn Sie einen neueren C 64 oder einen C 128 verwenden. In der Zeile 90 werden wieder die ersten 40 Zeichen — diesmal in die Bildschirmmitte (ab Speicherstelle 1504) — in den Bildschirmspeicher gePOKEt. Weiterhin haben Sie nun aber die freie Auswahl, wieviele Bytes Sie wohin verschieben möchten. Die Programmzeilen 140 bis 160 übernehmen die Berechnung der MSB und LSB der Adressen und der angegebenen Länge, Zeile 180 schließlich ruft unsere Verschiebe-Routine auf. Viel Spaß beim Ausprobieren!

häufig Thema von Leserfragen ist ein Programminstrument, das es erlaubt, die Inhalte zweier Speicherbereiche auszutauschen. Im Grunde genommen wird ja bei beiden Verschiebeprogrammen (BLTUC und BLOCK) nicht der Inhalt verschoben, sondern nur kopiert. Bei einer Tauschroutine aber verändern sich sowohl der Quell- als auch der Zielbereich.

Überlegen wir uns, wie ein Programm SWAP, das dieses Vertauschen leistet, konstruiert sein muß. Da erhebt sich zunächst wieder die Frage, welche grundsätzlichen Möglichkeiten hier auftreten können. Wieder sind die oben betrachteten Fälle 1 bis 4 ohne Probleme. Die Fälle 5 bis 8 allerdings, die mit Überschneidungen, halte ich hier für sinnlos. Allenfalls dürfte noch ein Fall nützlich sein, in dem beispielsweise die Endadresse des Bereiches 1 direkt unterhalb der Startadresse des Bereiches 2 liegt, also benachbarte Speicherteile miteinander vertauscht werden. Das kann aber in den Fällen 1 bis 4 erfaßt werden und erfordert daher keine besondere Behandlung.

Somit könnte man das Programm BLOCK als Ausgangsstruktur verwenden. Anstelle des Einsprunges in die Routine für überlappende Bereiche müßten wir nur eine Routine packen, die den Benutzer darauf aufmerksam macht, daß eine Überschneidung stattfindet. Anstelle der Sequenzen
LDA (V1),Y
STA (V2),Y

(VI und V2 sind die Vektoren, die auf die jeweilige Quell- und Zielbereichsadresse weisen) müßte bei SWAP eine Lösung gefunden werden, die zunächst ein Byte lädt, es dann beiseite legt, dann aus dem anderen Bereich das entsprechende Byte lädt, dieses dann anstelle des zuerst geladenen speichert, dann das beiseite gelegte wieder hervorholt und an die Stelle des zuletzt geladenen packt.

Zum Beiseitelegen könnte man irgendeine Speicherstelle parat halten. Wir verwenden aber einfach den Stapel:
LDA (V1),Y
PHA
LDA (V2),Y
STA (V1),Y
PLA
STA (V2),Y

Damit hätten wir es dann. Hier finden Sie nun noch das Programm SWAP (Listing 5) abgedruckt.

10  -;
20  -;
30  -.BASE $1300              ;IN HYPRA-ASS: .BA $C000
40  -;********************************************
50  -; VERTAUSCHEN ZWEIER SPEICHERBEREICHE (SWAP)
60  -;********************************************
70  -.DEFINE MVELEN   = $FA   ;IN HYPRA-ASS WIRD STATT DER
80  -.DEFINE MVDEST   = $FC   ; .DEFINE BEFEHLE JEWEILS DER BEFEHL
90  -.DEFINE MVSRCE   = $FE   ; .EQ = $... VERWENDET
100 -.DEFINE PRINT    = $FFD2 ;BILDSCHIRMAUSGABE
110 -;
120 -;IN MVELEN WIRD DIE LAENGE DER ZU VERTAUSCHENDEN BEREICHE ANGEGEBEN
130 -;IN MVDEST DIE STARTADRESSE DES 1. BEREICHES UND IN
140 -;MVSRCE DIE STARTADRESSE DES 2. BEREICHES.
150 -;
160 -;------ PROGRAMM ---------------------------------------------
170 -;ALS ERSTES WIRD BESTIMMT, OB DER ZIELBEREICH OBERHALB DES
180 -; QUELLBEREICHES LIEGT UND OB SICH DIE BEIDEN BEREICHE UEBER-
190 -; LAPPEN. EINE UEBERLAPPUNG LIEGT DANN VOR, WENN DIE DIFFERENZ
200 -; VON ZIELADRESSE MINUS QUELLADRESSE KLEINER ALS DIE ANZAHL DER
210 -; ZU VERSCHIEBENDEN BYTES IST.
220 -;
230 -START     LDA MVDEST     ;BERECHNUNG ZIEL MINUS QUELLE
240 -          SEC
250 -          SBC MVSRCE
260 -          TAX
270 -          LDA MVDEST+1
280 -          SBC MVSRCE+1
290 -          TAY
300 -          TXA            ;VERGLEICH MIT LAENGE DES VERSCHIEBEBEREICHES
310 -          CMP MVELEN
320 -          TYA
330 -          SBC MVELEN+1
340 -          BCS DOLEFT     ;VERZWEIGEN, WENN KEINE UEBERLAPPUNG
350 -          JSR MELDEN     ;SONST AUSGABE EINER FEHLERMELDUNG
360 -          JMP EXIT
370 -DOLEFT    JSR MVELFT     ;ZUM UP OHNE UEBERLAPPUNG
380 -EXIT      RTS
390 -;
400 -;**** UP ZUM VERSCHIEBEN OHNE UEBERLAPPUNG: MVELFT ****
410 -;
420 -MVELFT    LDY #0         ;INDEX AUF NULL
430 -          LDX MVELEN+1   ;ANZAHL PAGES IN X
440 -          BEQ MLPART     ;FALLS KEINE GANZEN PAGES DANN REST
450 -MLPAGE    LDA (MVSRCE),Y ;EIN BYTE LESEN
460 -          PHA            ;SICHERN DES BYTE
470 -          LDA (MVDEST),Y ;BYTE AUS ANDEREM BEREICH LESEN
480 -          STA (MVSRCE),Y ;UND UMTRAGEN
490 -          PLA            ;BYTE WIEDER ZURUECKHOLEN
500 -          STA (MVDEST),Y ;UND UMTRAGEN
510 -          INY            ;NAECHSTES BYTE
520 -          BNE MLPAGE     ;BIS 256 BYTES VERSCHOBEN SIND
530 -          INC MVSRCE+1   ;NAECHSTE PAGE DER QUELLE
540 -          INC MVDEST+1   ; UND DES ZIELBEREICHES
550 -          DEX            ;PAGEZAEHLER HERUNTERZAEHLEN
560 -          BNE MLPAGE     ;WEITERMACHEN BIS ALLE VOLLEN PAGES FERTIG
570 -MLPART    LDX MVELEN     ;LAENGE DES RESTBEREICHES IN X
580 -          BEQ MLEXIT     ;ZURUECK, WENN REST GLEICH NULL
590 -MLLAST    LDA (MVSRCE),Y ;EIN BYTE LESEN
600 -          PHA            ;SICHERN DES BYTE
610 -          LDA (MVDEST),Y ;BYTE AUS ANDEREM BEREICH LESEN
620 -          STA (MVSRCE),Y ;UND UMTRAGEN
630 -          PLA            ;BYTE WIEDER ZURUECKHOLEN
640 -          STA (MVDEST),Y ;UND UMTRAGEN
650 -          INY            ;NAECHSTES BYTE
660 -          DEX            ;ZAEHLER HERUNTERZAEHLEN
670 -          BNE MLLAST     ;WEITER BIS REST DURCHGEARBEITET IST
680 -MLEXIT    RTS            ;ZURUECK ZUM HAUPTPROGRAMM
690 -;
700 -;**** UP ZUR AUSGABE EINER FEHLERMELDUNG: MELDEN ****
710 -;
720 -MELDEN    LDY #0         ;INDEX AUF NULL
730 -WEITER    LDA TEXT,Y     ;TEXTZEICHEN LADEN
740 -          BEQ ENDE       ;WENN NULLBYTE, DANN ZURUECK ZUM HAUPTPROGRAMM
750 -          JSR PRINT      ;SONST AUF BILDSCHIRM AUSGEBEN
760 -          INY            ;INDEX ERHOEHEN
770 -          JMP WEITER     ;NAECHSTES ZEICHEN AUSGEBEN
780 -ENDE      RTS            ;ZURUECK ZUM HAUPTPROGRAMM
790 -;
800 -TEXT      .BYTE 13       ;HYPRA-ASS:  .BY 13
810 -          .BYTE "UEBERSCHNEIDUNG !";HYPRA-ASS: .TX "UEBERSCHNEIDUNG !"
820 -          .BYTE 13,0     ;HYPRA-ASS: .BY 13,0
830 -;
850 -;
Listing 5. »SWAP« — SWAP tauscht Speicherinhalte gegeneinander aus

Wie Sie sicher erkennen können, haben wir das Programm BLOCK etwas umgeschrieben, nämlich um die eben vorgestellten Teile. Die Meldung, daß eine Überschneidung vorliegt, wird mittels einer kleinen Schleife aus einer Tabelle herausgelesen und durch die Kernel-Routine CHROUT (oder auch BSOUT genannt) auf dem Bildschirm ausgegeben. Diese Routine haben wir schon in der Folge 2 kennengelernt.

Auch diesmal finden Sie anliegend noch ein kleines Basic-Programm (Listing 6), das sich der SWAP-Routine bedient. Die Belegung des Farb-RAM in Zeile 60 können sich Besitzer neuerer C 64 und auch des C 128 ersparen, für den alten C 64 ist diese Zeile wichtig. Ab Zeile 80 schreibt das Programm jeweils in die obere und die untere Bildschirmhälfte einen Text. Auf einen Tastendruck werden in den Zeilen 180 bis 240 die Adressen der beiden zu vertauschenden Bereiche und ihre Länge umgerechnet in MSB und LSB und danach in die Abrufspeicherstellen $FA bis $FF gePOKEt. Zeile 260 ruft SWAP auf, Zeile 270 führt den Programmlauf wieder zurück zur Tastaturabfrage in Zeile 160, von wo aus ein erneuter SWAP-Aufruf gestartet wird. Blitzartig wird bei jedem Tastendruck der untere gegen den oberen Text ausgetauscht.

10 rem ****** swap testprogramm ******
20 printchr$(147):rem c 128 = wait0,1
30 poke53280,0:poke53281,5
40 poke241,1:rem c64 = poke 646,1
50 rem ----- farbram belegen (aeltere c64) -----
60 for i=0 to 1000:poke55296+i,1:nexti:rem das dauert etwas
70 rem ----- bildschirm belegen -----
80 print"was die alte dame empfindet, wenn sie,  nachdem sie ihren kanarienvogel "
90 print"gefuettert hat und spazieren gegangen   ist, bei der rueckkehr den kaefig "
100 print"mit einem lebendigen truthahn zum       platzen voll findet,"
110 printchr$(17)chr$(17)chr$(17)chr$(17)chr$(17)
120 print"oder der alte herr,der,nachdem er ueber nacht seinen kleinen terrier "
130 print"an die kette gelegt hat, ein nilpferd   findet, das um die hundhuette "
140 print"herum schnaubt...":printtab(20)"(lewis carroll 1882)"
150 printchr$(17)chr$(17)"jeder tastendruck fuehrt zum tausch"
160 geta$:ifa$=""then160
170 rem ----- parameter festlegen -----
180 b1=1024:rem startadresse bereich 1
190 b2=1504:rem startadresse bereich 2
200 l =240:rem laenge = 6 zeilen zu je 40 zeichen
210 rem ----- parameter uebergeben -----
220 a=int(b1/256):poke255,a:poke254,b1-256*a
230 a=int(b2/256):poke253,a:poke252,b2-256*a
240 a=int(l/256):poke251,a:poke250,l-256*a
250 rem ----- swap ausfuehren -----
260 sys4864:rem c64 = sys 49152
270 goto160
Listing 6. »SWAP BAS« — Basic-Programm, das die Funktion von SWAP überprüft

Kombination von BLOCK und SWAP

BLOCK und SWAP sind kurze Routinen, die beide zusammen nur 219 Byte an Platz erfordern. Mit einigem Geschick lassen sich beide auch noch kombinieren. Falls Sie sicher sind, daß Ihnen nie der Fall unterkommt, der eine Fehlfunktion der BLTUC-Interpreter-Routine verursacht, können Sie sich natürlich auch einer Kombination von BLTUC und SWAP bedienen.

Vielfältige Einsatzmöglichkeiten sind denkbar:

(H. Ponnath/dm)
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →