Skip navigation

ARES


Page Content:

Adressierungsarten

Unter "Adressierungsart" versteht man die Art und Weise, in der die Zahlen, die in einer Redcode-Instruktion gespeichert sind, benutzt werden. Dabei geht es darum, wie Speicherstellen referenziert werden. Im Abschnitt MARS-Prozessor wurde schon gezeigt, wie eine MARS-CPU die Maschinenbefehle des Programmes verarbeitet: Nachdem die entsprechende Instruktion aus dem Core geholt wurde, werden zunächst die Operanden ausgewertet. Das bedeutet, daß die Adressen derjenigen Speicherzellen berechnet werden, mit denen der aktuelle Befehl etwas machen soll. Dabei werden die Werte (A- und B-Number) der beiden Operanden in Verbindung mit der jeweiligen Adressierungsart in konkrete Speicheradressen umgewandelt, die in Folge durch die Instruktion manipuliert werden sollen. Eine Redcode-Instruktion besteht immer aus einem Opcode und bis zu zwei Operanden (A und B). Die einfachste Adressierungsart ist:

Immediate Adress Mode

Wird einer Zahl ein "Kanalgitter" ("Number sign", "Hash sign") vorangestellt, so deutet man damit an, daß dieser Wert selbst gemeint ist. So wird z. B. der Wert 5 auf Gleichheit mit dem Wert 6 geprüft, indem man anschreibt:

  CMP    #5,   #6

Detail am Rande: Wird eine immediate adress mode angegeben, so wird nicht einfach der Zahlenwert der Zelle benutzt. Der Wert des CPU-Registers A-pointer wird einfach auf 0 gesetzt: Die nachgeschalteten Funktionen, die die eigentliche Anweisung ausführen, greifen dann über den Pointer auf den Speicher zu. Wird in einem Operand ein # angegeben, so heißt das, daß der gewünschte Wert eben 0 Zellen entfernt zu finden ist. Das führt zu einer Besonderheit in Redcode, denn der Befehl

  JMP   #1234    Endlosschleife in einer Zelle

ist gleichbedeutend mit

  JMP   +0       Endlosschleife in einer Zelle

Das liegt daran, daß A-Pointer als Ziel einer JMP-Anweisung verwendet wird. Welchen Inhalt diese Zelle hat, ist für die Ausführung eines JMP jedoch uninteressant. Fortgeschrittene Redcoder benutzen diese Eigenheit aus Gründen, die später noch erklärt werden.

Direct Adress Mode

Was man in herkömmlichen höheren Programmiersprachen als Variablen bezeichnet, wird in Redcode mit Hilfe der DAT-Instruktion realisiert. Soll nun ein Wert, der in einem DAT gespeichert ist, mit dem Wert 7 verglichen werden, so benutzt man eine "relative" Adresse, die man daran erkennt, daß der Zahl ein Dollarzeichen ("$") vorangestellt wird. Das Dollarzeichen kann weggelassen werden. Dann erkenn man eine "direct adress mode" an dem + bzw. - oder der gänzlich fehlenden Angabe zur Adressierungsart (+). Die folgenden Programmstücke sind identisch, es sind verschiedene gültige Schreibweisen für genau die selben Instruktionen:

  CMP   #7,   $+3    "7" und "8" auf Gleichheit prüfen
  JMP   different
  JMP   equal
  DAT   #8
  CMP   #7,   +3     "7" und "8" auf Gleichheit prüfen
  JMP   different
  JMP   equal
  DAT   #+8
  CMP   #7,   3      "7" und "8" auf Gleichheit prüfen
  JMP   different
  JMP   equal
  DAT   #8
    CMP   #+7,   A      "7" und "8" auf Gleichheit prüfen
    JMP   different
    JMP   equal
A   DAT   #8

Zu dem letzten Beispiel sei hier kurz angemerkt, dass der Assembler beim Übersetzen Sprungmarken einfach durch die passenden Zahlen (Entfernung der referenzierten Zelle zum jeweiligen Befehl) ersetzt. So ist auch eine Angabe von CMP #7, $A erlaubt, sogar CMP #7, $+A wäre möglich, aber von diesen beiden Möglichkeiten muß dringend abgeraten werden, da es bei manchen MARS-Assemblern eventuell zu Vorzeichenfehler führen könnte. Andere Redcode-Assembler verbieten die Angabe des Dollarzeichens sogar ausdrücklich!

