ACME

Aus C64-Wiki
Wechseln zu: Navigation, Suche
ACME
Screenshoot
Firma Smørbrød Software
Entwickler Marco Baye (marco at baye.de)
Verleger Der aktuelle Sourcecode
aktuelle Version 0.96.1 (Source)
Release 2003
Lizenz GNU General Public License
Plattform AmigaOS bzw. MorphOS, DOS, Linux/Unix,
RISC OS und Windows
Bedienung Icon tastatur.png
Sprache Sprache:englisch
Information Crossassembler

ACME ist ein kostenloser Crossassembler, der für mehrere Plattformen erhältlich ist. Das Programm ist unter der GNU General Public License erstellt und unterstützt 6502, 65c02 und 65816 Prozessoren. Er generiert auch einige Illegal-Opcodes des 6502.

Der Assembler kann mit dem Programm Relaunch64 von Daniel Lüdecke zusammen verwendet werden.

Ursprünglich wurde ACME unter RISC OS erstellt mit der Idee, dass es auch eine Version für den C64/C128 geben soll. Inzwischen gibt es zwar Portierungen auf viele Plattformen, aber immer noch keine C64-/C128-Version. Da der Sourcecode in Standard-C vorliegt, sind Portierungen leicht machbar. Insbesondere auf allen Linux-Varianten und anderen Unix-artigen Systemen ist ACME problemlos kompilierbar.

ACME hat einen Parser für mathematische Ausdrücke implementiert, der komplexe Berechnungen durchführen kann, z.B. um Sinustabellen zu erstellen.

Empfohlen wird die Endung ".a" als Dateierweiterung für ACME-Quelldateien. ACME kann mit unterschiedlichen Zeilenenden-Konventionen im Quellcode umgehen, akzeptiert nur-CR, nur-LF oder CRLF. Die Relaunch64-Umgebung bringt ein Icon mit, das mit dem Quelldateityp verbunden werden kann.

Alle Schlüsselwörter beschränken sich auf 7-Bit-ASCII, aber für Namen eines Labels sind auch 8-Bit-Zeichen erlaubt, was bei Quellcodedateien verschiedener Plattformen eigenartig aussehen kann.

Inhaltsverzeichnis



[Bearbeiten] ACME_Lib

Die Dateien im ACME_Lib-Verzeichnis können mithilfe der Pseudo-Befehle "!source" oder "!binary" in den Quellcode eingebunden werden.

  • Dateinamen im Format "..." werden im aktuellen Verzeichnis, wo ACME aufgerufen wird, gesucht.
  • Dateinamen im Format <...> werden in einem speziellen Library-Verzeichnis gesucht. Um dieses Verzeichnis zu finden, muss auf den meisten Plattformen die Umgebungsvariable "ACME" richtig gesetzt werden. Einige Plattformen, z.B. RISC OS und AmigaOS, kommen ohne Umgebungsvariable aus.

Die Dateien der Library waren früher nicht immer beim Download dabei und konnten separat unter ACME_Lib2.zip heruntergeladen werden. Inzwischen ist die Library im Source-Download enthalten. Die Library-Dateien sind public domain.

[Bearbeiten] Syntax Highlighting für ACME

Es gibt Ergänzungen für die Editoren Joe, Ultraedit und gEdit und für eine LaTex-Umgebung:

[Bearbeiten] Cross-Compiling für den ACME

Windows


Linux und gedit

[Bearbeiten] Quick-Referenz

Ein kleines Beispiel, wie Programme unter ACME aussehen und wie sie übersetzt werden:

		!to "tiny.o", cbm	; Setzen der Ausgabedatei und des Formats
; Mit diesem Pseudo-Befehl wird der Dateiname und das Format festgelegt.
; Das kann auch über die Kommandozeilenoptionen "-o" und "-f" gemacht
; werden.

		*= $c000		; Setzen des Programmzählers
; Das geht auch mit der Kommandozeilenoption "--setpc".

		basout = $ffd2		; explizite, globale Label-Definition
; Damit ist "basout" als globales Label mit dem Wert $FFD2 definiert.

		; Eine Stringausgabeschleife:
		ldx #0
		beq +			; Schleifenstart
; '+' ist ein anonymes Vorwärtslabel. Andere sind "++", "+++", etc.
; Es kann wie ein jedes andere Label verwendet werden, aber verweist
; immer auf die nächste vorkommende Definition. Das erspart ein Ausdenken von Namen
; für unwichtige Label. Der Wert ist noch nicht gleich definiert und ACME setzt diesen
; erst beim zweiten Durchgang.

