Generell verwendet der Code Blöcke a 5 Bits.
Diese liegen in dem Bereich von $08b1 - $095f und $0960 - $0a0e.
Die Daten liegen dabei so verteilt, dass die Bytes immer abwechselnd in den beiden Bereichen sind.
Also: $08b1, $0960, $08b2, $0961, $08b3, ...
Die 5-Bit Blöcke ergeben 32 verschiedene Zeichen.
Da es aber insgesamt 56 verschiedene Zeichen im Text gibt,
muss es hierfür bestimmte Zeichen geben, die anzeigen, dass noch mehr Bits benötigt werden.
Das sind die Zeichen $ca, $cb, $d1 und $d7. (im Assembler Source "Escape-values" genannt
Wie komme ich aber auf diese Zeichen, wo es doch nur 32 verschiedene, also $00-$1f gibt?
Das liegt an dem LDA #$ae, das vor der "shift" Routine gesetzt wird.
Massgeblich sind hier zunächst nur die 3 niederwertigen Bits, da ja 5 mal nach "links" geshiftet wird,
und somit nur die 3 niedrigsten im AKKU verbleiben (zu den höheren kommen wir später noch).
Also #$06. Daraus wird nach 5 mal verschieben ein $c0.
Zuzüglich den 5 variablen Bits aus dem Datenbereich bekommt man also: $c0 - $df.
Da die meisten Zeichen Kleinbuchstaben sind, werden die Zeichen $c1 - $da für die Zeichen a - z verwendet.
Einfach ein AND #$7f und wir erhalten ja den passenden Screencode.
Da das Zeichen $c0 (bzw. $40) nicht gebraucht wird, wäre es Schade, dieses nutzlos zu verschwenden.
Daher das ADCBitte melde dich an, um diesen Link zu sehen. am Ende der Shift-Routine.
Da im Akku-Ausgangswert ($ae) vor dem Shift das Bit $08 gesetzt ist, ist dieses Bit nach 5 mal links-shift im Carry-Flag,
und somit addiert der ADC #$00 genau um 1.
Wir bekommen also Werte von $c1 - $e0 (anstelle von $c0 - $df).
Zurück zu den so genannten "Escape-values"
Das wären die Zeichen j,k,q und w.
Das q kommt zum Glück im Text nicht vor. Aber für die anderen muss man noch eine Lösung finden.
Die sieht eigentlich genauso aus, wie für die Grossbuchstaben und Zeichen.
Die Escape-values wurden so gewählt, dass deren Bitmuster der gewünschte AKKU Initialisierung entsprechen
und möglichst selten im vorgegebenen Text vorkommen. Denn es wird ja hier nicht der Standard-Wert lda #$ae genommen.
j entspricht $ca. Das ergibt bei den niedrigsten 3 Bits eine $02. -> 5 mal geshiftet: $40 (für Kleinbuchstaben von $41 - $60)
k entspricht $cb. Das ergibt bei den niedrigsten 3 Bits eine $03. -> 5 man geshiftet: $60 (für Grossbuchstaben von $61 - $80)
q entspreich $d1. Das ergibt bei den niedrigsten 3 Bits eine $01. -> 5 mal geshiftet: $20 (für Zeichen+Zahlen von $20 - $3f)
für diese 3 Escape-values war es auch wichtig, dass die sich daraus ergebenden Zeichen unterhalb $d6 liegen,
da alle Werte darüben für die Wörterbucheinträge verwendet werden.
w entspreich $d7. Das ergibt bei den niedrigsten 3 Bits eine $07. -> 5 mal geshiftet: $e0 (für Wörterbucheinträge)
Da bedeutet, dass alle Zeichen, Zahlen, Grossbuchstaben und die Zeichen j, k und w,
sowie zusätzlich noch die Zeichen v,x und z (da sie über $d6 liegen) insg. 10 Bits benötigen.
Das spielt dann in der Überlegung, ob es sich lohnt für bestimmte Kombinationen ein Wörterbuch eintrag zu erzeugen oder nicht
(Das bringt dann manchmal eben 1 Bit Ersparnis, oder eben nicht).
Zu den Wörterbucheinträgen.
Alle Zeichen ab $d6 sind Wörterbucheinträge (im 2 Byte-Raster - Rückwärts in der Tabelle und mit Bit 7 terminiert.
Das wird, wie schon im Assembler Source beschrieben als Stack-Pointer gesetzt und auch so abgefragt.).
Um eine zusätzliche Tabelle für den Beginn der Wörter zu sparen, wird einfach das Byte mit 2 multipliziert.
Aus $d6 - $ff ergeben sich dann eben die möglichen Startpositionen: $ac - $fe.
$d7 ist allerdings schon für ein Escape-value vergeben, so dass die Position $ae nicht angesprochen werden kann.
Daher wurde das Wort "_ist_" genau auf Position $b0 gesetzt. Somit läuft diese Wort von $b0 - $ac (rückwärts).
Position $ac wird dann noch zusätzlich für das "Space" verwendet.
Das sollte ein 5-Bit (und nicht 10 Bit, wie die anderen Zeichen) sein, da es ja sehr oft vorkommt.
Ebenso das "Newline" Zeichen wurde so als Wort abgelegt, da es oft vorkommt, und auch in einem PETSCII Bereich liegt ($00 - $1f), der nicht erreicht wird.
Da ansonsten 2-Byte Silben wie "en", "er", "in" usw. am häufigsten vorkommen, wurde darauf geachtet,
dass diese mit einem 5-Bit Block (Zeichen $d6 - $e0) dargestellt werden.
Die Wörterbucheinträge $e1-$ff verbrauchen ja 10 Bits.
Da habe ich dann viel per Hand rumgerechnet, welche Kombination mehr Bits spart.
Oder wie man bestimmte Wörter hintereinander hängt, um nur ein Wörterbuch-Zeichen zu verwenden.
Zum Schuss noch die Erkennung des Ende des Textes.
Man könnte z.b. auf das "?" prüfen. Im Code wird aber auf das grosse "K" ($6b) geprüft.
Warum?
Hier kommt noch mal das initiale lda #$ae in Spiel.
Die ersten 4 Bits (sowie das Carry-Flag von vor dem Start der Shift-Routine) wird ja immer ins Ende des Datenbereichs hineingeschoben.
Und da der komplette Datenbereich immer weiter nach "vorne" geschoben wird, laden diese Bits ganz zum Schuss auch in der "Auswertung".
Das Carry-Flag ist bei den ersten 5 Zeichen immer leer (also 0).
Es wird also immer das Bitmuster 01010 ($0a) hinten angehängt.
Die Daten wurden so gewählt, dass das letzte Zeichen ein $d7 ist.
Also das Zeichen, dass nun noch mal 5 Bits geholt werden, die dann als Zeiger auf ein Wort aus dem Wörterbuch verwendet werden..
Und zwar ":-)?".
Das verbraucht eigentlich 2 Bits mehr, als wenn ich die Zeichen einfach so in den Daten hätte,
aber da ich den Zeiger gar nicht in den Daten habe, sondern das automatisch erzeugt $0a nehme, spart das wieder ein paar Bits.
Ebenso wird das nächste $0a zu einem $cb -> also Hinweis auf weitere 5 Bits für ein Grossbuchstabe.
Das nächste $0a wird dann zu einem $6b ($60 als Grossbuchstabe + 1 da das Carry-Flag gesetzt ist + $0a).
Ende.