Der Hauptunterschied zwischen Assembler und anderen Programmiersprachen besteht darin, daß man exakt den Programmcode vor schreibt, der auch auf dem Prozessor läuft. Dies ist der Schlüssel zur größtmöglichsten Optimierung des Programmcodes. Desweiteren gibt es keine Programmiersprache, die dem Programmierer derart viele Freiheiten gibt. Andererseits ist es aber eine harte Tatsache, daß Assembler für jede Maschine anders aussieht. Wenn man Assemblercode auf anderen Prozessoren verwenden will, muß man ihn entweder weitgehend neu schreiben.
Viele Leute behaupten, das Assembler veraltet ist weil er schwer zu handhaben ist und moderne Compiler schnelleren Code als Assemblerprogrammierer erzeugen können.
Nun, es ist nicht so einfach, einen guten nicht-x86-RISC-Compiler zu übertreffen. Aber mit genügend Wissen und Erfahrung kann man meist noch etwas mehr herausholen. Die meisten sind aber an der Programmierung für die x86-Prozessoren interessiert. Das x86-Design ist wesentlich komplizierter als das anderer Prozessoren. Viele Register haben einen speziellen Verwendungszweck, es gibt nur wenige Universalregister (nur 7 statt 32 oder mehr) und es gibt die Möglichkeit, komplexe Adressen anzugeben. Und zudem können die Register auch teilweise angesprochen werden, ohne den Rest des Registerinhalts zu verändern. Dies ist schwer in einem Compiler handzuhaben, aber relativ einfach für einen guten Assemblerprogrammierer.
Und Assembler ist nicht unbedingt schwieriger als andere Programmiersprachen. Wenn man das Grundgerüst eines Programmes (Einsprungpunkt, Programmende, Daten- und Codebereiche) fertig hat, ist es ebenso komfortabel zu programmieren wie andere Sprachen. Natürlich ist es aufwendiger, komplizierte Berechnungen in Assembler zu schreiben, aber dafür sind Stringmanipulationen einfacher handhabbar. Und man wird nicht mit unverständlichen Fehlermeldungen aufgrund falscher Definitionen und einer lästigen Typüberprüfung konfrontiert.
Und, zumindest in der x86-Welt, ist Assembler die Programmiersprache, die am aktuellsten ist. Neue Instruktionen wie MMX, SSE und 3DNow! lassen sich nur in Assembler effektiv verwenden. Viele andere Programmiersprachen unterstützen diese nicht einmal.
DOS stellt seine Dienste über Interrupts zur Verfügung. Die Parameter und Rückgabewerte werden dabei über die Register ausgetauscht. Dies ist neben dem unter DOS unbeschränktem Hardwarezugriff der Hauptgrund, weshalb DOS und Assembler eine ideale Kombination ist. Um die DOS-Interruptfunktionen nutzen zu können genügt eine gute Dokumentation wie Ralph Browns Interrupt List. Mit Hilfe von DPMI läßt sich auch unter DOS leicht 32Bit Code erstellen.
Windows-Programme, die ausschließlich in Assembler geschrieben wurden sind recht selten. Es ist vergleichbar mit der Programmierung in reinem C ohne die Verwendung von Hilfsbibliotheken. Die meisten Betriebsystemfunktionen sind über DLLs erreichbar und werden per NEAR CALL aufgerufen. Die Parameterübergabe erfolgt über den Stack, der Rückgabewert wird in EAX geliefert. Um diese Funktionen nutzen zu können benötigt man entweder passende Importbibliotheken oder einen Assembler/Linker, der die benötigten Funktionen automatisch importieren kann. Für die wichtigsten Funktionen werden passende LIBs meist mit dem Assembler mitgeliefert, weitere kann man mit Programmen wie IMPLIB erstellen.
Achtung: Unter Windows sind zwei Objektformate verbreitet: Das OBJ-Format, das vor allem von Borland verwendet und das COFF-Format, welches von Microsoft verwendet wird. Wer seine Objektdateien mit anderen Objektdateien verbinden will besorgt sich am besten einen Assembler, der beide Arten generieren kann wie z.B. NASM.
Linux bietet, ebenso wie Win32, ein flaches 32 Bit Protected Mode Speichermodell. Einige Funktionen lassen sich direkt ansprechen, die meisten werden jedoch über standardmäßig verbreitete Bibliotheken erledigt. Für Objektdateien wird meist ELF oder das COFF-Format verwendet, allerdings nicht in einer Form, die zu den Win32-COFF-Dateien kompatibel ist.
Zuerst einmal sollte man alle wichtigen Programmstrukturen (Sprünge, Schleifen, Variablen, Datenfelder,...) kennen. Wenn man sein erstes Assemblerprogramm zum laufen gebracht hat ist es nicht mehr schwierig, darauf neue aufzubauen. Es gibt nichts in Assembler, daß nicht in ähnlicher Form auch in anderen Programmiersprachen existiert.
Zudem sollte man sich einige gute Tutorials und eine Befehlsreferenz besorgen. Man kann mit Assembler beginnen, indem man entweder kleine Programme schreibt oder indem man den Inline-Assembler einer Hochsprache nimmt. Im ersten Fall sieht man besser, wie Programme intern aufgebaut sind, im zweiten Fall hat man einen leichteren Einstieg in die Funktionsweise der einzelnen Instruktionen. Ein Debugger, mit dem man die Abarbeitung des eigenen Programms und dessen Auswirkung auf Register und den Speicher in Echtzeit verfolgen kann ist für das Verständnis ebenfalls sehr hilfreich (zur Fehlersuche taugen sie aber nur bedingt).
Zu guter Letzt braucht man natürlich auch einen Assembler und dazugehörige Hilfsprogramme. Die aktuelle TASM-Version ist käuflich, NASM und MASM gibt es dagegen kostenlos. Für TASM oder MASM gibt es am meisten Beispiele, aber NASM ist dagegen die bessere Wahl, wenn man andere Betriebsysteme unterstützen will.
Es ist hilfreich, zu versuchen, den Code anderer nachzuvollziehen. Es gibt kaum etwas, bei dem man schneller Assembler zu lesen und verstehen lernt.