-		jsr basout		; Zeichen ausgeben
; '-' ist ein anonymes Rückwärtslabel. Andere sind "--", "---", etc.
; Es kann wie ein jedes andere Label verwendet werden, aber verweist
; immer auf die zuletzt vorkommende Definition. Das erspart ein Ausdenken von Namen
; für unwichtige Label. In dieser Zeile wird der Wert des Labels auf den des aktuellen
; Programmzählers gesetzt.

		inx			; erhöhe Zähler
+		lda .string,x		; hole Zeichen
; In dieser Zeile wird "+" auf den aktuellen Programmzähler gesetzt.
; ".string" ist ein lokales Label (weil es mit einem "." beginnt) und
; wird beim zweiten Durchgang von ACME ermittelt.

		bne -			; Prüfung, ob die Endekennung (=0) erreicht
; Hier ist die letzte Definition von "-" referenziert.

		rts
.string		!pet "Simples Beispiel", 13, 0
; Nun ist das Label ".string" auf den aktuellen Programmzähler gesetzt und
; alle Label sind definiert. Nach dem zweiten Durchgang von ACME wird die
; Binärdatei gespeichert. Der "!pet" Pseudo-Opcode speichert den String
; PetSCII-kodiert im Speicher als Byte-Werte. 

Die Datei sollte als "tiny.a" gespeichert werden und das Übersetzten geht dann mit:

acme tiny.a

ACME wird daraufhin die Datei einlesen und etwaige Fehler bei der Verarbeitung anzeigen. Falls kein Fehler auftritt, ist anschließend die Datei "tiny.o" vorhanden.

Nach der Erstellung kann die Datei auf einem C64 übertragen und laufen gelassen werden:

LOAD "tiny.o",8,1
SYS 49152

[Bearbeiten] Pseudo Opcodes

[Bearbeiten] Dateisteuerung

!to
Aufruf !to DATEINAME, DATEIFORMAT
Zweck Definiert den Namen der Ausgabedatei und den dazugehörigen Typ.
Ohne Angabe wird die Quelldatei von ACME zwar vollständig verarbeitet, aber keine Ausgabedatei erzeugt.
Dies wäre nur für eine Syntaxüberprüfung und Fehlersuche nützlich.
Über die Kommandozeilenoptionen "--outfile" und "--format" kann man die Angaben ebenfalls machen, was eine Verwendung in Makefiles erleichtert.
Parameter DATEINAME: Eine Datei in "..."-Zeichen.
DATEIFORMAT: Name des Formats. Werte sind:
cbm mit Ladeadresse (Commodore-Format)
plain ohne Ladeadresse.
Fehlt das DATEIFORMAT, gibt ACME eine Warnung aus und
nimmt cbm an. (Überschreibbar mit "--format"):
Beispiele !to "eprom.p", plain ; don't add a load address
!to "demo.o", cbm ; add c64-style load address
!source !src
Aufruf !source DATEINAME
Zweck Assembliert einen anderen Quellcode aus einer separaten Datei. ACME macht danach im Text weiter.
Parameter DATEINAME: In der Schreibweise "..." wird es aus dem aktuellen Verzeichnis genommen
und mit <...> aus dem LIB-Verzeichnis.
Andere Schreibweise !src
Beispiele !source <6502/std.a> ; Read library file
!src "Macros.a" ; Read file from current dir
!binary !bin
Aufruf !binary DATEINAME[, [GRÖSSE] [, [SKIP]]]
Zweck Fügt eine Binärdatei direkt in die Ausgabedatei ein.
Parameter DATEINAME: Dateiangabe in ".." wird aus dem aktuellen Verzeichnis geladen mit
<...> aus dem LIB-Verzeichnis.
GRÖSSE: Ein Ausdruck, den der Parser versteht, aber beim ersten Durchlauf berechenbar sein muss.
Ist GRÖSSE angeben, wird nur bis zum Erreichen von GRÖSSE geladen.
Ist die Datei kleiner, wird der Rest entsprechend aufgefüllt.
SKIP: Offset, ab der die Daten aus der Datei geladen werden.
Andere Schreibweise !bin
Beispiele !binary <Own/menudata.b> ; insert library file
!bin "asc2pet.b", 256, 2 ; insert 256 bytes
; from file offset 2.
!bin "table", 2, 9 ; insert 2 bytes from offset 9
!bin "list",, 9 ; insert from offset 9 to EOF

[Bearbeiten] Segmente

*=
Aufruf *= EXPRESSION [, MODIFIER]
Zweck Setzt den Programmzähler zu einem Wert und startet eine neues Segment.

Der Wert muss einmal gesetzt werden (geht auch mit Hilfe der Option "--setpc" in der Kommandozeile). Wenn Segmente überlappen, wird eine Warnung ausgegeben.

Parameter EXPRESSION: Ein Ausdruck, den der Übersetzer versteht und der im ersten Durchgang berechnet werden kann.

