Kurs: Effektives Programmieren
C 64
Kurs: Effektives Programmieren

Sortieren mit Computer (Teil 6)

Wie werden Sortierroutinen schneller? Wie kann die Garbage Collection verhindert werden? Wir beschreiben einige Verfahren dazu und bringen Quicksort in Maschinensprache.

Im letzten Teil unseres Sortierkurses sollen einmal Techniken erläutert werden, die die bisher besprochenen Sortieralgorithmen noch effektiver werden lassen. Außerdem werden wir auf die wichtigsten der vielen Leserreaktionen eingehen und etwaige Mißverständnisse und Fehler aus dem Weg räumen.

Die wichtigsten Sortieralgorithmen wurden in unserem Kurs ausführlich besprochen, wobei wir bisher jedoch recht wenig auf Programmiertechniken eingegangen sind, die unseren Programmen zu noch größeren Geschwindigkeiten verhelfen. Zwei Methoden seien an dieser Stelle schon einmal erwähnt:

  1. das Umschreiben der Sortieralgorithmen in Maschinensprache;
  2. das Verhindern der Garbage Collection durch Sortieren der String-Deskriptoren, wobei kein »Stringmüll« entsteht.

Leserreaktionen

Bevor wir uns jedoch auf die gestellten Probleme stürzen, möchte ich mich bei all jenen Lesern bedanken, die mir zu diesem Thema geschrieben haben. Wir machen ohnehin kein Geheimnis daraus, daß die Zeitschrift 64’er zu einem erheblichen Teil von der Mitarbeit aktiver Leser geprägt wird, und wir hoffen, daß das auch in Zukunft so bleibt.

Nun aber zu einigen wichtigen Informationen.

Ziemlich viel Rummel hat offensichtlich die Ankündigung von Sortieralgorithmen hervorgerufen, die schneller sein wollen als Quicksort.

Es kamen prompt Zuschriften von Lesern, die diese Behauptung mit den eingesendeten Quicksort-Algorithmen widerlegen konnten. Der Trick bei der Sache war ausschließlich auf ein System zurückzuführen, das ein Feld von Quicksort nur teilsortieren läßt und die Arbeit bei einer Teillistenlänge von beispielsweise 10 an ein »kleines« Sortierprogramm (Bubblesort 2 oder Straight Insertion) übergibt. Diese Methode ist natürlich korrekt! Es wurde in dem Kurs jedoch absichtlich vom Abdruck eines solchen Sortieralgorithmus abgesehen, weil es bei den Beispielprogrammen einzig und allein um die Struktur der großen Sortieralgorithmen ging. Auf die oben beschriebene Methode zum »Schnellermachen« von Quicksort (und auch Heapsort) hatte ich aber an anderer Stelle schon hingewiesen.

Quicksort ist doch am schnellsten

Ein sehr wichtiger Brief kam jedoch von unserem Leser Kurt Sörensen aus Hamburg. Er zeigte nämlich einen Fehler auf, der in der abgedruckten Beispielroutine von Quicksort steckt (Achtung Fehlerteufelchen…). Herr Sörensen analysierte das Quicksortprogramm und kam zu folgendem Ergebnis:

»… Beim Übergang von der linken zur rechten Hälfte wird die rechte Grenze der linken Hälfte, die nach der Theorie schon sortiert ist, zur linken Grenze der rechten Hälfte gemacht, die nach der Theorie noch nicht sortiert ist. Dadurch werden praktisch alle Elemente außer dem kleinsten und dem größten doppelt sortiert…«

Wie Sie aus unserem Kurs wissen, teilt Quicksort während des Sortierens das Variablenfeld in immer kleiner werdende Hälften (Teillisten) auf. Berücksichtigt man also diesen Fehler und erstellt das Quicksortprogramm neu (nach wie vor ohne anschließenden anderen Algorithmus), so kann sich Quicksort wieder unbeschadet an die Spitze unserer Stringsortierroutinen stellen. Es ist und bleibt der schnellste (und dabei der vielseitigste) Sortieralgorithmus (»Sonderanfertigungen« für spezielle Probleme laufen natürlich außer Konkurrenz!)

In Listing 1 sehen Sie die korrigierte Version von Quicksort abgedruckt.

Nun zu einem Problem, das offensichtlich in Zusammenhang mit unserem Hauptprogramm für die Sortieralgorithmen aufgetreten ist. Wie Sie wissen, haben wir zu den entsprechenden Sortierprogrammen auch ein Hauptprogramm abgedruckt, das einen Test der einzelnen Routinen ermöglichen sollte. Der Sinn dieses Programms ist aber von mir offensichtlich nicht genug verdeutlicht worden.

Bei dem beigefügten Rahmenprogramm handelt es sich um ein provisorisches Gerüst, das es dem Leser erlauben soll, die eingegebenen Sortierprogramme auf Funktionsfähigkeit und Geschwindigkeit zu testen. Daß das Rahmenprogramm also kein »Z« als Zufallswert verarbeitet und außerdem nur Elementzahlen in Zehnersprüngen zuläßt, dürfte bei der Arbeit kaum ein Hindernis darstellen, zumal das ja der Sinn der Sortierroutinen ist, nach Fertigstellung in andere Programme eingefügt zu werden. Im eigentlichen Sinne wichtig für den Anwender sind also jeweils die Programmteile in den Zeilen 10 000 bis 20 000: Der Rest entfällt.

Nun aber zu den schon erwähnten Programmiertechniken, die der Beschleunigung eines Sortiervorgangs dienen.

Es geht noch schneller

Wir wollen uns dazu zuerst mit einem Artikel in der 64’er, Ausgabe 1/1985 beschäftigen. Es handelt sich hierbei um die erste Ausgabe in der Reihe »Effektives Programmieren«, die damals von dem Stringspezialisten Boris Schneider geschrieben wurde. Vorherrschend ging es um ein Problem bei der Stringverwaltung, nämlich um die »Müllabfuhr im Computer«, die Garbage Collection. Damals wurde sehr ausführlich auf den Aufbau von Strings im Speicher des Computers eingegangen, weshalb an dieser Stelle nur eine sehr knappe Wiederholung folgen soll.

