Assembler für Einsteiger - Teil 4 ----------------------------------- von Tim-Philipp Müller Wie allen bekannt ist, gibt es den Akku als Register, in dem Werte 'verarbeitet werden'. Nun gibt es außer dem Akku aber noch zwei Hilfs- Register, die als Index- Register dienen (also als 'Zähler' fungieren) und wieder 8-Bit beinhalten: Das 'Y-REGISTER' und das 'X-REGISTER'. Beide lassen sich zum Programmieren von Schleifen und Bearbeiten von Feldern verwenden. Schreibt man 'LDA $600', wird der sich in $600 befindende Wert in den Akku geladen (business as usual). Nun befindet sich im Y-Register z.B der Wert $8F. Schreibt man nun 'LDA $600,Y' wird der Wert der Speicherstelle $600+Y (hier also $68F) geladen. Das gleiche gilt für das X-Register. Nun gibt es noch die s.g. 'Indirekte' Adressierung, z.B. 'LDA ($58),Y'. Nun wird der Wert geladen, der sich in der Adresse, auf die das WORD $58 (also Speicherzellen $58 und $59) zeigt, plus Y-Register befindet. Also in BASIC: A=PEEK(PEEK($58)+PEEK($59)*256 +Y) A=PEEK(DPEEK($58)+Y) in Turbo-Basic Wichtig ist, daß in Klammern nur eine Page-0 Adresse steht ($D0 geht, nicht aber z.B. $100 !!!) Es gibt auch über eine indirekte Adressierung, z.B. 'LDA ($58,X)'. Dies transportiert den Wert, der sich in der Adresse, auf die das WORD $58+X zeigt, in den Akku. In BASIC wieder: A=PEEK(PEEK($58+X)+PEEK($59+X)*256) A=PEEK(DPEEK($58+X)) in Turbo-Basic So, also erstmal zu den Index-Befehlen: nn=byte, adr=adresse, zadr=Page-0-adr. LDY #nn lädt Wert ins Y-Register LDY adr -"- LDY adr,X -"- INY erhöht Y-Register um 1 DEY erniedrigt Y-Register um 1 STY adr legt Wert vom Y-Register STY zadr,X in Speicherzelle ab. CPY #nn vergleicht mit Wert CPY adr vergleicht m.W. in Adr LDX #nn (siehe oben) LDX adr LDX adr,Y INX DEX STX adr STX zadr,Y CPX #nn CPX adr TXA übertrage in Akku TAX übertrage Akku in TYA übertrage in Akku TAY übertrage Akku in Auch mit dem Akku werden nun einige Operationen mehr möglich: LDA adr,X STA adr,X LDA adr,Y STA adr,Y LDA (zadr),Y STA (zadr),Y LDA (zadr,X) STA (zadr,X) INC adr,X DEC adr,X ADC, SBC, CMP, AND, ORA, EOR wie STA ASL, LSR, ROL, ROR wie INC Alle diese Funktionen setzten wieder das Zero- Flag, CPX und CPY setzen natürlich das Carry und Zero-Flag entsprechend. Jetzt komme ich gleich noch zu einem 'neuen' Flag, nämlich dem Negativ-Flag. Es wird gesetzt, wenn das Ergebnis einer Operation größer-gleich $80 ist (also Bit-7 gesetzt ist) und gelöscht, wenn dies nicht der Fall ist. Die Abfragung erfolgt durch BMI - Branch on minus (verzweigen, wenn negativ) BPL - Branch on plus (verzweigen, wenn plus) Befehle zum Setzen und Löschen gibt es nicht, da das Flag ja nach nahezu jedem Befehl neu 'positioiert' wird. So, das war jetzt ziemlich viel Stoff auf einmal, deshalb erstmal Beispiele: -------------------------------------- Schleifen: Abwärtszählend Aufwärtszählend LDA #0 LDA #0 LDX #66 LDX #1 .1 STA FELD,X .1 STA FELD,X DEX INX BNE .1 CPX #67 BNE .1 In beiden Fällen wird der Bereich von FELD+1 bis FELD+66 mit 0 gefüllt. Soll nun aber der Bereich FELD+0 bis FELD+66 geleert werden, muß die abwärtszählede Schleife leich geändert werden: LDA #0 LDA #0 LDX #67 !!! TAX .1 STA FELD-1 !!! .1 STA FELD,X DEX INX BNE .1 CPX #67 BNE .1 Warum? Tja, wenn =1 ist (FELD+1 wurde bear- beitet) und um 1 heruntergezählt wird, ist es =0. Dadurch verzweigt BNE nicht mehr. Man könnte es natürlich in der ersten Routine durch ein BMI ersetzen, doch dann darf der Anfangswert nicht größer als 127 sein. Als Alternative empfehle ich obige Lösung. Der Start- Wert muß dann um 1 höher sein als bei der ersten Lösung, da ja automatisch eine Speicherzelle 'weiter unten' gearbeiet wird. Die abwärts zählenden Schleifen sind meist die optimaleren in Speicherplatz (und somit Geschwindigkeit). -------------------------------------- 764 - $2FC - CH Hier steht der Tastencode der zuletzt gedrückten Taste. Wenn Control gedrückt ist, wird Bit-7 gesetzt, bei Shift ge- schieht das gleiche mit Bit-6. Die restlichen 5 Bits enthalten den Tasten- wert, der aber nur anhand einer Tabelle gezeigt werden kann. Wird $FF in dieses Register geschrieben, wird es gelöscht (also auf 'keine Taste gedrückt' ge- setzt). 121,122 - $79,$7A - KEYDEF (X) Diese Speicherzelle gilt nur für XL/XE- Computer und zeigt auf eine Tabelle im ROM, anhand der man die Werte aus 764 in ATASCII- Zeichen wandeln kann. An Stelle x der Tabelle befindet sich der ATASCII- Code für den Tastencode x. -------------------------------------- Beispiel 1: Ohne K:-Treiber Taste einlesen und in ATASCII- Zeichen umformen. KEYDEF=$79 CH=$2FC KEY LDY #$FF Register löschen STY CH .1 CPY CH Warten bis Taste BEQ .1 gedrückt wurde LDY CH LDA (KEYDEF),Y in ATASCII ....vergleichsroutine Erst wird das Tastatur-Register gelöscht. Nun befindet sich in der Wert $FF immer noch. Jetzt wird mit dem Tastatur- Register verglichen. Ist der Wert im T-Reg. gleich , wurde keine Taste gedrückt und der Vorgang wird wiederholt. Ansonsten wird der Tastatur- Wert in einen ATASCII- Wert umgewandelt. Wozu ist die Routine nützlich?? Tja, Sie könnten, anstatt gleich wieder zur Warte- Routine zu springen (.1), zum Beispiel noch die Consol- Tasten abfragen. ------------------------------------ 756 - $2F4 - CHBAS Dieses Register zeigt auf den auf dem Bildschirm darzustellenden Zeichensatz und beinhaltet das Hi-Byte der Adresse, das immer durch 4 teilbar sein muß. Das LO-Byte ist automatisch 0. Der normale Zeichensatz liegt ab $E000, der internationale bei $CC00 im ROM. Für jedes Zeichen werden 8 Bytes be- nötigt. Da es 128 verschiedene Zeichen gibt (129-256 sind die invertierungen), ist ein Zeichensatz also $400 Bytes lang. Die Zeichen sind nicht in ATASCII sondern in s.g. 'internen' Reihenfolge abgespeichert. (Ein Unterprogramm zum Umrechnen ist auf der Disk.) Beispiel 2: Internationalen Zeichensatz ins RAM ab $5000 kopieren und so ändern, das bei Druck von Control-S ein 'ß' gezeigt wird. INTCHAR=$CC00 CHAR=$5000 CHBAS=$2F4 S=$CC /$CD freies Page-0 Help-Register Z=$CE /$CF -"- CHAR LDA /INTCHAR Hi-Byte Start STA S+1 LDA /CHAR Hi-Byte Ziel STA Z+1 LDY #0 Lo-Bytes (=0) STY S STY Z LDX #4 .1 LDA (S),Y 256 Bytes STA (Z),Y kopieren INY (bis wieder BNE .1 gleich 0 ist) INC S+1 Ziel- und Start- INC Z+1 Adressen um 256 erhöhen DEX BNE .1 und das ganze 4 mal! (1 Zeichensatz ist $400 Bytes lang) Bemerkung:+=0 STX Z+1 Hi-Byte löschen LDA ICODE 'Control-S' intern STA Z in Lo-Byte ASL Z *2 jedes Zeichen ROL Z+1 ASL Z *2 hat 8 Bytes ROL Z+1 ASL Z *2 ($53*8 rechnen) ROL Z+1 LDA Z CLC ADC #CHAR STA Z LDA Z+1 ADC /CHAR STA Z+1 .2 LDA SSDAT,Y Daten für neues STA (Z),Y Zeichen in RAM- INY Zeichensatz CPY #8 kopieren BNE .2 LDA /CHAR Neuen Zeichensatz STA CHBASE einstellen RTS Ciao... SSDAT .HX 003C666C66666C60 ICODE .AT "ß" CTL+S Die Routine ersetzt ein beliebiges Zeichen (bei ICODE festlegen) durch ein sz (für die Süddeutschen: scharfes s). Anstatt Control+S könnte dort zum Beispiel genausogut ein 'A' stehen. Die Assembler- Direktive .AT sorgt dafür, daß an der Adresse ICODE der Interne Code (bzw. Screencode) für das ATASCII- Zeichen in Anführungszeichen steht (es könnte auch ein Text sein, doch dann würde von meiner Routine aus nur der erste Buchstabe berücksichtigt, da LDA ICODE). Ich habe jetzt keine Lust, den Source- Code groß zu erklären. Hier hilft, wie so oft auch, genaues Anschauen und Nachvollziehen der Aktionen. ------------------------------------ Beispiel 3: Andere Anwendung der Index-Register: (als 'Zwischenspeicher' für Akku) COLOR1=709 COLOR2=710 COLOR4=712 FARBE=$30 Farbe = Rot LDY #FARBE TYA ORA #$2 STA COLOR2 TYA ORA #$C STA COLOR1 TYA ORA #$4 STA COLOR4 RTS ------------------------------------- Noch ein praktisches Register: 88,89 - $58,$59 - SAVMSC Dieses Register zeigt auf die Adresse der linken,oberen Ecke des Bildschirms. Zur Indirekten Adressierung sind beim BIBO- Assembler die Speicherzellenr von $CB - $FF frei. Zu beachten ist immer, daß Pointer jeweils 2 Byte einnehmen. Viel Spaß beim Experimentieren Langsam haben wir dann doch sämtliche Befehle durchgepaukt. Die restlichen halte ich für weniger bedeutend, deshalb geht es in der nächsten Folge auch erst einmal um etwas praktisches und handfestes: PLAYER-MISSILE-GRAFIK ! Good Byte (one more time)