MODIFIER: "invisible" und/oder "overlay". Mit diesen Schlüsselwörtern können die Überlappungs-Warnungen unterdrückt werden.

Beispiele
!to "TinyDemo", cbm	; define output file + format
*= $0801		; Start at C64 BASIC start
+basic_header		; Call program header macro
!src "main.a"		; include main program
*= $1000		; jump to new segment
!bin "music.b"		; load music to $1000
*= $8000		; jump to new segment
!bin "pic.b"		; load graphics to $8000
; After assembly, ACME will save everything from $0801
; up to the highest address written to. The resulting
; file will contain some big unused areas (zero'd),
; but demos will get compressed anyway... :)
!initmem
Aufruf !initmem EXPRESSION
Zweck Definiert "nicht geänderten" Speicher. ACME füllt den

Speicher mit diesem Wert für undefinierte Bereiche.
Sind Zwischenbereiche frei, werden diese so mit dem Wert gefüllt.

Parameter EXPRESSION: Ein im ersten Durchgang berechenbarer Wert.
Beispiele
!to "TinyDemo", cbm	; define output file + format
!initmem $ea		; Default memory content $ea.
*= $0801		; Start at C64 BASIC start
+basic_header		; Call macro to create program header
!src "main.a"		; include main program
*= $1000		; jump to new segment
!bin "music.b"		; load music to $1000
*= $8000		; jump to new segment
!bin "pic.b"		; load graphics to $8000
; This is the same example as before, but now the big
; unused areas will contain the value $ea instead of
; zero.


!initmem $ff	; Default memory content is now $ff.
; Useful if you want to store your code in an EPROM.

[Bearbeiten] Werte einfügen

!8 !by !byte
Aufruf !8 EXPRESSION [, EXPRESSION]*
Zweck Einfügen von 8-Bit-Werten
Parameter EXPRESSION: Jeder Ausdruck, den der Parser versteht.
Andere Schreibweise "!08", "!by", "!byte"
Beispiele !08 127, label, -128 ; Ausgabe einiger Werte
!by 14, $3d, %0110, &304, <*, "c"
!byte 3 - 4, label1 XOR label2, 2 ^ tz, (3+4)*7
!16 !wo !word
Aufruf !16 EXPRESSION [, EXPRESSION]*
Zweck Einfügen von 16-Bit-Werten
Parameter EXPRESSION: Jeder Ausdruck, den der Parser versteht.
Andere Schreibweise "!wo", "!word"
Beispiele !16 65535, label, -32768  ; Ausgabe einiger Werte
!wo 14, $4f35, %100101010010110, &36304, *, "c"
!word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
!24
Aufruf !24 EXPRESSION [, EXPRESSION]*
Zweck Einfügen von 24-Bit-Werten
Parameter EXPRESSION: Jeder Ausdruck, den der Parser versteht.
Beispiele !24 16777215, label, -8388608, 14, $6a4f35
!24 %10010110100101010010110, &47336304, *, "c"
!24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
!32
Aufruf !32 EXPRESSION [, EXPRESSION]*
Zweck Einfügen von 32-Bit-Werten
Parameter EXPRESSION: Jeder Ausdruck, den der Parser versteht.
Beispiele !32 $7fffffff, label, -$80000000, 14, $46a4f35
!32 %1001011010010101001011010010, &4733630435, *, "c"
!32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
!align
Aufruf !align UNDWERT, VERGLEICHSWERT [, FÜLLWERT]
Zweck Füllt den Speicher bis VERGLEICHSWERT erreicht ist. ACME
erzeugt FÜLLWERT bis der Programmzähler UND-verknüpft mit UNDWERT gleich dem VERGLEICHSWERT ist.
Parameter UNDWERT: Jeder Ausdruck, den der Parser verarbeitet, der aber auch schon beim ersten Durchgang berechenbar sein muss.
VERGLEICHSWERT: Jeder Ausdruck, den der Parser verarbeitet und bereits beim ersten Durchgang berechenbar sein muss.
FÜLLWERT: Jeder Ausdruck, den der Parser verarbeitet. Fehlt der Wert, wird der Default-Wert genommen (derzeit 234, das entspricht dem NOP-Befehl der 6502-CPU).
Beispiele ;Vermeidet den 6502-JMP()-Bug:
!align 1, 0 ; wartet auf gerade Adresse (Bit 0 = 0)

Label !word Pointer
; align code an einer Page-Grenze für eine
; Geschwindigkeitserhöhung beim Zugriff
!align 255, 0

!fill !fi
Aufruf !fill ANZAHL[, WERT]
Zweck Speicher mit einem Wert anfüllen
Parameter ANZAHL: Jeder Ausdruck, den der Parser versteht. Er muss beim ersten Durchgang berechenbar sein.

