Amiga Assembler – Teil 2
Der letzte Artikel zu Amiga Assembler war eine Einführung für Anfänger. Nun werden wir einige weitere Kommandos kennenlernen und uns den Debugger noch genauer ansehen. Nach dem 2. Teil der Serie sollte man soweit sein erste eigene Programme für den Amiga zu schreiben.
Amiga Assembler – Teil 2
Aktuell sind wir in der Lage Werte in eines der 8 Datenregister zu schreiben und darauf binäre Operationen anzuwenden. Beim Crashkurs für den Amiga Debugger MonAm habe ich dazu kurz das sehr speziell aussehende Statusregister vorgestellt, aber nicht näher beschrieben. Was ist das Status Register und wozu benötigt man dieses?
Status Register
Eines der ersten Probleme auf die man stößt, wenn man mit einem Register arbeitet, sind Informationen die verloren gehen. Addiert man beispielsweise zwei große Zahlen deren Summe nicht ins Register passt hat man ein Problem. Angenommen das Register wäre nicht 32bit sonder nur 8bit groß, dann können wir die folgende Rechnung zwar ausführen, wir verlieren aber Information:
1 | 200 + 100 = 300 |
im Dezimalsystem eine recht simple Rechnung. Binär sieht das so aus:
1 | 1100 1000 + 0110 0100 = 1 0010 1100 |
Ups! Es ist zu einem Überlauf gekommen. Um die Zahl 300 darzustellen würden wir 9 Bits benötigen. Da wir nur 8bits haben fällt die führende 1 Weg und das Ergebnis der Rechnung ist Dezimal 44 (0010 1100). Wie reagiert die Motorola 68000er CPU auf diese Rechnung? Probieren wir das einfach mal aus. Dazu verwenden wir den Befehl ADD (führt eine Addition aus).
1 | add.[size] [source operand],[destination operand] |
Der Befehl funktioniert wie die bereits vorgestellten binären Operationen. Ich empfehle wieder das Programmers Reference Manual, dort sind alle Befehle detailliert in allen Ausprägungen beschrieben. Möchtest du statt dessen eine Subtraktion probieren, dann kannst du dort mal nach… genau, SUB suchen. Das Testprogramm
1 2 3 4 5 6 7 | START: move.b #200,d0 add.b #100,d0 nop nop |
zeigt uns im Debugger nun genau was die CPU macht:
Die Operation hat in dem dafür verfügbaren Speicherbereich korrekt funktioniert. Die Bitfolge entspricht dem korrekten Ergebnis ohne der führenden Stelle. Das es einen Überlauf bei der Berechnung gegeben hat zeigt uns das nun gesetzte Carry Bit (C) im Status Register an. Welche Bits werden dort gesetzt?
- Carry
Die Rechenoperation ist sich im dafür vorgesehenen Platz nicht ausgegangen. Das Carry Bit zeigt diesen Umstand an. - Extend
Funktioniert genau wie Carry, nur für andere Rechenoperationen. Dazu muss man die Dokumentation der Befehle lesen! - Negative
Das Ergebnis des Befehls ist eine negative Zahl. - Zero
Das Ergebnis des Befehls ist 0. - Overflow
Der Befehl hat einen Overflow verursacht.
Bei allen Status gilt: wir als Entwickler müssen diese abfragen und darauf reagieren. Im folgenden Beispiel zeige ich euch noch wie man den Status Negative, Zero und Overflow bekommt. Im Source Code verwende ich noch Kommentare. Diese werden mit einem führenden Semikolon erstellt und sind insbesondere beim Assembler Code sehr hilfreich:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | START: ; Negative Flag move.b #50,d0 sub.b #100,d0 nop ; Zero Flag move.b #$ff,d0 sub.b #$ff,d0 nop ; Overflow move.b #100,d0 add.b #$1E,d0 nop |
Am besten ihr testet das am eigenen Rechner und denkt euch eigene Beispiele aus. Nur so gehen diese neuen Befehle in den Wortschatz ein.
Im ersten Fall kommt es bei der Subtraktion zu einem Negativen Wert. Im Statusregister wird neben dem Carry Bit auch das Negative Bit gesetzt.
Im zweiten Fall wird bei der Subtraktion der Wert 0 produziert und dementsprechend das Zero Bit gesetzt.
Zuletzt erzeugen wir noch einen Überlauf in dem wir 100 + 30 rechnen. Moment, das ist doch 130 und in ein Byte passen 255. Wir gehen in diesem Fall davon aus, dass das MSB (most significant bit) als Indikator für eine negative Zahl verwendet wird, deshalb wird neben dem Negative Bit auch das Overflow Bit gesetzt. Die Bitfolge kann somit entweder als negative Zahl repräsentiert werden oder als positive Zahl mit dem Hinweis, dass es zu einem Überlauf gekommen ist.
Sprungmarken und Subroutinen
Höhere Programmiersprachen verwenden zur Strukturierung des Programmflusses if/else Anweisungen und Schleifen (do/while/for). Auf Assembler Ebene gibt es diese Strukturen nicht. Das was existiert sind Sprungmarken. Ein if oder eine while ist auf binärer Ebene nichts anderes als ein Sprung an eine bestimmte Adresse im Programmcode. Man denke da an das viel gehasste Schlüsselwort GOTO aus C, das in Basic am C64 exzessiv eingesetzt wird. Ich habe ein kleines Beispielprogramm, dass den Umgang mit Sprungmarken und Subroutinen verdeutlichen soll:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | START: ; Hauptprogramm move.b #$0A,d0 bsr ADDIERE bsr SUBTRAHIERE rts ADDIERE: ; Funktion 1 Addiere 10 add.b #10,d0 rts SUBTRAHIERE: ; Funktion 2 Subtrahiere 5 sub.b #5,d0 rts |
Wenn ihr das Programm durchsteppt stellt ihr fest, dass STRG + T nicht in die Subroutinen geht. Dafür verwenden wir STRG + Z:
- STRG + T
Step over - STRG + Z
Step into
Eine Sprungmarke wird als beliebiger Text und einen Doppelpunkt im Source Code gesetzt. Es gibt einige neue noch nicht bekannte Befehle:
- BSR
steht für „Branch to SubRoutine“ und sprint mit dem Program Counter zu der angegebenen Sprungmarke - RTS
steht für „ReTurn from Subroutine“ und springt zum zuvor gesetzten Punkt des Hauptprogramm zurück
Wenn man beim Debuggen ganz genau aufpasst versteht man auch wie so eine Subroutine funktioniert. Beim BSR Befehl springt der Program Counter auf die Adresse der Sprungmarke. Zusätzlich wird im Adressregister a7, also im Stack Pointer die nächste Adresse im Hauptprogramm abgelegt. Sobald man zum nächsten RTS Befehl kommt setzt dieses den Program Counter auf eben jene Adresse des Adressregisters.
Fazit
In diesem weiterführenden Artikel zum Amiga Assembler haben wir uns das Status Register genauer angeschaut und in mehreren praktischen Beispielen die einzelnen Bits gesetzt. Außerdem wurden die ersten Subroutinen erstellt und ich habe euch gezeigt wie der Rücksprung der Funktion im Speicher der CPU funktioniert. Ich bin mir sicher du bist jetzt schon ganz heiß auf weitere Beispiele. Wir wollen schließlich nicht nur Werte anlegen und verändern…wir wollen was am Bildschirm sehen!
Alle Artikel dieser Artikelserie:
Programmieren auf dem Amiga
Programmieren auf dem Amiga – Teil 2
Amiga Assembler
Amiga Assembler – Teil 2
Amiga Assembler – Teil 3
Amiga Assembler – Teil 4
Amiga Assembler – Teil 5
Amiga Bibliotheken verwenden