Die Berechnung der referenzierten Zelle ergibt sich aus der Formel A/B-pointer = IP + A/B-value. Im Folgenden Beispiel ergibt sich die Adresse der Zelle, die durch den B-Operanden der CMP-Instruktion aus der Adresse des aktuellen Befehls 3402 + 3 = 3405.

  &3402   CMP   #7,   3      "7" und "8" auf Gleichheit prüfen
  &4033   JMP   different
  &3404   JMP   equal
  &3405   DAT   #8

Indirect Adress Mode

Mit den bisherigen Adressierungsarten sind die Programme ziemlich starr. Vergleiche werden immer mit den selben Speicherzellen ausgeführt, MOV würde immer nur die selben Speicherinhalte kopieren. Will man aber das Verhalten eines Befehles dynamisch gestalten, so kann man auf die Technik der Pointer zurückgreifen: Man gibt nicht einfach an "Kopiere die Zelle +3 in die Zelle +1000", sondern man setzt Variablen ein. Es würde dann lauten "Kopiere Zelle X in Zelle Y", wobei sich X und Y im während des Programmlaufs verändern können. In Redcode benutzt man dafür die indirekte Adressierung, sie wird mit einem Klammeraffen ("@") vor dem Operanden angeschrieben:

  &3803   MOV   +2,   @+3
  &3804   ...
  &3805   DAT   #7
  &3806   DAT   #1000

Die Quelle der Kopie ist mit +2 angegeben, das ist der Wert 7 in dem DAT-Befehl. Die Angabe für das Ziel der Transaktion mit @+3, bedeutet, daß der Wert in der Instruktion an der Stelle +3 als eigentliche Entfernung zum Ziel benutzt wird. Somit wird der Wert 7 in die Speicherzelle (IP + 3 = 3806) + 1000 = 3906 geschrieben. Nun kann im weiteren Verlauf der Inhalt des Pointers in &3806 verändert werden. Wenn dann der MOV-Befehl erneut ausgeführt wird, dann wäre das Ziel der Operation eine andere Speicherzelle. Dies kann leicht an einem der einfachsten Kampfprogramme, dem "Dwarf", beobachtet werden:

  loop    MOV   bomb,   @ptr    kopiere die "DAT-Bombe" 104 Zellen vorwärts
          ADD   #5,     ptr     erhöhe den Pointer um 5
          JMP   loop            Endlosschleife
  bomb    DAT   $0
  ptr     DAT   #100

Im zweiten Schleifendurchlauf wird die Bombe +109 Zellen weiter nach vorne kopiert, dann +114 usw. Der Dwarf überschreibt jede fünfte Zelle des Core mit der Instruktion DAT #0. Wenn du noch nicht ganz sicher bist, wie der Dwarf funktioniert, dann starte ihn mit ARES und beobachte die Ausführung Schritt für Schritt, bevor du weiterliest.

Predecrement-Indirect und Postincrement-Indirect Adress Modes

Das Programm "Dwarf" benutzt einen ADD-Befehl, um den Pointer zu verändern, er überschreibt nur jede fünfte Speicherstelle. Manchmal will man einen sogenannten "Core Clear" programmieren, d. h. jede Speicherzelle soll attackiert werden. Das könnte man so realisieren:

  loop    MOV   bomb,   @ptr    kopiere die "DAT-Bombe" 5 Zellen vorwärts
          ADD   #1,     ptr     erhöhe den Pointer um 1
          JMP   loop            Endlosschleife
  bomb    DAT   $0
  ptr     DAT   #1

Dieses Programm "löscht" eine Speicherzelle alle 3 Zyklen. Das geht allerdings noch effizienter, wenn man anstelle der indirekten Adressierung die "Post-Inkrement Indirekte Adressierung" benutzt:

  loop    MOV   bomb,   >ptr    Bombe kopieren, ptr um 1 erhöhen
          JMP   loop
  bomb    DAT   $0
  ptr     DAT   #1