Generell legt der C 64 seine Variablen direkt im Anschluß an das Basic-Programm im Speicher ab. Auch die Stringvariablen stehen dort. Der Textinhalt dieser Variablen wird jedoch an anderer Stelle im Speicher, nämlich von oben anfangend, rückwärts nach unten gespeichert. Anstelle des Textes hinter dem Variablennamen wird dort ein Zeiger (Deskriptor) abgelegt, der auf die jeweilige Position des Textes zeigt.

Wird nun eine Stringvariable neu angelegt, nachdem sie zuvor einen anderen Inhalt aufwies, wird der neue Text an die Stringkette im oberen Speicherbereich angehängt und der Deskriptor der Variablen auf diesen neuen Text eingestellt. Der »alte« Variablentext bleibt im Speicher stehen und bildet »Stringmüll«. Dieser Müll steigt mit vielen Neudefinitionen von Strings rapide an, so daß es nur eine Frage der Zeit ist, wann die von oben kommenden Strings mit den von unten kommenden Variablen zusammenstoßen und einen »?OUT OF MEMORY ER-ROR« verursachen.

Um diesem Fehler vorzubeugen, gibt es die Garbage Collection. Der Interpreter überwacht laufend den Zustand des Speichers. Wird es zu eng, dann tritt die Garbage Collection in Kraft und räumt den gesamten Stringmüll weg. Dieser Vorgang erfordert enorme Speichersuch- und verschiebevorgänge und kann ungünstigenfalls sogar mehrere Stunden benötigen: Der Computer scheint »abgestürzt«.

Bei unseren Sortiervorgängen wird ziemlich häufig der sogenannte Dreiecktausch durchgeführt. Es handelt sich hierbei um das Vertauschen der Inhalte von zwei Stringvariablen, wobei eine dritte Variable als Zwischenspeicher dient: zum Beispiel: x$ = a$(1):a$(1) = a$(2):a$(2) = x$

Bei diesem Tauschverfahren werden gleich drei Müllstrings erzeugt, nämlich der Inhalt von x$, der alte Inhalt von a$(1) und der alte Inhalt von a$(2).

Nun gibt es Basic-Interpreter, die bieten zu diesem Zweck den Befehl SWAP an. Mit diesem Befehl können die Inhalte zweier Strings direkt vertauscht werden. Zum Beispiel: SWAP a$(1), a$(2)

Hiermit sparen wir Zeit und Speicherplatz. Zeit sparen wir durch die Ausführung eines einzigen Befehls anstatt der drei Variablenzuordnungen. Speicherplatz sparen wir durch das Wegfallen der Hilfsvariable x$, so daß nur zwei Müllstrings entstehen.

SWAP - ein Programm mit Pfiff

Aber pingelig, wie wir Computermenschen nun einmal sind, stellt uns auch diese Methode nicht zufrieden. Hatten wir vorhin nicht etwas von Deskriptoren, also von Zeigern auf den jeweiligen String gehört? Genau! Das ist unser neuer Ansatzpunkt!

Bei dem Vertauschen von zwei Variablen ändert sich nämlich eigentlich gar nichts im Speicher. Es bleiben sowohl die beiden Strings als auch die beiden Variablennamen erhalten. Warum reicht es also nicht aus, einfach die beiden Stringdeskriptoren zu vertauschen? Diese Frage ist überflüssig! Es reicht nämlich in der Tat aus, wenn wir den Deskriptor von Variable 1 auf den String von Variable 2 und umgekehrt setzen.

Und genau das macht das Programm SWAP, das Boris Schneider schon in der 64’er, Ausgabe 1/1985, Seite 123 vorgestellt hat.

Mit Hilfe dieser kleinen Maschinenspracheroutine sparen wir also Zeit und Stringmüll, da kein einziger überflüssiger String entsteht (Listing 2). Wenn Sie das »Progrämmchen« eingetippt haben, dann starten Sie es mit RUN. Sie werden anschließend nach der Startadresse des Maschinenprogramms gefragt. Diese sollten Sie vorzugsweise in den $C-Bereich (49152 bis 53247) legen, wobei darauf zu achten ist, daß als Startadresse maximal 53199 gelten darf (Das Programm benötigt 48 Byte).

Muß die Routine aus irgendeinem Grund woanders untergebracht werden, so wäre noch der Kassettenpuffer (828 bis 1023) zu empfehlen, um Basic-Speicherplatz zu sparen. Andernfalls müssen Sie eben die maximale Speicheradresse entsprechend heruntersetzen, um die Swap-Routine vor dem Überschreiben mit Strings zu schützen. Der Einbau des SWAP in die Sortierprogramme ist vollkommen unproblematisch. Sie suchen sich einfach jeweils die Stelle mit dem Dreiecktausch heraus. Sie wird in den abgedruckten Sortierprogrammen durch die Variable S$ als Hilfsvariable gekennzeichnet. Bei Straight Select ist das beispielsweise die Zeile 10080: 10080 S$ = A$(X):A$(X) = A$(Z): A$(Z) = S$

Diese Zeile wird nun wie folgt geändert: 10080 SYS startadresse(A$(X), A$(Z))

»startadresse« gibt hierbei die Zahl an, die Sie beim Start des SWAP-Programms angegeben hatten. Die abgeänderte Version von Straight Select zeigt Listing 3. Sinnvoll wäre es, die SWAP-Routine direkt vor unser Sortier-Hauptprogramm zu setzen, so daß sie immer direkt vor dem Arbeiten automatisch installiert wird.

Von der Speicherplatzersparnis einmal ganz abgesehen, arbeitet auch diese SWAP-Routine schon erheblich schneller als der Dreiecktausch, so daß zum Beispiel beim Sortieren von 100 Elementen mit Bubblesort2 aus einer Sortierzeit von 1 Minute 46 Sekunden »nur« 1 Minute und 33 Sekunden wurden. Besonders bei sehr großen Elementzahlen zeigt sich aber dann die Effizienz dieses Programms, da sich die Garbage Collection kaum mehr blicken läßt. Unser Quicksort erfährt dadurch wiederum neue Bestzeiten im Sortieren von zufallsbesetzten Feldern.

Es geht doch nichts über Maschinensprache

Wem aber auch dieser Trick noch nicht reicht: Wer immer noch auf der Suche nach dem »HYPRA« ist, dem bleibt nichts anderes übrig, als auf der Maschinenspracheebene sein Glück zu versuchen.

