Der DLX-Assembler DLXasm

DLXasm ist ein einfacher DLX-Assembler. Er sollte uns ursprünglich bei der Erstellung von Testprogrammen für unsere DLX-CPU behilflich sein. Aus dem kleinen Tool ist mittlerweile ein 3-Pass-Assembler geworden, der ein relativ komfortables Schreiben von DLX-Programmen ermöglicht.

DLXasm liest Quellcode in Mnemonic-Schreibweise ein und übersetzt diesen in für den DLX-Simulator lesbare Instruktionen. Diese Instruktionen werden in Form von 32-Bit Integerwerten in einer Haskell-Liste gespeichert. Ausgegeben wird also eine hs-Datei, die im DLX-Simulator direkt als Speicherinhalt eingelesen und weiterverarbeitet werden kann. Dazu wird es im fertigen DLX-Simulator eine Eingabemöglichkeit geben, mit der man ein mit DLXasm assembliertes Programm einlesen und auszuführen kann.

Nachfolgendes Beispiel zeigt eine Ausgabe von DLXasm. Es ist die assemblierte Version des Beispiel-Programms "bubble.as". Der Quelltext dazu befindet sich weiter unten.

program = [ 	5787655,
		6051847,
		605290496,
		264905,
		264267,
		1343574080,
		3671385,
		3409178,
		106505,
		(-257399),
		266377,
		1209337984,
		(-2357927),
		6279,
		270471,
		1343561920,
		(-1833639),
		8328,
		268424,
		605290496,
		(-2754),
		63,
		96,
		128,
		34,
		2344,
		0,
		333,
		(-34),
		1,
		444,
		10,
		1 ] :: Memory


Syntax:

Die Syntax der Mnemonic-Schreibweise ist die gleiche, wie sie im Hennessy-Patterson vorgestellt wird. Also der Form:

	ADDI R1, R2, #123	
	LHI  R4, #-1000
	LB   R30, 48(R0)	
	SW   -80(R20), R21
	NOP
	JAL  -1234	
	BNEZ R15, 20
Immediates werden mit einer vorausgehenden Raute # gekennzeichnet und Register mit einem R. Displacements erfordern die bekannte Klammerung.
Zusätzlich zu den DLX-Befehlen im Mnemonic-Format kann DLXasm noch mit einigen Assembleranweisungen umgehen:

  • Kommentare werden mit einem Semikolon ";" eingeleitet und erstrecken sich dann bis zum Ende der Zeile.

  • Es dürfen Sprungmarken definiert werden. Sie müssen als erstes Wort in der Zeile stehen und mit einem Doppelpunkt ":" enden. Das erste Zeichen muß ein Buchstabe sein, danach dürfen auch Ziffern vorkommen, also z.B. "Loop2Begin:". Soll ein Sprungmarkenname ('Label') als Operand innerhalb eine Sprungbefehls vorkommen, dann muß der Doppelpunkt weggelassen werden.
    Labels können maximal 20 Zeichen lang sein und es können maximal 100 Sprungmarken angelegt werden. Ich habe dies der Einfachheit halber so festgelegt, da mir diese Grenzen im Hinblick auf die geringe zu erwartete Komplexität der Programme für den DLX-Simulator für sinnvoll erschienen. Sollten irgendwann mal größere Wert benötigt werden, kann dies leicht in der Datei "DLXasm.y" geändert werden (siehe dazu die Preprozessor-Konstante 'MAXLABEL').

  • Es können - wie aus x86-Assemblern bekannt - mit der Assembleranweisung "dw" (DefineWord) Speicherzellen mit 32-Bit-Integerwerten vordefiniert werden.
DLXasm ist case-insensitive, d.h. es wird nicht zwischen Groß- und Kleinschreibung unterschieden. Dies gilt allerdings nicht für Sprungmarken! So sind z.B. "loop" und "Loop" zwei verschiedene Labels.
Desweiteren sind jegliche Abstände zwischen Befehlen, Kommata, Klammern und Operanden völlig egal.

Es folgt das Beispiel-Programm "bubble.as". Es veranschaulicht gut, welchen Quellcode der Assembler verarbeiten kann.

; Bubble Sort (mit gezielten Abbruch), sortiert das Int-Array von Adresse lo bis hi
; (C) Adam Sosnowski 2000

