Assembler Code ist ein in Assemblersprache (ASM) geschriebener Code, der von einem Prozessor fast direkt ausgeführt werden kann.
ASM ist eine eigene Programmiersprache, die sehr niedrig ist. Niedrig bedeutet, dass sie nah an Maschinencode, den der Prozessor tatsächlich ausführt, gelegen ist.
Assemblys müssen nicht erst, wie z.B. C-Programme, kompiliert werden! Sie werden nur Assembliert, es ist nur eine Übersetzung von ASM zu Maschinensprache.
Daher kann jedes Programm, welches kompiliert vorliegt, in Assembler-Code umgewandelt werden.
ASM ist somit (fast) das niedrigste Level, auf der ein Prozessor programmiert werden kann. Warum das so hilfreich ist?
Man kann Programme direkt für eine CPU „maßgeschneidert“ schreiben. Das ist jedoch recht selten, viel häufiger wird der umgekehrte Weg genutzt:
Wir können jedes beliebige Programm in Assembler-Code übersetzen, und somit analysieren. Das hilft ungemein beim Reverse Engeneering, beim Analysieren von Viren und Würmern, aber auch beim Schreiben von Viren, Würmern und Exploits. (Wer daran interessiert ist: Ich werde nach der DCPU-Reihe auch eine Exploit-Reihe anfangen)
Jedoch können unterschiedliche Prozessoren auch unterschiedliche Maschinenbefehle haben, wodurch ein Assembly-Programm nicht auf jedem Prozessor läuft. Man Programmiert also immer für bestimmte Maschinen. (Nicht wie beispielsweise in Java, wo der Programmcode erst in Bytecode umgewandelt, und erst beim Ausführen für die jeweilige CPU Kompiliert und Assembliert wird.)
Die wesentlichen Opcodes der DCPU-16 in 0x10c stehen schon fest, sie sind einsehbar unter http://0x10c.net/doc/dcpu-16.txt
Es gibt 16 Opcodes. Jeder Opcode ist durch eine Zahl identifizierbar.
Wird dem Prozessor die Zahl 2 (0x2) übermittelt, weiß er, er soll eine Addition durchführen.
Die nötigen Schritte dazu übernimmt der Prozessor nun selbst, er erledigt seine Aufgabe.
Ich will nicht zu lange herum reden, wir fangen sofort mit einem Programm für die DCPU-16 an.
Da die meisten Spezifikationen schon öffentlich bekannt sind, sind auch schon einige Emulatoren erhältlich, auf denen wir Programme testen können.
Für den Anfang empfehle ich dcpubin.com
Wenn wir die Seite aufrufen, sehen wir schon einige Sachen im rechten Textfeld:
Register A-J
PC, SP, O(V)
Die Step-Anzahl
Und den RAM
In den Registern A-J können Werte gespeichert werden. A-J sind Speicher in der CPU selbst, nicht im RAM. Jedes Register kann 16 Bits Daten speichern.
PC ist der ProgramCounter
SP der StackPointer
O gibt an, ob bei einer Operation ein Overflow entstanden ist
Wir müssen noch nicht alles davon verstehen, wir schreiben einfach blind drauf los:
SET A, 10 SET B, 20 SET [0x2000], 3 MUL A, [0x2000] ADD B, A
Assemblieren wir dies nun mit dem Button „ASSEMBLE“ über dem Eingabefeld, erhalten wir diesen Output:
a801 0000 SET A 10 d011 0001 SET B 20 8de1 0002 SET [0x2000] 3 2000 7804 0004 MUL A [0x2000] 2000 0012 0006 ADD B A
Dies ist das gleiche wie unser Assembly Code oben, nur in Opcode, also Zahlenform.
Drücken wir nun LOAD, wird unser Code in den RAM geladen:
= RAM: = 0000: [a801] d011 8de1 2000 7804 2000 0012 0000
Das sind unsere Befehle. Wir können nun schrittweise durchgehen (STEP), oder das komplette Programm durchlaufen lassen. Wir lassen es erst einmal komplett durchlaufen (RUN), und analysieren dann das Resultat:
= REGISTERS: = A: 001e B: 0032 C: 0000 X: 0000 Y: 0000 Z: 0000 I: 0000 J: 0000 PC: [0007] SP: *0000* OV: 0000 STEP: 5 = RAM: = 0000: *a801* d011 8de1 2000 7804 2000 0012 [0000] 2000: 0003 0000 0000 0000 0000 0000 0000 0000
Zunächst analysieren wir die Register:
In A haben wir den Wert 0x1e, in B 0x32. Warum?
SET A, 10 weist dem Speicher in Register A den Wert 10d zu. Also 0xA
SET B, 20 bewirkt dass der Wert an Stelle B 0x14 wird (20d).
SET [0x2000], 3 verfährt auch nach dem Prinzip. Nur wird hier kein Register genutzt, sondern eine Speicheradresse. [0x2000] Steht für den Wert des Speichers an der Stelle 0x2000. Zu sehen ist dies im RAM: „2000: 0003“ – Hier sieht man, dass im Block 0x2000 der wert 0x0003 gespeichert wurde.
MUL A, [0x2000] Multipliziert den Wert der Stelle A mit dem Wert der Stelle 0x2000, also 0xA(=10d) * 0x3(=3d). Das Ergebnis, 1e(=30d) wird an der Stelle A gespeichert.
ADD B, A Addiert die Werte an den Stellen B 0x14(=20d) und A 0x1e(=30d), und Speichert das Ergebnis 0x32(=50d) in B.
Wir können den Emulator, Also CPU und RAM nun leeren (RESET), das Programm laden (LOAD), und Schrittweise durch das Programm gehen (STEP):
STEP | PC | A | B | [0x2000] |
LOAD | 0x0000 | 0x0000 | 0x0000 | 0x0000 |
0 | 0x0000 | 0x000A | 0x0000 | 0x0000 |
1 | 0x0001 | 0x000A | 0x0014 | 0x0000 |
2 | 0x0004 | 0x000A | 0x0014 | 0x0003 |
3 | 0x0006 | 0x001e | 0x0014 | 0x0003 |
4 | 0x0007 | 0x001e | 0x0032 | 0x0003 |
Wir sehen nun auch, STEP wird bei jedem Schritt um 1 Erhöht (logisch), der PC zeigt immer auf die Adresse des Befehls, der als nächstes ausgeführt wird.
Dies war ein sehr einfaches, primitives Programm. Linearer Ablauf, einen Nutzen hat dieses Programm nicht wirklich… Daher beschäftigen wir uns im nächsten Artikel mit Schleifen, Konditionen und erweiterter ASM Programmierung.
Bis dann könnt ihr ja schon etwas üben und auf dcpubin.com herumspielen.
Ihr könnt übrigens auch das Programm aus dem Artikel aufrufen.
MfG
Damon