1
0
mirror of https://github.com/fachat/xa65.git synced 2024-11-19 20:31:03 +00:00
xa65/xa/doc/xa-de.txt
2011-12-16 23:04:51 +01:00

395 lines
14 KiB
Plaintext
Raw Blame History

----------------------------------------------------------------------------
XA 2.1.0
65(c)02 Cross-Assembler
von Andre Fachat
----------------------------------------------------------------------------
* Block-Struktur (Versteckte Label)
* Sehr schnell durch hashing
* C-ähnlicher Preprozessor
----------------------------------------------------------------------------
1. Was ist das überhaupt und Geschichte
2. Aufruf und Features
3. 6502 Assembler
4. Pseudo-Opcodes, Block-Struktur und Gültigkeitsbereich von Labels
5. Preprozessor
----------------------------------------------------------------------------
1. Was ist das überhaupt und Geschichte
----------------------------------------
Mit dem Cross-Assembler können auf einem Rechner Programme für einen
anderen Rechner(typ) erstellt werden. Die Programmdateien müssen dann
nur noch auf das Zielsystem übertragen werden. In diesem Fall handelt
es sich um einen 6502-Cross-Assembler. Der 6502 ist ein 8-Bit-Prozessor,
der ursprünglich von MOS Technologies entwickelt wurde. Er tut seine
Dienste u.a. im Apple II, Commodore PET, VC20 und C64 (in abgewandelter
Form) und vielen anderen.
Inzwischen gibt es ihn in vielen Varianten mit verschiedenen Zusätzen
und in verschiedenen Gehäusen. Gemeinsam ist allen der Befehlssatz,
der für den Assembler die Grundlage bildet. Die CMOS-Versionen bieten
allerdings Erweiterungen des Befehlssatzes.
Die Idee zu einem Cross-Assembler entstand, als ich mir einen 6502-Computer
selbst baute und der (ebenfalls selbstgeschriebene) Assembler auf dem C64
zu langsam wurde. Nachdem auch noch ein Atari ST bei mir rumstand, war
die Idee schnell in die Tat umgesetzt.
2. Aufruf und Features
-----------------------
Der Assembler besteht nur aus dem Programm "xa".
Der Assembler verarbeitet eine oder mehrere Quelldateien zu einer
Objektdatei, die direkt verwendet werden kann. Das Linken entfällt, da der
Aufwand zu groß und die Geschwindigkeit hoch genug ist, um die
'Libraries' im Quelltext einzubinden. Ca. 350kByte Quelltext werden in
1 Minute und 50 Sekunden zu einer 32kByte Objektdatei für ein EPROM
assembliert (naja, der 8MHz Atari war nicht schnell. Aber dafür ist der
Wert ziemlich gut. Mein 486DX4/100 braucht vielleicht 2 Sekunden)!
Als Ausgabedateien werden eine Objektdatei, eine Fehlerdatei und eine
Labeldatei geschrieben.
Der Aufruf lautet:
XA Quell1 [Quell2 ...] [-oObject] [-eFehler] [-lLabel] [-C] [-v] [-x]
Die Angabe der Objekt-, Fehler- und Labeldatei ist optional, ohne Angabe
wird die Endung der ersten Quelldatei auf 'obj', 'err' und 'lab' verändert,
wenn "-x" angegeben ist. Ansonsten wird "a.o65" als Ausgabedatei verwendet
und keine Fehler- und Labeldatei geschrieben.
Die Option -C erzeugt Fehlermeldungen bei CMOS-Befehlen.
Im Environment werden die Variablen XAOUTPUT und XAINPUT unterstützt.
Falls die Quell- und Include-Dateien nicht gefunden werden, werden die in
XAINPUT aufgeführten Pfade der Reihe nach durchgetestet. Falls XAOUTPUT
existiert, wird dieser Pfad als Pfad für .err, .lab und .obj-Dateien
benutzt. Die Komponenten des Pfades sind mit ',' getrennt.
Die Labeldatei enthält hinterher eine Liste aller Labels mit Block-Nummer
und Wert in dezimal in der Form: 'Label, 1,-1234' in lesbarem ASCII.
Die Fehlerdatei enthält die Version des Assemblers, Datum und Uhrzeit des
Assemblerlaufs, die Liste der Fehler, die Ausdrücke, die mit #echo
und #print im Preprozessor erzeugt werden und eine Statistik über die
benutzten Resourcen (Speicherplatz etc.).
Die Objektdatei wird nur durch die Quelldatei bestimmt, es wird kein Code
vorgesetzt oder angehängt.
Die Quelldatei muß im ASCII-Format vorliegen.
3. 6502 Assembler
------------------
Da dies kein 6502-Assemblerkurs werden soll, nur eine ganz kurze
Beschreibung. Unterstützt wird der Code für alle Standard-6502 sowie
der Code für die Rockwell 65C02-CPU. Der Prozessor hat drei Register,
über die die meisten Operationen laufen. Transferoperationen benötigen
deshalb immer einen Load- und einen Store-Befehl.
Zusätzlich gibt es das Statusregister und den Stackpointer sowie,
natürlich, den Programmzähler. Der Stack liegt immer im Bereich $100 und
$1ff (Hexadezimal mit vorangestelltem '$'), weshalb der Stackpointer ein
8-Bit-Register ist. Eine besondere Behandlung durch kürzere Befehle
erfährt die Zeropage ($0-$ff), bei deren Adresse das Hi-Byte Null ist.
Das Statusregister besitzt folgende Flags:
N = Negativ
O = Overflow
B = Break
D = Dezimal
I = Interrupt
Z = Zeroflag
C = Carry
Befehle:
LDA lade Akkumulator
LDX lade X-Register
LDY lade Y-Register
STA speichere Akkumulator
STX speichere X-Register
STY speichere Y-Register
STZ speichere NULL (*)
TAX Kopiere Akku nach X
TAY Kopiere Akku nach Y
TXA Kopiere X nach Akku
TYA Kopiere Y nach Akku
TSX Kopiere Stackpointer nach X
TXS Kopiere X nach Stackpointer
ADC Addiere zu Akku mit Übertrag (Carry) (D)
SBC Subtrahiere von Akku mit Carry (D)
AND Logisches Und mit Akku
ORA Logisches Oder mit Akku
EOR Exklusiv-Oder mit Akku
BIT Bit-Test: Z=A&M, N=M7, O=M6
ROL Rotiere Links Akku oder Speicher A=A*2+C, C=A7
ROR Rotiere Rechts A=A/2+C*127, C=A0
ASL Arithmetisches Linksschieben A=A*2
LSR Logisches Rechtsschieben A=A/2
INX Erhöhe X-Register um eins
INY Y
INC Erhöhe Akku oder Speicher um eins
DEX Erniedrige X-Register um eins
DEY Y
DEC Erniedrige Akku oder Speicher um eins
CMP Vergleiche mit Akku (Substraktion ohne Akku zu ver„ndern)
CPX Vergleiche mit X-Register
CPY Vergleiche mit Y-Register
BNE Verzweige falls nicht Null
BEQ Null
BMI Negativ
BPL Positiv
BVC Overflow Clear
BVS Overflow Set
BCS Carry Set
BCC Carry Clear
BRA Verzweige immer (*)
JMP Springe an Adresse
JSR Springe in Unterroutine, R<>cksprungadresse auf dem Stack
RTS Return from Subroutine
CLC Carry-Flag löschen
SEC setzen
CLD Dezimal-Flag löschen
SED setzen
CLI Interrupt freigeben
SEI sperren
CLV Overflow-Flag löschen
PHA Akku auf Stack legen
PHX XR (*)
PHY YR (*)
PHP Status
PLA Akku vom Stack holen
PLX XR (*)
PLY YR (*)
PLP Status
BRK Löst Interrupt mit gesetztem Break-Flag aus
RTI Return from Interrupt
NOP No Operation
TRB Test und Reset Speicher mit Akku (*)
BBR Branch on Bit Reset (*)
BBS Branch on Bit Set (*)
RMB Reset Memory Bit (*)
SMB Set Memory Bit (*)
Die mit (*) markierten Befehle sind CMOS-Befehle. Außerdem haben einige
der anderen Befehle zusätzliche Addressierungsarten. Die mit (D) markierten
Befehle arbeiten im Dezimal-Mode (Dezimal-Flag gesetzt) anders, nämlich
im BCD-Mode (eine Ziffer von 0-9 in 4 Bit).
Addressierungsarten:
-Immediate LDA #$12
-Absolute STA $1234
-Zeropage EOR $10
-Bit,ZP,REL BBR #7,$10,label
-Akku ASL
-Implied TAX
-(Indirect,x) LDA ($10,X)
-(Indirect),y STA ($3e),Y
-Zeropage,x CMP $12,X
-Absolut,x LDY $4356,x
-(Absolut,x) jmp (jumptabelle,x)
-Absolut,y ORA $2345,y
-Relative BNE irgendwohin
-(Indirect) jmp (<28>berVektor)
-Zeropage,y ldx $12,y
-Bit,Zeropage RMB #1,zeropage
Bei Adressierungsarten, die in der Zeropage und Absolut existieren, wird,
soweit möglich die Zeropage-Adressierung angewendet. Ein vorangestelltes
'!' erzwingt absolute Adressierung, auch bei einem Wert kleiner 256.
Als Wert oder Adresse können arithmetische Ausdrücke mit Hierarchie und
Klammerung verwendet werden. Der Assembler versteht folgende Operanden:
123 -Dezimal
$234 -Hexadezimal
&123 -Oktal
%010110 -Binär
* -Programmzähler
"A" -ASCII-Code
labelx -Label
-(lab1+1) -Ausdruck
Folgende Operatoren können benutzt werden:
+ -Addition 9
- -Subtraktion 9
* -Multiplikation 10
/ -Integer-Division 10
<< -Shift nach links 8
>> -Shift nach rechts 8
>=,=> -größer oder gleich 7
<=,=< -kleiner oder gleich 7
< -kleiner 7
> -größer 7
= -gleich 6
<>,>< -ungleich 6
&& -Logisches UND 2
|| -Logisches ODER 1
& -Bitweises UND 5
| -Bitweises ODER 3
^ -Bitweises Exklusiv-Oder 4
Die Operatoren mit der höheren Priorität werden zuerst bearbeitet.
Ein gültiger Ausdruck ist dann z.B.
LDA base+number*2,x
Bei Addressierungsarten, die nicht mit einer Klammer beginnen, darf
auch im ersten Ausdruck keine Klammer am Anfang stehen:
LDX (1+2)*2,y ; Falsch !
LDX 2*(1+2),y ; Richtig !
Vor einem Ausdruck kann ein unärer Operator stehen:
< bildet Lo-Byte des Wertes
> bildet Hi-Byte des Wertes
LDA #<adresse
Die Einzelnen Befehle werden durch ':' oder eine neue Zeile getrennt.
Hinter jedem Befehl kann, durch ';' abgetrennt ein Kommentar stehen.
Der Kommentar gilt bis zum nächsten Doppelpunkt oder bis zur nächsten
Zeile.
4. Pseudo-Opcodes, Block-Struktur und Gültigkeitsbereich von Labels
-------------------------------------------------------------------
Folgende Pseudo-Opcodes stehen noch zur Verfügung:
.byt wert1,wert2,wert3, ...
.word wert1,wert2, ...
.asc "text1","text2", ...
.dsb länge [,füllbyte]
*=
.(
.)
Hierbei sind '.byt' und '.asc' identisch und legen Daten Byteweise im
Speicher ab. '.word' legt Daten Wortweise (=2 Byte) im Speicher ab.
'.dsb' füllt einen Speicherbereich der Länge 'länge' mit dem Wert
'füllbyte' ab. Falls füllbyte nicht abgegeben ist, wird mit Null gefüllt.
Die folgenden Opcodes haben keine direkte Einwirkung auf die Objektdatei.
'*=' definiert den Programmzähler.
'.(' eröffnet einen neuen 'Block'. Alle Labels innerhalb eines solchen
Blocks sind lokal. Allerdings darf vorher kein Label gleichen Namens in
einem höheren Block definiert worden sein. '.)' schließt den Block wieder.
Die Maximale Blockschachtelungstiefe beträgt 16 Blocks.
Ein Label wird definiert dadurch, daß es kein Opcode ist:
label1 LDA #0 ; Position(=Programmzähler) des Opcodes
label2 =1234 ; direkte definition
label3 label4 label5 ; implizit Programmzähler, auch mehrere
; Labels werden definiert
label6 label7 = 3 ; label6 wird mit dem Wert des Programm-
; zählers gesetzt, label7 mit 3
label8: sta label2 ; Da Opcodes mit ':' getrennt werden,
; wird auch die übliche Schreibweise mit
; 'label:' erkannt.
Dabei werden Groß- und Kleinbuchstaben unterschieden.
Labels, die mit vorangestelltem '+' definiert werden, sind global (Block=0),
d.h. überall gültig. Mit vorangestellten '&' kann ein Label jeweils eine
Hierarchiestufe höher definiert werden als ohne '&'.
Mit vorangestelltem '-' kann ein Label umdefiniert werden:
-sysmem +=4 ; da gibts ==, +=, -=, *=, /=, &=, |=
-syszp =123
5. Preprozessor
---------------
Der Preprozessor ist stark an den Preprozessor der Sprache C angelehnt.
So sind die für C typischen /* */ -Kommentare möglich.
Ein Preprozessor-Befehl wird mit einem '#' am Beginn der Zeile eingeleitet.
#include "Dateiname" fügt die Datei 'Dateiname' an dieser Stelle in den
Quelltext ein. Beim Laden wird zuerst der Name
direkt gesucht, danach mit den Pfaden aus
XAINPUT.
#echo Kommentar Gibt Kommentar in der Fehlerdatei aus.
#print ausdruck Gibt Ausdruck direkt, nach Preprzessorbehandlung
und nach dem Ausrechnen aus.
#printdef DEFINIERT Gibt die Definition in der Fehlerdatei aus.
#define DEF Text Definiert DEF als Text, deshalb wird hinterher
immer DEF durch Text ersetzt
#ifdef DEF Der Quelltext bis zum folgenden #endif oder #else
wird nur assembliert, falls DEF vorher mit #define
definiert wurde.
#else else halt...
#endif Beendet #if..-Konstrukt. Nach jedem #if.. muž!
ein #endif stehen.
#ifndef DEF .... falls DEF nicht definiert wurde.
#if ausdruck .... falls ausdruck ungleich null ist.
#iflused label .... falls Label schon benutzt wurde.
#ifldef label .... falls Label schon definiert wurde.
Dabei beziehen sich #iflused und #ifldef auf Labels, nicht auf Preprozessor-
Definitionen! Damit l„žt sich z.B. eine Bibliotheksstruktur aufbauen:
#iflused label
#ifldef label
#echo label schon definiert, nicht aus Library
#else
label lda #0
....
#endif
#endif
Die #if-Schachtelungstiefe beträgt 15.
Mit #define können auch wie in C 'Funktionen' mit Parametern definiert
werden.
#define mult(a,b) ((a)*(b))
Literaturangaben
----------------
-"Das Maschinensprachebuch zum Commodore 64"
Lothar Englisch
Data Becker GmbH
-"Controller Products Data Book"
Rockwell International, Semiconductor Products Division
-"Programmieren in C"
Kernighan, Ritchie
Hanser Verlag