- Win Ali
-
WinAli ist ein Modell-Assembler (siehe auch Assemblersprache) für Windows und DOS. Der generierte Maschinencode wird mit einem Modellrechner ausgeführt, der eine Emulation eines echten Prozessors darstellt. WinAli ist dazu gedacht, um Assembler zu lernen. Der Entwickler richtet sich dabei an Schüler, die bereits Kenntnisse (objektorientierter) Hochsprachen, vorrangig Pascal, haben.
Inhaltsverzeichnis
Datentypen
Ordinal
WinAli kennt nur einen einzigen Datentyp. Laut der WinAli Dokumentation handelt es sich um einen Integer. Allerdings ist der Datentyp zwei Byte groß und entspricht somit nicht dem pascal’schen Datentyp
Integer
noch dem C’schenint
(auf 32bit Systemen) welche vier Byte belegen.Die Tatsache, dass der WinAli-Datentyp zwei Byte große, vorzeichenbehaftete Werte speichern kann, macht ihn zu einem
Smallint
(Pascal) bzw. zu einemshort
(C).Register
WinAli stellt 16 Register bereit, welche allerdings keine spezifischen Bedeutungen besitzen, wie es etwa bei x86 Assemblern der Fall ist.
Register wie
CX
sind also bei WinAli nicht implementiert. Die Register werden statt dessen einfach mit den Zahlen0
bis15
angesprochen.Stack
So wie es 16 Register gibt, gibt es auch 16 von einander unabhängige Stacks, die genauso angesprochen werden wie die Register. Folglich hängt es also von dem Kontext ab, ob das Zeichen
0
als das erste Register oder als der erste Stack interpretiert wird.Variablen
Da in WinAli Variablen nur einen einzigen Datentyp haben können, ist es auch nicht erforderlich, diesen explizit als
Smallint
auszuweisen. Eine VariablewVar
wird dabei wie folgt definiert;wVar ds f
Allerdings gibt es nicht nur die Möglichkeit eine einzelne Variable sondern auch ein ganzes (konstantes) Array
rgwVar
mit beispielsweise 16 Elementen zu definieren;rgwVar ds 16f
Konstanten
Konstanten kann man gleich auf zwei Arten definieren. Die elegantere davon ist die Konstante
wTwo = 2
als solche auch zu deklarieren;wTwo dc '2'
Man muss allerdings nicht für jede Konstante ein neues Symbol deklarieren. Notiert man an einer Stelle, an der eine Variable oder eine Konstante erwartet wird, statt eines Symbols (wie zum Beispiel:
wTwo
) die Zahl in Hochkommata, so deklariert WinAli sie beim assemblieren automatisch.wFive dc '5' ... lda 0,wFive ;gleichbedeutend mit: lda 0,'5' ...
Syntax
Formal
Die Syntax von WinAli ist stark an einen echten Assembler wie etwa TASM (Borland) oder MASM (Microsoft) orientiert. Man kann jede Codezeile in vier Spalten unterteilen;
label command params comment
Das Einrücken der Wörter dient dabei der besseren Leserlichkeit.
Schlüsselwort Bedeutung label
Eine Marke oder ein Sprungziel, zu dem aus einer anderen Zeile hingesprungen werden kann. Oft bleibt diese Spalte leer. command
Auszuführender Befehl in dieser Zeile. Eine ausführliche Erläuterung aller Befehle ist unter dem Abschnitt Befehlsreferenz geführt. params
Jeder Befehl hat einen bis zwei Parameter, dies ist mindestens ein Register und, je nach Befehl, ein weiteres Register, einen Stack oder eine Adresse zu einer Speicherstelle. Hat der Befehl zwei Parameter, werden diese durch ein Komma „,“ getrennt. comment
Ein beliebig langer Kommentar, welcher durch ein ";" oder ein "*" eingeleitet und durch das Zeilenende abgeschlossen wird. Beispiel
An dem folgenden Beispiel erkennt man die Struktur und das Aussehen eines WinAli Programmes.
loop sta 0,wSav ;wSav = r lda 0,cwMult sub 0,'1' ;cwMult-- sta 0,cwMult cmp 0,'1' add 0,'1' mul 0,wSav ;r:=cwMult*wSav bne loop
Wie man an den rechts im Kommentarbereich notierten C-Befehlen anschaulich erkennt, ist ein WinAli Programm immer länger und auch weniger intuitiv als ein Programm in einer Hochsprache, wie etwa C, C++ oder Pascal.
Befehlsreferenz
Es kursieren mehrere Versionen der WinAli Befehlssyntax, sodass die Befehlsnamen von einander abweichen. Der Befehl
lda
kann in einer anderen Version auch stattdessenl
heißen. Die Anzahl der Befehle ist jedoch gleich.Befehle
Ein- und Ausgabebefehle ini A
Der vom Benutzer eingegebene Wert wird in der Adresse A
gespeichert.outi A
Der in der Adresse A
gespeicherte Wert wird auf dem UI ausgegeben.Transport lda R,A
Lädt den in der Adresse A
gespeicherten Wert in das RegisterR
.R:=A;
ldr R0,R1
Lädt den Wert in dem Register R1
in das RegisterR0
.R0:=R1;
ldcr R0,R1
Lädt das Komplement zu dem Wert in dem Register R1
in das RegisterR0
.R0:=-R1;
sta R,A
Speichert den Wert in dem Register R
in die AdresseA
.A:=R;
Arithmetik add R,A
Addiert den Wert in der Adresse A
zu dem Wert des RegistersR
.R:=R+A;
addr R0,R1
Addiert den Wert des Registers R1
zu dem Wert des RegistersR0
.R0:=R0+R1;
sub R,A
Subtrahiert den Wert in der Adresse A
von dem Wert des RegistersR
.R:=R-A;
subr R0,R1
Subtrahiert den Wert des Registers R1
von dem Wert des RegistersR0
.R0:=R0-R1;
mul R,A
Multipliziert den Wert in der Adresse A
mit dem Wert des RegistersR
.R:=R*A;
mulr R0,R1
Multipliziert den Wert des Registers R1
mit dem Wert des RegistersR0
.R0:=R0*R1;
div R,A
Dividiert den Wert des Registers R
durch den Wert in der AdresseA
.R:=R div A;
divr R0,R1
Dividiert den Wert des Registers R0
durch Wert des RegistersR1
.R0:=R0 div R1;
Vergleich (wird relativ zum ersten Parameter ausgewertet) cmp R,A
Vergleicht den Wert des Registers R
mit dem Wert in der AdresseA
.if (R ## A) then
cmpr R0,R1
Vergleicht den Wert des Registers R0
mit dem Wert des RegistersR1
.if (R0 ## R1) then
Sprung (wenn bedingt, abhängig von dem Ergebnis eines Vergleichs) b A
Springt an die Programmzeile, die in der Adresse (dem Label) A
spezifiziert wird (Kurz: Springt zuA
).goto A;
be A
Springt zu A
, wenn die Operanden des Vergleiches gleich waren.if (R = O) then goto A;
bne A
Springt zu A
, wenn die Operanden des Vergleiches ungleich waren.if (R <> O) then goto A;
bh A
Springt zu A
, wenn der erste Operand des Vergleiches größer war als der zweite.if (R > O) then goto A;
bnl A
Springt zu A
, wenn der erste Operand des Vergleiches größer/gleich dem zweiten war.if (R >= O) then goto A;
bnh A
Springt zu A
, wenn der erste Operand des Vergleiches kleiner/gleich dem zweiten war.if (R <= O) then goto A;
bl A
Springt zu A
, wenn der erste Operand des Vergleiches kleiner war als der zweite.if (R < O) then goto A;
Sprung in ein Unterprogramm (alle unbedingt) bal R,A
Springt zu A
und speichert die Rücksprungadresse in das RegisterR
.balr R0,R1
Springt zu R1
und speichert die Rücksprungadresse in das RegisterR0
.la R,A
Lädt die Adresse von A
in das RegisterR
.R:=@A;
br R
Springt zu der Adresse, die in dem Register R
gespeichert ist.Stackoperationen push R,S
Push't den Wert in dem Register R
in den StackS
.S.Push(R);
pop R,S
Pop't das oberste Element aus dem Stack S
in das RegisterR
.R:=S.Pop();
top R,S
Kopiert das oberste Element aus dem Stack S
in das RegisterR
. Dabei bleibt das Element jedoch in dem Stack.R:=S.Top
Steuerung eoj
Markiert das Ende des Quellcodes. end.
nop
Führt keine Operation aus. Kann genutzt werden um den Code übersichtlicher zu machen. nopr
Gleiche Wirkung wie nop
, belegt jedoch nur zwei, statt vier Bytes.Anmerkung zu der Tabelle:
- Erläuterungen sind in (Object) Pascal verfasst.
- Auf die korrekte Notation des Codes MIT Zeilenumbruch wurde zu Gunsten der Übersichtlichkeit verzichtet.
Befehlsgröße
Die Größe eines Befehls beträgt entweder zwei oder vier Bytes.
Jeder Befehl mit zwei Parametern, dessen zweiter Parameter eine Adresse (also kein Register und kein Stack) ist, und der Befehl
nop
belegen vier Bytes. Alle anderen Befehle belegen zwei Bytes.Datenstrukturen
Arrays
Im Gegensatz zu vielen anderen Assemblern sind Arrays in WinAli direkt implementiert. Ein Adressieren von Elementen durch die direkte Berechnung seiner Adresse mithilfe eines Offsets und der Basisadresse ist daher nicht nötig. WinAli führt beim Adressieren eines Elementes sogar eine Bereichsprüfung und löst bei einem falschen Index einen Laufzeitfehler aus. Hat man das Array rgwVar deklariert;
rgwVar ds 8f
dann kann man ein Element adressieren, indem man das Offset in eines der Register,
1
bis15
lädt und anschließend jenes Register in Klammern nach dem Namen des Arrays notiert (das benutzen des Registers0
zum indizieren, funktioniert nicht). Das Offset ist die relative Adresse des Elementes in dem Array, also das Produkt aus Index und Datengröße (welche immer 2 ist). Folgendes Beispiel kopiert den Wert an der Stelle'3'
des ArraysrgwVar
in die VariableiwDrei
, dazu wird das Register1
zum Indizieren benutzt;iwDrei ds f rgwVar ds 8f ... lda 1,'6' ;Index * Datengröße = Offset <=> '3' * '2' = '6' lda 0,rgwVar(1) sta 0,iwDrei
Dies entspricht folgendem Code in Pascal, bzw. C
//Pascal: var iwDrei: SmallInt; rgwVar: array[0..7] of SmallInt; ... iwDrei:= rgVar[3]; //------ //C: short iwDrei; short rgwVar[8] ... iwDrei = rgwVar[3];
Das Speichern eines Wertes in ein Array funktioniert analog.
Absolutes Adressieren
Allerdings gibt es noch eine zweite Variante ein Element in einem Array zu adressieren. Diese ist etwas umständlicher, da man dazu nicht - wie oben - das relative Offset benutzt, sondern das absolute. Dazu muss allerdings erst die Adresse des Arrays ausgelesen werden;
la 1,rgwVar ;die absolute Adresse des ersten Elementes des Arrays add 1,'6' lda 0,0(1) ;WICHTIG: Die erste 0 bezeichnet das Register 0, die zweite 0 nicht. sta 0,iwDrei
Das wirklich Praktische an diesem Verfahren ist die Tatsache, dass man so Zeiger auf beliebige Variablen dereferenzieren kann. Obwohl dies von WinAli nicht so vorgesehen ist, ist das Auslesen und Schreiben von Werten so durchaus möglich.
Stack
Wie bereits erwähnt, verfügt der WinAli Assembler über 16 Stacks mit den Bezeichnungen
0
bis15
]. Aus stilistischen Gründen sollte jedoch nur einer verwendet werden, da ein „echter“ Assembler ebenfalls nur über einen Stack verfügt. Allerdings ist es bei der Implementation von rekursiven Methoden wesentlich einfacher, den Stack1
zum Speichern der Rücksprungadressen zu benutzen.Sonstige
Alle anderen Datenstrukturen, die man benötigt, muss man selbst implementieren, da es keine Bibliotheken, etc. gibt. Dazu ist es die wohl beste Strategie, eine eigene Methode
(void*) malloc (char);
und ihr Pendantfree (void *,char);
(in Pascal etwafunction malloc (byte): Pointer;
bzw.procedure free (Pointer,byte);
) zu schreiben, die in einem Array eine Art Arbeitsspeicher verwalten.Objekte
Es wäre damit sogar möglich Objekte (im objektorientierten Sinn) zu erzeugen und freizugeben. Eine Klasse TAuto könnte dabei wie folgt notiert werden.
TAuto dc '4' ;Größe eines Objektes dieser Klasse FcwSize dc '0' FdwVelo dc '1' FwColor dc '2' FdwLast dc '3'
Siehe auch
- Programmfehler
- Assembler
- Assemblersprache
- Emulator
- Virtuelle Maschine
- Ungarische Notation – die hier benutzten Variablen wurden nach dieser Notation benannt
Wikimedia Foundation.