Assembler

Man könnte es als pubertäre Selbstüberschätzung werten, als ich schon in frühen Jugendjahren meinte:

Ach, Programmieren kann ich so gut, ich muss jetzt die schwerste Art Code zu schreiben lernen, nämlich Assembler.

Hmm … aus heutiger Sicht war das recht überheblich. Ich weiß nun, dass ich heute in Summe immer noch recht wenig (viel zu wenig) verstanden habe … und vor 20 Jahren war das noch viiieeel schlimmer.

Und trotzdem, bis heute profitiere ich aus den Experimenten und dem Wissen von damals …


In meiner kleinen Welt stand am Anfang QBASIC, gefolgt von Pascal. Und in beiden Fällen waren die Grenzen schnell erreicht. In QBASIC fehlte es vor allem an “ordentlichen” Dateisystem-Routinen und in Pascal an “schneller” Grafik.

Durch QuickBasic 4.5 bekam ich etwas von Interrupts mit, die über die Bibliothek QB.BI möglich gemacht wurden.
Und in Turbo Pascal 6 fand ich immer mehr Beispiele, wie diese Interrupts benutzt wurden um “geheime” Systemfunktionen auszulösen.

So ganz nebenbei ergab sich daraus das Wissen, dass der Prozessor mit ganz besonderen “Variablen” arbeitet, die als Register bekannt sind.
Eigentlich kann der Prozessor überhaupt nur mit Werten in seinen Registern rechnen und Daten aus Variablen im Speicher, die wir aus Programmiersprachen kennen, müssen stets in Register geladen oder aus diesen gespeichert werden.

… und das macht man in: Assembler.

Maschinensprache

Assembler ist eigentlich KEINE Programmiersprache wie C oder BASIC. Jeder Prozessortyp versteht eine bestimmte Menge an Operationscodes, die durch Bytesequenzen kodiert sind. Und da wir Menschen uns recht schwer tun, uns hunderte Bytecodes mit ihren Verknüpfungen zueinander zu merken, wurden menschen-lesbare Codes eingeführt (Mnemonics genannt), mit denen wir Maschinenbefehle als Textbefehle darstellen können.

… und das heißt dann Assembler, sieht aber für jede Plattform anders aus.

Eigentlich wurden Programmiersprache (sogenannte “Hochsprachen” oder “high-level languagues”) eingeführt, damit wir bequem mit Variablen und Funkionen arbeiten können und uns nicht um maschinen-nahe Details kümmern müssen.
Fakt ist aber, dass auch der beste Compiler der besten Hochsprache immer von einer genialen Programmieridee ausgestochen werden kann.
Das heißt also: Theoretisch kann man in Assembler “das perfekte Programm” schreiben, in anderen Sprachen sind sie nur “annähernd perfekt” oder “gut”.

Genau das war wohl auch mein Ehrgeiz, hier in die Tiefe zu gehen.

Und tatsächlich war es zu Zeiten von MS-DOS möglich und auch teils notwendig, Programmteile mit Assembler-Tricks zu optimieren. Denn die damaligen Compiler waren gezwungen mit wenig RAM und CPU-Kraft auszukommen und konnten keine Codeanalysen durchführen, die sie heute problemlos schaffen.

Und so wurde ein Funktionsaufruf einer Hochsprache oft sehr linear in Maschinenbefehle übersetzt.
Genau hier konnte man die Zugriffe auf Variablen im RAM reduzieren und mehr aus den Registern herausholen.

Das Z80 Problem

Nun, hätte mir damals schon jemand gesagt, dass mein PC einen X86 Prozessor beinhaltet und ich somit nur mit einem X86-Assembler darauf programmieren kann, wäre mir das folgende peinliche Missgeschick wohl erspart geblieben.

Selbstbewusst lief ich nämlich in den Bücherladen und wollte ein Assembler-Buch kaufen. Die dortige Angestellte fischte sogleich eines aus ihrer Liste, bestellte es und eine Woche später hielt ich es in meinen Händen.

Die ersten Seiten zeigten bereits, dass das Buch wohl etwas “alt” war … aber egal, schließlich hatte ich ja auch 15 Jahre alte Pascal Bücher gelesen und die Befehle dort funktionierten alle noch.
Doch die Befehle im Buch waren mir alle fremd und passten so überhaupt nicht zu jenen, die ich in Pascal schon kennen gelernt hatte.

Tja, das Buch behandelte den Z80 Prozessor, der so gut wie gar nichts mit den PC-Prozessoren gemein hatte.
Hmm, dumm gelaufen … also blieb meine Quelle für Assemblerwissen weiter nur die eine oder andere PC-Zeitschrift, wo mal ein bisschen ASM-Quellcode abgedruckt war.
Book about Z80 Assembler

Dennoch las ich das Buch und versuchte mich in die dort beschriebene Welt hineinzudenken. Und das war gold-richtig.

Gameboy-Emulationen und Pokemon