; Die Adresse des Arrayanfangs(=lo) steht an Adresse 88, die des Arrayendes(=hi) an Adresse 92
; Dort können die Arraygrenzen verändert werden, wenn andere Arrays sortiert werden sollen

; Persönliche Konvention:
; Im Programm werden Register 1-9 für Indizes und sonstige Variablen, Register 10-19 für Konstanten 
; und Register 20-29 für Flags/Boolvariablen bei Vergleichs- und Branchbefehlen verwendet

	lw	r10, 88(r0)	; Anfangsadresse des Arrays in R10 
	lw	r11, 92(r0)	; Endadresse des Arrays in R11 

	add	r20, r0, r0	; R20=0(=False)	: Flag zum gezielten Abbruch auf False setzen
	
	;äußere Schleife "for (R1=hi; R1>lo; --R1)"
	addi	r1, r11, #4	; R1=hi+4 : äußerer Index, zeigt auf das kleinste Element der geordneten 
				; 	    Teilliste, hier kontrollstrukturbedingte Korrektur um +1
AUSSEN: subi	r1, r1, #4	; R1=R1-4 : äußeren Index runterzählen 
	sgt	r21, r1, r10	; if (R1>lo) R21=1 else R21=0
	beqz	r21, ENDE	; if (R21==0) goto ENDE else mache weiter : wenn R1=lo, dann Array sortiert
	bnez	r20, ENDE	; if (R20!=0) goto ENDE : wenn R20==True, dann vorzeitiger Abbruch
	addi	r20, r0, #1	; R20=1(=True) : wenn R20 in der inneren Schleife True bleibt, 
				; 		 dann Array schon sortiert

	;innere Schleife "for (R2=lo; R2<R1; ++R2)"
	addi	r2, r10, #-4	; R2=lo-4 : innerer Index, fängt immer vom ersten Listenelement an zu zählen
				;	    auch hier kontrollstrukturbedingte Korrektur um -1	
INNEN:	addi	r2, r2, #4	; R2=R2+4
	slt	r21, r2, r1	; if (R2<R1) R21=1 else R21=0
	beqz	r21, AUSSEN	; if (R21==0) goto AUSSEN
	
	;jetzt kommt das Vertauschen der Zellen
	lw	r3, 0 (r2)	; R3=Array[R2]
	lw	r4, 4 (r2)	; R4=Array[R2+1]
	sgt	r21, r3, r4	; if (R3>R4) R21=1 else R21=0
	beqz	r21, INNEN	; if (R21==0) goto INNEN else vertausche Zellen und setze Flag:
	sw	0 (r2), r4
	sw	4 (r2), r3
	add	r20, r0, r0	; R20=0(=False)	: kein vorzeitiger Abbruch
	j	INNEN		; goto INNEN : innere Schleife wiederholen 
	
ENDE:	trap 	0  		; ciao

; Datenbereich:
	dw	96,128				; defineWord: Arrayanfang, Arrayende
	dw	34,2344,0,333,-34,1,444,10,1	; und nun die zu sortierenden Zahlen


DLXasm kann nicht mit Bezeichnern für Variablen umgehen. D.h. man kann nicht Befehle der Art "SW X, R2" schreiben, wobei "X" eine Variable sein soll. Daher muß bei Speicherzugriffen mit Displacements gearbeitet werden. Da der DLX-Simulator sowieso Lehrzwecken dienen soll, ist dies kein Nachteil.
Bei der Verwendung von Displacements ist zu beachten, daß die Zahlen Vielfache von vier sein müssen. Es wird PC-relativ zugegriffen. Beispiel:

	LW   R1, 8(R0)	;lade Word aus der Speicherzelle hinter dem nächsten Befehl
	NOP
	DW   4711	;auf diese Speicherzelle wird zugegriffen
Last not least verfügt DLXasm über eine umfassende Fehlerbehandlung. Je nach Fehlerart wird eine Meldung mit Zeilennummer ausgegeben.


Kodierung und Befehlsformat:

