Makros in Assembler
Makros sind ein mächtiges Werkzeug im Assembler, das die Wiederverwendung von Code erleichtert. Sie funktionieren ähnlich wie Funktionen in Hochsprachen, jedoch auf Quelltextebene – d. h. sie werden vom Assembler vor dem eigentlichen Assemblierungsvorgang expandiert.
Was sind Makros?
Ein Makro ist eine benannte Codevorlage, die beim Aufruf durch die entsprechende Makrodefinition ersetzt wird. Dadurch kann redundanter Code vermieden und die Lesbarkeit verbessert werden.
Makros werden mit den Direktiven .macro und .endm definiert.
Syntax
.macro name [Parameter[, Parameter...]]
; Anweisungen
.endm
Einfaches Beispiel
.macro save_regs reg1, reg2
str \reg1, [sp, #-16]!
str \reg2, [sp, #-16]!
.endm
Aufruf:
save_regs x0, x1
Parameter in Makros
Makros können benannte Parameter enthalten, die beim Aufruf ersetzt werden. Parameter werden mit einem umgekehrten Schrägstrich (`\`) verwendet, um sie im Makro zu referenzieren.
Beispiel mit drei Parametern
.macro add_and_store dest, src1, src2
add \dest, \src1, \src2
str \dest, [sp, #-16]!
.endm
Aufruf:
add_and_store x3, x1, x2
Makros mit Labels
Ein Problem bei Makros kann die Wiederverwendung von Labels sein. Labels innerhalb eines Makros sollten lokal sein, sonst kann es bei mehrfacher Verwendung zu Namenskonflikten kommen.
Lösung: Lokale Labels verwenden
Verwende numerische Labels oder eindeutige Namen mit \@:
.macro blink delay
1:
nop
subs \delay, \delay, #1
bne 1b
.endm
Oder mit \@ für eindeutige Labels:
.macro wait_loop delay
loop\@:
subs \delay, \delay, #1
bne loop\@
.endm
Verschachtelte Makros und Bedingungen
Makros dürfen auch andere Makros aufrufen oder einfache Bedingungen enthalten:
.macro print_val reg
mov x0, \reg
mov x8, #64 // write syscall
svc #0
.endm
Bedingte Makro-Ersetzungen in GAS (GNU Assembler)
GNU as erlaubt präprozessorartige Bedingungen mit folgenden Direktiven:
.if,.ifdef,.ifndef,.else,.elseif,.endif.irp,.irpc,.rept– für Wiederholungen.macro– mit bedingten Code-Blöcken innerhalb der Makrodefinition
Beispiel: Bedingung basierend auf Parameterwert
.macro load_value reg, val
.ifc \val, #0
mov \reg, xzr
.else
mov \reg, \val
.endif
.endm
Erklärung:
.ifc A, Bprüft, ob die Strings A und B gleich sind.ifncprüft, ob sie nicht gleich sind
Aufruf:
load_value x0, #0 // ergibt: mov x0, xzr
load_value x1, #42 // ergibt: mov x1, #42
Weitere Beispiele:
.macro set_direction pin, dir
.ifc \dir, out
// mache was für Ausgang
.elseifc \dir, in
// mache was für Eingang
.else
.error "Unbekannter Parameter bei set_direction"
.endif
.endm
Einschränkungen:
- Es handelt sich nicht um Laufzeitbedingungen, sondern Assemblerzeitbedingungen
- Es ist nur ein Stringvergleich möglich, keine numerischen Bedingungen wie
if \param > 5 - Du kannst
.if,.ifc,.ifdef,.ifne,.ifequ.a. verwenden
Übersicht der Direktiven
| Direktive | Bedeutung |
|---|---|
.if expr |
Wenn `expr` ≠ 0 |
.ifeq expr |
Wenn `expr` = 0 |
.ifne expr |
Wenn `expr` ≠ 0 |
.ifc A, B |
Wenn A = B (String) |
.ifnc A, B |
Wenn A ≠ B |
.ifdef symbol |
Wenn Symbol definiert |
.ifndef symbol |
Wenn nicht definiert |
.else, .endif, .elseif |
wie in C |
Tipps zur Makro-Programmierung
- Verwende eindeutige Labels innerhalb von Makros.
- Teste Makros isoliert, bevor du sie mehrfach einsetzt.
- Makros sind zur Compilezeit sichtbar – Fehler treten oft erst beim Expandieren auf.
- Nutze `\@` für eindeutige IDs (z. B. für Labels).
- Makros können nicht rekursiv aufgerufen werden (kein Call-Stack).
Nützliche Anwendungen von Makros
- Wiederholte Codeblöcke kapseln (z. B. Register sichern/wiederherstellen)
- Debug-Ausgaben standardisieren
- GPIO-Ansteuerung vereinfachen
- Speicherzugriffe vereinheitlichen
Einschränkungen
- Keine Rückgabewerte
- Keine Laufzeitparameterprüfung
- Nur syntaktische Ersetzung – kein Kontrollfluss wie bei echten Funktionen
Praktische Makros
.macro push1 register str \register,[sp,#-16]! .endm .macro push2 register1,register2 stp \register1,\register2,[sp,#-16]! .endm .macro pop1 register ldr \register,[sp],#16 .endm .macro pop2 register1,register2 ldp \register1,\register2,[sp],#16 .endm
Zusammenfassung
Makros im ARM64-Assembler helfen dir dabei, deine Programme kürzer, wartbarer und verständlicher zu schreiben. Richtig eingesetzt, erhöhen sie die Produktivität erheblich – besonders bei Hardwarezugriffen, Systemaufrufen und Wiederholungen.