Hannenz hat gut aufgepasst und einen kleinen Fehler gefunden. RTS heißt natürlich RETURN FROM SUBROUTINE. Ich hab diesen Fehler im zweiten Teil korrigiert. Gut aufgepasst!
Mich freut es zu hören, das die Basic geweihten meine Assembler Erklärungen gut verstanden haben. Das macht Mut, und legen gleich nach.
Fragen:
@Pohli:
Du würdest also gerne einen Befehl haben, der so aufgebaut ist wie der Basic Poke.
Quasie: POKE ADRESSE,WERT
Ein entsprechender Assemblerbefehl könnte so aussehen:
STA ADRESSE,WERT
Leider gibt es diesen nicht im 6502 Befehlssatz (auch nicht im 65816). Du fragst dich, wie dann das Basic das macht? Grob erklärt: Der Basicinterpreter prüft Intern den Befehl POKE, und weiß das hinter dem Befehl eine Adresse und danach ein Wert kommt. Der Interpreter macht tausend sachen und baut aus dem Poke ein LDA, in den der Wert geladen wird, und speichert dann gefolgt von einem STA in die geforderte Adresse. Wie du siehst, kommt am Ende eine LDA & STA Sequenz heraus, wenn du einen Poke benutzt.
Ich habe heute in 2 Minuten eine kleine Scrollroutine fertig geschrieben, die 55 Bytes gross ist. Diese sollte am Ende des kleinen Kurses, ganz oder teilweise verstanden werden. Aber bis wir uns davon erste Codebrocken anschauen, müssen wir noch ein paar andere dinge lernen.
Wir wissen nun, wie wir Werte in den Akku laden, und an andere Stellen in den Speichern schreiben können. Das ist doch schonmal was! Als heutige Aufgaben trauen wir uns an eine Routine, welche die ersten 40 Spalten des Textscreens mit einem "A" füllt. In Basic würde unsere Umsetzung so aussehen, wenn diese Assembler nah geschrieben wäre.
1 A=1 : REM Buchstabe A hat den Screencode 1.
2 X=0
3 Poke 1024+X,A
4 X=X+1
5 IF X<>40 then 3
6 end
Wie wir sehen, steckt im heutigen Teil eine Bedingung und eine neue Variable "X".
Die Variable A in Zeile 1 steht wieder für unseren Akku. Diesen befüllen wir mit einer 1, doch nun soll noch ein zweiter Wert geladen werden. Wenn wir diesen auch gleich in den Akku laden, ist unsere 1 wieder futsch. Um dieses Problem zu lösen, holen wir einfach einen Kollegen von LDA heran. Dieser nennt sich LDX. Der Vollständigkeit halber erwähne ich schnell, das es noch einen gibt, und zwar LDY, das wir heute aber nicht brauchen.
Wenn jemand sagt,
ich lade etwas in den Akku, dann meint er - LDA #xx
ich lade etwas ins X-Register, dann meint er - LDX #xx
ich lade etwas ins Y-Register, dann meint er - LDY #xx
Den Akku kennen wir ja schon. Das X-Register und Y-Register sind neu für uns. Was ist an denen anders?
Gar nichts! Es sind auch 1 Byte grosse Speicherzellen, genau wie der Akku. Auch hier könnt ihr Werte von 0 bis 255 laden. Aber wie speichert man diese X & Y Register Werte in andere Speicherzellen? Die Lösung wird euch schon auf der Zunge liegen. Natürlich mit einem STX & STY Befehl, die ebenfalls sehr Identisch mit dem STA sind.
Um nochmal auf unser kleines Hintergrundfarben Programm zu kommen, könnten wir es jetzt so schreiben:
* = 4096
LDX #10
LDY #2
STX 53280
STY 53281
RTS
Wir sind nun in der Lage, gleich mehrere Werte zu laden, und diese dann hintereinander zu speichern. Vorher mussten wir immer einen Wert laden, dann speichern, dann wieder laden, dann nochmal speichern. Wir haben somit 3 Variablen zur Verfügung (AKKU, X-REG, Y-REG). Mehr gibt es leider nicht und wir müssen damit leben.
Zusammengefasst kennen wir nun schon ganze 6 Befehle des 6502 Befehlssatzes:
LDA - load Akku STA - store Akku
LDX - load X-Reg STX - store X-Reg
LDY - load Y-Reg STY - store Y-Reg
Aber die reichen noch nicht aus, um das obige Basic Programm umzusetzen. Zeile 1 & 2 könnten wir problemlos in Assembler hinbekommen. Zeile 3 wird nun etwas kniffelig. Hier wird auf die Adresse 1024, die X Variable addiert, in welche dann Variable A geschrieben wird. Wie geht nun sowas in Assembler? Des Rätselslösung ist nicht schwer. Wie wir wissen, sollen wir den Wert von Variable A in eine Speicherzelle schreiben. Also handelt es sich schonmal um den "STA" Befehl. Doch leider reicht ein einfaches STA 1024 (STA Adresse) nicht aus. Wir wollen das X unsere Adresse beeinflusst. Der korrekte Befehl lautet: STA 1024,X
Das ganze nennt sich "Absolut X Indiziert". Boah!
Ein "STA 1024,X" ist also das gleiche wie ein POKE 1024+X,A. Interessant nicht war?
Prüfen wir diese Erkenntniss doch einfach mal in der Praxis mit folgendem Beispielcode.
* = 4096
LDA #1 (1=Screencode für Buchstaben A)
LDX #0
STA 1024,X
RTS
So. Starten wir das ganze und sehen ganz oben in der linken Ecke ein "A" stehen, denn ab der Adresse 1024 fängt Standardmässig der Screenram an, der 1000 Bytes weiter endet. Da X=0 ist, wird null zu 1024 addiert. ALso ändert sich an der Adresse nichts.
Versuchen wir nun, das "A" eine Spalte weiter darzustellen in Adresse 1025. Wir fassen aber den STA Befehl nicht an, und ändern nur den LDX #0 auf ein LDX #1 um. Unser Beispiel Code sieht dann so aus:
* = 4096
LDA #1 (1=Screencode für Buchstaben A)
LDX #1
STA 1024,X
RTS
Und tatsächlich ist das "A" um eine Spalte weiter gerückt. Also es funktioniert! Auf diese Weise kommen wir 255 Spalten weit, da unser X-Register nicht größere Werte annehmen kann! Schlaumeier würden jetzt vielleicht auf die Idee kommen, das Y-Register auch noch anzuhängen, aber solch einen Befehl gibt es nicht. Also ein STA Adresse,x,y steht nicht im 6502 Angebot. Basic Zeile 3 haben wir abgehakt und easy kapiert.
Zeile 4 ist wieder ein neuer Assembler Befehl. Diesmal einer der ganz leichten. Im Basic Part haben wir hier ein: X=X+1 zu stehen, und wie der Zufall so will, gibt es genau dafür einen 1 Byte grossen ASS Befehl Namens: "INX". Was versteckt sich diesmal hinter der Abkürzung? "IN" steht für Inkrement, was nichts anderes bedeutet wie: Erhöhe! Das X in "inX" bezieht sich auf das X-Register. Der Befehl erhöht einfach den aktuell im X-Register befindlichen Wert im 1. Wer meint, das sowas auch für das Y-Register geben muss, liegt richtig! Ein INY existiert tatsächlich! Nun könnte man fast schon annehmen, das es sowas auch für den Akku gibt, aber leider enttäuscht uns hier der 6502 Befehlssatz. Ein "INA" finden wir nicht. Damit müssen wir leben, oder einen 65816 Prozessor (SuperCPU/COne) anschaffen. Dort gibt es jenen vermissten Befehl.
Testen wir das doch mal wieder etwas aus:
* = 4096
LDX #0 ; Farbwert Schwarz
STX 53280 ; als Rahmenfarbe setzen
INX ; erhöht X um 1
STX 53281 ; als Hintergrundfarbe setzen
RTS
Ich glaube das ganze benötigt keine weitere Erklärung. Also Basic Zeile 4 haben wir auch durchgekaut.
Für Basic Zeile 5 müssen wir zwei neue Assemblerbefehle lernen. Zum besseren Verständnis, trennen wir mal die Basic Zeile in "WENN" UND "DANN". Es sind ja streng genommen zwei Aktionen. Einmal einen Vergleich durchführen (Wenn), und dann etwas tun, wenn diese erfüllt, oder nicht erfüllt wurde (Dann). Genauso ist das auch in Assembler. Wir benötigen also als erstes einen Befehl, mit dem wir unser X-Register auf #40 (40 Zeichen = 1 Bildschirmzeile) prüfen könnten. Da drängt sich der Befehl: "CPX" förmlich auf! Wie immer, nehmen wir die Abkürzung auseinander und schauen was genau dahinter steckt. "CP" steht für "Compare". Also ein Vergleichsbefehl. Das "X" steht natürlich wieder für den Register, der verglichen werden soll. Ich erwähne hier noch schnell, das auch der CPX, zwei Brüder hat. Und zwar gibt es zum einen noch "CPY" zum prüfen des Y-Registers, und "CMP" zum prüfen des Akkus. Das der Prüfbefehl für den Akku nicht, wie vielleicht angenommen, CPA sondern CMP heißt, müssen wir leider so akzeptieren!
Wie bereiten wir nun den Prozessor auf eine Prüfung auf X = 40 genau vor?
Mit einem: CPX #40 (<direkt, daher mit # davor).
Vergleiche ob X Register gleich 40 ist. Er hat also nun verglichen, aber wo läßt er das Ergebniss seines Vergleichs? Das kann doch nicht weg sein! Irgendwo muss diese Information geblieben sein. Diese Compare Befehle haben eine tolle Eigenschaft. Sie
setzen je nach Vergleichsergebnis, ein FLAG im Status Register. Um genau zu sein, das ZERO Flag in unserem Fall. Oh Gott werdet ihr jetzt denken, aber das ist ganz einfach. Man muss sich das Statusregister so vorstellen, das 7 Leute auf einem Tisch stehen und Fahnen in der Hand halten, die sie hoch (gesetzt) unter runter (gelöscht) halten. Eine Fahne davon ist für unseren nachfolgenden Sprungbefehl wichtig. Die Fahne, auf die wir später prüfen, heißt ZERO. Der Typ, der die ZERO Fahne hält, ist so cool drauf und hebt sie immer dann, wenn die beiden Zahlen auf die geprüft werden soll, Identisch sind. Angenommen X = 10, und wir prüfen X mit einem CPX #40. Tja, aber X ist nicht 40, sondern 10. Somit bleibt die ZERO Fahne unten (ZERO Flag gelöscht). Wenn X = 40 ist, und es wird auf 40 verglichen, dann hebt sich die ZERO Fahne. Das ZERO Flag ist gesetzt. Die Flags bleiben solange unverändert, bis wieder ein Befehl kommt, der sich darauf auswirkt. Hier ist eine Befehlsübersicht und ihre Einflüsse auf das Statusregister ratsam. Wir wissen nun, das Vergleichsbefehle die Flags im Statusregister setzen oder löschen, je nach Vergleichsergebnis. Auf diese gesetzten Flags, kann man mit BRANCH Befehle (=Verzweig Befehle) reagieren.
(Weiter gehts im nächsten Posting, da ein Posting nicht länger wie 10000 Chars lang sein Darf