Montag, 19. Mai 2008

Assembler - die Wahl der Mittel

Natürlich soll das Motto DRY (don't repeat yourself) auch beim Entwickeln in Assembler voll und ganz beherzigt werden - jedenfalls auf Ebene des Quelltextes möchte man als Entwickler mehrfach verwendete Codestrecken nur einmal hinschreiben (das ist einerseits der Entwicklertugend "Faulheit" zuzuschreiben, hat sich andererseits in der Wartung bewährt, denn duplikativer Quellcode wird auseinanderlaufen).

Für Assemblerentwicklungen stehen uns die untersten Ebenen der Wiederverwendbarkeit zur Verfügung:

- Includeprogramme
- Macros
- Unterprogramme

Was darüber hinausgeht, ist für Assembler von Übel! Auf OO-Features, wie sie in Borland's Turbo-Assembler (TASM) eingebaut sind, verzichten wir dankend, denn die IDE soll uns keinen - ausser den allersimpelsten - Maschinencode generieren. Wer OO programmieren will, möge in C++ weitermachen (oder die Objektorientierung von Hand dazuprogrammieren).

Die Frage "Macro oder Unterprogramm" ist m.E. nicht pauschal entscheidbar. Die Macroaufrufe vergrössern den erzeugten Maschinencode, da sie auf Maschinencode-Ebene dupliziert werden. Dafür wird zur Laufzeit kein Callstack aufgebaut, was das Macro schneller macht als das Unterprogramm. Eine kleine Codesequenz, die aber einige hundert Mal im Programm verwendet wird, wird man wohl eher als Macro definieren. Hingegen wäre eine mehrfach verwendete längere Codestrecke eher als Unterprogramm zu realisieren.

Für Macros wie für Unterprogramme ist es nützlich, zwei verschiedene Abstraktionsebenen einzuführen: Jedes Macro oder Unterprogramm wird auf seine Wiederverwendbarkeit in anderen Projekten geprüft. Je nachdem ob es spezifisch für das aktuelle Projekt ist oder übergreifend verwendbar, wird es in verschiedenen Dateien abgelegt.

In der Frage, welchen Assembler ich wählen möchte, MASM oder TASM (oder gar NASM), habe ich mich - nach eingehendem Studium der "Assembler Referenz" Müller [1] und des Assemblerbuchs von Rohde und Roming [2], für den MASM entschieden. Was mich letztlich überzeugte war die Möglichkeit, strukturierte Datentypen ähnlich wie in C definieren zu können (wohlgemerkt, hier hilft der Assembler, ohne extra Bytes im Maschinencode zu erzeugen, denn das Type-Casting von Speicherbereichen und das Ansprechen von Strukturkomponenten wird während der Assemblierung bereits aufgelöst). Das ist ein nützliches Feature, das den Assemblercode lesbarer macht - vor allem wenn es wie bei meinem Projekt zunächst um das Parsen von Binärdateien geht.

Bei der Berechnung von Planetenpositionen werden die Winkelfunktionen Sinus, Cosinus, Tangens massiv eingesetzt. Schön, dass es die Floating-Point-Unit gibt und Funktionen dieser Art direkt aufgerufen werden können! Um Bytes zu sparen, habe ich mich entschieden, die in der MASM-Distribution enthaltene FPULIB von Raymond Filiatreault, die für Rechenfunktionen wie Sinus, Cosinus etc. Wrapper-Routinen bereithält, *nicht* einzusetzen. Wenn ich schon die Möglichkeit habe, einen Sinus mit einem einzigen Assemblerbefehl aufzurufen, so möchte ich diese Option auch nutzen. Das Umrechnen von Bogenmass in Grad und vice versa kann ich selbst erledigen. Dennoch habe ich mir den ASM-Code der FPULIB angesehen und ihn für lehrreich befunden.


[1] Oliver Müller: Assembler-Referenz, Franzis Verlag, Ping 2000.
[2] Joachim Rohde und Marcus Roming: Assembler, mitp Verlag, Heidelberg 2006.

Keine Kommentare :