Ich habe mich strikt an die R, I und J-type Befehlsformate aus Hennessy-Patterson gehalten. Einzige Ausnahme sind die Befehle mit den speziellen Opcodes. Dort wird func nicht in den höchstwertigen 11 Bit kodiert sondern in den höchstwertigen 6 Bit. Dies ist eine der Festlegungen, die am Anfang des Praktikums in Übereinstimmung mit der Gruppe 2 gemacht wurden. Desweiteren werden die Opcodes wie abgesprochen durchnummeriert (siehe dazu die Liste der DLX-Befehle).

Der Befehl TRAP wird auch kodiert, ist aber (zumindest bei unserer DLX-CPU) nicht im Befehlssatz enthalten. Er wird von uns als "Programmende"-Befehl verwendet bzw. für eine kontrollierte Ausführung benutzt.


Aufruf und Compilierung:

DLXasm wird folgendermaßen aufgerufen:

 	DLXasm [-o ziel.hs] quell.as
Bei Aufruf ohne Parameter wird diese Meldung ausgegen. Wird keine Ausgabedatei angegeben, erzeugt DLXasm standardmäßig als Ausgabe eine Datei "out.hs", in der dann der assemblierte Code liegt. Ein Aufruf der Art
 	DLXasm datei.as
würde dies bewirken. Es kann aber auch explizit eine Ausgabedatei angegeben werden:
	DLXasm -o datei.hs datei.as
Nach dem Assemblieren liegt das Programm in für den DLX-Simulator verständlicher Form im Verzeichnis. Außerdem wird eine Datei "DLXasmPass2.as" erzeugt. In dieser Datei befindet sich der ursprüngliche Quellcode, so wie er nach der zweiten Assemblierungsphase aussieht. D.h. sämtliche Labels wurden durch Sprung-Offsets ersetzt. Diese Datei dient ausschließlich zur Fehlersuche.

Wer den Assembler selbst unter Linux compilieren möchte, muß folgendes eingeben:

	flex -i DLXasm.l
	yacc DLXasm.y	(oder: bison -y DLXasm.y)
	gcc -o DLXasm y.tab.c -lfl
Diese Arbeit auch automatisch von dem Bash-Script "Make" ausgeführt werden. (Nicht vergessen, das Execute-Bit für Make zu setzen.)
Falls jemand eine GCC (GNU Compiler Collection) unter MS-Windows installiert hat, kann den Assembler auch unter Windows compilieren und dazu das Batch-Script "Make.bat" verwenden. Ich persönlich empfehle hierfür "mingw32", Download unter "http://agnes.dida.physik.uni-essen.de/~janjaap/mingw32/". Die Datei "DLXasm.exe" sollte aber unter allen MS-Windows 9x Versionen laufen (NT habe ich nicht getestet), so daß eine Neucompilierung ohnehin nicht notwendig ist.

WICHTIG: Sollte unter Linux das Compilieren des Assemblers nicht klappen oder wenn der Assembler unerklärliche Ausgaben bzw. Fehlermeldungen erzeugt, ist zu überprüfen, ob nicht vielleicht eine Quelldatei im DOS-Format (also mit CR/LF) vorliegt bzw. im UNIX-Format (nur CR ?), falls unter MS-Windows compiliert wird.


Zu den Dateien in diesem Verzeichnis:

- DLXasm	: Ich habe diese Version unter Suse 6.5 compiliert, sollte
	  	  somit auch unter Suse 6.5 laufen
- DLXasm.exe	: Version für MS-Windows
- DLXasm.l	: flex-Quelldatei
- DLXasm.y	: yacc-Quelldatei
- Make		: Bash-Script zur automatischen Compilierung von DLXasm unter Linux
- Make.bat	: Batch-Script - " - unter MS-Windows
- DLX-Befehle	: Eine Auflistung der Formate und Opcodes der DLX-Befehle, quasi die "Spezifikation"
- bubble.as	: Beispiel-Programm: BubbleSort mit gezieltem Abbruch
- bubble.hs	: assemblierte Version
- fibo.as	: Beispiel-Programm: Berechnet Fibonacci-Zahl
- fibo.hs	: assemblierte Version

Über alle Änderungen/persönlichen Anpassungen am Quellcode - sofern sie erfolgen - bitte ich, mich zu benachrichtigen.


Fragen, Kommentare sowie Tips bitte an Adam Sosnowski