Assembler für Einsteiger - Teil 2 ----------------------------------- von Tim-Philipp Müller Noch ein 'Nachschlag' vom letzten Mal: WICHTIG: steht für das Semikolon Wir können also nun auch Labels im Programm verwenden. Schön! Es gibt allerdings noch eine Möglichkeit, diese zu verwenden: 00010 DL=$BC21 00020 00030 START LDA #DL 00040 STA $230 00050 LDA /DL 00060 STA $231 00070 RTS Beim Atmas-II muß in Zeile 50 anstatt von 'LDA /DL' 'LDA #DL:H' stehen. Wie bekannt ist, werden Werte, die größer als 255 sind, ja in LO- und HI- Byte aufgeteilt (bzw. LSB/MSB). Mit dem Befehl in Zeile 30 wird das LO-Byt der Adresse, durch die DL ersetzt werden soll, in den Akku geladen. Der Befehl in Zeile 50 macht das gleiche, nur mit dem HI- Byte. -------------------------------------- Mit der Zeit wird es aber doch etwas langweilig, immer nur irgendwelche Werte irgendwohin zu poken. Nutzen wir doch den Computer, wozu er eigentlich konstruiert wurde, nämlich zum Rechnen. Was tun wir mit einem Taschenrechner, wenn er 5+6 ausrechnen soll? Wir bringen 5 aufs Display (Anzeigefeld), addieren 6 und speichern das Ergebnis (auf dem Display). Der Computer macht das eigentlich fast genauso. Dazu stellt er die Befehle ADC- ADd with Carry (Addieren) SBC- SuBtract with Carry (Subtrahieren) zur Verfügung. Schreiben wir also: 00010 .LI OFF 00020 .OR $4000 00030 00040 ERGEBNIS=$600 00050 LDA #5 00055 CLC 00060 ADC #6 5+6 00070 STA ERGEBNIS 00080 RTS cioa Schauen Sie doch jetzt mal mit Hilfe des Monitors, welcher Wert sich in Speicherstelle $600 (ERGEBNIS) befindet. Es müßte $0B sein. Jetzt stellt sich nur noch die Frage, was 'Carry' ist. Damit kommen wir zu einer weiteren wichtigen Assembler- Eigenschaft: Der Prozessor verwaltet so genannte Flags, die er in bestimmten Situationen entweder setzt (=1) oder löscht (=0). Das Carry -das Übertragsflag- ist eines davon. Wie Sie sicher schon wissen, ist unser ATARI ein 8-Bit Computer, daß heißt, er kann pro Speicherzelle nur 8 Bits (also ein Byte) speichern. Damit kann man Werte im Bereich von 0-255 darstellen. Der Computer besitzt aber nun 65536 verschiedene Speicherstellen. Wie kann man dem Computer nun mitteilen, daß man z.B. Speicherstelle 712 ansprechen möchte ? Tja, dazu benutzt man dann zwei Bytes (ein Word) wobei das zweite Byte (Das HI-Byte = higher significant byte) so- zusagen das 256-Fache seines Inhalts wert ist, und das erste (das LO-Byte = lower significant byte) ganz normal 'zählt'. Die Zahl $E456 wird also als $56 $E4 im Speicher dargestellt. Mit dem Befehl ADC addieren wir nun also etwas zum Akku. Ist das Carry- Flag gesetzt, wird nochmal eins zum Akku hinzuaddiert. Also: VOR JEDER ADDITION DAS CARRY- FLAG LÖSCHEN. Dies geschieht mit dem Befehl CLC - CLear Carry (Carry löschen) SEC - SEt Carry (Carry setzen) Sollte das Ergebnis größer als 255 sein (also eine Überlauf aufgetreten sein), setzt der Prozessor das Carry- Flag (da der Akku nicht größer als 255 sein darf, enthält er dann den Wert-256 z.B. $FE+$4=$102 / $102-$100=$2 = Akku) Dies ist zum Beispiel beim Addieren von Words unabläßlich. Zuerst addiert man die beiden LO-Bytes. Tritt ein Überlauf auf, müßte das HI-Byte um eins erhöht werden. Der Prozessor setzt nun aber das C- Flag. Addiert man nun die beiden HI-Bytes wird das C-Flag ja automatisch mitaddiert (und somit das HI- Byte des Ergebnis um 1 erhöht). Das Carry- Flag kann man mit zwei Befehlen abfragen: BCC - Branch on Clear Carry (Verzweige bei gelöschtem Carry) BCS - Branch on Set Carry (Verzweige bei gesetztem Carry) Eines noch zu den Verzweigungsbefehlen, die Flags abfragen. Man kann mit ihnen nur Sprünge im Bereich von 127 Bytes vor und zurück ausführen. Wird dieser Bereich überschritten, meldet der Assembler eine BRANCH TOO LONG- Error. Hier einige Beispiele: WORD = WORD + BYTE (=$BD21) 00010 START LDA WORD 00020 CLC 00030 ADC BYTE 00040 STA WORD 00050 BCC .1 00060 INC WORD+1 00070 .1 RTS 00080 00090 WORD .DA $BC40 00100 BYTE .DA $E1 Erst wird der Inhalt des Lo- Bytes von WORD ($40) um den Wert in BYTE ($E1) erhöht und wieder ins LO-Byte zurückge- schrieben ($40+$E1=$121 / $121-$100=$21). Falls kein Übertrag aufgetreten ist (Carry=0), verzweigt das Programm nach '.1'. Falls ein Übetrag aufgetreten ist, wird das Hi-Byte von WORD um eins erhöht ($BC+$1=$BD), und zwar mit dem Befehl INC - INCrement memory (Speicherzelle um 1 erhöhen) DEC - DECrement memory (Speicherzelle um 1 erniedrigen) INC und DEC wirken nie auf den Akku, sondern nur auf eine Speicherzelle. Mit dem Befehl RTS - RETurn Subroutine (vom Unterprogramm zurückkehren) gelangen wir zum Assembler zurück. ------------------------------ WORD1 = WORD1 + WORD2 (=$C000) 00010 LDA WORD1 00020 CLC 00030 ADC WORD2 00040 STA WORD1 00050 LDA WORD1+1 00060 ADC WORD2+1 00070 STA WORD1+1 00080 RTS 00090 00100 WORD1 .DA $BC40 00110 WORD2 .DA $3C0 Erst wird das LO-Byte von WORD1 ($40) mit dem LO-Byte von WORD2 ($C0) addiert und das Ergebnis ($40+$C0=$100 / $100-$100 = 0) im LO-Byte von WORD1 ab- gelegt. Falls ein Übertrag erfolgt ist, wird das C-Flag gesetzt. Jetzt werden die beiden HI-Bytes addiert und das Ergebnis im Hi-Byte von WORD1 abgelegt. Ein möglicher Übertrag der ersten Addition wird automatisch berücksichtigt, da das Carry-Flag ja automatisch hinzuaddiert wird. ----------------------------------- ERG = WORD + BYTE 00010 LDA WORD 00020 CLC 00030 ADC BYTE 00040 STA ERG 00050 LDA WORD+1 00060 ADC #0 00070 STA ERG+1 00080 RTS 00090 00100 WORD .DA $BC40 00110 BYTE .DA $E1 00120 ERG .DA $0 Das Verfahren ist eigentlich dem ersten (WORD=WORD+BYTE) gleich, nur daß das Ergebnis in eine andere Speicherzelle übertragen werden soll. Dummerweise kann man hier keine INC- Funktion verwenden, da ja auch ohne Überlauf das HI- Byte übertragen werden soll. Also muß man mit 0 addieren, bei gesetztem Carry wird dann automatisch 1 addiert. ------------------------------- Im Addieren sind wir ja nun Meister, doch wie subtrahiert man? Dazu gibt es ja den Befehl SBC, der eigentlich wie der ADC- Befehl funktioniert. Einzige Ausnahme: Anfangs muß das Carry gesetzt sein, und wenn ein Übertrag (eigentlich ja ein Untertrag) aufgetreten ist, wird das Flag gelöscht. Bei Übertrag muß der Computer die $100 (LO:00 /HI=Carry:01) die er zuviel hat, ja irgendwohin packen, und zwar ins C- Flag. Beim Subtrahieren muß er irgendwo noch $100 herbekommen, damit die 0 nicht unterschritten wird. Diese nimmt er aus dem Carry. Beispiele: OK LDA WORD LDA WORD SEC SEC SBC BYTE SBC WORD2 STA WORD STA WORD BCS .1 LDA WORD+1 DEC WORD+1 SBC WORD2+1 .1 RTS STA WORD+1 RTS LDA WORD SEC SBC BYTE STA ERG LDA WORD+1 SBC #0 STA ERG RTS -------------------------------- Nachprüfen der Ergebnisse: Wenn Sie herausfinden wollen, welcher Wert nun in ERG gespeichert ist, müssen Sie erstmal wissen, an welcher Adresse ERG liegt. Dazu können Sie die Symbol- Tabelle mit 'SYM' aufrufen und suchen, oder mit 'VAL ERG' sich die Adresse direkt zeigen lassen. Dann gehen Sie in den Monitor, etc... -------------------------------- Nun geht es an einen weiteren Befehl: CMP - CoMPare to accu (mit Akku vergleichen) Wenn der Akku kleiner ist, wird das Carry- Flag gelöscht. Falls der Akku größer oder gleich ist, wird es gesetzt. (Abfrage mit BCC + BCS!) Jetzt lernen wir ein weiteres Flag kennen, nämlich das Zero- (Null-) Flag. Es wird immer dann gesetzt, wenn das Ergebnis der letzten Operation =0 war. Wurde eine Speicherzelle also mit 'INC' von $FF auf $00 erhöht, wird das Zero- Flag gesetzt. Wird der Befehl 'LDA 712' angewendet, und das Register 712 ist gleich 0, wird das Zero- Flag z.B. auch gesetzt. Das Zero- Flag kann mit den Befehlen BEQ - Branch if EQual to zero (Wenn gleich 0 verzweigen) BNE - Branch if Not Equal to zero (Wenn ungleich 0 verzweigen) abgefragt werden. Befehle zum Löschen oder Setzen des Flags gibt es nicht, da das Flag ja nach fast jedem Befehl neu 'geschaltet' wird. WORD UM 1 ERHÖHEN: PLUS INC WORD BNE .1 INC WORD+1 .1 RTS WORD 1 ERNIEDRIGEN: MINUS LDA WORD BNE .1 DEC WORD+1 .1 DEC WORD RTS Alles klar? ------------------------ Hier noch ein Beispiel zu 'INC': 00010 FLASH LDA 20 00020 .1 CMP 20 00030 BEQ .1 00040 INC 712 00050 BNE FLASH 00060 RTS der Rahmen blendet einmal alle Farben durch, (wenn die Rahmenfarbe wieder 0 ist) dann wird zum Assembler zurück- gesprungen. Die Zeitverzögerung vor jede Erhöhung der Rahmenfarbe ist not- wendig, da sonst so schnell durchge- blendet werden würde, daß wir nicht mehr als ein Flimmern sehen! ------------------------ REGISTER 20 = $14 = RTCLOK wird alle 1/50 Sekunde (und somit vor jeden neuen Bildschirmaufbau) vom VBI erhöht. ------------------------ Demo zum 'CMP'- Befehl: 00010 .LI OFF 00020 .OR $4000 00030 00040 COLOR1=709 00050 00060 START LDA #0 00070 STA COLOR1 00080 .1 LDA 20 00090 .2 CMP 20 00100 BEQ .2 00110 INC COLOR1 00120 LDA COLOR1 00130 CMP #$C 00140 BNE .1 00150 RTS In Zeile 60+70 wird die Schrift dem Hintergrund angepaßt. in Zeile 80-100 erfolgt eine Zeitverzögerung im 1/50 Sekunde: Der Momentane Timer- Wert wird in den Akku gelesen (Zeile 80) und dann wird verglichen (Zeile 90) ob der Wert noch gleich ist. Wenn ja (Zeile 100) wird weiter gewartet. Wurde er aber erhöht, ist er ja nicht mehr gleich, also wird das Programm fortgesetzt. Nun wird in Zeile 110 die Schrift ein bißchen heller gemacht (= Farbregister erhöht). In Zeile 120 wird geprüft, ob die Schrift schon so hell ist, wie sie sein soll. Wenn nicht (140) wird sie weiter heller geblendet. Die Zeitver- zögerung ist nötig, damit Sie überhaupt noch sehen können, wie die Schrift eingeblendet wird (sonst ginge es zu schnell). Probieren Sie doch einmal, jeweils 3/50 Sekunden zu verzögern (wirkt besser!). Anregung: Schreiben Sie doch ein Programm, das bei Druck auf START den Rahmen einmal 'durchblendet', bei Druck auf SELECT die Schrift, und bei OPTION den Hintergrund. Außerdem wäre es nicht schlecht, wenn START+OPTION+SELECT zum Assembler zurückführen würde. CONSOL - 53279 - $D01F =0: START+OPTION+SELECT =1: OPTION+SELECT =2: START+OPTION =3: OPTION =4: START+ SELECT =5: SELECT =6: START ---------------------------- Viel Spaß beim Experimentieren! Bis zum nächsten Mal..... Good Byte! (ich weiß, bis jetzt kam noch nichts phänomenales, das Sie nicht auch mit BASIC machen könnten doch warten Sie erstmal bis zur nächsten Folge...)