WERT: Jede Ausdruck, den der Parser versteht. Falls nicht gesetzt, wird der Defaultwert verwendet (derzeit 0).

Andere Schreibweise "!fi"
Beispiele !fi 256, $ff ; Reserviere 256 Bytes
!fill 2 ; Reserviere zwei Bytes

[Bearbeiten] Strings einfügen

!convtab !ct
Aufruf !convtab KEYWORD [ { BLOCK } ]
oder !convtab DATEINAME[ { BLOCK } ]
Zweck Eine Textübersetzungstabelle.
Parameter KEYWORD: Name der Übersetzungstabelle:
pet Konvertierung zu PetSCII
raw keine Konvertierung.
scr konvertiert zu C64 Bildschirmzeichen.
DATEINAME: Name der Datei mit Konvertierungstabelle - Schreibweise "..." lädt aus dem aktuellen Verzeichnis, <...> aus dem Lib-Verzeichnis. Die Datei muss extakt aus 256 Zeichen bestehen.
BLOCK: Ein Assemblerblock, für den diese Übersetzungstabelle gilt. Der Defaultwert ist raw.
Andere Schreibweise "!ct"
Beispiele !convtab raw
!text "Test" ; gibt aus $54 $65 $73 $74
!ct pet
!tx "Test" ; gibt aus $d4 $45 $53 $54
!ct scr {
!tx "Test" ; gibt aus $54 $05 $13 $14
!ct "my_own_table_file"
!tx "äöüßÄÖÜ" ; wasauchimmer ... :)
}
!tx "Test" ; gibt erneut aus $d4 $45 $53 $54
Tipp: Die Erstellung einer Tabelle von ACME erledigen lassen:
!to "asc2pet.ct", plain ; no load address
*=0 ; pc = table index
; first create "as-is" table
!for i, 0, 255 {!byte i}
; now exchange upper and lower case characters
*=65, overlay
!for i, 1, 26 {!byte *+128}
*=97, overlay
!for i, 1, 26 {!byte *-32}
Diese Tabelle mittels convtab benutzt werden, um nach
PetSCII zu konvertieren (ok, ACME kann das eigentlich schon von selbst). :-)
!Text !tx
Aufruf !text STRING_WERT[, STRING_WERT]*
Zweck Ausgabe eines Textes in der aktuellen Konvertierungstabelle.
Parameter STRING_WERT: Kann ein String in doppelten Anführungszeichen sein, oder ein Formelausdruck, den der Parser versteht.
Achtung: Ergebnisse von Berechnungen werden nicht konvertiert, Einzelzeichen schon.
Andere Schreibweise !tx
Beispiele !text "Loading...", Char_NewLine, "Filename:", 0
!tx "Offset character is ", offset-1+'a', 0
!pet
Aufruf !pet STRING_WERT[, STRING_WERT]*
Zweck Ausgabe eines Textes in der PetSCII Konvertierungstabelle (Klein-/Großbuchstaben des C64).
Parameter STRING_WERT: Kann ein String in doppelten Anführungszeichen sein, oder ein Ausdruck, den der Parser als Ausdruck versteht.
Achtung: Ergebnisse von Berechnungen werden nicht konvertiert, Einzelzeichen jedoch schon.
Beispiele !pet "Loading...", Char_NewLine, "Filename:", 0
!pet "Offset character is ", offset-1+'a', 0
!raw
Aufruf !raw STRING_WERT[, STRING_WERT]*
Zweck Ausgabe eines Textes ohne Konvertierung
Parameter STRING_WERT: Kann ein String in doppelten Anführungszeichen sein, oder ein Ausdruck, den der Parser als Ausdruck versteht.
Beispiele !raw "Loading...", Char_NewLine, "Filename:", 0
!raw "Offset character is ", offset-1+'a', 0
!scr
Aufruf !scr STRING_WERT[, STRING_WERT]*
Zweck Zum Darstellen von Text für C64 Bildschirm.
Parameter STRING_WERT: Kann ein String in doppelten Anführungszeichen sein, oder ein Ausdruck, den der Parser als Ausdruck versteht.
Achtung: Ergebnisse von Berechnungen werden nicht konvertiert, Einzelzeichen schon.
Beispiele !scr "Loading...", Char_NewLine, "Filename:", 0
!scr "Offset character is ", offset-1+'a', 0
!scrxor
Aufruf !scrxor XOR_WERT, STRING_WERT[, STRING_WERT]*
Zweck Benutzt die C64 Bildschirmkonvertierungstabelle und die XOR-Verknüpfung mit einem festen Wert (z.B. praktisch für die inverse Darstellung).
Parameter XOR_WERT: Jeder Ausdruck, den der Parser versteht.

