Amiga Assembler
In dieser Tutorialserie mache ich meine ersten Amiga Assembler Programmiererfahrungen und lade euch ein mit zu machen. Mit dem richtigen Debugging Tool und etwas Zeit versteht man die Assembler Sprache schneller als gedacht und lernt dabei den Motorola 68000 Prozessor und dessen Befehlssatz kennen. In den letzten Artikeln haben wir dazu bereits eine Toolchain erstellt.
Amiga Assembler
Der Motorola 68000 Prozessor ist eine für damalige Zeiten recht moderne 16/32bit CPU. In Kombination mit der Architektur der Amiga Plattform hat er über Jahre den Markt dominiert und wurde letztlich vom schlechteren PC abgelöst. Die Faszination vom Amiga hat die erste Hälfte der 90er Jahre des letzten Jahrtausend überlebt und begeistert immer noch interessierte Entwickler und Gamer. Insbesondere deshalb, weil viele mit diesem Prozessor die ersten Erfahrungen mit Computer gemacht haben.
VASM
In der Toolchain haben wir Notepad++ derart modifiziert, dass man über ein Kommando (Tastatur oder über das Menü) den geschriebenen Assembler Code mit dem VASM Compiler in eine *.exe Datei wandelt. Diese wird dann in den virtuellen Amiga Systemen getestet oder debuggt. Wir müssen nur noch Assembler Code schreiben. Doch wo beginnen wir?
Assembler leicht gemacht
Für mich sind Assembler Befehle schwer zu verstehen. Ich weiß nicht ob es euch auch so geht, aber als Programmierer ist man einen gewissen Satz an Anweisungen gewohnt. Man legt Variablen an und schreibt Zuweisungen, Berechnungen, konditionelle Abfragen (if/else) oder Schleifen (do/while/for). Einmal verstanden ist das in jeder Sprache nahezu identisch. Bei Assembler hat man es plötzlich mit Registern zu tun und Kommandos die Werte der Register manipulieren. Konditionen, Funktionen (Subroutinen) und Schleifen muss man selber mit Sprungmarken definieren. Bevor man sich also den grundlegenden Unterschieden widmet sollte man mit den Gemeinsamkeiten anfangen.
Grundlagen
Assembler ist maschinennahe Programmierung. Deshalb müssen wir lernen binäre Informationen zu verstehen und zu lesen. In diesem Zusammenhang unterscheidet man:
- Nibble
ein Nibble sind 4 aufeinander folgende Bits, als ein halbes Byte. Das Nibble ist aus mehreren Gründen eine wichtige Größe. Fürs erste reicht es, wenn man weiß, dass diese 4 Bits im Hexadezimalsystem als ein Zeichen angezeigt werden. 1010 ist zum Beispiel $A - Byte
die kleinste Speichereinheit am Rechner. 8 Bits repräsentieren ein Byte, welches aus 2 Nibbles besteht und in hexadezimaler Schreibweise mit zwei Zeichen dargestellt wird. Zum Beispiel 10101100 wird als $AC geschrieben. - Word
als Wort bezeichnet man 2 Bytes. Der Begriff kommt aus der Zeit der 16bit Rechner, der Datenbus war plötzlich 2 Bytes groß, ein Datenwort in 16 Bits dargestellt. Die Schreibweise: $9A3B - Double Word / Longword
die Entwicklung hält nicht an, so wurde das 16bit System bald durch das 32bit System abgelöst und man sprach von DWord Wörtern. Das geht dann mit 64bit Systemen natürlich so weiter…
Um ein wenig mit den Zahlensystemen Binär, Hexadezimal und Dezimal vertraut zu werden empfehle ich euch den Windows Taschenrechner. Diesen kann man in den Programmierer Modus umschalten und dort schnell zwischen Zahlensystemen umrechnen:
Im Zusammenhang dazu ist auch noch die Bitwertigkeit ein sehr wichtiger Begriff:
- MSB
das Most Significant Bit ist das erste Bit (von links nach rechts gelesen) einer Größe (egal ob Nibble, Byte, …). Dieses zeigt an ob es einen Überlauf gab, oder ob die Zahl negativ ist. - LSB
das Least Significant Bit ist das letzte Bit (von links nach rechts gelesen) und gibt an ob die Zahl gerade oder ungerade ist.
Binäre Logik
Die einfachsten Assembler Kommandos die jede CPU kann sind Binäre Operatoren wie AND, OR, XOR, NOT die man ebenfalls aus den höheren Programmiersprachen kennt. Die Kommandos AND, OR und XOR funktionieren dabei alle auf die selbe Art und Weise:
1 | and.[size] [source operand],[destination operand] |
Für size gibt man die größe des Speicherbereichs an auf dem die Operation angewendet werden soll. Möglichkeiten sind die zuvor beschriebenen Größen b (Byte), w (Word), und l (Long Word). Danach gibt man die Quelle und dann das Ziel an.
Einige Beispiele:
1 2 3 | and.w #$f0,d0 or.w #$f0,d0 eor.b #$a,d0 |
Das Kommando für das XOR heißt eor, im Source Operand gibt man entweder ein Register oder einen Wert an. #123 ist dabei ein Dezimalwert, #$f0 ein Hexadezimalwert. Das Ziel ist eines der verfügbaren Register wie beispielsweise d0 (1. Datenregister). Eine Ausnahme ist der NOT Befehl, welcher auf einen Speicher ausgeführt und kein Argument benötigt:
1 | not.[size] [source operand] |
Zum Beispiel:
1 | not.w d0 |
Ausführlichere Informationen rund um die Assemblersprache vom Amiga Prozessor findet man auf wikibooks oder weiterführend in der Programmierreferenz der M68er Prozessorreihe.. Dort sind auch zahlreiche Beispiele angeführt. Bevor wir nun zum Abschluss noch einige Beispiele in der Praxis mit unserer Toolchain durchgehen ein weiteres wichtiges Kommando:
1 | move.[size] [source operand],[destination operand] |
Zum Beispiel:
1 | move.w #120,d0 |
Damit legt man einen Wert in ein Datenregister. Das ist im Prinzip das gleiche wie die Zuweisung eines Werts zu einer Variable in einer höheren Programmiersprache.
Praxis
Genug Theorie fürs erste. Die paar Kommandos reichen bereits aus um auf dem Debug Amiga ein bisschen mit dem Debugger und einem eigenen Programm zu experimentieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | START: move.b #$ff,d0 and.b #$7a,d0 nop nop move.b #$0f,d0 or.b #$f0,d0 nop nop move.w #231,d0 not.b d0 nop nop move.b #$ff,d0 eor.b #$39,d0 nop nop |
Ich habe basierend auf dem Youtube Tutorial ein paar Beispiele zusammengeschrieben. Der Befehl NOP heißt übrigens (No OPeration) und macht außer einen Eintrag im Program Counter gar nichts. Der Source Code wird mit VASM compiliert und am Debug Amiga wie zuletzt beschrieben ausgeführt. MonAm zeigt beim Start das geladene Programm an.
MonAm Crashkurs
MonAm zeigt ein dreiteiliges Interface an. Oben sind die CPU Register aufgelistet. Das sind die mit 1 markierten 8 Datenregister d0-d7, rechts daneben die 8 Adressregister a0-a7 das Statusregister sr und den Program Counter pc.
- Datenregister
Die CPU stellt 8 32bit große Register für unsere Berechnungen bereit. MonAm Zeigt dessen Inhalt als Hexadezimalzahl und daneben als entsprechendes Zeichen (so wie in einem HexEditor üblich) - Adressregister
Es gibt ebenso 8 32bit große Adressregister mit denen wir externe Ressourcen ansprechen können. Man erkennt im Debugger, dass a7 bereits als Stack Pointer genutzt wird. - Statusregister
Im Statusregister werden Informationen der letzten Berechnung abgelegt (Überlauf, Carry, negative Zahl, …). Mehr dazu im nächsten Artikel. - Program Counter
Der Program Counter zeigt immer auf das nächste abzuarbeitende Kommando. MonAm zeigt neben der Adresse auch den Assembler Code an. Im unteren Bereich wird der Zeiger als > angezeigt.
Für uns im ersten Beispiel auch interessant ist der mit 2 markierte Disassembly Bereich interessant. Dort steht der Source Code, der aktuell ausgeführt wird. Neben den Speicheradressen vom Source Code ist auch der Assembler Code und der aktuelle Program Counter abgebildet. Du erkennst vielleicht, dass ich bereits das erste move.b Kommando ausgeführt habe, da der Program Counter auf dem and.b Befehl steht und in d0 Bereits der Wert FF steht.
Mit der Tastenkombination STRG+T kann man ein Kommando ausführen. Du kannst nun deinen eigenen Code Schritt für Schritt durchspielen. Achte dabei auf das Datenregister d0. Nach Ausführung vom AND Kommando springt dessen Wert beispielsweise von FF auf 7A. Man stellt sich vor FF entspricht einer folge lauter 1er und 7A einer folge von 1er und 0er. Eine Und-Verknüpfung dieser Binärfolgen ergibt dann 7A (alle 1er bleiben 1er, alle 0er bleiben 0er). Das selbe kann man nun für OR, NOT und XOR durchspielen. Falls du nachrechnen willst was genau passiert empfehle ich den Taschenrechner von Windows und das notieren der Bitfolge auf Papier.
Fazit
In diesem Basistutorial zu Amiga Assembler habe ich einige Begriffe erklärt und binäre Operatoren, die auch aus höheren Sprachen bekannt sind, kennengelernt. Danach haben wir mit der Toolchain und dem Debugger erste praktische Erfahrungen gemacht.
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
Eine Antwort
[…] letzte Artikel zu Amiga Assembler war eine Einführung für Anfänger. Nun werden wir einige weitere Kommandos kennenlernen und uns […]