Mit diesem Thema hat sich auch unser Leser Frank Probst aus Zweibrücken beschäftigt. Was dabei herausgekommen ist, wollen wir Ihnen jetzt vorstellen: Quicksort in Maschinensprache!

Es war natürlich von vornherein klar, daß wirklich gute Zeiten beim Sortieren von Feldern nur in Maschinensprache zu erreichen sind. Wenn man jedoch das Prinzip eines Sortieralgorithmus verstehen will, ist es in der Regel besser, sein Glück erst einmal in Basic zu versuchen und das erworbene Wissen dann in Maschinensprache umzusetzen.

Quicksort in Maschinensprache!

Da Quicksort ja nun schon in Basic am schnellsten ist und erstaunliche Sortierzeiten hervorbringt, darf man auf die Maschinenpracheversion gespannt sein. Ich kann schon vorwegnehmen, daß dieses Programm (natürlich) alles bisher Dagewesene voll in den Schatten stellt. Bevor wir uns jedoch diesem Programm zuwenden, soll an dieser Stelle erwähnt sein, daß durchaus nicht alle Tricks, die der Geschwindigkeit von Nutzen sein könnten, angewendet wurden. So teilt dieses Quicksort alle Teillisten bis auf die Länge 1 herunter, anstatt bei zum Beispiel 10 aufzuhören und dann Straight Select ans Werk zu lassen. Die Methode des Deskriptortausches wurde jedoch auch hier angewendet, was zur Folge hat, daß die Garbage Collection so gut wie keine Arbeit bekommt (selbst wenn große Mengen an Daten sortiert werden müssen).

Die Bedienung von Quicksort-M (so möchte ich es für den weiteren Verlauf nennen, um es von der Basic-Version zu unterscheiden) ist denkbar einfach. Nachdem Sie das Listing 4 mit dem MSE eingetippt und auf Diskette gespeichert haben, steht Quicksort-M sofort zur Verfügung. Es belegt im Speicher den Bereich von $CB20 bis $CFFF und verträgt sich so mit einem eventuell eingeschalteten Turbo-Tape. Es wird mit SYS52000 aufgerufen, wobei jedoch einige Regeln zu beachten sind:

Das zu sortierende Feld muß ein Stringarray sein und als allererstes Feld in einem Programm dimensioniert werden. Andernfalls kann ein »Aussteigen« des Computers die Folge sein. Bei der Arbeit benötigt Quicksort-M die Speicherstellen von $00B2-$00B8 und $00FB-$00FE. Die Werte aus $00B2 bis $00B8 werden gerettet und nach der Sortierung wieder zurückgeschrieben.

Das Diagramm zeigt deutlich wie schnell Quicksort OBJ arbeitet

Das eigentliche Quicksort-M-Programm belegt nur die Speicherstellen $CB20 bis $CDE7. Es benötigt jedoch den anschließenden Bereich als Speicher für den jeweiligen Vergleichsstring und für einen Software-Stack, der bei Quicksort ja generell notwendig ist.

Wer sich mit Quicksort-M weiter beschäftigen will, der findet in Listing 5 ein Source-Listing.

Nun aber zu den Daten von Quicksort-M. Hier erübrigt sich jeder weitere Kommentar, wenn Sie sich Bild 1 betrachten. Diese Zeiten wurden mit dem Basic-Programm Quicktester (Listing 6) ermittelt und lassen einen das Schwärmen anfangen. Quicksort-M benötigt beispielsweise für 1 000 zufällig ausgewählte Elemente nur noch acht Sekunden (!). Diese Zeit dürfte sich dabei auf ein Maß beschränken, das auch den pingeligsten Anwender des C 64 zufriedenstellen dürfte. Immerhin schlägt Quicksort-M seine Basic‑Konkurrenten alle um einige 1 000 Prozent: Sogar das »normale« Quicksort wird hier haushoch geschlagen.

Ursprünglich hatte ich vor, an dieser Stelle auch noch einen Bubblesort-Algorithmus in Maschinensprache vorzustellen. Doch was brauchen wir jetzt noch Bubblesort? Quicksort-M dürfte, was die Geschwindigkeit angeht, wohl allen Anwendungen gewachsen sein. Wer sich dennoch mit Bubblesort auseinandersetzen will, der findet in einer anderen Zeitschrift aus dem Markt & Technik-Verlag einen Beitrag zu diesem Thema: Er steht unter dem Titel »Schneller als Quicksort« und erschien in der Ausgabe 14/1985 des Magazins Computer persönlich. Weitere Sortierprogramme finden Sie in Computer persönlich, Ausgabe 14/1984 (Top-Sort) und im 64’er, Ausgabe 11/84 (Exsort), ebenfalls als Maschinenprogramm.

Ich möchte mich an dieser Stelle von Ihnen verabschieden und hoffe, daß Ihnen die letzten Kurse ein wenig Spaß gemacht haben. Vielleicht haben Sie jetzt das Werkzeug, um sich das eine oder andere Projekt, das Sie sich schon länger vorgenommen hatten, zu verwirklichen.