STRING_WERT: Kann ein String in Anführungzeichen sein oder ein Ausdruck, den der Parser versteht.
Ergebnisse von Berechnungen werden nicht konvertiert und nicht XOR-verknüpft.
Einzelzeichen in Berechnungen werden konvertiert aber nicht XOR-verknüpft.

Beispiele !scrxor $80, "Loading..."
!scrxor $a0, "Offset char is ", (offset-1+'a') XOR $a0

[Bearbeiten] Symbole (Labels)

!zone
Aufruf !zone [TITLE] [ { BLOCK } ]
Zweck Wechselt zu einer neuen Zone mit eigenen, lokalen Symbolen. Zonen können verschachtelt oder auch hintereinander angeordnet sein.
Parameter TITEL: Kann aus Zeichen und Ziffern bestehen. Wird für nur Fehlermeldungen verwendet und meistens nicht angezeigt.

BLOCK: Ein Block von Assembleranweisungen. Falls kein Block vorhanden ist, wird die vorige Zone beendet und eine neue eröffnet. Wenn ein Block da ist, dann gilt die alte Zone danach weiter.

Andere Schreibweise !zn
Beispiele
.backgroundcolor = 0	; some local symbol
!zone File_IO		; new zone begins here
.backgroundcolor = 1	; so this is a different symbol
!zn LinkedList_Init
.backgroundcolor = 2
!zone LinkedList {	; start of nested zone
	; imagine some code here...
	!zone LinkedList_Init
	; imagine some more code here...
	!zone LinkedList_Body {
		; imagine yet some more code here...
		!zone LinkedList_SecondPart
		; imagine still some more code here...
	}
	!zone LinkedList_End
	; you know what to imagine here...
}
.backgroundcolor = 3	; => "Symbol already defined."
!sl
Aufruf !sl DATEINAME
Zweck Speichert die globalen Symbole in die gegebene Datei nach der Assemblierung. Diese Tabelle kann für eine weitere Assemblierung mit "!source" geladen werden.
Parameter DATEINAME: Eine Datei in "..." Anführungszeichen
Beispiele !sl "Symbols.a" ; produce symbol list after assembly

!sl "global" ; produce symbol list after assembly

[Bearbeiten] Code-Steuerung

!if
Aufruf !if BEDINGUNG{ BLOCK } [ else { BLOCK } ]
Zweck Bedingte Übersetzung. Falls die Bedingung wahr ist, dann wird der erste Block übersetzt, sonst der zweite, auf "else" folgende (sofern vorhanden).
Parameter BEDINGUNG: Jeder Formelausdruck, den der Parser als Wert akzeptiert. Dieser muss bereits beim ersten Durchgang berechenbar sein.
BLOCK: Ein Block von Assembler-Statements.
Beispiele
!text "Black", 0        ; Choose wording according to
!if country = uk {        ; content of "country" label.
        !text "Grey"
} else {
        !text "Gray"
}
!byte 0
!text "White", 0