Ich wusste es damals noch nicht, doch einen Z80 Prozessor hatte ich schon einmal besessen. Er war in meinem Nintendo Gameboy verbaut, den ich Dummkopf kurz vor meinem Engagement für Assembler verkauft hatte um (heute unvorstellbar) eine legale Visual Basic Edition zu kaufen.

Und so lag das Buch und das Z80 Wissen nutzlos bis zum Jahr 2000 herum, als mir ein Schulfreund einen Download aus dem Internet aufschwatzte. Es war der “gute alte” Gameboy-Emulator NO$GMB, der auch auf meiner alten Gebraucht-Hardware flüssig lief.
NO$GMB Emulator

NO$GMB war nicht nur ein Emulator, sondern enthielt auch einen Assembler- Debugger, mit dem die Z80-Opcodes als Mnemonics dargestellt wurden … also genau die Befehle, die mein Z80 Buch beschrieb.

An dieser Stelle hätte es eigentlich passieren können, dass ich mich zum Z80-Assembler-Spezialist hätte entwickeln können … doch leider Fehlanzeige. Denn mein lieber Freund packte auch das damals beliebteste Gameboyspiel dem Emulator bei, und das war: Pokemon - Blaue Edition.

Damit dauerte es nicht lange, dass ich das Buch wieder bei Seite legte und eine bis heute andauernde Zuneigung zu den kleinen virtuellen Kreaturen aufbaute.

Perfektion

Eines habe ich aber sehr schnell zu respektieren gelernt: Die Macher von Gameboy-Spielen, die aus Platzgründen häufig direkt in Assembler arbeiten mussten, waren absolute Genies der Programmierung.
Sie schafften es, mit wenigen Maschinenbefehlen das hinzubekommen, wofür Compiler hunderte Bytes hätten generieren müssen.

Das beginnt bei Limitierungen, wie einer fehlenden Multiplikationseinheit auf der CPU und endet bei der Nutzung von einer Hand voll Bytes, die reichten um den ganzen Bildschirm mit Grafiken zu füllen.

Ja ich bewundere jene Vollblutentwickler, die sich in Maschinencodes hineindenken und “perfekte” Lösungen für ein Problem schaffen. Und es ist fast schade, dass die heutigen Leistungsdaten unserer Computer und der gebotene Komfort unserer Compiler uns diese alte ehrwürdige Kunst langsam vergessen lassen.

X86 Assembler

Nach dem Z80-Buch-Kauf folgte der Kauf meiner Turbo-Assembler 4.0 CD (die im Jahr 2000 auch schon etwas veraltet war). Damit hatte ich die folgenden Jahre immer wieder Spaß, brachte aber leider keine wirklich konkreten Programme hervor. Ein DOS-basierter Text-Editor war so ziemlich das höchste der Gefühle.

Turbo Assembler 4 CD

Sehr interessant waren allerdings viele auf der CD gesammelten Codebeispiele, für die unterschiedlichsten Probleme aus der “guten alten DOS-Welt”.
Doch leider wurde auch dieses Wissen mit dem Jahr 2000 mehr und mehr obsolet. Und sowohl unter Windows als auch unter Linux nahmen C und C++ mit ihren immer besser werdenen Compilern den Assembler-Gurus den Wind aus den Segeln.

Der 32-bit X86 Assembler begleitet mich jedoch bis heute weiter. Wenn es darum geht, den Callstack einer Exception-Behandlung nachzuvollziehen oder zu sehen, ob ein Template-Konstrukt besser ist als ein Makro, dann schalte ich für C++ den Assembler-Source-Generator ein, um zu sehen, wie der C++ Compiler meine Anweisungen übersetzt.

Da kommen oft interessante Ergebnisse zum Vorschein, wie dass Template-Code mal besser aber leider auch mal schlechter kompiliert wird, als mehrere makro-isierte statische Funktionen.

Am interessantesten sind aber Funktionen, wo der Compiler intelligente mathematische Zusammenhänge erkennt, die mir als Programmierer verborgen blieben. z.B.: wenn ich einen switch(...) Block nutze um unterschiedlich Return-Codes zurückzugeben und der Compiler das in 3 oder 4 Rechenzeilen übersetzt, wo der “geswitchte” Code ohne Vergleiche in die Returncodes umgerechnet wird. (Und damit meine ich nicht nur simple Additionen und Multiplikationen).

Fazit

Assembler ist und bleibt für die höchste Disziplin in der Programmierung.
Wirtschaftlich relevant ist sie heute leider nicht mehr. Die einzige Branche, die sie noch aktiv nutzt, sind die Compilerbauer selbst, gefolgt von einigen wenigen Mikrocontroller-Puristen und gelegentlich mal bei einem Treiber.

Hier sehne ich mich doch etwas nach der Zeit, wo alles noch “schlechter” war, weil man dann mit Assemblerwissen ein kleiner Wunderheiler sein konnte.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!