(K. Schramm/F. Probst/gk)
10000 rem        quicksort
10010 :
10020 t=ti:lg(1)=1:rg(1)=a:z=0:gosub 10040
10030 return
10040 z=z+1:if lg(z)>=rg(z) then 10170
10050 x=lg(z):y=rg(z):if y<=x+1 goto 10170
10060 b=(x+y)/2:b=int(b):vg$=a$(b)
10070 if x>y then 10150
10080 if a$(x)<vg$then x=x+1:goto 10080
10090 if a$(y)>vg$then y=y-1:goto 10090
10100 if x>y then 10150
10110 s$=a$(x):a$(x)=a$(y):a$(y)=s$
10120 x=x+1:y=y-1:goto 10070
10130 if a$(x)<=a$(y)goto 10170
10140 s$=a$(x):a$(x)=a$(y):a$(y)=s$:goto 10170
10150 rg(z+1)=y:lg(z+1)=lg(z):gosub 10040
10160 lg(z+1)=rg(z+1)+1:rg(z+1)=rg(z):gosub 10040
10170 z=z-1:return
Listing 1. Die korrigierte Version von Quicksort
10 data32,250,174,32,158,173,32,143
20 data173,165,100,133,247,165,101,133
30 data248,32,253,174,32,158,173,32
40 data143,173,160,0,177,247,133,249
50 data177,100,145,247,165,249,145,100
60 data200,192,3,208,239,32,247,174
70 data96,0,0,0,0,0,0,0
100 input"startadresse";sa
110 fori=sa to sa +48
120 readx:pokei,x:cs=cs+x
130 next i
140 if cs<>7314 then print"fehler!!"
150 end
Listing 2. Die SWAP-Routine
10040 ti$="000000":g=a-1:forx=a-1to1step-1
10050 f=0:fory=1tog
10060 ifa$(y)<=a$(y+1)then10080
10070 f=y:sys49152(a$(y),a$(y+1))
10080 nexty
10090 g=f:iff=0then50000
10100 nextx
Listing 3. Sortieren ohne »Stringmüll«
PROGRAMM : QUICKSORT.OBJ  CB20 CDE8
-----------------------------------
CB20 : 20 E0 CC A2 00 B5 B2 9D   EB
CB28 : BC 02 E8 E0 06 D0 F6 20   3E
CB30 : 3F CB A2 00 BD BC 02 95   F2
CB38 : B2 E8 E0 06 D0 F6 60 20   DE
CB40 : 03 CC 20 B6 CD 20 EE CB   B9
CB48 : C9 00 F0 58 C9 02 F0 54   72
CB50 : 20 A8 CD 20 30 CD 20 D3   D5
CB58 : CB C9 02 F0 3B 20 1A CD   5F
CB60 : 20 A7 CB C9 02 F0 0A C9   E3
CB68 : 00 F0 06 20 27 CC 4C 5D   2B
CB70 : CB 20 20 CD 20 A7 CB C9   0F
CB78 : 01 F0 0A C9 00 F0 06 20   8D
CB80 : 39 CC 4C 71 CB 20 D3 CB   05
CB88 : C9 02 F0 0C 20 A8 CC 20   CB
CB90 : 27 CC 20 39 CC 4C 56 CB   6D
CB98 : 20 8B CD 20 3F CB 20 6E   A5
CBA0 : CD 20 3F CB 4C 15 CC A0   A8
CBA8 : FF C8 C4 B2 D0 05 A9 01   71
CBB0 : 4C CA CB C4 B5 D0 05 A9   36
CBB8 : 02 4C CA CB B1 B3 D1 B6   7A
CBC0 : F0 E7 90 03 A9 02 2C A9   D7
CBC8 : 01 60 A6 B2 E4 B5 D0 02   3C
CBD0 : A9 00 60 AD EA CD CD EC   75
CBD8 : CD D0 08 AD E9 CD CD EB   E1
CBE0 : CD F0 05 B0 06 A9 01 2C   87
CBE8 : A9 00 2C A9 02 60 AD F0   8D
CBF0 : CD CD F2 CD D0 08 AD EF   FE
CBF8 : CD CD F1 CD F0 EA B0 EB   E3
CC00 : A9 01 60 18 AD E7 CD 69   69
CC08 : 04 8D E7 CD AD E8 CD 69   B2
CC10 : 00 8D E8 CD 60 38 AD E7   19
CC18 : CD E9 04 8D E7 CD AD E8   02
CC20 : CD E9 00 8D E8 CD 60 18   42
CC28 : AD E9 CD 69 01 8D E9 CD   2A
CC30 : AD EA CD 69 00 8D EA CD   A7
CC38 : 60 38 AD EB CD E9 01 8D   E8
CC40 : EB CD AD EC CD E9 00 8D   62
CC48 : EC CD 60 AD E9 CD 0A AA   73
CC50 : AD EA CD 20 8D CC 6D E9   B2
CC58 : CD AA 98 6D EA CD 4C 92   C1
CC60 : CC AD EB CD 0A AA AD EC   3E
CC68 : CD 20 8D CC 6D EB CD AA   05
CC70 : 98 6D EC CD 4C 92 CC AD   9B
CC78 : ED CD 0A AA AD EE CD 20   ED
CC80 : 8D CC 6D ED CD AA 98 6D   FC
CC88 : EE CD 4C 92 CC 2A A8 8A   98
CC90 : 18 60 A8 18 8A 69 07 AA   6B
CC98 : 98 69 00 A8 18 8A 65 2F   C4
CCA0 : 85 FB 98 65 30 85 FC 60   D9
CCA8 : 20 4B CC A5 FB 85 FD A5   85
CCB0 : FC 85 FE 20 61 CC A0 00   31
CCB8 : B1 FB AA B1 FD 91 FB 8A   B9
CCC0 : 91 FD C8 C0 03 D0 F1 60   D9
CCC8 : 18 AD E9 CD 6D EB CD 8D   73
CCD0 : ED CD AD EA CD 6D EC CD   04
CCD8 : 4A 8D EE CD 6E ED CD 60   AC
CCE0 : A9 00 8D E9 CD 8D EA CD   BA
CCE8 : A9 07 8D E7 CD A9 CE 8D   F6
CCF0 : E8 CD 20 4B CC EE E9 CD   B8
CCF8 : 38 A5 FB E9 02 85 FB A5   C6
CD00 : FC E9 00 85 FC A0 01 38   EB
CD08 : B1 FB E9 01 8D EB CD 88   D2
CD10 : B1 FB E9 00 8D EC CD 4C   49
CD18 : D7 CD 20 4B CC 4C 23 CD   9F
CD20 : 20 61 CC A0 00 B1 FB 99   E9
CD28 : B2 00 C8 C0 03 D0 F6 60   78
CD30 : 20 C8 CC 20 77 CC A0 00   4C
CD38 : B1 FB 99 B5 00 C8 C0 03   53
CD40 : D0 F6 A5 B5 F0 1C C9 15   ED
CD48 : 90 04 A9 14 85 B5 A0 00   50
CD50 : B1 B6 99 F3 CD C8 C4 B5   E3
CD58 : D0 F6 A9 F3 85 B6 A9 CD   DC
CD60 : 85 B7 60 AD E7 CD 85 FD   8E
CD68 : AD E8 CD 85 FE 60 20 B6   8E
CD70 : CD 20 C6 CD A0 00 B9 E9   7D
CD78 : CD 91 FD C8 C0 02 D0 F6   F3
CD80 : B9 EF CD 91 FD C8 C0 04   07
CD88 : D0 F6 60 20 B6 CD 20 C6   D7
CD90 : CD A0 00 B9 EF CD 91 FD   94
CD98 : C8 C0 02 D0 F6 B9 E9 CD   DB
CDA0 : 91 FD C8 C0 04 D0 F6 60   DD
CDA8 : A0 00 B9 EF CD 99 E9 CD   A1
CDB0 : C8 C0 04 D0 F5 60 20 63   9D
CDB8 : CD A0 00 B1 FD 99 EF CD   14
CDC0 : C8 C0 04 D0 F6 60 20 63   BD
CDC8 : CD 18 A5 FD 69 04 85 FD   93
CDD0 : A5 FE 69 00 85 FE 60 20   61
CDD8 : C6 CD A0 00 B9 E9 CD 91   F2
CDE0 : FD C8 C0 04 D0 F6 60 20   78
Listing 4. Der Quicksort in Maschinensprache. Bitte beachten Sie die Eingabehinweise auf Seite 54
   10 SYS36864:.OPT P,OO:*= 52000
   20 LAENGE1 = $B2
   30 LAENGE2 = $B5
   40 STR1    = $B3
   50 STR2    = $B6
   55 UMULT1  = $28
   56 UMULT2  = $71
   57 UMULT   = $B357
   58 AARRAY  = $2F
   59 VEKTOR1 = $FB
   60 VEKTOR2 = $FD
  100            JSR REGSET
  101            LDX #0
  102 MARKE1     LDA LAENGE1,X
  103            STA 700,X
  104            INX
  105            CPX #6
  106            BNE MARKE1
  110            JSR HAUPTSCHL
  111            LDX #0
  112 MARKE2     LDA 700,X
  113            STA LAENGE1,X
  114            INX
  115            CPX #6
  116            BNE MARKE2
  117            RTS
  130 ;
  140 HAUPTSCHL  JSR HOCHZ
  145            JSR HOLLR
  150            JSR LRVERGL
  160            CMP #0
  170            BEQ Z350
  180            CMP #2
  190            BEQ Z350
  195            JSR HOLXY
  210            JSR EVINDI
  220 Z270       JSR XYVERGL
  230            CMP #2
  240            BEQ Z330
  250 Z280       JSR EXINDI
  260            JSR EINSPR
  270            CMP #2
  280            BEQ Z290
  285            CMP #0
  286            BEQ Z290
  290            JSR HOCHX
  300            JMP Z280
  310 Z290       JSR EYINDI
  320            JSR EINSPR
  330            CMP #1
  340            BEQ Z300
  345            CMP #0
  346            BEQ Z300
  350            JSR RUNTERY
  360            JMP Z290
  370 Z300       JSR XYVERGL
  380            CMP #2
  390            BEQ Z330
  400            JSR SWAP
  410            JSR HOCHX
  420            JSR RUNTERY
  430            JMP Z270
  435 ;
  440 Z330       JSR PUSHLY
  460            JSR HAUPTSCHL
  470 ;
  480            JSR PUSHXR
  500            JSR HAUPTSCHL
  510 ;
  940 Z350       JMP RUNTERZ
 1005 ;
 1010 ;    VERGLEICH STR1 MIT VERGL$
 1011 ;1) STR1>VERGL  2) STR1<VERGL
 1015 ;
 1020 EINSPR    LDY #$FF
 1030 SCHL1     INY
 1040           CPY LAENGE1
 1050           BNE WEITER1
 1060           LDA #1
 1070           JMP RAUS
 1080 WEITER1   CPY LAENGE2
 1090           BNE WEITER2
 1100           LDA #2
 1110           JMP RAUS
 1120 WEITER2   LDA (STR1),Y
 1130           CMP (STR2),Y
 1140 BEQ SCHL1
 1150           BCC WEITER3+1
 1160           LDA #2
 1170 WEITER3   BIT $1A9 ;MASKIERUNG FUER LDA #1
 1200           RTS
 1210 RAUS      LDX LAENGE1
 1220           CPX LAENGE2
 1230           BNE FERTIG
 1240           LDA #0
 1250 FERTIG    RTS
 1260 ;
 1270 ;    VERGLEICHEN VON X UND Y
 1275 ;X<Y LDA #2  X>Y LDA #1  X=Y LDA #0
 1280 ;
 1290 XYVERGL   LDA XREG+1
 1300           CMP YREG+1
 1310           BNE WEITER4
 1320           LDA XREG
 1330           CMP YREG
 1340           BEQ GLEICH+1
 1350 WEITER4   BCS GROESSER+1
 1360           LDA #1
 1370 GLEICH   .BYT $2C,$A9,0 ;BIT $00A9
 1380 GROESSER  BIT $2A9
 1390           RTS
 1400 ;
 1410 ;    VERGLEICHEN VON L UND R
 1415 ;L<R LDA #2  L>R LDA #1  L=R LDA #0
 1420 ;
 1430 LRVERGL   LDA LREG+1
 1440           CMP RREG+1
 1450           BNE WEITER5
 1460           LDA LREG
 1470           CMP RREG
 1480           BEQ GLEICH+1
 1490 WEITER5   BCS GROESSER+1
 1500           LDA #1
 1510           RTS
 1985 ;
 1990 ; REGISTER HOCH- UNG RUNTERZAEHLEN
 1995 ;
 2000 HOCHZ     CLC
 2020           LDA ZREG
 2030 ADC #4
 2040           STA ZREG
 2050           LDA ZREG+1
 2060           ADC #0
 2070           STA ZREG+1
 2090           RTS
 2100 RUNTERZ   SEC
 2120           LDA ZREG
 2130           SBC #4
 2140           STA ZREG
 2150           LDA ZREG+1
 2160           SBC #0
 2170           STA ZREG+1
 2190           RTS
 2200 HOCHX     CLC
 2220           LDA XREG
 2230           ADC #1
 2240           STA XREG
 2250           LDA XREG+1
 2260           ADC #0
 2270           STA XREG+1
 2290           RTS
 2300 RUNTERY   SEC
 2320           LDA YREG
 2330           SBC #1
 2340           STA YREG
 2350           LDA YREG+1
 2360           SBC #0
 2370           STA YREG+1
 2390           RTS
 2985 ;
 2990 ; DIE MIT X/Y INDIZIERTE VARIABLE
 2991 ; WIRD GESUCHT Z.B. ( A$(X) )
 2995 ;
 3000 XSUCH     LDA XREG
 3010           ASL
 3015           TAX
 3020           LDA XREG+1
 3030           JSR PRG1
 3040           ADC XREG
 3050           TAX
 3060           TYA
 3070           ADC XREG+1
 3080           JMP PRG2
 3100 YSUCH     LDA YREG
 3110           ASL
 3115           TAX
 3120           LDA YREG+1
 3130           JSR PRG1
 3131           ADC YREG
 3132           TAX
 3133           TYA
 3134           ADC YREG+1
 3135           JMP PRG2
 3140 VSUCH     LDA VERGL
 3150           ASL
 3155           TAX
 3160           LDA VERGL+1
 3170           JSR PRG1
 3171           ADC VERGL
 3172           TAX
 3173           TYA
 3174           ADC VERGL+1
 3175           JMP PRG2
 3200 PRG1      ROL
 3210           TAY
 3220           TXA
 3230           CLC
 3240           RTS
 3250 PRG2      TAY
 3260           CLC
 3270           TXA
 3280           ADC #7
 3281           TAX
 3282           TYA
 3283           ADC #0
 3284           TAY
 3285           CLC
 3286           TXA
 3290           ADC AARRAY
 3300           STA VEKTOR1
 3310           TYA
 3320           ADC AARRAY+1
 3330           STA VEKTOR1+1
 3340           RTS
 3985 ;
 3990 ;SWAP - VERTAUSCHEN ZWEIER STRINGS
 3995 ;
 4000 SWAP      JSR XSUCH
 4010           LDA VEKTOR1
 4020           STA VEKTOR2
 4030           LDA VEKTOR1+1
 4040           STA VEKTOR2+1
 4050           JSR YSUCH
 4060           LDY #0
 4070 SCHL2     LDA (VEKTOR1),Y
 4080           TAX
 4090           LDA (VEKTOR2),Y
 4100           STA (VEKTOR1),Y
 4110           TXA
 4120           STA (VEKTOR2),Y
 4130           INY
 4140           CPY #3
 4150           BNE SCHL2
 4160           RTS
 4985 ;
 4990 ;     VERGL = (XREG+YREG)/2
 4995 ;
 5000 RECHNUNG  CLC
 5010           LDA XREG
 5020           ADC YREG
 5030           STA VERGL
 5040           LDA XREG+1
 5050           ADC YREG+1
 5060           LSR
 5070           STA VERGL+1
 5080           ROR VERGL
 5090           RTS
 5100 ;
 5110 ;REGISTER AUF AUSGANGSWERTE SETZEN
 5120 ;
 5200 REGSET    LDA #0
 5210           STA XREG
 5215           STA XREG+1
 5220           LDA #>STACK
 5225           STA ZREG
 5230           LDA #<STACK
 5235           STA ZREG+1
 5240           JSR XSUCH
 5245           INC XREG
 5250           SEC
 5260           LDA VEKTOR1
 5270           SBC #2
 5280           STA VEKTOR1
 5290           LDA VEKTOR1+1
 5300           SBC #0
 5310           STA VEKTOR1+1
 5320           LDY #1
 5325           SEC
 5330           LDA (VEKTOR1),Y
 5335           SBC #1
 5340           STA YREG
 5350           DEY
 5360           LDA (VEKTOR1),Y
 5365           SBC #0
 5370           STA YREG+1
 5380           JMP PUSHXY
 5985 ;
 5990 ;DISCRIPTOREN IN DER ZP EINRICHTEN
 5995 ;
 6000 EXINDI    JSR XSUCH
 6010           JMP DISCRIP1
 6020 ;
 6030 EYINDI    JSR YSUCH
 6040 ;
 6050 DISCRIP1  LDY #0
 6060 SCHL3     LDA (VEKTOR1),Y
 6070           STA LAENGE1,Y
 6080           INY
 6090           CPY #3
 6100           BNE SCHL3
 6110           RTS
 6120 ;
 6130 EVINDI    JSR RECHNUNG
 6135           JSR VSUCH
 6140           LDY #0
 6150 SCHL4     LDA (VEKTOR1),Y
 6160           STA LAENGE2,Y
 6170           INY
 6180           CPY #3
 6190           BNE SCHL4
 6200           LDA LAENGE2
 6205           BEQ KZEICHEN
 6210           CMP #21
 6220           BCC KLEINER
 6230           LDA #20
 6240           STA LAENGE2
 6250 KLEINER   LDY #0
 6260 NZEICHEN  LDA (STR2),Y
 6270           STA VSTR,Y
 6280           INY
 6290           CPY LAENGE2
 6300           BNE NZEICHEN
 6310           LDA #>VSTR
 6320           STA STR2
 6330           LDA #<VSTR
 6340           STA STR2+1
 6350 KZEICHEN  RTS
 7000 STCKVEK   LDA ZREG
 7010           STA VEKTOR2
 7020           LDA ZREG+1
 7030           STA VEKTOR2+1
 7040           RTS
 7045 ;
 7100 PUSHXR    JSR HOLLR
 7105           JSR VEKTOR4
 7110           LDY #0
 7120 SCHL5     LDA XREG,Y
 7130           STA (VEKTOR2),Y
 7150           INY
 7160           CPY #2
 7170           BNE SCHL5
 7172 SCHL6     LDA RREG-2,Y
 7173           STA (VEKTOR2),Y
 7174           INY
 7175           CPY #4
 7176           BNE SCHL6
 7177           RTS
 7178 ;
 7180 PUSHLY    JSR HOLLR
 7185           JSR VEKTOR4
 7190           LDY #0
 7200 SCHL7     LDA LREG,Y
 7210           STA (VEKTOR2),Y
 7230           INY
 7240           CPY #2
 7250           BNE SCHL7
 7261 SCHL8     LDA YREG-2,Y
 7262           STA (VEKTOR2),Y
 7263           INY
 7264           CPY #4
 7265           BNE SCHL8
 7266           RTS
 7270 ;
 7280 HOLXY     LDY #0
 7310 SCHL9     LDA LREG,Y
 7320           STA XREG,Y
 7330           INY
 7340           CPY #4
 7350           BNE SCHL9
 7360           RTS
 7370 ;
 7380 HOLLR     JSR STCKVEK
 7400           LDY #0
 7410 SCHL10    LDA (VEKTOR2),Y
 7420           STA LREG,Y
 7430           INY
 7440           CPY #4
 7450           BNE SCHL10
 7460           RTS
 7465 ;
 7645 ;
 7650 VEKTOR4   JSR STCKVEK
 7660           CLC
 7670           LDA VEKTOR2
 7680           ADC #4
 7690           STA VEKTOR2
 7700           LDA VEKTOR2+1
 7710           ADC #0
 7720           STA VEKTOR2+1
 7730           RTS
 7735 ;
 7740 PUSHXY    JSR VEKTOR4
 7750           LDY #0
 7760 SCHL11    LDA XREG,Y
 7770           STA (VEKTOR2),Y
 7780           INY
 7790           CPY #4
 7800           BNE SCHL11
 7810           RTS
 9985 ;
 9990 ; REGISTER & EIN SIMULIERTER STACK
 9995 ;
