Wichtige Makros zum Assembler Hypra-Ass
Der beschränkte Befehlssatz des 6502-Prozessors macht Maschinenprogramme unübersichtlich und fehleranfällig. Wir zeigen Ihnen hier, wie sich mit Hypra-Ass der Maschinen-Befehlssatz durch Makros erweitern läßt. Aber nicht nur das ist möglich, Sie können sich auch Ihre eigene, ganz persönliche Sprache basteln.
esonders dem Maschinensprache-Anfänger dürfte das Wort »Makro« gänzlich unbekannt sein. Denn weder das im C 64 implementierte noch irgendein anderes Basic kennt die Definition eines Makros. Das ist wahrscheinlich auch der Grund dafür, warum Makros nur selten angewendet werden. Sie spielen jedoch gerade beim 6502- beziehungsweise 6510-Prozessor eine wichtige Rolle. Durch Makros läßt sich nämlich der bescheiden ausgefallene Befehlssatz des Prozessors um wichtige Befehle erweitern. Es werden durch Makros prinzipiell keine neuen Maschinenbefehle geschaffen, sind aber Makros einmal definiert, lassen sie sich aufrufen wie ganz normale Maschinenbefehle. Was sind nun eigentlich Makros? Dies soll an einem kleinen Beispiel erklärt werden.
Angenommen, Sie möchten in einem Maschinenprogramm 20 verschiedene 16-Bit Adressen inkrementieren, dann müßte Ihr Programm zwangsläufig zwanzigmal folgende Befehlsfolge enthalten:
INC ADRESSE
BNE LBL
INC ADRESSE+1
LBL
Diese 20 Befehlsfolgen machen aber das Programm unübersichtlich und vor allen Dingen fehleranfällig. Genau so gut ließe sich auch ein Makro mit dem Namen »INCW (adresse)« für INCWord definieren, das dann anstelle der Befehlsfolgen 20mal im Quelltext erscheint. Das Makro selbst würde wie folgt aussehen:
10 -.MA INCW (ADRESSE)
20 - INC ADRESSE
30 - BNE LBL
40 - INC ADRESSE+l
50 -LBL
60 -.RT
Aufgerufen wird das Makro im Quelltext nun durch den neuen Befehl»… INCW (adresse)«.
Gefolgt von dem Makronamen und in Klammern den Übergabeparametern, diedurch Kommatagetrenntwerden, leitet der ».MA«-Pseudo-Opcode die Definition eines Makros ein. Dies geschieht in Zeile 10. Der Pseudo-Opcode ».RT« in Zeile 60 schließt die Definition des Makros ab. Alle im Makro stehenden Label sind lokal. Das heißt, daß dem Programm außerhalb des Makros die internen Label unbekannt sind. Würde dies nicht so sein, dann würde der Assembler den zweiten Makroaufruf mit der Fehlermeldung »label twice error« ahnden. Was macht der Assembler, wenn er auf einen Makroaufruf stößt? Er assembliert in den Objektcode, wie man das erzeugte Maschinenprogramm auch nennt, die Befehlsfolgen, die im Makro definiert wurden. Das heißt, daß letztendlich im erzeugten Maschinenprogramm wieder zwanzigmal, um bei dem Beispiel zu bleiben, die oben stehenden Befehlsfolgen auftauchen.
Im Listing sind die wichtigsten Makros aufgeführt. Neben den »Befehlserweiterungen« ist noch eine interessante Gruppe von Makros definiert worden, die die strukturierte Programmierung durch »Repeat…Until«- und »While…Endwhile«-Schleifen unterstützt. Zu beachten istjedoch, daß die Schleifen nichtverschachteltwerden dürfen. Schleifenkonstruktionen wie
REPEAT
.
.
REPEAT
.
.
UNTIL
UNTIL
sind veboten. Die einzelnen Makros haben folgende Wirkung:
TXY: Das Y-Register wird mit dem Inhalt des X-Registers geladen.
TYX: Das X-Register wird mit dem Inhalt des Y-Registers geladen.
PHX: Das X-Register wird auf dem Stack abgelegt
PHY: Das Y-Register wird auf dem Stack abgelegt
PLX: Das X-Register wird vom Stack geholt.
PLY: Das Y-Register wird vom Stack geholt.
Die folgenden vier Makros definieren einen Userstack, der an eine beliebige Stelle gelegt werden kann. Dazu muß im Hauptprogramm eine globale Variable mit dem Namen »USER« in der Zeropage angelegt werden. Anschließend muß in die Adresse, die die Variable repräsentiert, die Startadresse des Stacks geschrieben werden. Das könnte so aussehen:
10 -.GL USER = 3
20 - LDA #0 ;LO-BYTE STARTADRESSE USERSTACK
30 - STA USER
40 - LDA #$C0 ;HI-BYTE STARTADRESSE USERSTACK
50 - STA USER+1
Hier wurde ein Userstack angelegt, der bei Adresse $C000 beginnt. Der Stackpointer, als oder Zeiger, der auf die aktuelle Stackadresse zeigt, steht in der Zeropage in den Speicherzellen 3 und 4.
PUSHA: Der Inhalt des Akkumulators wird auf dem Userstack abgelegt.
PUSHAY: Der Inhalt des Akkumulators und der Inhalt des Y-Registers werden auf dem Userstack abgelegt.
PULLA: Der Akkumulator wird vom Userstack geholt.
PULLAY: Der Akkumulator und das Y-Register werden vom Userstack geholt.
ADW (adresse): 16-Bit Addition. Der Inhalt einer beliebigen Adresse wird zum Inhalt des Akkumulators (Low-Byte) und zum Inhalt des Y-Registers (High-Byte) addiert. Das Ergebnis steht anschließend im Akkumulator (Low-Byte) und im Y-Register (High-Byte).
ADMW (adr1,adr2,summe): 16-Bit Addition. Der Inhalt von adr1 und adr1 + 1 wird zum Inhalt der Adresse adr2 und adr2 +1 addiert und das Ergebnis in der Adresse summe und summe+1 abgelegt.
SBCW (adresse): 16-Bit Subtraktion. Der Inhalt von adresse und adresse+1 wird vom Inhalt des Akkumulators (Low-Byte) und vom Inhalt des Y-Registers (High-Byte) abgezogen. Das Ergebnis steht anschließend im Akkumulator (Low-Byte) und im Y-Register (High-Byte).
SBCMW(adr1,adr2,diff): 16-Bit Subtraktion. Vom Inhalt adr1 und adr1 + 1 wird der Inhalt von adr2 und adr2 + 1 abgezogen. Das Ergebnis wird in der Adresse diff und diff+1 abgelegt.
INCW(adresse): Der Inhalt von adresse und adresse+1 wird inkrementiert. Das Ergebnis steht in adresse und adresse+1. DECW (adresse): Der Inhalt von adresse und adresse+1 wird dekrementiert. Das Ergebnis steht in adresse und adresse+1.
LDAY (adresse): Der Akkumulator wird mit dem Inhalt von adresse und das Y-Register mit dem Inhalt von adresse+1 geladen.
STAY (adresse): Der Inhalt des Akkumulators wird nach adresse und der Inhalt des Y-Registers nach adresse+1 geschrieben.
LDAYI (wert): Der Akkumulator und das Y-Register wird mit »wert« unmittelbar geladen. Dabei steht das Low-Byte im Akkumulator und das High-Byte im Y-Register.
Die folgenden Makros unterstützen die strukturierte Programmierung.
REPEAT, EXITREPEAT, UNTIL (übergabe.bedingung): Die Schleife wird so lange fortgesetzt, bis die Speicherzelle »übergabe« den Wert »bedingung« enthält. Beispiel:
10 - LDX #255
20 - ... REPEAT
30 - DEX
40 - STX $FB
50 - ... UNTIL ($FB,0)
Das X-Register wird solange dekrementiert, bis es den Wert »0« enthält.
WHILE (übergabe,bedingung), EXITWHILE, ENDWHILE: Die Schleife wird solange fortgesetzt, bis der Inhalt der Speicherzelle »übergabe« gleich »bedingung« ist. Beispiel:
10 - LDX #255
20 - ... WHILE ($FB,0)
30 - DEX
40 - STX $FB
50 - ... ENDWHILE