; Insert debug commands if label "debug" is not zero:
!if debug { lda #"z":jsr char_output }
!ifdef / !ifndef
Aufruf !ifdef LABEL { BLOCK } [ else { BLOCK } ]

!ifdef LABEL STATEMENT
!ifndef LABEL { BLOCK } [ else { BLOCK } ]
!ifndef LABEL STATEMENT

Zweck Bedingte Assemblierung, abhängig davon, ob ein Label gesetzt ist oder nicht. Wenn das Label definiert ist, wird der erste Statement-Block verarbeitet. Wenn das Label nicht definiert ist, dann wird der zweite Block gelesen (sofern vorhanden). Bei "!ifndef" ("if not defined") mit entsprechend umgekehrter Bedeutung.
Dieser Pseudobefehl ist besonders nützlich, um das Einlesen von Bibliotheksdateien effizient zu gestalten.

Sollte in eigenen Dateien nur dann benutzt werden, wenn man wirklich weiß, was man tut. - Falsch eingesetzt provoziert man damit leicht Fehlermeldungen.

Parameter LABEL: Jeder gültige Label-Name.
BLOCK: Ein Block von Assemblerbefehlen
STATEMENT: Jeder Assemblerausdruck.
Beispiele
; this was taken straight from <6502/std.a>:
!ifdef Lib_6502_std_a !eof	; parse this file once
Lib_6502_std_a = 1
!for
aktueller Aufruf

(seit Version 0.94.12)

!for LABEL, START, END { BLOCK }
älterer Aufruf

(vor Version 0.94.12)

!for LABEL, END { BLOCK }
Zweck Schleifen in Assembler. Der Block der Assemblerbefehle wird mehrfach übersetzt, während LABEL von START bis ENDE gezählt wird. Flexiblere Schleifenstrukturen sind mit dem Pseudo-Opcode "!do" möglich.

Die ältere Syntax beginnt die Zählung immer mit dem Wert Eins. Seit Version 0.94.12 erzeugt diese Syntax eine Warnung.

Parameter LABEL: Jeder valide Labelname. Startet mit dem Wert START und zählt dann bis END.
START: Jede Berechnung, die der Parser umsetzen kann, aber der Wert muss beim ersten Durchgang berechenbar sein.
END: Jede Berechnung, die der Parser umsetzen kann, allerdings muss der Wert beim ersten Durchlauf berechenbar sein.
BLOCK: Ein Block von Assemblerbefehlen.
Es ist nicht möglich, durch die im Block verwendeten Befehle, die Anzahl der Schleifendurchläufe zu ändern.
Beispiele
 ; conversion table: integer to BCD
 nt2BCD      !for Outer, 0, 9 {
                 !for Inner, 0, 9 {
                        !byte (Outer << 4) OR Inner
			}
		}
		!fill 156, $ff	; values above 99 give 255 (invalid)


		; conversion table: BCD to integer
	BCD2int	!for Outer, 0, 9 {
			!for Inner, 0, 9 {
				!byte 10 * Outer + Inner
			}
			!fill 6, $ff	; invalid BCD values give 255
		}
		!fill 96, $ff		; invalid BCD values give 255
!set
Aufruf !set LABEL = VALUE
Zweck Weist einem Label einen neuen Wert zu. Dies wird dazu gebraucht, um Schleifenzähler zu setzen, wie z.B. für "!do". Ist nur dann für anderes zu benutzen, wenn man weiß, was man tut.
Parameter LABEL: Jeder gültige Labelname
VALUE: Jeder Formelausdruck, den der Übersetzer akzeptiert.
Beispiele Siehe "!do" unten.
!do
Aufruf !do [KEYWORD CONDITION] { BLOCK } [KEYWORD CONDITION]
Zweck Assemblerschleife: Der Block wird mehrfach übersetzt, in Abhängigkeit von einer oder mehreren Bedingungen. Die Bedingungen können am Anfang und/oder Ende des Blocks stehen und werden bei jedem Durchlauf ausgewertet.
Parameter KEYWORD: Angabe von "while" oder "until" (ohne Quotierung).
CONDITION: Jede Formelausdruck, die der Übersetzer versteht, aber er muss bei jedem Durchlauf berechnet werden können.
BLOCK:Ein Block von Assembleranweisungen.
Beispiele
; a loop with conditions at both start and end
!set a = 0			; init loop counter
!do while loop_flag = TRUE {
	lda #a
	sta label+a
	!set a = a + 1
} until a > 6
; a loop with a condition at the start !do while * < $c000 { nop }
; a loop with a condition at the end !do { !wo * + base } while * < base + 345
; a never ending loop - this will cause an error !do while 3 < 4 { nop } until 3 = 4
; an empty loop - this will hang ACME !do until 3 = 4 { } while 3 < 4
!endoffile
Aufruf !endoffile
Zweck Stoppt das Übersetzen der aktuellen Quelldatei. So kann man den verbleibenden Rest der Quelldatei einfach übergehen.
Andere Schreibweise !eof
Beispiele
rts	; some assembler mnemonic
!eof
Though this text isn't preceded by a semicolon, it is
treated as if it were a comment. In fact, ACME doesn't
even parse this anymore - the file gets closed when
"!eof" is reached.
!warn
Aufruf !warn STRING_VALUE
Zweck Zeigt eine Warnung beim Übersetzen an.
Parameter STRING_VALUE: Ein String in Quotierungzeichen.
Beispiele
!if * > $a000 {
       !warn "Program reached ROM area."
}
!error
Aufruf !error STRING_VALUE
Zweck Erzeugt einen Fehler und es wird keine Datei erzeugt.
Parameter STRING_VALUE: Ein String unter Quotierungszeichen stehend.
Beispiele
rts	; end of some function
start	!source "colors.a"
end	!if end-start > 256 {
	!error "Color strings exceed 256 chars!"
}
!serious
Aufruf !serious STRING_VALUE
Zweck Erzeugt einen schwerwiegenden Fehler und bricht die Übersetzung ab.
Parameter STRING_VALUE: Ein String unter Quotierungszeichen stehend.
Beispiele
!source "part1.a"	; sets part1_version
!source "part2.a"	; sets part2_version
!if part1_version != part2_version {
      !serious "part1.a and part2.a don't match!"
}

[Bearbeiten] Makros

!macro
Aufruf !macro TITLE [[~]LABEL [, [~]LABEL]*] { BLOCK }
Zweck Definiert ein Makro, mit dem ein Code-Muster angelegt wird, das erst später bei Aufruf, eventuell auch parametrisiert, den entsprechenden Code generiert.
Parameter TITLE: Der Makroname (es gelten gleiche Regeln, wie für ein Label)

Ist das erste Zeichen ein ("."), gilt es nur lokal bezogen auf die jeweilige umgebende Zone (d.h. der Name kann ohne Gefahr einer Kollision mit globalen Makrodefinitionen verwendet werden).
LABEL: Der Name des Parameters beim Aufruf.
Normalerweise sollten die Namen lokal mit einem "." beginnen, damit verschiedene Aufrufe verschiedene Werte haben können.
Wenn das Label mit "~" beginnt, wird der Wert als Referenz und nicht als Wert übergeben.
Eine Änderung ändert dann auch den globalen Label-Wert.
BLOCK: Block der Assemblerbefehle.

Beispiele
; far branch, as defined in <6502/std.a>                                   
!macro bne .target {
	beq * + 5
	jmp .target
}


; increase 16-bit counters                                                 
!macro dinc .target {
	inc .target
	bne +	; "bne * + 5" would not work in zp
	inc .target + 1
+
}

Anonyme Label-Referenzen dürfen auch in Makros verwendet werden (im Gegensatz zu manch anderem Assembler). Das liegt an der Implementierung der Makros, die sich bei ACME mehr an reale Funktionen anlehnt.

; load A and X                                                             
!macro ldax .target {
	lda .target
	ldx .target + 1
}


; store A and X                                                            
!macro stax .target {
	sta .target
	stx .target + 1
}


; use call-by-reference for return value                                   
!macro reserve ~.address, .amount {
	.address = external_pc
	!set external_pc = external_pc + .amount
}


; define a pixel row of a C64 hardware sprite                              
!macro SpriteLine .v {
	!by .v>>16, (.v>>8)&255, .v&255
}
+TITLE
Aufruf +TITLE [ARGUMENT [, ARGUMENT]*]
Zweck Aufruf eines Makros mit den passenden Argumenten.
Parameter TITEL: Der Name der bei der Definition vergeben wurde.

ARGUMENT: Ein Wert oder Formel, die der Übersetzer verarbeitet.
Entsprechend der der Definition mit "~" als Call-By-Reference.

Beispiele
inc label
bne mark	; "near" branch
inc label2
+bne mark2	; "far" branch                                        


inc $fa		; increase  8-bit counter                             
+dinc $fb	; increase 16-bit counter


ldy label	; get byte                                            
+ldax label2	; get two bytes


; using macro calls in a macro definition                              
!macro cp16 .source, .target {
	+ldax .source
	+stax .target
}


; use call-by-reference for return value                               
!set external_pc = $0400
+reserve ~.line_buffer, 80
+reserve ~.in_buffer, 256
+reserve ~.out_buffer, 256
+reserve ~.byte_var, 1


; define a C64 hardware sprite                                         
;            765432107654321076543210
+SpriteLine %........................
+SpriteLine %.#......................
+SpriteLine %.##.....................
+SpriteLine %.###....................
+SpriteLine %.####...................
+SpriteLine %.#####..................
+SpriteLine %.######.................
+SpriteLine %.#######................
+SpriteLine %.########...............
+SpriteLine %.#########..............
+SpriteLine %.########...............
+SpriteLine %.######.................
+SpriteLine %.######.................
+SpriteLine %.##..##.................
+SpriteLine %.#....##................
+SpriteLine %......##................
+SpriteLine %.......##...............
+SpriteLine %.......##...............
+SpriteLine %........##..............
+SpriteLine %........##..............
+SpriteLine %........................
!byte 0	; pad to 64-byte block

Seit Version 0.86 können verschieden Makros den gleichen Namen haben, solange die Parameterliste eine unterschiedliche Argumentenanzahl aufweist oder deren Typ unterschiedlich ist (Call-By-Value bzw. Call-By-Reference). Daher können die folgenden Makros gleichzeitig verwendet werden, ohne dass sie kollidieren:

!macro process_bytes b1,b2 {...}                                       
!macro process_bytes b1,b2,b3 {...}
!macro process_bytes b1,b2,~b3 {...}

[Bearbeiten] Versetzte Assemblierung

!pseudopc
Aufruf !pseudopc EXPRESSION [ { BLOCK } ]
Zweck Assembliert jenen Code, als wäre der Programmzähler (PC) auf den durch EXPRESSION vorgegeben Wert. Dieser Pseudo-PC-Wert gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block. Damit kann z.B. ein Code-Fragment erzeugt werden, das für einen anderen Adressbereich vorgesehen ist und erst zu einem späteren Zeitpunkt dorthin kopiert wird und erst dort lauffähig sein soll.

Mithilfe der Blocksyntax sind sogar Verschachtelungen möglich. Nach dem Ende des jeweiligen Blocks wird der zuvor aktive Pseudo-PC oder letztendlich der ursprüngliche PC wieder gültig.

Parameter EXPRESSION: Ein Ausdruck, der den neuen Pseudo-PC-Wert festlegt.

BLOCK: Ein Block von Assembleranweisungen.

Beispiele
*=$C000
casbuf = 828             ; Kassettenbuffer, für Code-Sequenz
tastbuf = $277           ; Tastaturpuffer
tbuffered = 198          ; Zeichen im Tastaturpuffer

install
    LDX #(routine_end-routine_start-1)
copyloop
    LDA routine_start,X
    STA casbuf,X
    DEX
    BPL copyloop
run
    JSR casbuf
    RTS

routine_start            ; Routine, die an anderer Stelle laufen soll
!pseudopc casbuf {
    LDX #0
.nextchar
    LDA .tcmd,X
    BEQ .leave           ; Endemarkierung
    STA tastbuf,X        ; Tastaturpuffer füllen
    INX
    BNE .nextchar
.leave
    STX tbuffered
    RTS
.tcmd
    !text ":run500",13,0 ; Kommando für den Tastaturpuffer
}
routine_end

[Bearbeiten] Unterstützung für verschiedene CPUs

!cpu 
Aufruf !cpu TYP [ { BLOCK } ]
Zweck Wählt jenen CPU-Typ aus, dessen Instruktionssatz verstanden und für den der Code erzeugt wird. Gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block.

Vorgabe ist 6502 oder jene Angabe, die mit Parameter --cpu beim ACME-Aufruf gemacht wurde.

Parameter TYP: CPU-Typ als eines der folgenden Schlüsselwörter:
  • 6502 Familie der 6502
  • 6510 Variante von 6502 inklusive der spezifischen illegalen Opcodes des 6510 (und Verwandten)
  • 65c02 Übermenge zu 6502, deckt Rockwell- und WDC-Varianten ab
  • 65816 Übermenge zu 65C02 für den 16-Bit-Ableger von WDC
  • c64dtv2 Übermenge zu 6510, erweitert um DTV2-Erweiterungen (BRA/SAC/SIR)

BLOCK: Ein Block von Assembleranweisungen.

Beispiele
!if cputype = $65c02 {                                          
    !cpu 65c02 {	; temporarily allow 65c02 stuff
        stz .todelete
    }
} else {
    pha
    lda #0
    sta .todelete
    pla
}
rts


!cpu 65816	; allow 65816 commands from here on            
!al
Aufruf !al [ { BLOCK } ]
Zweck Accumulator Long: Teilt dem Assembler mit, dass Akkumulator und Speicheroperationen 16-bittig sind und entsprechende Operanden im Code als 16-Bit-Werte auftreten. Gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block.
Parameter BLOCK: Ein Block von Assembleranweisungen.
Beispiele
 rep #$20
 !al
 LDA #1        ; takes 3 bytes (opcode + 16-bit immediate value, the accumulator is loaded with)!
!as
Aufruf !as [ { BLOCK } ]
Zweck Accumulator Short: Teilt dem Assembler mit, dass Akkumulator und Speicheroperationen 8-bittig sind und entsprechende Operanden im Code als 8-Bit-Werte auftreten. Gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block.
Parameter BLOCK: Ein Block von Assembleranweisungen.
Beispiele
 sep #$20
 !as
 LDA #1        ; takes 2 bytes (opcode + 8-bit immediate value, the accumulator is loaded with)!
!rl
Aufruf !rl [ { BLOCK } ]
Zweck Register Long: Teilt dem Assembler mit, dass Index-Register-Operationen 16-bittig sind und entsprechende Operanden im Code als 16-Bit-Werte auftreten. Gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block.
Parameter BLOCK: Ein Block von Assembleranweisungen.
Beispiele
 rep #$10
 !rl
 LDX #1        ; takes 3 bytes (opcode + 16-bit immediate value, register X is loaded with)!
!rs
Aufruf !rs [ { BLOCK } ]
Zweck Register Short: Teilt dem Assembler mit, dass Index-Register-Operationen 8-bittig sind und entsprechende Operanden im Code als 8-Bit-Werte auftreten. Gilt ab der aktuellen Stelle im Sourcecode oder für den folgenden Block.
Parameter BLOCK: Ein Block von Assembleranweisungen.
Beispiele
 sep #$10
 !rs
 LDX #1        ; takes 2 bytes (opcode + 8-bit immediate value, register X is loaded with)!

[Bearbeiten] Weblinks