Die Angabe des "Größer-als" Zeichens bedeutet zunächst das gleiche, wie eine "indirect adress mode", jedoch wird der Pointer anschließend noch um eins erhöht. Dieses Programm "löscht" alle zwei Zyklen eine Speicherzelle. Wird als Adressierungsart ein "Kleiner-als"-Zeichen angegeben, so wird vom Pointer Eins abgezogen - allerdings bevor die Zieladresse berechnet wird. Daher auch die Bezeichnung "Pre-dekrement".

Erweiterte ICWS-94 Sprachdefinition

Der Internationale Core War Standard von 1994 beschreibt nur die bisher aufgezählten Adressierungsarten. In den Beispielen wurden manche Befehle mit nur einem Operanden angeschrieben. Wie schon erwähnt wurde, speichert jedoch jede Zelle des Core alle der möglichen Felder, auch wenn diese "bedeutungslos" sind, wie etwa bei der JMP-Instruktion. Die Definition des ICWS-94 war eine Erweiterung des ICWS-88, der bei einem JMP tatsächlich auch nur die Verwendung eines einzelnen Operanden vorsah. Aktuelle Core War Simulatoren nach ICWS-94 ergänzen Angaben im Quellcode automatisch, wenn diese vom Programmierer nicht komplett (mit beiden Operanden) angegeben werden:

  JMP x    JMP x, $0
  SPL x    SPL x, $0
  DAT x    DAT $0, x

Das hat deshalb eine besondere Bedeutung, weil vor der Ausführung der Instruktionen alle Operanden ausgewertet werden, egal ob sie später gebraucht werden oder nicht. So kann man einen unbenutzten Operanden dazu verwenden, einen Pointer mittels pre-decrement oder post-increment zu verändern, ohne eine eigene Instruktion dafür verschwenden zu müssen. Das ist allerdings vorläufig nicht von Bedeutung, wichtiger ist im Augenblick die Frage, wann welcher Operand, A oder B, benutzt wird. Die drei indirekten Adressierungsarten des ICWS-94 geben vor, daß der Pointer immer der Wert des B-Operanden der referenzierten Zelle ist. Das Programm Dwarf sieht in Wirklichkeit eigentlich wie folgt aus:

  MOV   +3,   @+5    Kopiere Bombe über B-value von ptr
  ADD   #5,   +3     Addiere 5 zu B-value von ptr
  JMP   -2,   $0
  DAT   $0,   $0
  DAT   $0,   #5

Nachdem das ICWS-Consortium seine Arbeit 1994 eingestellt hat, wurden von verschiedenen Programmierern Erweiterungen in die unterschiedlichen Simulatoren eingebaut. Heute gilt die Core-War Umsetzung des Programmes pmars als De-Facto-Standard, man nennt den durch pmars erweiterten Standard manchmal auch "Extended-94". Nachdem also jede Instruktion A- und B-Operator beinhaltet, stellt sich die Frage, warum man eigentlich nur den B-Operanden manipulieren darf. Extended-94 Redcode erlaubt auch den Zugriff auf den A-Operanden. Neben den neuen, sogenannten Opcode-Modifiers (dazu später mehr), gibt es auch drei neue Adressierungsarten, nämlich:

Adress Modes in Extended-94
SymbolAdressierungsart
*Indirect, using A-Field
{ Pre-Decrement-Indirect, using A-Field
} Post-Increment-Indirect, using A-Field

Eine weitere Konsequenz aus den ICWS94-Erweiterungen ist, daß man unbenutzte Operatoren genauso wie jene einer DAT-Instruktion als Pointer benutzen kann. Dadurch kann man die Kampfprogramme kürzer gestalten, was ein Vorteil im Spiel ist. Eine Variation des Dwarf, die das macht, sieht so aus:

  loop    MOV   bomb,   @ptr
          ADD   #4,     ptr
  ptr     JMP   loop,   #5
  bomb    DAT   $0


Content Management:

μCMS α1.6