10000 ZREG    .BYT 0,0
10010 XREG    .BYT 0,0
10020 YREG    .BYT 0,0
10050 VERGL   .BYT 0,0
10060 LREG    .BYT 0,0
10070 RREG    .BYT 0,0
10080 VSTR    .BYT 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  ; 20 * 0
10100 STACK   .BYT 0
20000 .END
READY.
Listing 5. Listing 4 als Quelltext
10 sys36864:.opt p,oo:*= 52000
20 laenge1 = $b2
30 laenge2 = $b5
40 str1    = $b3
50 str2    = $b6
55 umult1  = $28
56 umult2  = $71
57 umult   = $b357
58 aarray  = $2f
59 vektor1 = $fb
60 vektor2 = $fd
100            jsr regset
101            ldx #0
102 marke1     lda laenge1,x
103            sta 700,x
104            inx
105            cpx #6
106            bne marke1
110            jsr hauptschl
111            ldx #0
112 marke2     lda 700,x
113            sta laenge1,x
114            inx
115            cpx #6
116            bne marke2
117            rts
130 ;
140 hauptschl  jsr hochz
145            jsr hollr
150            jsr lrvergl
160            cmp #0
170            beq z350
180            cmp #2
190            beq z350
195            jsr holxy
210            jsr evindi
220 z270       jsr xyvergl
230            cmp #2
240            beq z330
250 z280       jsr exindi
260            jsr einspr
270            cmp #2
280            beq z290
285            cmp #0
286            beq z290
290            jsr hochx
300            jmp z280
310 z290       jsr eyindi
320            jsr einspr
330            cmp #1
340            beq z300
345            cmp #0
346            beq z300
350            jsr runtery
360            jmp z290
370 z300       jsr xyvergl
380            cmp #2
390            beq z330
400            jsr swap
410            jsr hochx
420            jsr runtery
430            jmp z270
435 ;
440 z330       jsr pushly
460            jsr hauptschl
470 ;
480            jsr pushxr
500            jsr hauptschl
510 ;
940 z350       jmp runterz
1005 ;
1010 ;    vergleich str1 mit vergl$
1011 ;1) str1<vergl  2) str1>vergl
1015 ;
1020 einspr    ldy #$ff
1030 schl1     iny
1040           cpy laenge1
1050           bne weiter1
1060           lda #1
1070           jmp raus
1080 weiter1   cpy laenge2
1090           bne weiter2
1100           lda #2
1110           jmp raus
1120 weiter2   lda (str1),y
1130           cmp (str2),y
1140 beq schl1
1150           bcc weiter3+1
1160           lda #2
1170 weiter3   bit $1a9 ;maskierung fuer lda #1
1200           rts
1210 raus      ldx laenge1
1220           cpx laenge2
1230           bne fertig
1240           lda #0
1250 fertig    rts
1260 ;
1270 ;    vergleichen von x und y
1275 ;x>y lda #2  x<y lda #1  x=y lda #0
1280 ;
1290 xyvergl   lda xreg+1
1300           cmp yreg+1
1310           bne weiter4
1320           lda xreg
1330           cmp yreg
1340           beq gleich+1
1350 weiter4   bcs groesser+1
1360           lda #1
1370 gleich   .byt $2c,$a9,0 ;bit $00a9
1380 groesser  bit $2a9
1390           rts
1400 ;
1410 ;    vergleichen von l und r
1415 ;l>r lda #2  l<r lda #1  l=r lda #0
1420 ;
1430 lrvergl   lda lreg+1
1440           cmp rreg+1
1450           bne weiter5
1460           lda lreg
1470           cmp rreg
1480           beq gleich+1
1490 weiter5   bcs groesser+1
1500           lda #1
1510           rts
1985 ;
1990 ; register hoch- ung runterzaehlen
1995 ;
2000 hochz     clc
2020           lda zreg
2030 adc #4
2040           sta zreg
2050           lda zreg+1
2060           adc #0
2070           sta zreg+1
2090           rts
2100 runterz   sec
2120           lda zreg
2130           sbc #4
2140           sta zreg
2150           lda zreg+1
2160           sbc #0
2170           sta zreg+1
2190           rts
2200 hochx     clc
2220           lda xreg
2230           adc #1
2240           sta xreg
2250           lda xreg+1
2260           adc #0
2270           sta xreg+1
2290           rts
2300 runtery   sec
2320           lda yreg
2330           sbc #1
2340           sta yreg
2350           lda yreg+1
2360           sbc #0
2370           sta yreg+1
2390           rts
2985 ;
2990 ; die mit x/y indizierte variable
2991 ; wird gesucht z.b. ( a$(x) )
2995 ;
3000 xsuch     lda xreg
3010           asl
3015           tax
3020           lda xreg+1
3030           jsr prg1
3040           adc xreg
3050           tax
3060           tya
3070           adc xreg+1
3080           jmp prg2
3100 ysuch     lda yreg
3110           asl
3115           tax
3120           lda yreg+1
3130           jsr prg1
3131           adc yreg
3132           tax
3133           tya
3134           adc yreg+1
3135           jmp prg2
3140 vsuch     lda vergl
3150           asl
3155           tax
3160           lda vergl+1
3170           jsr prg1
3171           adc vergl
3172           tax
3173           tya
3174           adc vergl+1
3175           jmp prg2
3200 prg1      rol
3210           tay
3220           txa
3230           clc
3240           rts
3250 prg2      tay
3260           clc
3270           txa
3280           adc #7
3281           tax
3282           tya
3283           adc #0
3284           tay
3285           clc
3286           txa
3290           adc aarray
3300           sta vektor1
3310           tya
3320           adc aarray+1
3330           sta vektor1+1
3340           rts
3985 ;
3990 ;swap - vertauschen zweier strings
3995 ;
4000 swap      jsr xsuch
4010           lda vektor1
4020           sta vektor2
4030           lda vektor1+1
4040           sta vektor2+1
4050           jsr ysuch
4060           ldy #0
4070 schl2     lda (vektor1),y
4080           tax
4090           lda (vektor2),y
4100           sta (vektor1),y
4110           txa
4120           sta (vektor2),y
4130           iny
4140           cpy #3
4150           bne schl2
4160           rts
4985 ;
4990 ;     vergl = (xreg+yreg)/2
4995 ;
5000 rechnung  clc
5010           lda xreg
5020           adc yreg
5030           sta vergl
5040           lda xreg+1
5050           adc yreg+1
5060           lsr
5070           sta vergl+1
5080           ror vergl
5090           rts
5100 ;
5110 ;register auf ausgangswerte setzen
5120 ;
5200 regset    lda #0
5210           sta xreg
5215           sta xreg+1
5220           lda #<stack
5225           sta zreg
5230           lda #>stack
5235           sta zreg+1
5240           jsr xsuch
5245           inc xreg
5250           sec
5260           lda vektor1
5270           sbc #2
5280           sta vektor1
5290           lda vektor1+1
5300           sbc #0
5310           sta vektor1+1
5320           ldy #1
5325           sec
5330           lda (vektor1),y
5335           sbc #1
5340           sta yreg
5350           dey
5360           lda (vektor1),y
5365           sbc #0
5370           sta yreg+1
5380           jmp pushxy
5985 ;
5990 ;discriptoren in der zp einrichten
5995 ;
6000 exindi    jsr xsuch
6010           jmp discrip1
6020 ;
6030 eyindi    jsr ysuch
6040 ;
6050 discrip1  ldy #0
6060 schl3     lda (vektor1),y
6070           sta laenge1,y
6080           iny
6090           cpy #3
6100           bne schl3
6110           rts
6120 ;
6130 evindi    jsr rechnung
6135           jsr vsuch
6140           ldy #0
6150 schl4     lda (vektor1),y
6160           sta laenge2,y
6170           iny
6180           cpy #3
6190           bne schl4
6200           lda laenge2
6205           beq kzeichen
6210           cmp #21
6220           bcc kleiner
6230           lda #20
6240           sta laenge2
6250 kleiner   ldy #0
6260 nzeichen  lda (str2),y
6270           sta vstr,y
6280           iny
6290           cpy laenge2
6300           bne nzeichen
6310           lda #<vstr
6320           sta str2
6330           lda #>vstr
6340           sta str2+1
6350 kzeichen  rts
7000 stckvek   lda zreg
7010           sta vektor2
7020           lda zreg+1
7030           sta vektor2+1
7040           rts
7045 ;
7100 pushxr    jsr hollr
7105           jsr vektor4
7110           ldy #0
7120 schl5     lda xreg,y
7130           sta (vektor2),y
7150           iny
7160           cpy #2
7170           bne schl5
7172 schl6     lda rreg-2,y
7173           sta (vektor2),y
7174           iny
7175           cpy #4
7176           bne schl6
7177           rts
7178 ;
7180 pushly    jsr hollr
7185           jsr vektor4
7190           ldy #0
7200 schl7     lda lreg,y
7210           sta (vektor2),y
7230           iny
7240           cpy #2
7250           bne schl7
7261 schl8     lda yreg-2,y
7262           sta (vektor2),y
7263           iny
7264           cpy #4
7265           bne schl8
7266           rts
7270 ;
7280 holxy     ldy #0
7310 schl9     lda lreg,y
7320           sta xreg,y
7330           iny
7340           cpy #4
7350           bne schl9
7360           rts
7370 ;
7380 hollr     jsr stckvek
7400           ldy #0
7410 schl10    lda (vektor2),y
7420           sta lreg,y
7430           iny
7440           cpy #4
7450           bne schl10
7460           rts
7465 ;
7645 ;
7650 vektor4   jsr stckvek
7660           clc
7670           lda vektor2
7680           adc #4
7690           sta vektor2
7700           lda vektor2+1
7710           adc #0
7720           sta vektor2+1
7730           rts
7735 ;
7740 pushxy    jsr vektor4
7750           ldy #0
7760 schl11    lda xreg,y
7770           sta (vektor2),y
7780           iny
7790           cpy #4
7800           bne schl11
7810           rts
9985 ;
9990 ; register & ein simulierter stack
9995 ;
10000 zreg    .byt 0,0
10010 xreg    .byt 0,0
10020 yreg    .byt 0,0
10050 vergl   .byt 0,0
10060 lreg    .byt 0,0
10070 rreg    .byt 0,0
10080 vstr    .byt 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  ; 20 * 0
10100 stack   .byt 0
20000 .end
2 ifpeek(2051)<>3thenpoke2051,3:load"quicksort.obj",8,1
5 rem bitte zeile 2 genauso eingeben
10 input "anzahl=  100{left}{left}{left}{left}{left}";a
20 dim a$(a)
23 rem
25 rem  jedes element mit 3 zeichen
28 rem
30 for i = 1 to a
35 : for j = 0 to 2
40 :   a$(i) = a$(i)+chr$(rnd(1)*26+65)
50 : next j
55 next i
56 rem
57 rem       elememte ausgeben
58 rem
60 for i = 1 to a
63 : print a$(i);" ";
65 next i
66 print
67 rem
68 rem       elemente sortieren
69 rem
70 ti$ = "000000"
74 sys 52000
76 t = ti
77 rem
78 rem       elemente ausgeben
79 rem
80 for i= 1 to a
85 : print a$(i);" ";
90 next i
95 print t/60
Listing 6. »Quicksort« demonstriert die Sortiergeschwindigkeit
Kurs: Effektives Programmieren
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →