English (UK) Wegweiser (Sitemap)

Krieg der Kerne

Bild einer roten Kugel
Hochkonzentriert beobachtete er den Zwerg, der gegen einen Riesen kämpfte. Beim Beobachten dieser Szene mußte er unweigerlich an David und Goliath denken. Dieser Riese war besonders groß und es schien, als hätte der Zwerg nicht die geringste Chance. Es war ein unfairer Kampf: Der Zwerg war nicht nur kleiner, sondern auch blind und von Kampftaktik hatte er offensichtlich noch nie etwas gehört. Aber er hatte eine Stärke, seine Sturheit. Ohne zu wissen, wo der Goliath gerade war, warf er Steine durch die Gegend, natürlich ohne einen einzelnen Treffer zu landen. Er dachte nicht ans Aufgeben, war bereit, bis zum Äussersten zu gehen. Vermutlich wußte er nicht einmal, wie stark sein Gegner war. Doch plötzlich passierte das scheinbar Unmögliche: David traf Goliath mit einem Stein so hart, daß der Riese sofort zu Boden ging und auf der Stelle tot war. Der Gegner war geschlagen; er starb leise, ohne ein Ächtzen oder Stöhnen.

Thomas hob eine Augenbraue, und an seinem Blick hätte jeder seiner Freunde sofort erkannt, daß er definitiv nicht erwartet hatte, was eben vor seinen Augen geschah. Aber einen Augenblick später war er wieder hochkonzentriert, bewegte die Maus seines Computers und tippte ein paar Zeichen mit hoher Geschwindigkeit ein. Nach einer kurzen Denkpause klickte er ein weiteres Mal und Goliath stand wieder auf dem Schlachtfeld, entschlossener als zuvor, den Zwerg endlich zu besiegen.

Thomas verbrachte noch einige Zeit damit, die beiden Kämpfer zu beobachten, bis er schließlich auf seiner Tastatur einschlief. Er hatte einen besonders seltsamen und intensiven Traum über Kämpfe im Cyberspace. Der Traum begann damit, daß er vor seinem Computer aufwachte und eine Nachricht auf seinem Bildschirm stand: "Wach auf, Neo..."

Aber das ist eine andere Geschichte, die anderswo erzählt werden soll...

Überblick
Tutorial
Referenz
Download

Vorwort

In den 1980ern wurde der "Core War" der Öffentlichkeit von Alexander Keewatin DewdneyExternal Link zum ersten mal vorgestellt. Ein ähnliches Spiel ("DarwinExternal Link") wurde bereits in den 1960ern gespielt. Die Wenigen, die schon einmal von Core War gehört, es aber noch nie gespielt haben, glauben oft, daß es sich dabei um ein "Hackerspiel" handelt, wobei dem Wort Hacker oft eine, von den Medien verbreitete, negative Bedeutung beigemessen wird. Ich wurde sogar schon mehrmals gefragt, ob man bei Core War Viren programmiert. Die Antwort ist klar und deutlich: Nein! Man lernt weder, wie man Viren schreibt, noch lernt man etwas darüber, wie man in andere Computer einbricht. Wenn man Core War-Programme schreibt, hat man ein ganz anderes Ziel vor Augen: Es geht im Grunde darum, Programme zu optimieren. Hacker sind in Wirklichkeit selten die bösen Jungs, die in die Rechenzentren des Pentagons eindringen, sondern eher Personen, die gerne schwierige Probleme lösen. Der Begriff bezieht sich dabei nicht unbedingt auf den Computerbereich. (Mehr dazu unter: How to become a hackerExternal Link)

Auf dieser Homepage findest du im Kapitel Tutorial Erklärungen zur allgemeinen Arbeitsweise von Prozessoren sowie zu grundlegenden Programmieraufgaben, die immer wieder vorkommen. Dieses Kapitel setzt teilweise vorraus, daß du die Sprachelemente von Redcode schon kennst, aber die Programmtexte sind kommentiert, damit du sie auch so verstehen kannst. Wenn dir etwas nicht klar ist, dann schlage in der Referenz nach. Dort werden die Sprachelemente ausführlich behandelt und es gibt auch eine überblicksartige Zusammenfassung aller Redcode-Befehle zum Nachschlagen für Profis. Das Handbuch erklärt, wie man mit dem Programm ARES umgeht und welche Unterschiede es zu anderen MARS-Programmen gibt. Zuletzt gibt es unter Links noch eine Liste ausgesuchter Webseiten. Manche davon werden dir von besonderem Nutzen sein, wenn du mit dem studieren der ARES-Homepage fertig bist, und mehr über die Programmierung von Kampfprogrammen lernen möchtest.

Mars

Krieg der Kerne ist ein Programmier-Spiel. Kämpfe werden von Programmen ausgetragen, die zuvor von den Spielern in einer Assemblersprache namens "Redcode" geschrieben werden. Die Arena ist der Speicher eines simulierten Computers, der (wie moderne Rechner) dazu in der Lage ist, mehrere Programme gleichzeitig auszuführen. So ein Simulator wird M.A.R.S. (Memory Array Redcode Simulator) genannt, der Speicher Core (eine Anspielung auf die uralten RingkernspeicherExternal Link). Der MARS lädt die gegnerischen Kampfprogramme an zufällige Orte in den Core und beginnt abwechselnd immer einen Befehl des einen Spielers und dann einen des anderen auszuführen, solange, bis nur noch eines der beiden Programme aktiv ist. Du spielst Core War also, indem du ein Programm schreibst, es solange verbesserst, bis du damit zufrieden bist, und es dann gegen ein anderes Kampfprogramm antreten läßt.

Zeus

Bildschirmfoto des "Core View"
Der "Core View" von ARES
Das Programm, mit dem du den MARS steuerst und überwachst, wird Zeus-Einheit genannt, in diesem Falle ist es das Programm ARES. Die Zeus-Einheit hat die Aufgabe, Programme in den Core zu laden, die Simulation auf Wunsch zu pausieren, Punkte zu berechnen, und den Kampf grafisch darzustellen.

Die grafische Ansicht des Speichers (Core View) repräsentiert eine lineare Folge von Speicherzellen, pro Zeile werden 100 Zellen angezeigt. Jedesmal, wenn ein Programm etwas im Speicher verändert, wird die jeweilige Zelle entsprechend eingefärbt. Auf diese Weise kann man ganz gut beobachten, was im Speicher vor sich geht, und ob es so aussieht, wie man es sich erwartet hat.

Ares

Mit A.R.E.S. steht dir eine komplette Core War Entwicklungs-UmgebungExternal Link zur Verfügung. Du kannst deine Programme editieren, in den Speicher laden lassen und die Ausführung Schrittweise verfolgen. Dabei kannst du die Simulation jederzeit unterbrechen und die genauen Inhalte aller Speicherzellen einsehen, um deine Kampfprogramme gründlich zu testen. Du kannst den Simulator auf vielfache Weise benutzen - So kannst du den MARS einfach laufen lassen und das Muster im Core View analysieren, oder du gibst neue Befehle direkt in den Speicher ein. Du kannst ausgesuchte Speicherzellen extra im "Watches"-Fenster anzeigen lassen, damit du die Variablen deines Programmes bequem überwachen kannst - dabei hast du auch die Möglichkeit sogenannte Break Points und Break Conditions zu definieren: Dadurch kannst du dein Programm anhalten lassen, sobald es einen bestimmten Befehl erreicht hat oder der Inhalt einer Zelle einen bestimmten Wert annimmt. ARES bietet noch weitere Hilfen, die besonders für Anfänger nützlich sind, mehr dazu im Abschnitt "Handbuch".

Hermes

Schließlich ist ARES nicht nur ein Krieg der Kerne-Spiel; Es gibt noch "Hermes", eine experimentelle Erweiterung des Simulators, die eine virtuelle "Konsole" bietet: Tastatur und Bildschirm. Dadurch kann man ein richtiges Betriebsystem in Redcode schreiben. Diese Erweiterung ist besonders interessant, weil man so ganz grundlegend erfahren kann, wie Computer und Betriebsysteme funktionieren. (Ein Miniatur-System-ROMExternal Link wird mit ARES ausgeliefert)

Spielregeln

Spielregeln

DAT-Bomben

Es gibt mehrere Möglichkeiten, wie ein Warrior seinen Gegner besiegen kann. Meistens geht es aber darum, den Programmcode des Gegners mit "illegalen Instruktionen" zu überschreiben. Wenn es einem Kampfprogramm gelingt, eine sogenannte "DAT-Bombe" über den Code des anderen Programmes zu schreiben, dann wird das andere Programm früher oder später versuchen, diese illegale Instruktion auszuführen, in der Annahme, es sei an dieser Speicherstelle immer noch ein sinnvoller Befehl vorhanden. Der DAT-Befehl ist zwar wichtig, aber er kann nicht als Anweisung benutzt werden. Versucht ein Programm den Befehl DAT auszuführen, dann hat das zur Folge, daß das Programm nicht weiter ausgeführt wird - es "stirbt".

Relative Adressierung

Nun wäre es ziemlich einfach, wenn ein Programm von Anfang an wüßte, wo im Core sein Gegner gespeichert ist - ein einzelner MOV-Befehl könnte schon den gegnerischen Code mit einer DAT-Anweisung überschreiben. Damit das Spiel zur Herausforderung wird, gilt es ein paar Schwierigkeiten zu überwinden: Zum einen werden die Programme in einen zufällige Speicherposition geladen, zum anderen ist es (im Gegensatz zu echten Computern) nicht möglich, die einzelnen Speicherzellen über ihre absolute Adresse anzusprechen. Eine MOV-Anweisung kann also nicht lauten: "Speichere Wert W an Speicher-Zelle Nr. A", sondern jede Adressierung muß relativ zur Position des ausführenden Befehles erfolgen: "Speichere in der Zelle, die von hier aus Z Zellen entfernt ist".

Entfernungsbeschränkung (Read/Write Limit)

Man kann noch eine optionale Erschwernis einstellen - die Limits. Wenn Limits eingeschaltet sind, dann kann mittels relativer Adressierung nur eine bestimmte Distanz überwunden werden. Wenn ein Kampfprogramm nun den gesamten Speicherraum mit DAT bombardieren will, dann bleibt ihm nichts anderes übrig, als seinen eigenen Programmcode an eine andere Stelle des Speichers zu kopieren - womit von dort aus natürlich anderere Speicherbereiche erreicht werden können. Limits sind jedoch eine Erweiterung, die vor allem für fortgeschrittene Redcoder interessant ist.

Aufgaben der Zeus-Einheit

Wie ein Kampf ausgetragen wird

Nachdem der Benutzer die Kampfprogramme ausgewählt hat, werden die Quelldateien der beiden Warriors vom Assemblerprogramm eingelesen und, sofern möglich, übersetzt. Die entsprechenden Maschinenbefehle werden an zufällige Positionen in den Core geschrieben und die Simulation beginnt. Die Zeus-Einheit ist die Fernsteuerung des Simulators, mit ihr kann man den Programmablauf vorrübergehend unterbrechen, den Inhalt des Speichers untersuchen oder Speicherinhalte während des Kampfes für Testzwecke verändern.

Wie ein Kampf beendet wird

Ein Programm gewinnt, wenn es alleinige Kontrolle über den Prozessor des MARS innehat. Das ist der Fall, wenn das gegnerische Programm keine aktiven Prozesse mehr hat. Wenn entweder ein Programm gewinnt oder eine bestimmte Anzahl von Befehlen ausgeführt wurde, zeigt die Zeus-Einheit das Ergebnis des Kampfes an.

Tournament

Es gibt mehrere Möglichkeiten, unter welchen Umständen Kämpfe stattfinden können. Üblicherweise läßt man zwei Programme eine gewisse Anzahl von Kämpfen ausführen, denn der Ausgang einer Schlacht hängt nicht nur von der "Stärke" eines Warriors ab, sondern auch die Startposition im Speicher kann einem der beiden Gegner einen Vorteil verschaffen. Es gibt aber auch die Möglichkeit, mehr als nur zwei Warriors gleichzeitig antreten zu lassen.

Benchmark

Um die Stärke eines Warriors zu messen, läßt man ihn gegen ein Set ausgesuchter Kampfprogramme antreten. Jede der einzelnen Konfrontationen wird mehrfach ausgetragen, weil die Startposition einen Einfluß auf das Ergebnis hat. Der Durchschnitt aller erreichten Punkte ist als Ergebnis des BenchmarkExternal Link eine ungefähre Information über die Stärke des geprüften Warriors.

Konfiguration des MARS

Es ist möglich, mehrere Eigenschaften des virtuellen Computers zu verändern. So kann man zum Beispiel Kämpfe in einem größeren Speicher ablaufen lassen, was andere (komplexere) Vorgehensweisen ermöglicht. Neben der Speichergröße gibt es noch einige andere Einstellungen, wie Read/Write Limits, P-Space, maximale Programmgöße usw.

King of the Hill

Es gibt mehrere Server im Internet, die permanente online-TournamenteExternal Link anbieten. Man kann sein Programm per e-Mail an den Server senden und gegen die Programme, die es schon auf den Hill geschafft haben, antreten lassen. Allerdings sind fundierte Kenntnisse über Redcode-Programmierung, wie auch eine gewisse Portion Kreativität, von Nöten, wenn man einen Warrior schreiben will, der das Zeug zum König hat.

Zusammenfassung

Das Ziel des Spieles ist es, dafür zu sorgen, daß sich das Gegnerprogramm selbst beendet. Das geschieht häufig durch Überschreiben mit illegalen Instruktionen, wie z. B. ein DAT-Befehl oder aber auch eine Division durch 0.
Die wichtigste Aufgabe der Zeus-Einheit ist das Laden der Warriors in den Core, sowie die Überwachung der Kämpfe. Darüber hinaus kann sie Veränderungen im Core visuell darstellen und Funktionen zur FehlersucheExternal Link bereitstellen.

Tutorial

Einführung in Programmierung mittels Corewars und Redcode

Dieser Abschnitt erläutert allgemeine Grundlagen der EDV in Hinblick auf Core War Programmierung. Wenn du schon Erfahrungen mit Core War hast, dann brauchst du hier gar nicht weiterlesen. Wenn du Maschinensprache kennst, dann solltest du dir vielleicht den Aufbau einer MARS-CPU ansehen.

Dieses Tutorial setzt vorraus, daß du schon einmal etwas über EDV-Grundlagen gehört hast, es soll nämlich nur ganz bestimmte Aspekte der Computerei verständlich machen und vertiefen. Laß dich von den folgenden Begriffen nicht abschrecken - wenn du ein Laie bist, dann ist dieses Kapitel genau richtig für dich.

Überblick

Dieses Tutorial soll als Leitfaden für den Einstieg in das Spiel dienen. Obwohl freilich nicht alles erklärt wird, wird der Leser sozusagen an der Hand durch das rote Wunderland geführt. Der Kurs richtet sich in erster Linie an Laien, aber auch Fachpersonen werden sich hiermit nicht langweilen. Da es sich hier um eine grundlegende Einführung handelt, werden nicht alle Kniffe und Details fortgeschrittener Core War-Techniken erläutert - an den entsprechenden Stellen sind Verweise auf andere (spezifische) Turtorials angegeben.

Die Einführung erklärt, wie Computer im allgemeinen und ein MARS im speziellen funktionieren. Dabei geht es um folgende Begriffe:
  • Maschinensprache und Assembler
  • Prozessor, ALU, Register
  • Instruction Pointer (IP), Multitasking und Task Queue (FIFO)
  • Was einen MARS-Prozessor von einem echten unterscheidet
Des Weiteren werden folgende Themen behandelt:
  • Wie man grundlegende Programmieraufgaben löst
  • Wie man einfache Kampfprogramme schreibt
  • Ausgesuchte Profitechniken
Zuletzt wird die Sprache Redcode im Detail besprochen:
  • Die Verwendung der Redcode Instruktionen
  • Adressierung und Speicherzugriff
  • Möglichkeiten des Preprozessors

    • Text ersetzen mit EQU (Konstanten definieren)
    • Text ersetzen mit FOR (Bereiche wiederholen)
    • Direktiven (Zusätzliche Sonderbefehle, die nicht zu Redcode gehören)

Einleitung

Assemblerprogramme unterscheiden sich deutlich von Programmen, die in einer HochspracheExternal Link wie z. B. BASIC, Pascal, JavaScript oder C++ geschrieben sind. Jeder dieser Befehle einer Hochsprache ist letztlich nur ein Platzhalter für eine meist recht große Anzahl von Maschinenbefehlen. Eine spezielle Software (der CompilerExternal Link) übersetzt die Befehle der Hochsprache in Maschinencode, damit das Programm ausgeführt werden kann. Wenn man z. B. einen Text am Bildschirm anzeigen lassen möchte, dann lautet der entsprechende Befehl in der Programmiersprache BASIC: PRINT "Hello, World!". Ein Programm in Maschinensprache muß dagegen zunächst vergleichsweise umständlich dafür sorgen, daß jeweils ein Zeichen nach dem anderen in den Speicher der Grafikkarte kopiert wird. Darüber hinaus wird man nicht einen MOV-Befehl für jedes einzelne Zeichen eingeben, sondern eine Automatik verwenden, die einen einzigen MOV solange wiederholt, bis das Ende der Zeichenkette erreicht ist. Wer schon Erfahrungen mit der einen oder anderen Hochsprache hast, wird sich vielleicht fragen, was Assemblerprogrammierung denn überhaupt bringt; die Antwort leuchtet sofort ein: Man kann damit Core War spielen. Ganz im Ernst: Maschinenprogramme werden von Menschen geschrieben und können dadurch besonders gut optimiert werden. Sie nutzen so die Möglichkeiten der Hardware besser, als es ein Programm tun könnte, das von einem Compiler übersetzt oder gar von einem InterpreterExternal Link ausgeführt wird (z. B. JavaScript). Assemblerprogramme werden für den PC nur noch in zeitkritischen Abschnitten von Betriebsystemen, Treibern und 3D-Grafikprogrammen (Spiele, 3D-StudioExternal Link) geschrieben. Moderne Hochsprachen erlauben es, Assembler-Abschnitte direkt in den Quellcode einzubinden (Inline AssemblerExternal Link).

Redcode Assembler

Die in diesem Kapitel gezeigten Programm(teile) können meistens nicht direkt in den Simulator eingegeben werden, denn sie beinhalten zusätzliche Angaben für den Präprozessor. Sie müssen in einer Datei gespeichert sein und werden vor Beginn einer Schlacht vom Assemblerprogramm eingelesen, übersetzt und im Core des MARS gespeichert. Teile einer Zeile, die hinter einem Strichpunkt (";") notiert werden, dienen Kommentierungszwecken - sie werden vom Assembler ignoriert. Das gilt allerdings nicht für Direktiven; das sind besondere Befehle, die als Kommentar angeschrieben werden, und den KoTH-Serverern bzw. dem ARES-Debugger zusätzliche Informationen liefern.

ARES zeigt den Inhalt des Core im "Monitor-View" normalerweise gekürzt an, da der Standard ICWS-88 nicht alle Teile des Speichers nutzte. So benutzt der Befehl JMP +1 nur einen Operanden (+1). Da aber Programme, die für ICWS-94 geschrieben werden, auch den "unsichtbaren" zweiten Operanden ansprechen dürfen, kann man mit der Tastenkombination Strg-O zwischen vollständiger und ICWS-88 konformer Ansicht umschalten. Die Beispiele in diesem Kapitel sind in '88er Schreibweise dargestellt, damit Anfänger sie besser verstehen können. Dieser Unterschied bezieht sich hauptsächlich auf die Anzeige im Monitor, man darf die Befehle im Editor in beiden Schreibweisen notieren. Der einzige Unterschied besteht darin, daß weggelassene Opcode Modifiers automatisch ergänzt werden.

Maschinensprache

Funktionsweise eines Computers

Die Erklärungen auf dieser Seite sind so weit wie möglich vereinfacht, um denjenigen, die überhaupt keine Vorstellung von der Funktionsweise eines Computers haben, einen ungefähren Eindruck zu vermitteln, worum es eigentlich geht. Ich habe mich bemüht, alles so einfach wie nur möglich zu erklären, und unnötige Details weggelassen, trotzdem ist der folgende Text schon etwas anspruchsvoller. Wenn du aber die Erklärungen auf dieser Seite einmal verstanden hast, dann hast du das Tutorial eigentlich schon hinter dir, denn der Rest wird dann sofort einleuchtend sein.

Schon wieder Nulleinsnulleins

Wir Menschen rechnen mit zehn verschiedenen Ziffern. Das ist aber nicht zwingenderweise nötig - man kann eine beliebeige Anzahl von Zahlzeichen benutzen, zB. {0,1,2,3,4} oder {0,1,2,3,4,5,6,7,9,a,b,c,d,e,f} oder eben auch {0,1}. Wie das genau funktioniert, ist hier nicht wichtig, es zählt, daß es eigentlich keinen Unterschied ausmacht, mit wievielen Ziffern man etwas ausrechnet. Wenn man nun mit zwei Ziffern (0 und 1) arbeitet, dann wird das Rechnen sehr einfach - so einfach, daß man alle Rechenoperationen auch ersatzweise mit den logischen Gattern AND, OR und NOT darstellen kann (Boole'sche ArithmetikExternal Link). Diese Gatter kann man wiederum sehr leicht mit Relais oder Transistoren direkt realisieren, und dadurch auch Schaltungen, die binäre Berechnungen durchführen können.

Aufbau eines Prozessors (CPU)

Ein ProzessorExternal Link besteht aus vielen unterschiedlichen Schaltungen und beinhaltet eine sehr große Anzahl von logischen Gattern. Er kann damit im Wesentlichen nur ein paar Berechnungen durchführen, sowie Speicherinhalte kopieren und diese vergleichen. Wie das Kopieren und Vergleichen von Daten im technischen Detail funktionert, ist hier nicht weiter von Bedeutung, es reduziert sich jedenfalls auch auf ziemlich einfache Formeln in Boole'scher Arithmetik, die wiederum durch Gatter umgesetzt werden können. Ein Taktgeber erzeugt in regelmäßigen Abständen einen Stromimpuls, wodurch der Prozessor veranlaßt wird, eine weitere Operation durchzuführen. Die Geschwindigkeit, in der ein Prozessor mit solchen SignalenExternal Link versorgt wird, nennt man Taktfrequenz, die Zeitspanne zwischen den Signalen bezeichnet man als einen Takt-Zyklus (englisch: "Cycle").

ALU

Die eigentliche Rechenarbeit wird von einer Ansammlung unterschiedlich spezialisierter Schaltungen vorgenommen. So gibt es eine eigene Schaltung, die zwei Zahlen addiertExternal Link, eine weitere für Multiplikation, eine für das Kopieren von Daten aus dem Hauptspeicher in den Prozessor, eine andere für das Kopieren von Daten aus dem Prozessor in den Hauptspeicher, verschiedene andere für das Kopieren von Daten innerhalb des Prozessors, manche Schaltungen stellen fest, ob ein Wert größer ist als ein anderer, andere prüfen wieder auf Gleichheit, etc. Die ersten Homecomputer hatten etwa einhundert, moderne Prozessoren haben einige hundert solcher unterschiedlich spezialisierter Schaltungen. Die Gesamtheit dieser Schaltungen wird Arithmetic and Logic Unit (ALU) genannt.

Register

Um nicht ständig mit dem langsamen externen Hauptspeicher arbeiten zu müssen, benutzen die Schaltungen der ALU meistens sogenannte Register - das sind sehr kleine RAM-Speicher von jeweils nur wenigen Bytes Größe, die gemeinsam mit der ALU auf dem Chip untergebracht und passend "verdrahtet" sind.

Steuerwerk (Control Unit)

Jeder einzelnen Schaltung der ALU ist eine eindeutige Zahl zugeordnet. Damit der Prozessor die Funktionen der ALU sinnvoll einsetzen kann, benötigt er ein Programm. So ein "Maschinenprogramm" liegt als Zahlenfolge im Hauptspeicher vor. Das Steuerwerk holt sich diese Zahlen der Reihe nach aus dem Speicher und aktiviert dann die entsprechenden Teile der ALU. Diese Zahlenfolge des Programms nennt man auch "Maschinensprache". So könnte die Zahlenfolge 23,00,01,24,00,02,77 Folgendes bedeuten:
  MOV &0001, X   Kopiert Wert aus Hauptspeicher-Zelle Nr. 0001 in das X-Register
MOV &0002, Y Kopiere Wert aus Hauptspeicher-Zelle Nr. 0002 in das Y-Register
ADD X, Y Addiere den Wert im Y-Register zum X-Register
(Beispiel, Disassembly von Notepad.exe)

Der Instruction Pointer

Der IP ist ein besonders wichtiges Register - in ihm ist immer die Adresse derjenigen Hauptspeicher-Zelle abgelegt, die den nächsten Maschinencode beinhaltet. (Jede Speicherzelle hat eine fortlaufend zugeordnete Nummer, die "Adresse") Zuerst benutzt das Steuerwerk den IP, um den entsprechenden Machinen-Befehl aus dem RAM auszulesen. Nach Ausführung dieses Befehls durch die ALU erhöht das Steuerwerk den Inhalt des IP-Registers um eins, wodurch es auf den folgenden Befehl im RAM "zeigt". Nun kann der nächste Befehl aus dem Speicher geholt werden, usw.

Der JUMP Befehl

Einen besonderen Maschinensprachebefehl möchte ich hier kurz erläutern, den Programmsprung. Wie oben schon erwähnt, wird der IP nach Ausführung der Operation automatisch um eins erhöht. Die Anweisung
  JMP +100
bewirkt aber, daß die Zahl 100 zum IP addiert wird. Dadurch wird für den nächsten Befehl nicht die folgende Speicherzelle ausgelesen, sondern diejenige, die 100 Zellen weiter hinten im Speicher liegt. Es gibt auch sogenannte bedingte Sprünge, die nur unter bestimmten Umständen durchgeführt werden.

Multitasking und Task Queues

Wie wir nun wissen, führt ein Prozessor einen Befehl nach dem anderen aus. Aber moderne Computer können doch mehrere Programme "gleichzeitig" ausführen? Dabei handelt es sich nur um einen scheinbaren Effekt, denn in Wirklichkeit kann auch dein Computer immer nur einen Befehl nach dem anderen abarbeiten. Die Gleichzeitigkeit wird mit einem Trick erreicht: Das Programm des Betriebsystems schaltet sehr schnell zwischen den verschiedenen aktiven Programmen (Tasks, auch Prozesse genannt) um, jedes Programm bekommt eine "Zeitscheibe". In so einer Zeitscheibe werden dann ein paar Befehle durchgenommen, bis der nächste Task-Switch passiert. Moderne Prozessoren unterstützen die Betriebsysteme beim Multitasking. Für unsere Zwecke darfst du dir vereinfacht vorstellen, daß der Prozessor nicht nur ein einzelnes Instruction Pointer Register besitzt, sondern eine ganze Liste von IPs. Jedesmal, wenn ein Befehl durchgeführt wurde, wird der (inkrementierte) IP aber nicht einfach ins Register zurückgeschrieben, sondern er wird von unten in diese sogenannte Task Queue "hineingeschoben". Dadurch rückt der IP des nächsten Prozesses an oberste Stelle und kommt zur Ausführung. Je mehr Einträge die Task Queue hat, um so langsamer laufen natürlich die einzelnen Programme.

Assembler

Maschinensprache besteht nur aus Zahlen und diese Zahlen bedeuten auf unterschiedlichen Prozessoren auch unterschiedliche Befehle. Damit der Mensch leichter verstehen kann, was so ein Programm macht, gibt es die Assemblerschreibweise. Die Opcodes (Operation Codes, also die Befehle) werden mit dreibuchstabigen Kürzeln ("Mnemonics") angeschrieben, und falls vorhanden, die zugehörigen Werte. Diese Werte (Parameter) beziehen sich fast immer auf Speicherbereiche - entweder eine bestimmte Zelle des Hauptspeichers oder ein Register der CPU. Mitunter können auch konkrete Zahlenwerte zum Rechnen gemeint sein. Im Abschnitt "Steuerwerk" hatte ich den (erfundenen) Maschinencode noch einmal in Assemblerschreibweise wiedergegeben ("disassembliert").

Assembler ist also nur eine besser lesbare Darstellung von Maschinensprachen-Code.

Programme werden schon lange nicht mehr direkt in Maschinensprache eingegeben - man schreibt Assemblercode und läßt diesen dann von einem Programm, ebenfalls "Assembler" genannt, in Maschinencode übersetzen. Redcode ist ebenfalls eine Assemblersprache, allerdings eine vergleichsweise einfache, sie kennt nur 17 Opcodes. Ein simples Kampfprogramm ("Dwarf") zur Veranschaulichung, du mußt es jetzt aber noch nicht verstehen. Die Bezeichnungen vor den eigentlichen Instruktionen sind Sprungmarken (Labels), sie werden bei der Übersetzung durch passende Zahlen ersetzt:
  loop   MOV   bomb,   @ptr   kopiere DAT #0-"Bombe" an die Zieladresse
ADD #5, ptr erhöhe den Wert in ptr um 5
JMP loop Springe zurück - Endlose "Programmschleife"
ptr DAT #10 DAT darf nicht ausgeführt werden, speichert nur Daten
bomb DAT #0 Kopien dieses DAT Befehls werden als Bombe verwendet

Zusammenfassung

Ein Prozessor ist eine Anhäufung unterschiedlicher Gruppen von logischen Schaltungen. Das Maschinensprachenprogramm beschreibt, in welcher Reihenfolge diese Schaltungen aktiviert werden sollen. Durch Multitasking kann das "parallele" Verarbeiten von Programmen simuliert werden. Das wird erreicht, indem mehrere Instruction Pointer in einer Task Queue verwaltet werden. Ein Assembler ist ein Programm, das Assembler-Quellcode in Maschinencode umwandelt.

Für ganz Neugierige:

Moderne Prozessoren sind schon sehr komplex aufgebaut. Sie analysieren den Maschinencode und optimieren die Verarbeitung in Echtzeit. So gibt es etwa "Branch Prediction" - der Prozessor berechnet alle möglichen Sprungadressen noch während ein Vergleich durchgeführt wird, oder Befehle werden in Portionen zerlegt und parallel verarbeitet und Vieles mehr. Das Poster zeigt einen CPU-Die ("Chip") in starker Vergrößerung und Beschreibung der einzelnen Schaltgruppen:


MARS-Prozessor

Besonderheiten der MARS-CPU

Der "Prozessor", der die Core War Programme ausführt, unterscheidet sich etwas von herkömmlichen Prozessoren. So ein Prozessor ist nie wirklich gebaut worden, der MARS-Computer existiert nur als virtuelle MaschineExternal Link, die speziell für das Spiel erdacht wurde. Das Redcode-Instruction Set ist für bestimmte Aufgaben optimiert und die Speicherorganisation eines MARS ist ebenfalls anders aufgebaut, als es bei echten Computern der Fall ist. Ein wesentlicher Unterschied zu herkömmlichen Rechnern ist, daß Redcodeprogramme nur auf Daten zugreifen können, die sich im Hauptspeicher befinden. Es gibt also keine Arbeitsregister, die dem Programmierer zur Verfügung stehen würden und Redcode-Programme können auch keine PeripheriegeräteExternal Link ansteuern, weil schlichtweg keine vorhanden sind. Die Aufgabe der Register wird in Redcode von den DAT-Befehlen übernommen - Befehle wie ADD oder CMP (Compare) beziehen sich meistens auf Werte, die in DAT-Befehlen gespeichert sind.

Adressierung

Programme, die für den Krieg der Kerne geschrieben werden, unterliegen bestimmten Einschränkungen. Die übliche absolute Adressierung von Zellen des Hauptspeichers über deren Adresse ist nicht erlaubt, Redcode-Programme dürfen nur relativ adressieren. Adressen von Speicherzellen werden durch einen Offset zum aktuellen Instruction Pointer beschrieben, also durch den Abstand zum aktuellen Befehl. Das folgende "Programm" verwendet nur relative Adressen:
  "Endlos-Schleife" mit relativer Adressierung:
JMP +1 Adresse des nächsten Befehls (Current IP + 1) in den IP kopieren
JMP -1 Adresse des vorigen Befehls (Current IP - 1) in den IP kopieren
In einem "echten" Assembler könnte man das Programm aber auch absolute Adressen benutzen lassen:
  "Endlos-Schleife" mit absoluter Adressierung:
&0234 JMP &0235 Den Wert 235 in den IP kopieren
&0235 JMP &0234 Den Wert 234 in den IP kopieren

Speicherorganisation

Ein weiterer Unterschied zu einem richtigen Computer ist, daß jeder Befehl mitsamt allen zusätzlichen Angaben immer in genau einer einzigen Speicherzelle abgelegt wird. Um Speicher zu sparen, können normalerweise verschieden komplexe Maschinenbefehle auch verschieden viele Speicherzellen belegen. Ein Befehl wie JMP &001234 würde etwa als Zahlenfolge 83,00,12,34 gespeichert werden, ein Befehl wie ADD X, Y würde dagegen nur durch eine Zahl (zB. 77) repräsentiert. (ADD X, Y benötigt keine weiteren Parameter - die entsprechende Schaltung der ALU ist direkt mit den Registern X und Y verdrahtet.) Beispiel: Disassembly von Notepad.exe.

Der Speicher eines MARS besteht aus Zellen, die jeweils eine komplette Instruktion speichern können. Eventuell unbenutzte Teile werden dabei freigelassen bzw. ignoriert. Eine Speicherzelle des MARS besteht aus sechs "Feldern":

  1. Opcode (Operation Code) - dieser Wert erklärt, was eigentlich getan werden soll, z. B. add, mov, jmp usw.
  2. Opcode Modifier - dieser Wert beschreibt, welche Felder der adressierten Speicherbereiche angesprochen werden sollen. Das kann die gesamte Zelle (.I) sein , oder nur ein bestimmtes Feld (.A, .B) darin. So würde der Befehl mov.i -1, +1 die komplette Instruktion der Zelle darüber (-1) in die Zelle darunter (+1) kopieren, mov.a -1, +1 würde dagegen nur den Wert "A-Number" übertragen.
  3. Adress Mode A - Beschreibt, wie die Zahl des Feldes "A-Value" gemeint ist: Konkreter Wert, Relative Speicheradresse, usw.
  4. A-Number - Der erste "Parameter" eines Befehls. Beschreibt oft, woher die Daten genommen werden sollen ("Source").
  5. Adress Mode B - Beschreibt die Adressierungsart für den Wert von "B-Value".
  6. B-Number - der zweite "Parameter". Häufig eine Speicheradresse, an die das Ergebnis geschrieben werden soll.

Ringförmiger Speicher

Da Redcode nur relative Adressierung erlaubt, kann es vorkommen, daß ein Befehl versucht, eine Zelle zu adressieren, die sich hinter dem Ende des Speichers befinden müßte. In diesem Fall wird einfach am Anfang des Speichers weitergezählt (Genauer gesagt wird jede Adresse moduloExternal Link CORESIZE gerechnet). Das ähnelt einer Addition mit Übertrag, wobei in unserem Fall der Übertrag auf die nächste Stelle einfach verworfen wird. Man kann sich vorstellen, daß sich der Anfang des Speichers gleich hinter dessen Ende befindet, der Speicher ist quasi ringförmig.

Zwei getrennte Task Queues

Im Gegensatz zu einem realen Computer verwendet ein MARS nicht nur eine Task Queue, sondern jeweils eine eigene für jeden Spieler. Der MARS teilt die Rechenzeit auch nicht in Zeitscheiben ein, sondern wechselt nach jedem ausgeführten Befehl zur Queue des anderen Spielers. Auf diese Weise ist sichergestellt, daß beide Programme gleich viel Rechenzeit erhalten, egal wieviele (Unter-)Prozesse ein einzelnes Kampfprogramm starten mag.

Wie die MARS-CPU arbeitet

Alle Prozessoren haben selbst einen vorgegebenen Ablauf von Einzelhandlungen. Dieser Abschnitt erklärt das Mikroprogramm einer MARS-Control-Unit, also die Einzelschritte, in denen ein MARS-Prozessor Core War-Programme zur Ausführung bringt.

Der folgende Vorgang wird für jeden einzelnen Befehl des Kampfprogrammes durchgeführt:

  1. Der oberste Instruction Pointer wird aus der Task Queue des aktuellen Spielers "gezogen" - Er wird in ein Arbeitsregister kopiert und aus der Queue entfernt.
  2. Die Speicherzelle an der Adresse IP wird ausgelesen und komplett in ein weiteres Arbeitsregister ("C-Register") kopiert, sie beinhaltet nun die "Current Instruction".
  3. Der Inhalt des C-Registers wird weiter analysiert. Die absoluten Adressen der Speicherzellen, auf die sich A-Value und B-Value beziehen, werden berechnet. Diese Adressen werden in den Registern "A-Pointer" und "B-Pointer" abgelegt.
  4. Die entsprechenden Speicherzellen werden jeweils in die Register "A-Instruction" und "B-Instruction" kopiert.
  5. Der Opcode im C-Register wird verwendet, um zu entscheiden, wie mit den Daten in A-Instruction und B-Instruction vorgegangen werden soll. Die Veränderungen werden dabei direkt in diesen beiden Registern vorgenommen.
  6. Die Inhalte von A-Instruction und B-Instruction werden wieder in den Hauptspeicher zurückgeschrieben. Dazu werden die Werte der Register A-Pointer und B-Pointer benutzt.
  7. Wenn der Befehl im C-Register ein erlaubter Befehl war, dann wird aktuelle Instruction Pointer inkrementiert (um eins erhöht) und von unten in die aktuelle Task Queue "hineingschoben". War der Befehl ungültig (z. B. eine DAT-Anweisung oder eine Division durch Null), dann wird der IP einfach verworfen - das Programm "stirbt".
  8. Nun wird dieser gesamte Vorgang für den obersten Befehl der anderen Task Queue erneut durchgeführt.
Das Ganze geht im Grunde endlos so weiter. Für das Spiel Core War ist jedoch noch anzumerken, daß der MARS die Simulation beendet, sobald eine der beiden Task Queues leer ist. Daraus kann erkennen, daß ein Kampfprogramm alle Prozesse seines Gegners "töten" muß.

Zusammenfassung

Redcode-Programme dürfen Speicherzellen nicht direkt ansprechen, Quell- und Zieladressen von Speicheroperationen werden immer relativ zur Position der ausgeführten Anweisung angegeben. Der Core-Speicher wird dabei als ringförmig verstanden; Zugriffe über eines der Enden hinaus werden am anderen Ende des Speichers weitergezählt. Das Multitasking ist in einem MARS mit zwei getrennten Task Queues umgesetzt, wobei nach jedem einzelnen Befehl auf die Task Queue des anderen Spielers umgeschaltet wird. Der Kampf ist zu Ende, wenn eine der beiden Task Queues leer wird.

Programmschleifen

Schleifen und Bedingungen

Programmschleifen

Eine immer wieder anzutreffende Programmiertechnik ist das Bilden von sogenannten Programmschleifen. Damit ist gemeint, daß ein Teil des Programmes immer wieder ausgeführt wird. Am Ende der Schleife ist normalerweise ein Befehl zum Sprung an den Anfang zu finden. Ein (eher sinnloses) Beispiel ist das folgende Codefragment; es addiert den Wert 5 zu einer "Variablen", ohne jemals damit aufzuhören (Man spricht von einer Endlosschleife):
  ADD   #5,   +2    Addiere 5 zu dem Wert, der in dem DAT gespeichert ist
JMP -1 Nächster Befehl soll wieder der ADD sein
DAT #0 Platzhalter, "Variable"

Sprungmarken

Damit man nicht ständig mit den reinen Zahlen arbeiten muß, darf man den einzelnen Anweisungen Namen ("Labels") geben. Dadurch wird der Programmcode besser lesbar, und man kann leicht weitere Befehle einfügen, ohne die Zahlen von Hand anpassen zu müssen. Das obige Beispiel sieht mit Labels (Sprungmarken) wie folgt aus:
  loop  ADD   #5,   A   Addiere den Wert 5
JMP loop Nächster Befehl soll wieder der ADD sein
A DAT #0 Platzhalter, "Variable"
In BASIC würde man dieses Programm so schreiben:
  10 LET A = 0 
20 LET A = A + 5
30 GOTO 20
und in JavaScript:
  var A = 0;      Variable deklarieren und mit 0 vorbelegen

while (true) Wiederholen, solange der Ausdruck "Wahr" ergibt (Endlosschleife)
{
A += 5; Inhalt der Variable um 5 erhöhen
}

Bedingte Sprünge

Damit man einem Programm "Intelligenz" einhauchen kann, muß man ihm die Fähigkeit verleihen, Entscheidungen zu treffen. Dazu muß man angeben, was geprüft werden soll, und was bei einem entsprechenden Ergebnis geschehen soll. Normalerweise sind das Programmsprünge, wie mit JMP, die an eine Bedingung geknüpft werden. Redcode kenn z. B. den Befehl JMN ("Jump if Non-Zero"), der wie ein gewöhnlicher JMP funktioniert, jedoch nur, wenn der zweite Wert, den man angeben muß, Null ist. Das folgende Programm durchläuft die Schleife zehn mal, bevor das Programm fortgesetzt wird:
  loop        SUB   #5,   countdown    Zähler um 5 verkleinern
JMN loop, countdown Mit Null vergleichen. Sprung bei Nicht-Null
... Fortsetzung
countdown DAT #50
Dieses Programm würde in BASIC so geschrieben werden:
  10 LET A = 50 
20 A = A - 5
30 IF A <> 0 THEN GOTO 20
40 ...
und in Pascal:
  var A : integer = 50;    Ganzzahlige Variable deklarieren und mit 50 vorbelegen

repeat
A := A - 5; Inhalt der Variable um 5 verringern
until (A = 0); Wiederholen, solange A ungleich 0 ist
...
Der Befehl JMZ ist nur in bestimmten Fällen einsetzbar. In manchen Fällen wird man den Befehl CMP (Compare) benutzen wollen. Der Erweiterte Befehlssatz von pmars erlaubt eine andere Schreibweise für diesen Befehl, nämlich SEQ (Skip on Equal). SEQ beschreibt etwas besser, wie der Befehl funktioniert: Zuerst wird ein Vergleich durchgeführt. Waren die verglichenen Werte identisch, dann wird der nächste Befehl übersprungen. Waren die Werte ungleich, wird nichts übersprungen - der Befehl hat einfach keinen weitern Effekt:
  start     CMP   A,   B      Nächsten Befehl überspringen, wenn a = b
skipequal JMP differs
JMP wasequal
A DAT #a Die Zahlen in diesen beiden DAT-...
B DAT #b ...-Instruktionen werden verglichen

differs ... do this, if a ≠ b

wasequal ... do this, if a = b
In JavaScript würde man das so anschreiben:
  if (A == B) 
{
// do this, if A = B
}
else
{
// do that, if A ≠ B
}
Das Countdown-Beispiel von oben kann man natürlich auch mit CMP programmieren. In diesem Fall ist die Verwendung von CMP allerdings nicht sinnvoll, weil das Programm daurch einen Befehl länger ist und entsprechend mehr Rechenzeit verbraucht:
  loop    SUB   #5,   A       Wert in A um 5 verkleinern
CMP A, #0 Nächsten Befehl überspringen, wenn 0
skip JMP loop Ungleich: Schleife wiederholen
exit ... Fortsetzung hier nach 30 Zyklen
A DAT #50
Warum sind es 30 Zyklen?

Redcode bietet noch weitere bedingte Sprungbefehle: JMZ (Jump if Zero) und SLT (Skip less than). Diese beiden Befehle sollten inzwischen selbsterklärend sein, sie funktionieren analog zu den Befehlen JMN bzw. SEQ. Im ICWS-94 Draft ist noch ein besonderer bedingter Sprungbefehle definiert: DJN (Decrement and Jump, if NOT Zero). Zuerst wird die zu prüfende Zahl um eins verkleinert (dekrementiert), und dann auf Null getestet. DJN funktioniert quasi wie die Befehle SUB #1, A und JMN loop, A in Kombination. Ein Beispiel wird es klar machen:
  loop   ...                Inhalt der Schleife
DJN loop, A A vermindern und auf Null prüfen
... Wurde A = 0, dann gehts hier weiter
A DAT #a
Mit DJN kann man sehr schön und einfach festlegen, wie oft eine Schleife durchlaufen werden soll. Es ist sogar möglich, den Wert a direkt in dem DJN-Befehl abzulegen, indem man nicht, wie oben, eine relative Adresse angibt, sondern eine unmittelbare:
  DJN   loop,   #10    10 Mal nach loop springen

Mathematik

Mathematik mit Redcode

Die Hauptaufgabe von Computern ist ja eigentlich das Berechnen verschiedenster Dinge. Als Benutzer bemerkt man das vielleicht nicht so direkt, aber als Programmierer hat man ständig damit zu tun. Auch Core War-Kampfprogramme müssen gelegentlich etwas berechnen. Redcode kennt fünf mathematische Instruktionen:

Mathematische Instruktionen
Opcode Bedeutung
ADD Addition
SUB Subtraktion
MUL Multiplikation
DIV Ganzzahlige Division
MOD Rest einer Division

Jeder dieser Befehle verwendet zwei Operatoren, wobei das Ergebnis in der Zelle gespeichert wird, die durch den Operand B ("Target") angegeben ist. Bei Division und Modulo ist zu beachten, daß B ÷ A gerechnet wird.

Die folgenden Beispiele dienen nur der Erklärung, solche Berechnungen kommen normalerweise nicht in Kampfprogrammen vor:

Potenzen

Da es nur die arithmetischen Grundoperationen als Maschinenbefehle gibt, muß man die höheren Funktionen durch Programme nachbilden. Ein einfaches Beispiel dafür ist die Berechnung einer Potenz be = b * b * ... * b, e-Mal. Also bilden wir eine Programmschleife, die b e-Mal mit sich selbst multiplizert. Dieses Beispiel berechnet p = be, mit b=2 und e=3:
  loop   MUL   b,    p    p mit b multiplizieren
DJN loop, e e um eins verkleinern
...
p DAT #1 p = 1 * b * b * b...
b DAT #2 Basis
e DAT #3 Exponent
Nach dem die Schleife dreimal durchlaufen wurde, ist e = 0 und in p liegt der Wert #8. Die Werte für b und e können beliebig gewählt werden.

Absoluter Betrag

Diese Funktion wäre eigentlich auch sehr leicht zu implementieren. Der absolute BetragExternal Link einer Zahl ist die selbe Zahl, nur ohne Vorzeichen. Also: |n| ist gleich n, wenn n ≥ 0 ist, und n * (-1), wenn n < 0. Ein Programm für diese Berechnung würde so aussehen:
  start     SLT   #0,    n    ist 0 < n, dann wird der nächste Befehl übersprungen
skipless MUL #-1, n ist n <= 0, dann mit (-1) multiplizieren
...
n DAT #123
Dieses nette Programm funktioniert in einem MARS-Computer allerdings nicht, denn...

Negative Zahlen

gibt es im Krieg der Kerne nicht. Weil der Speicher ringförmig aufgebaut ist, ist es egal, ob man eine Zelle zurück geht, oder CORESIZE-1 Zellen nach vorne. Werte, die z. B. durch Addition oder Subtraktion ausserhalb des Bereiches 0 ... CORESIZE-1 zu liegen kommen, werden vom MARS automatisch modulo CORESIZE gerechnet, d. h. sie werden auf den Rest der Division durch CORESIZE gekürzt: n ← n % CORESIZE. Negative Zahlen werden dabei in den positiven Bereich "verschoben". ARES zeigt allerdings negative Zahlen an - sofern man nicht mit STRG-I auf Anzeige absoluter Werte umschaltet. Üblicherweise wird Core War mit einer Speichergröße von 8000 Zellen gespielt. In diesem Fall werden alle Werte ab 4000 von ARES als negative Werte angezeigt, was meistens auch sinngemäß ist. Negative Werte n sind also als 8000 - n zu verstehen.

Wenn man unbedingt mit negativen Zahlen arbeiten möchte, kann man auch in einem MARS-Programm einfach vorsehen, daß alle Werte die größer als 4000 sind, als negative Zahlen betrachtet werden. Der eigentliche Wert dieser Zahl ist dann der Abstand zu 8000 und der absolute Wert ist durch die Formel Abs(n) = 8000 - n gegeben.

Ein Programm zur Berechnung des Absolutwertes könnte daher so aussehen:
  start   SLT   #CORESIZE/2,   n       ist n "negativ"?
JMP done Nein: dann fertig
SUB n, temp quasi * (-1)
MOV temp, n Ergebnis in n ablegen
done ...
n DAT #-1000
temp DAT #CORESIZE Im MARS-Speicher eigentlich = 0

Primzahlen

Die Aufgabe, festzustellen, ob eine Zahl eine PrimzahlExternal Link ist, ist schon etwas schwieriger zu lösen: Eine Primzahl zeichnet sich bekanntlich dadurch aus, daß sie nur durch sich selbst und durch Eins teilbar ist. Das folgende Programm testet zunächst, ob bei der Division der Zahl n durch 2 ein Rest bleibt. Wenn nicht, dann handelt es sich um eine gerade Zahl. Falls ein Rest geblieben ist, prüft die Routine die Division durch alle ungeraden Zahlen, bis entweder eine Division keinen Rest hat oder eine Division durch n getestet wird. Sollte bis dahin keine Teilbarkeit durch eine der kleineren ungeraden Zahlen gefunden worden sein, so handelt es sich um eine Primzahl. (Das folgende Programm verwendet Opcode Modifiers, die bisher noch nicht erklärt wurden)
  start       MOV     n,           temp 
MOD #2, temp Prüfe, ob n/2 keinen Rest hat
JMZ not_prime, temp Ende bei gerader Zahl

loop MOV n, temp
check MOD #3, temp Probiere div (x*2)+1
JMZ not_prime, temp Kein Rest, dann fertig!

ADD.a #2, check Nächste ungerade Zahl in den MOD schreiben

SLT.ab check, n Dividieren wir n / n ?
JMP is_prime Ja: fertig!
JMP loop Nein: weiter

temp DAT #0
n DAT #73*73

not_prime ...
is_prime ...
Es sei noch angemerkt, daß dieses Programm die Aufgabe nicht optimal löst: Zum einen bräuchte man nicht durch 9 dividieren, wenn schon festgestellt wurde, daß die Zahl nicht durch 3 teilbar ist. Zum anderen könnte man aufhören zu probieren, wenn man bereits erfolglos versucht hat, n durch die abgerundete Wurzel von n zu dividieren.

Multitasking

Multitasking

(P)resümee

Wie auf der Seite Maschinensprache (Instruction Pointer, Multitasking) bereits erklärt wurde, verwendet der Prozessor Register, in denen gespeichert wird, an welcher Adresse der nächste Befehl zu finden ist. Darüberhinaus ist es nicht nur ein einzelnes Register, sondern gleich eine Liste - genannt "Task Queue". Daß bei Core War zwei getrennte Queues benutzt werden, ist für die Erklärungen auf dieser Seite nicht weiter von Bedeutung - am Ende solltest du diese Gegebenheit problemlos verstehen können - wir gehen vorerst davon aus, daß nur ein Programm geladen wird.

Task Queue mit nur einem Prozess

Bei den meisten bisher besprochenen Programmen gibt es nur einen einzelnen Eintrag in der Task Queue. Dieser Eintrag wird vor der Ausführung einer Instruktion in ein internes Prozessor-Register IP ("Instruction Pointer") kopiert, und der Eintrag aus der Queue entfernt (sie ist danach also leer). Dann wird die Instruktion, die sich an der Adresse IP im Core (IP = Zellennummer) befindet, in den Prozessor geladen, analysiert und, falls möglich, ausgeführt. In den meisten Fällen wird daraufhin das IP-Register um Eins erhöht und der neue Wert wird in die Task Queue zurückgeschrieben. Wenn im MARS nur ein Programm aktiv ist, dann beginnt diese Prozedur von vorne, nur wird diesmal der nächste Befehl ausgeführt.

Die Task Queue

funktioniert nach dem FIFO-PrinzipExternal Link, "First in - First out". Damit ist gemeint, daß Einträge von "oben" genommen, neue Einträge aber von unten "nachgeschoben" werden. Der erste Eintrag, der hineinkommt, kommt auch zuerst wieder hinaus. (Im Gegensatz zu FIFO, funktioniert ein Stapel Papier nach dem LIFO-PrinzipExternal Link, "Last In - First Out" - Dort wird das zuletzt hinzugefügte Blatt von oben als erstes wieder entfernt.) Wenn ein Progamm nun 2 Prozesse benutzt, dann befinden sich auch zwei Einträge in der Queue. Diese beiden Einträge tauschen effektiv ihre Plätze in jedem Zyklus. Bei mehreren Einträgen "rollt" die Liste nach oben.

Vor der Ausführung eines Befehls wird also der "topmost entry" aus der Task Queue extrahiert, während alle Elemente darunter nach oben "nachrutschen". Nach Verwendung dieses extrahierten Wertes als IP wird der IP+1 unten an die Queue angefügt. Diese Reihenfolge ist besonders wichtig, wenn du z. B. "Papers" schreiben möchtest, für andere Programme mag es nicht von Bedeutung sein, wie z. B. dem "Dual-Task-Scanner":

Dual-Task-Scanner

Zunächst möchte ich einen Core-Clear, der in zwei Richtungen gleichzeitig arbeitet, vorstellen:
  ;core clear in two directions
bomb
cptr DAT #4000, #4000 Ziel-Pointer für die Angriffe

cloop MOV bomb, >cptr Angriff via B-value, Post-Inkrement
MOV bomb, {cptr Angriff via A-value, Pre-Dekrement
JMP cloop
Die Adress Modes > und { funktionieren zum Teil genauso wie @: Sie benutzen den Wert der angesprochenen DAT-Zelle als Pointer für den Speicherzugriff mittels MOV. Zusätzlich wird der Wert des DAT vor/nach dem Zugriff verändert; > bewirkt, daß der B-Wert des DAT nach dem Zugriff um Eins vergrößert wird, wogegen { bewirkt, daß der A-Wert als Pointer benuzt und vor dem Zugriff um Eins verkleinert wird. Die Zahlen in dem DAT-Befehl verändern sich in jedem Schleifendurchlauf um Eins. Bei der erstmaligen Ausführung des JMP beinhaltet cptr die Werte #3999, #4001, dann #3998, #4002, usw. Dieses Programm schreibt also DAT-Bomben "um" die Position +4000 herum.

Der folgende Scanner "splittet" sich auf zwei Prozesse auf, wobei ein Prozess nach dem Gegner sucht, während der andere den doppelten "Core Clear" durchführt. Sobald der Scanner eine Zelle findet, in der nicht Null gespeichert ist, überträgt er diese Adresse in den DAT des Coreclear, damit dieser die entdeckte Stelle angreift:
         SPL     cloop             Neuen Prozess in die Task Queue eintragen

;scanner
sloop ADD #9, sptr sptr um 9 erhöhen
JMZ.F sloop, @sptr Befindet sich etwas an der Zieladresse?
found MOV sptr, cptr Ja: Adresse in den DAT des anderen Prozesses kopieren
MOV.ba sptr, cptr Ebenfalls in den A-value übertragen
ADD #100, sptr 100 Zellen dahinter weiterscannen
sptr JMP sloop, #1000 B-value ist frei und wird als "scan-pointer" benutzt

;core clear in two directions
bomb
cptr DAT #4000, #4000 Ziel-Pointer für die Angriffe

cloop MOV bomb, >cptr Angriff via B-value
MOV bomb, {cptr Angriff via A-value
JMP cloop

Papier und Seide

Papers sind Programme, die sich selbst replizieren. Das bedeutet, daß sie den eigenen Programmcode an eine andere Stelle des Speichers kopieren und dann dort hin springen. Silk-Style Papers sind besondere Replikatoren, die einen Trick benutzen, um besonders schnell zu laufen: Sie tragen die Adresse des neuen Prozesses schon vor dem Kopieren der eigentlichen Instruktionen in die Task Queue ein. Die Funktionsweise solltest du Schrittweise mit ARES beobachten, um zu begreifen, wie ein Silk funktioniert:
  boot  SPL   1             Zweiten Prozess erzeugen 

silk SPL +10, #0 Zweimal SPL +10
MOV >-1, }-1 Zwei Zellen kopieren
Dieses Programm funktioniert, weil die neuen Prozesseinträge unten an der Task Queue eingefügt werden! Hier ist also die Reihenfolge der Tasks wichtig. Damit dieses Programm nicht nur sich selbst kopieren, sondern auch weitere Befehle "mit sich führen" kann, müssen genausoviele Prozesse erzeugt werden, wie Instruktionen repliziert werden sollen:
  boot  SPL   2               Drei Prozesse erzeugen
SPL 1

silk SPL +10, #0 Dreimal SPL +10
MOV >-1, }-1 Drei Zellen kopieren
imp MOV imp, imp+1 Jede Kopie startet einen Imp
Wenn so ein Programm mit 4 oder mehr Prozessen arbeiten soll, dann ist das erzeugen der Prozesse etwas schwieriger, denn der erste SPL +10 Befehl darf erst ausgeführt werden, wenn bereits alle Prozesse erzeugt wurden. Viel Spaß beim Tüfteln!

Ein (englisches) Tutorial zu Silk und PaperExternal Link geht näher auf diese schon recht fortgeschrittene Programmiertechnik ein.

Kampfprogramme

Kampfprogramme

Die hier beschriebenen Programme kommen in der Wirklichkeit von Core War höchstens als Komponenten komplexerer Kampfprogramme vor. Sie dienen hier als Beispiele verschiedener Strategien und Möglichkeiten und sollen nur ein grundlegendes Verständnis ermöglichen. Die meisten dieser Routinen könnten noch stark optimiert werden.

Der Imp

Eigentlich ist der Imp kein richtiges Kampfprogramm, weil er nicht attackiert ("Bomben" wirft). Kämpfe gegen einen Imp gehen eigentlich immer unentschieden aus, wenn der Gegner keine besondere Abwehrstrategie gegen Imps beherrscht:
  MOV   +0,   +1   kopiere diese Instruktion (+0) in die nächste Zelle (+1)
Versuche zuerst, selber zu verstehen, was der Imp macht, bevor du weiterliest. (Jetzt!) Der Imp ist das kleinste Programm, das in der Lage ist, sich selbst zu replizieren. Nachdem der Befehl ausgeführt wurde, befindet sich eine Kopie der Instruktion in der Zelle darunter, der IP wird erhöht und ein weiterer MOV 0, +1 wird ausgeführt, und so weiter. Wenn der Imp beginnt, die Befehle des Gegners zu überschreiben, dann wird der Gegner selbst zu einem Imp. Beide Programme laufen endlos durch den Speicher, bis die Anzahl der maximalen Zyklen erreicht wird.

Dwarf

Der Dwarf ist der simpelste Vertreter der Art von Programmen, die als Stones bezeichnet werden. Ein Stone wirft mit Steinen (DAT-Instruktionen) um sich. Dieser hier kopiert DAT-Instruktionen in jede vierte Zelle des Core. Die Zahlen am Anfang jeder Zeile sind die absoluten Adressen der jeweiligen Speicherzelle:
  &0785    MOV   +3,   @+3    Kopiere die Bombe an die Zieladresse
&0786 ADD #4, +2 Erhöhe den Zeiger auf die Zieladresse um 4
&0787 JMP -2 Endloschleife
&0788 DAT #4 "Pointer" auf Ziel und "Bombe" zugleich
Um dieses Programm zu verstehen, mußt du zuerst das Prinzip eines Pointers kennen: Wenn der Wert eines DAT-Befehls als Pointer verwendet wird, dann errechnet sich die Zieladresse aus der Adresse des DAT-Befehls plus dem Wert, der in ihm gespeichert ist. Der DAT-Befehl unseres Beispiels beinhaltet den Wert 4, wird er nun als Pointer benutzt, dann "zeigt" er auf die Zelle mit der Adresse 792.

Der erste Befehl des Dwarf ist eine MOV-Instruktion. Sie kopiert die Zelle mit der relativen Adresse +3 an die Zelle, auf die der DAT-Pointer zeigt. So wird im allerersten Durchlauf die Instruktion "DAT #4" an die Adresse 792 kopiert.

Danach kommt die ADD-Instruktion dran, sie erhöht den Wert im DAT auf 8.

Schließlich bewirkt der Befehl JMP -2, daß die Programmausführung in der Zelle des MOV fortesetzt wird. Der kopiert nun wie zuvor die DAT-Instruktion in die Zelle, auf die der DAT-Pointer zeigt, das ist inzwischen die Zelle mit der Adresse 796.

Wenn der Core wie üblich 8000 Zellen hat, dann wird der DAT-Pointer nach 6001 Zyklen um imsgesamt 8000 auf 8004 erhöht worden sein. Da Adressen immer modulo CORESIZE berechnet werden, entspricht das dem Ursprungswert von 4. Danach beginnt der Dwarf, den Speicher erneut mit DAT-Bomben zu überschreiben.

Imp-Factory

Dieses Programm verwendet im Gegensatz zu den ersten beiden Kämpfern nicht nur einen einzelnen Prozess, sondern erzeugt laufend weitere. Dabei werden immer mehr Imps auf den Weg geschickt:
  SPL   +0          Adresse dieser Zelle der Task Queue hinzufügen
MOV +0, +1 Imp
Adressierungsarten

Redcode-94 Referenz

../under_construction.gif
Die Angaben auf dieser Seite entstammen
dem veralteten ICWS-94 Draft V3.2
und bedürfen einer Überarbeitung.
Das Betrifft die Befehle JMN, DJN, DIV
und MOD, wenn sie mit den Modifiern
.F/.X/.I verwendet werden.
Diese Seite ist zum Nachschlagen gedacht. Detailliertere Erklärungen für Einsteiger sind ebenfalls verfügbar: Quelldateien, Adressierungsarten, Opcode Modifiers, Ausdrücke, Pseudo-Instruktionen, Direktiven

Redcode nach ICWS-94

Adress Modes

# Immediate (Unmittelbar)
$ Direct (Relativ)
@, * Indirect
<, { Predecrement Indirect
>, } Postincrement Indirect

Opcode Modifiers

.A A-value von beiden adressierten Zellen wird verwendet
.B B-value von beiden adressierten Zellen wird verwendet
.AB A-value der Zelle, die vom ersten Operanden adressiert wird, und B-value der vom zweiten Operanden adressierten Zelle wird verwendet
.BA B-value der Zelle, die vom ersten Operanden adressiert wird, und A-value der vom zweiten Operanden adressierten Zelle wird verwendet
.F Beide values der adressierten Zelle werden verwendet, quasi .A und .B zugleich
.X Beide values der adressierten Zelle werden verwendet, allerdings vertauscht. Quasi .AB und .BA zugleich
.I Die gesamte Instruktion wird verwendet

Opcodes

DAT wird verwendet, um Daten zu speichern
MOV Kopiert Daten oder ganze Instruktionen
ADD Addiert zwei Werte
SUB Subtrahiert zwei Wertw
MUL Multipliziert zwei Werte
DIV Dividiert einen Wert durch einen anderen
MOD Berechnet den Rest einer Division
JMP Unbedingter Programmsprung
JMZ Programmsprung, wenn ein Wert Null ist
JMN Programmsprung, wenn ein Wert nicht-Null ist
DJN Dekrementiert den Wert, und springt, falls er dann Null ist
CMP Bedingter Sprung, wenn zwei Werte gleich sind
SLT Bedingter Sprung, wenn ein Wert kleiner ist als der andere
SPL Erzeugt neuen Eintrag in der Task Queue

Grundlegende Definitionen

Eine Instruktion besteht aus opcode, einem opcode modifier, dem A-operand and dem B-operand.
A-operand besteht aus A-mode und A-number.
A-mode ist die Adressierungsart des A-operand.
A-number ist eine Ganzzahl von 0 bis CORESIZE-1.
B-Operand besteht aus B-mode und B-number.
B-mode ist die Adressierungsart des B-operand.
B-number ist eine Ganzzahl von 0 und CORESIZE-1.

Spezifische Definitionen

Der program counter (PC) ist der Pointer auf die Zelle im Core, deren Instruktion vom Core kopiert wurde, um sie auszuführen.
Die current instruction ist diese Instruktion als Kopie im instruction register des MARS-Prozessors.
Der A-pointer ist ein Zeiger auf die Zelle im Core, die der A-operand der aktuellen Instruktion referenziert.
Die A-instruction ist eine Kopie jener Instruktion im Core, auf die A-pointer zeigt. (Die Kopie wird vor der Durchführung der aktuellen Instruktion erzeugt)
Der A-value ist die A-number und/oder B-number der A-instruction, oder die A-instruction selbst, je nach opcode modifier.
Der B-pointer ist ein Zeiger auf die Zelle im Core, die der B-operand der aktuellen Instruktion referenziert.
Die B-instruction ist eine Kopie jener Instruktion im Core, auf die B-pointer zeigt. (Die Kopie wird vor der Durchführung der aktuellen Instruktion erzeugt)
Die B-value ist die A-number und/oder B-number der B-instruction, oder die B-instruction selbst, je nach opcode modifier.
Das B-target ist die A-number und/oder B-number der Instruktion, auf die B-pointer zeigt, oder die Instruktion selbst, je nach opcode modifier.

Alle MARS-Instruktionen werden nach folgendem Schema ausgeführt:

  1. Der oberste instruction pointer der Task Queue des aktuellen Warriors wird von der Queue extrahiert (entfernt) und im Register program counter gespeichert.
  2. Die entsprechende Zelle des Core wird in das current instruction-Register kopiert.
  3. Der A-operand wird ausgewertet.
  4. Die Ergebnisse der Auswertung des A-operand, der A-pointer und die A-instruction werden in den entsprechenden Registern abgelegt.
  5. Der B-operand der aktuellen Instruktion wird ausgewertet.
  6. Die Ergebnisse dieser Auswertung werden in die Register B-pointer und B-instruktion gespeichert.
  7. Die Operation wird durchgeführt, jenach opcode modifier im Register current instruction. Mit Ausnahme der DAT-Instruktion wird ein aktualisierter instruction pointer dem Ende der Task Queue hinzugefügt. (Wie der IP aktualisiert wird, hängt von der Ausführung der Instruktion ab)
Alle Pointer sind relativ zum PC (IP der aktuellen Instruktion). Sie stellen einen Offset zu der Speicherzelle des Core dar, in der sich die current instruction im Core befindet. Alle arithmetischen Operationen werden modulo CORESIZE gerechnet, wobei negative Werte von CORESIZE abgezogen werden. Des Weiteren werden alle Lesezugriffe modulo READLIMIT und alle Schreibzugriffe modulo WRITELIMIT durchgeführt.

Adress Modes

Immediate #

Ein immediate mode operand dient eigentlich nur dem Speichern von Werten. Eine immediate A/B-mode setzt die A/B-pointer auf Null.

Direct (PC-Relative) $

Ein direct mode operand wird als Offset zum Program Counter verwendet. Eine direct A/B-mode in der current instruction bedeutet, daß A/B-pointer eine Kopie des Offset sind, also die A/B-number von current instruction. Die angesprochene Adresse ist also PC + A/B-number.

Indirect *, @

Ein indirect mode operand bedeutet, daß die adressierte Zelle indirekt ermittelt werden soll. B-value von der angesprochenen Zelle wird als sekundärer Offset verwendet, das Ziel einer indirekten Adressierung ist von der angegeben Zelle so weit entfernt, wie durch den B-value jener Zelle, die durch den primären Offset beschrieben wurde. Die angesprochene Adresse ist PC + A/B-number + B-value.

Wird als adress mode ein Stern "*" angegeben, dann wird A-value als sekundärer Offset verwendet, im ICWS-94 ist aber nur der adress mode "@" definiert. Bei * ist die angesprochene Zelle dann PC + A/B-number + A-value.

Predecrement Indirect <, {

Ein predecrement indirect mode operand bewirkt eine indirekte Berechnung der Adresse der gewählten Zelle. B-value wird dekrementiert, und als sekundärer Offset verwendet. Die Adresse der angesprochenen Zelle ist also PC + A/B-number + B-value - 1.

Wird als adress mode eine öffnende geschwungene Klammer angegeben, dann wird A-value als sekundärer Offset verwendet, die Adresse ist dann PC + (A/B-number - 1) + A-value.

Postincrement Indirect >, }

Ein postincrement indirect mode operand bewirkt eine indirekte Berechnung der Adresse der gewählten Zelle. B-value der angegeben Zelle wird als sekundärer Offset verwendet, danach wird B-value inkrementiert. Die Adresse der angesprochenen Zelle ist PC + A/B-number + B-value.

Wird als adress mode eine schließende geschwungene Klammer angegeben, dann wird A-value als sekundärer Offset verwendet, die Adresse ist dann PC + A/B-number + A-value.

Opcode Modifiers

.A

Vor Ausführung der Instruktion werden A-value auf den Wert der A-number der Instruktion im Register A-instruction und B-value auf den Wert der A-number vom B-instruction-Register gesetzt. Eine Schreiboperation in den Core ersetzt die A-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.A A-number der A-instruction mit A-number der B-instruction vergleichen. Ein MOV.A würde die A-number der Zelle, auf die B-pointer zeigt, mit dem Wert A-number der A-instruction ersetzen.

.B

Vor Ausführung der Instruktion werden A-value auf den Wert B-number der A-instruction und B-value auf den Wert B-number der B-instruction gesetzt. Eine Schreiboperation in den Core ersetzt die B-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktino CMP.B die B-number der A-instruction mit B-number der B-instruction vergleichen. Ein MOV.B würde die B-number der Zelle im Core, auf die B-pointer zeigt, mit der B-number der A-instruction ersetzen.

.AB

Vor Ausführung der Instruktion werden A-value auf den Wert von A-number der A-instruction und B-value auf den Wert B-number der B-instruction gesetzt. Schreiboperationen in den Core ersetzen die B-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.AB die A-number der A-instruction mit der B-number der B-instruction vergleichen. Ein MOV.AB würde die B-number der Instruktion, auf die B-pointer zeigt mit dem Wert von A-number der A-instruction ersetzen.

.BA

Vor Ausführung der Instruktion werden A-value auf die B-number der A-instruction und B-value auf die A-number der B-instruction gesetzt. Schreiboperationen in den Core ersetzen die A-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.BA die B-number der A-instruction mit der A-number der B-instruction vergleichen. Ein MOV.BA würde die A-number der Instruktion, auf die B-pointer zeigt, mit dem Wert B-number der A-instruction ersetzen.

.F

Für die Ausführung der Instruktion wird A-value zuerst auf A-number und dann auf B-number der A-instruction gesetzt, B-value wird erst auf A-number und dann auf B-number gesetzt. Schreiboperationen ersetzen zuerst A-number und dann B-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.F die A-number der A-instruction mit der A-number der B-instruction vergleichen und dann B-number der A-instruction mit B-number der B-instruction. MOV.F würde die A-number der Instruktion, auf die B-pointer zeigt, mit der A-number der A-instruction sowie die B-number der Instruktion, auf die B-pointer zeigt, mit B-number der A-instruction ersetzen.

.X

Für die Ausführung der Instruktion wird A-value zuerst auf A-number und dann auf B-number der A-instruction gesetzt, B-value wird erst auf B-number und dann auf A-number gesetzt. Schreiboperationen ersetzen zuerst B-number und dann A-number der Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.X die A-number der A-instruction mit der B-number der B-instruction vergleichen und dann B-number der A-instruction mit A-number der B-instruction. MOV.X würde die B-number der Instruktion, auf die B-pointer zeigt, mit der A-number der A-instruction sowie die A-number der Instruktion, auf die B-pointer zeigt, mit B-number der A-instruction ersetzen.

.I

Vor Ausführung der Instruktion wird die A-instruction in A-value und die B-instricion in B-value kopiert. Schreibzugriffe auf den Core ersetzen die gesamte Instruktion, auf die B-pointer zeigt.

So würde die Instruktion CMP.I die A-instruction mit der B-instruction vergleichen. MOV.I würde die Instruktion, auf die B-pointer zeigt durch A-instruction ersetzen.

Opcodes

DAT

Nach der evaluierung der Operanden findet keine weitere Verarbeitung statt. Dadurch wird der aktuelle Task von der Task Queue des aktuellen Warriors entfernt.

MOV

MOV ersetz das B-target mit A-value und hängt die nächste Instruktion (PC + 1) an die Task Queue an.

ADD

ADD ersetzt das B-target mit der Summe von A-value und B-value (A-value + B-value) und hängt die nächste Instruktion (PC + 1) an die Task Queue an. ADD.I funkioniert genauso, wie ADD.F funktionieren würde.

SUB

SUB ersetzt das B-target mit der Differenz von B-value und A-value (B-value + A-value) und hängt die nächste Instruktion (PC + 1) an die Task Queue an. SUB.I funkioniert genauso, wie SUB.F funktionieren würde.

MUL

MUL ersetzt das B-target mit dem Produkt von A-value und B-value (A-value * B-value) und hängt die nächste Instruktion (PC + 1) an die Task Queue an. MUL.I funkioniert genauso, wie MUL.F funktionieren würde.

DIV

DIV ersetzt das B-target mit dem ganzzahligen Ergebnis der Division von A-value durch B-value (A-value / B-value) und hängt die nächste Instruktion (PC + 1) an die Task Queue an. DIV.I funkioniert genauso, wie DIV.F funktionieren würde. Falls A-value Null ist, bleibt B-value unverändert und der aktuelle Task bleibt von der Task Queue entfernt.

MOD

DIV ersetzt das B-target mit dem Rest der Division von A-value durch B-value (A-value % B-value) und hängt die nächste Instruktion (PC + 1) an die Task Queue an. MOD.I funkioniert genauso, wie MOD.F funktionieren würde. Falls A-value Null ist, bleibt B-value unverändert und der aktuelle Task bleibt von der Task Queue entfernt.

JMP

JMP fügt die Summe von program counter und A-pointer der Task Queue hinzu.

JMZ

Wenn B-value Null ist, wird die Summe von program counter und A-pointer, andernfalls program counter + 1 der Task Queue hinzugefügt. JMZ.I funktioniert, wie JMZ.F funktionieren würde. So würde der Sprung ausgeführt, wenn A-number sowie B-number der B-instruction Null sind.

JMN

Wenn B-value nicht Null ist, wird die Summe von program counter und A-pointer, andernfalls program counter + 1 der Task Queue hinzugefügt. JMN.I funktioniert, wie JMN.F funktionieren würde. So würde der Sprung ausgeführt, wenn A-number sowie B-number der B-instruction nicht Null sind. Dies ist nicht die Umkehrung von JMZ.F!

Achtung: pmars behandelt JMN.F anders; der Sprung erfolgt auch, wenn nur einer der beiden Werte A-number oder B-number ungleich Null ist!

DJN

Dekrementiert B-value des B-target und prüft dann auf Null. Wenn der dekrementierte B-value nicht Null ist, wird die Summe von program counter und A-pointer, andernfalls program counter + 1 der Task Queue hinzugefügt. DJN.I funktioniert, wie DJN.F funktionieren würde. So würden A-number sowie B-number von B-value und B-target dekrementiert und dann der Sprung ausgeführt, wenn A-number sowie B-number der B-instruction nicht Null sind.

Achtung: pmars behandelt DJN.F anders; der Sprung erfolgt auch, wenn nur einer der beiden Werte A-number oder B-number ungleich Null ist!

CMP

CMP verlgleicht A-value mit B-value. Wenn beide Werte gleich waren, wird die übernächste Instruktion (PC + 2) der Task Queue hinzugefügt (Die nächste Instruktion wird übersprungen). War das Ergebnis ungleich, wird die nächste Instruktion (PC + 1) der Task Queue hinzugefügt.

SLT

SLT vergleicht A-value mit B-value. Wenn A-value kleiner als B-value ist, wird die übernächste Instruktion (PC + 2) der Task Queue hinzugefügt (Die nächste Instruktion wird übersprungen). Andernfalls wird die nächste Instruktion (PC + 1) der Task Queue hinzugefügt. SLT.I funktioniert wie SLT.F.

SPL

SPL fügt die nächste Instruktion (PC + 1) der Task Queue hinzu. Wenn die Queue noch nicht voll ist, wird auch noch die Summe von program counter und A-pointer der Task Queue hinzugefügt.

Redcode Quelldateien

Man kann zwar mit ARES seine Programme direkt in den Core eingeben, was praktisch ist, wenn man kurz etwas ausprobieren will. Meistens wird man seine Kampfprogramme aber in Dateien speichern. Vor dem Programmstart werden diese Dateien von einem Programm (dem "Assembler") eingelesen, ausgewertet und als Maschinencode im Hauptspeicher des MARS abgelegt.

Load File Format

Die Quelldateien können Redcode-Anweisungen beinhalten, wie sie auch im Monitor angezeigt werden. Wenn es sich um reinen Redcode handelt, dann spricht man von einem "Load File" - alle aktuellen MARS-Programme können solche Dateien einlesen und übersetzen. Der Dwarf sieht im LoadFile-Format so aus:
  MOV +3, @+3 
ADD #4, +2
JMP -2
DAT #0

Präprozessor

Solche Quellcodes sind nicht besonders übersichtlich, besonders bei längeren Programmen. Darum erlauben viele Assembler weitere Angaben, die die Lesbarkeit der Programme verbessern und dem Programmierer Arbeit ersparen. Vor der eigentlichen Übersetzung in den Core werden diese zusätzlichen Angaben ausgewertet und in reine Redcode-Anweisungen, wie sie in einem Load File vorkommen könnten, übersetzt. Solche Quellcodes können schon sehr komplex aussehen und können an den Code einer Hochsprache erinnern. Der Dwarf kann auch so aussehen:
  ;redcode 
;name Dwarf
;strategy bombs every fourth cell
;assert (CORESIZE % 4 == 0)

STEP EQU 4

ORG loop

loop MOV bomb, @ptr ; move bomb to target position
ADD #STEP, ptr ; increment pointer
JMP loop ; infinite loop
bomb
ptr DAT #0 ; ptr is also used as bomb

END
Leerzeilen werden bei der Übersetzung einfach ignoriert, ein Strichpunkt (";") bedeutet, daß der Text danach ein Kommentar ist. Wenn man seine Programme gut Kommentiert, kann man sie sehr viel leichter wieder verstehen, wenn man sie ein Jahr später liest. Manche Kommentare haben allerdings eine Sonderbedeutung, es handelt sich um die Direktiven, wie in diesem Beispiel die Direktive ;assert. Die Anweisungen ORG/END und EQU sind sogenannte "Pseudo-Instruktionen". Sie werden nicht in den Core geladen, sondern geben dem Assembler lediglich Zusatzinformationen, wie das Programm zu übersetzen ist.

Bezeichner und Sprungmarken

Manchen Befehlen ist ein sogenannter "Bezeichner" ("Label") vorangestellt, wie etwa loop in der Zeile mit der ersten "echten" Instruktion. Labels sind case sensitive, man muß also auf die Groß/Kleinschreibung achten. Es gibt zwei Möglichkeiten, wie Labels zum Einsatz kommen können: Man kann bestimmten Instruktionen einen Namen geben, und diesen Namen in anderen Anweisungen anstelle der relativen Adresse angeben:
  JMP  loop
Die andere Anwendung kommt in Verbindung mit der Pseudoinstruktion EQU vor. Es handelt sich um ein Text-Ersetz-Werkzeug, im obigen Beispiel des Dwarf wird der Text STEP in den Instruktionen durch den Text "4" ersetzt, bevor die Instruktion in den Core geladen wird.

Ausdrücke

Um dem Programmierer noch mehr Flexibilität zu geben, darf man einfache mathematische Ausdrücke verwenden. Die Zeile
  imp    MOV    imp,   imp+1
würde bei der Auswertung der Sprungmarken umgewandelt in:
  MOV    +0,   +0+1
und nach der Auswertung der Ausdrücke in:
  MOV  +0,  +1
Dieser Text kann vom Ladeprogramm übernommen und im Core gespeichert werden.

Kommentare

[...]

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 (Zahlen) 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") 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, daß 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
&3403 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, Kopierbefehle würden immer nur die selben Speicherinhalte bewegen. 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 allerdings auch nur ein Fünftel des Speichers. Manchmal will man einen sogenannten Coreclear 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 drei neue Adressierungsarten, nämlich:

Adress Modes in Extended-94
Symbol Adressierungsart
* 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
Opcode Modifiers

Opcode Modifiers

Nachdem man mit den Adressierungsarten *, @, {, <, > sowie } festlegen kann, welches Feld (A oder B) als Pointer benutzt werden soll, fehlt noch eine Möglichkeit, z. B. den ADD-Befehl dahingehend zu steuern, daß das gewünschte Feld in der Ziel-Zelle benutzt wird. Dafür wurden im Extended-94 Standard die sogenannten Opcode Modifiers eingeführt. Damit kann bestimmen, mit welchen Operanden Ziel und Quelle arbeiten sollen.

Der ADD-Befehl, wie er bisher in den Beispielen gezeigt wurde, verwendet per Voreinstellung den Modifier .AB, was bedeutet, daß sich der Operand A des ADD auf das A-Feld (vermutlich desselben ADD) bezieht, und der B-Operator mit einem B-Feld arbeiten soll.

Wenn aber ein Wert zum A-Feld einer anderen Zelle hinzuaddiert werden soll, dann muß der Befehl lauten:
  ADD.A   #5,   +1    Addiere 5 zur 3
DAT #3, #4
Verwendet man den Modifier .B, dann passiert etwas seltsames:

Vor der Ausführung:
  ADD.B   #5,   +1    Addiere 1 zur 4
DAT #3, #4
Nach der Ausführung:
  ADD.B   #5,   +1    Addiere 1 zur 4
DAT #3, #5
Der A-Operand #5 bewirkt, daß als Quelle die selbe Zelle verwendet wird (A-Pointer = 0, siehe Adressierungsarten), jedoch der Wert des B-Feldes zum jeweiligen Feld des Zieles addiert wird. Im Ziel wird hier ebenfalls das B-Feld angesprochen.

Wenn du noch nicht verwirrt bist, dann versuche vorherzusagen, was passiert, wenn man den Modifier .F benutzt - er bewirkt, das beide Felder beider adressierter Zellen benutzt werden, quasi .A und .B zugleich:
  ADD.F   #5,   +1 
DAT #3, #4
Das Ergebnis ist ein DAT #8, #5, weil als Quelle mit #5 die Speicherzelle mit dem ADD-Befehl adressiert wurde. Als nächstes kommt der Modifier .BA:
  ADD.BA  #5,   +1    1 zur 3 hinzuaddieren
DAT #3, #4
Dann wäre noch der Modifier .X, er funktioniert wie .F, jedoch werden die Felder verkreuzt, als wäre es .AB und .BA zugleich. Versuche vorherzusagen, was folgender ADD bewirkt:
  ADD.X   +1,   #1 
DAT #3, #4
Wenn du glaubst, das Ergebnis sei eine Änderung im DAT, dann hast du übersehen, daß die Adress-Modi in diesem Beispiel anders angeschrieben sind, als in den vorigen. Das Ergebnis ist ADD.X +5, #4, denn als Quelle wurde +1 angegeben, als Ziel eine immediate-Adresse (b-Pointer = 0), wodurch 3 und 4 kreuzweise zu den Werten des ADD addiert wurden.

Aufmerksame Leser werden schon bemerkt haben, daß es hier schnell zu Verwechslungen kommen kann. Das bekommt man nur mit Übung in den Griff. Im Zweifelsfall muß man sich einfach ein kleines Testprogramm schreiben, um die Wirkungsweise auszuprobieren, bevor man sein eigentliches Programm weiterschreiben kann. Weitere Information zur Wirkungsweise der Opcode Modifiers findest du auch in der Redcode-Referenz. Die Modifier gelten natürlich für alle Opcodes, haben aber nicht immer Sinn.

Solltest du jetzt nicht verwirrt sein, dann hast du vermutlich nicht versucht, jedes Beispiel ganz zu verstehen ;-)

Default Modifiers

Die folgende Liste zeigt, wie Opcodes ohne angegebe Modifier automatisch ergänzt werden, wenn man mit dem ICWS-94 Standard arbeitet. Bis auf manche Experimental Mode-Befehle verwendet pmars die selbe Tabelle. Die Default Modifiers sind so gewählt, daß sie der intuitiven Erwartung entsprechen. Es ist üblich, Modifiers nur dann anzuschreiben, wenn sie von den Defaults abweichen. Für ICWS-88 gelten andere Defaults, als für ICWS-94, die Tabelle kann man in den Optionen von ARES nachschlagen ("Conversion") oder im Draft-94.

Default Modifiers für ICWS-94 und Bluecode
Opcode A-Mode B-Mode Default
DAT,NOP #$@<>*{}& #$@<>*{}& F
MOV,CMP,SEQ,SNE # #$@<>*{}& AB
MOV,CMP,SEQ,SNE $@<>*{}& # B
MOV,CMP,SEQ,SNE $@<>*{}& $@<>*{}& I
ADD,SUB,MUL,DIV,MOD # #$@<>*{}& AB
ADD,SUB,MUL,DIV,MOD $@<>*{}& # B
ADD,SUB,MUL,DIV,MOD $@<>*{}& $@<>*{}& F
SLT,LDP,STP,PSH # #$@<>*{}& AB
SLT,LDP,STP,PSH $@<>*{}& #$@<>*{}& B
JMP,JSR,SPL,JMZ,JMN,DJN #$@<>*{}& #$@<>*{}& B
BRK,RET,POP #$@<>*{}& #$@<>*{}& B

Ausdrücke

Ausdrücke (Expressions)

Die meisten MARS-Assembler verstehen mathematische (und Boole'sche) Ausdrücke. Sie dürfen bestimmte mathematische und logische Operatoren, Klammern und Labelnamen beinhalten. Solche Ausdrücke werden bei der Übersetzung ausgewertet und die Ganzzahligen Ergebnisse in die Felder der Instruktionen eingesetzt. Sie sind nicht Teil des Maschinencodes, sondern nur Elemente des Präprozessors ("Parser"). Wenn die Option gewählt ist, dann werden bestimmte Variablen der MARS-Umgebung, für die das Programm compiliert werden soll, vorgegeben (Mehr dazu unter Pseudo-Instruktionen). Ein Beispiel soll es verdeutlichen:

Dieses Assembler-Programm wird für eine CORESIZE == 8000 übersetzt:
  loop    MOV   ptr+1,    @ptr 
ADD #ptr+4, ptr
ptr JMP loop, #CORESIZE/(ptr-loop)
Der Resultierende Code sieht dann so aus:
  MOV   $+3,   @+2 
ADD #+5, $+1
JMP $-2, #+4000
(Das Tutorial gibt Erklärungen zur Mathematik mit negativen Zahlen in Redcode)

Operatoren

Bis auf den Zuweisungsoperator ("=") versteht ARES die selben Ausdrücke, wie sie auch von pmars interpretiert werden.

Mathematische Operatoren
Symbol Operator
* Multiplikation
/ Division
% Modulo (Rest der Division)
+ Addition oder unäres Plus
- Subtraktion oder unäres Minus


Vergleichs- Operatoren
Symbol Operator
== Gleichheit
!= Ungleichheit
< Kleiner als
> Größer als
<= Kleiner oder gleich
>= Größer oder gleich


Logische Operatoren
Symbol Operator
&& logisches Und (AND)
|| logisches Oder (OR)
! unäre logische Negation (NOT)


Wie in der Mathematik üblich, haben bestimmte Operatoren Vorrang gegenüber anderen. Gleichrangige Operatoren werden von links nach rechts abgearbeitet. Vergleichs- und logische Operatoren ergeben 1 für wahr und 0 für falsch. Klammern können benutzt werden, um die Priorität zu umgehen. Die Operatoren ihrer Priorirät nach sortiert sind:

Priorität der Operatoren
Priorität Symbol Operator
1 ! - + Negation, Vorzeichen
2 * / % Multiplikation, Division, Modulo
3 + - Addition, Subtraktion
4 == !=
< >
<= >=
Vergleich
5 && AND
6 || OR


Pseudo-Instruktionen

Pseudoinstruktionen

Pseudoinstruktionen sind Opcodes, die zwar einen Einfluß auf die Übersetzung des Programmes haben, aber selbst im fertigen Programm nicht aufscheinen. Es handelt sich um die Befehle ORG, END, EQU und FOR/ROF.

ORG und END

Mit ORG <start> und END <start> kann man festlegen, bei welcher der Instruktionen das Programm beginnt, man kann den Einstprungspunkt frei wählen. Wird eine Zahl angegeben, so ist zu beachten, daß die erste "echte" Instruktion die Nummer 0 hat.
  ORG boot 

bomb DAT $0

boot
loop MOV bomb, @ptr
ADD #4, ptr
JMP loop
ptr DAT #4
Die END-Anweisung unterschiedet sich von ORG dadurch, daß jeglicher Text danach ignoriert wird: Der Assembler liest, nachdem er eine Zeile mit END vorfindet, keine weiteren Zeilen aus der Datei mehr ein.

EQU

Diese Pseudo-Instruktion funktioniert wie eine automatische Ersetzen-Funktion. Eine EQU-Anweisung muß nach einem Labelnamen stehen. Bei der Übersetzung wird jedes Vorkommen dieses Labels durch den Text, der nach dem EQU definiert wurde, ersetzt. Hierbei können einzelne Zahlenwerte angegeben werden, wie auch ganze Instruktionen. Sogar Mehrzeilige Werte können definiert werden:
  STEPSIZE     EQU   CORESIZE/100              "Einfaches" EQU

SOME_DWARF EQU MOV bomb, @+3 Multi-Line-EQU
EQU ADD #STEPSIZE, +2
EQU JMP -2

SOME_DWARF Dwarf hier einfügen
ptr DAT #4
bomb DAT $0
Wenn die entsprechende Option eingeschaltet ist (Voreinstellung), dann werden sowohl vom ARES-Assembler, wie auch von pmars folgende EQUs automatisch definiert, mit Ausnahme von READLIMIT und WRITELIMIT, welche von pmars nicht unterstützt werden:

Vorbelegte EQU Werte
Name Bedeutung
CORESIZE Größe des Core
READLIMIT Größte relative Entfernung für Lesezugriffe
WRITELIMIT Größte relative Entfernung für Schreibzugriffe
MAXPROCESSES Größe der Task-Queue eines Spielers
MAXCYCLES Untentschieden nach Anzahl Zyklen
MAXLENGTH Maximale erlaubte Instruktionen/Warrior beim Laden
MINDISTANCE Minimaler Start-Abstand zwischen den Programmen
ROUNDS Anzahl der Runden in dieser Konfrontation
PSPACESIZE Größe des Privatspeichers
VERSION Version von PMARS 93 = pmars 0.9.3
WARRIORS Anzahl der aktiven Kämpfer im Core (Bei ARES max. 2)
CURLINE Nummer der aktuellen Instruktion

Der Wert für CURLINE ist besonders: Er repräsentiert die Nummer der Instruktion, in der er verwendet wird. Die erste Instruktion hat die Nummer 0.

Auch bei Bezeichnern für EQU ist die Groß/Kleinschreibung zu beachten, CORESIZE ist also ein anderer Bezeichner als CoreSize.

FOR/ROF

Bei der FOR-Anweisung handelt es sich um eine Schleife. Der Text, der zwischen FOR und ROF steht, wird mehrmals eingefügt. Aus
  FOR 3 
DAT #1
ROF
wird:
  DAT #1 
DAT #1
DAT #1
Man kann aber, ähnlich zu EQU, dem ganzen Block einen Label voranstellen. Dieser Label dient innerhalb des Blocks als "Zählvariable", die die Nummer der aktuellen Wiederholung beinhaltet. Aus
  n FOR 3 
DAT #n
ROF
wird:
  DAT #1 
DAT #2
DAT #3
Innerhalb einer FOR-Schleife kann man aber keine gewöhnlichen Labels verwenden, eine Angabe von
  FOR 3 
lab JMP lab
ROF
würde einen "Duplicate Label Error" bewirken, weil der Text lab JMP lab dreimal hintereinander eingesetzt würde. Um innerhalb einer FOR-Schleife Labels verwenden zu können, muß man sie um die fortlaufende Nummer erweitern. Aus
  n FOR 3 
lab&n JMP lab&n
ROF
wird:
  JMP   $0 
JMP $0
JMP $0

Verschachtelte FOR-Schleifen

Innerhalb von FOR-Schleifen dürfen weitere FOR-Schleifen enthalten sein. Aus
  n FOR 3 
m FOR 2
lab&n&m DAT #n, #m+4
ROF
ROF
wird
  DAT #1, #5 
DAT #1, #6
DAT #2, #5
DAT #2, #6
DAT #3, #5
DAT #3, #6
Bei verschachtelten Schleifen gibt es Unterschiede zwischen pmars und ARES - das betrifft EQU-Deklarationen innerhalb einer Schleife. Mehr dazu unter Kompatibilität

Direktiven

Direktiven

Direktiven sind zusätzliche Angaben zum Quellcode, die nicht Bestandteil der Sprache Redcode sind. Manche Direktiven sind nur für die KoTH-Server interessant, andere für Debugger. Da diese Befehle nicht zum Redcode-Sprachumfang gehören, haben die Programmierer der Hills einfach beschlossen, daß diese Befehle als besondere Kommentare in die Quellcodes eingefügt werden sollen.

KoTH-Direktiven

Dies sind zunächst einmal Angaben zum verwendeten Redcode-Standard, sowie Namen und Author des Kampfprogramms. Des weiteren ist eine Direktive vorgesehen, die sicherstellt, daß ein Warrior nur unter bestimmten Bedingungen (z. B. eine vorgegebene CORESIZE) ausgeführt wird. Damit ein Kampfprogramm mit allen Core-Settings läuft, muß man einfach TRUE (1) angeben:
  ;redcode 
;name WARRIOR-Name
;author Der Programmierer
;assert 1
Um anzugeben, daß ein Kampfprogramm nur für eine bestimmte Speichergröße oder andere Umstände geschrieben wurde, kann man Ausdrücke der folgenden Art notieren:
  ;redcode-nop                  Nur auf Hills ohne P-Space
;assert CORESIZE == 8000 Das Programm läuft nur in einem Core mit 8000 Zellen
;assert CORESIZE % 10 == 0 Core muß ein Vielfaches von 10 Zellen haben
;assert (CORESIZE == 8000) && (MAXLENGTH >= 200)
Leider sind die KoTH-Server empfindlich, was die Formatierung dieser Befehle angeht. Es ist ratsam, solche Direktiven ganz am Anfang der Quelldatei zu notieren und das Tabulator-Zeichen nicht zu gebrauchen. Die ;redcode-Angabe muß die allererste Zeile sein, Text davor wird ignoriert. Die Codes zu dieser Angabe unterscheiden sich auch auf den verschiednen Servern. Für ARES spielen diese Direktiven in seiner aktuellen Version keine Rolle, er gibt aber noch zwei weitere KoTH-Direktiven vor, die jedoch von den Hills derzeit gänzlich ignoriert werden:
  ;version 1 
;date 23-12-2006
Es gibt noch weitere Direktiven, wie z. B. ;passwd, die von Hill zu Hill unterschiedlich oder nicht implementiert sind. Informationen dazu findet man für gewöhnlich auf der Webseite des jeweiligen Servers.

Debugger-Direktiven

ARES kennt zwei weitere Direktiven, die für die Fehlersuche von Nutzen sind:
  loop   JMP   target    ;break    Breakpoint für diesen Befehl aktivieren
ptr DAT #n ;watch Diese Zelle der "Watches"-Liste hinzufügen

Send-To-Hill Direktiven

Wer Warriors für bestimmte Hills schreibt, kann folgende Angaben machen, die voreingestellten Werte der Send-To-Hill Funktion werden dadurch übergangen:
  ;hill <adresse>     An einen bestimmten Hill senden
;reply <adresse> Bestimmte Absenderadresse verwenden
../under_construction.gif

Glossar


A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z

A

A-Field

A-Value

A-Pointer

Adress Mode

Adresse

Adressierung

ALU

B

B-Field

B-Value

B-Pointer

Bombe

C

Code

CPU

Cursor

D

DJN-Stream

F

Funktion (Funktions-Aufruf)

D

Dwarf

I

Imp

Instruction Pointer

IP

L

Label

Loop

M

MARS

Multitasking

O

Offset

P

Port

Programm

Programmschleife

Prozedur

Prozess

Prozessor

Puffer (-speicher)

Q

Quellcode

R

Register

Relative Adressierung

Routine

S

Schleife

Source

Source Code

Speicher

Speicherzelle

Sprungmarke

Statusregister

Stream

Stone

U

Unterprogramm

T

Task

Tastaturprozessor

Tastaturpuffer

Textcursor (Texteingabe-Cursor)

W

Warrior

Z

Zeus

Core War Trickkiste

Hier sollen eines Tages fortgeschrittene Core War-Techniken besprochen werden. Außerdem gibt es im Netz einige sehr gute Seiten, die tiefer in die Materie eindringen. Wenn du das Tutorial dieser Homepage bereits durchgearbeitet hast, dann solltest du dir die Links der Abschnitte "Pflichtlektüre" und "Nach dem Tutorial" zu Gemüte führen.

Wenn du einmal nicht weiter weißt, dann kannst du dich in den IRC-Kanal #corewars auf dem Server irc.koth.org einloggen. Falls du keinen IRC-Client hast, kannst du Plouf's Java IRC Client Applet benutzen:



In diesem Chatroom herrscht manchmal Totenstille, gelegentlich dauert es auch länger, bis jemand reagiert. In diesem Fall zahlt es sich aber wirklich aus, geduldig zu sein, denn die Leute dort helfen gerne und sind zumeist besonders kompetent.

PJIRC HomepageExternal Link (Herstellerseite des Java-IRC-Programmes)




Der folgende Text ist nur mein Spickzettel, auf dem ich Material für diese Seite zusammentrage.

Stone-Steps

Der Dwarf überschreibt den Speicher vor sich in Schritten von 4, wodurch er leicht von Scannern aufgespürt werden kann. Verwendet er aber in einem Core mit 8000 Zellen eine STEPSIZE von z. B. 3044, so verschleiert er seine Position im Speicher, obwohl er ebenfalls letztlich ebenfalls jede vierte Zelle attackiert. Solche Abarten des Dwarf werden übrigens "Stone" genannt:
  stone   MOV   #bomb,   @ptr 
ADD #3044, ptr
JMP stone
bomb
ptr DAT #0

A-Driven Imp

  MOV   #1,   @0

Shrapnel Bomb

Führt der Gegner diesen oder einen ähnlichen DAT-Befehl aus, so wird nicht nur der Prozess beendet, sondern der umliegende Code wird auch noch in Mitleidenschaft gezogen.
  DAT    <+1,   <-1 

Stun Bomb

  SPL   #0

DJN-Stream

  DJN.F   #0,   {0

A.R.E.S. Benutzerhandbuch

Willkommen, Redcoder! Mit dem Active Redcode Elaboration System steht dir ein luxuriöser Debugger für Redcode-Programme zur Verfügung. ARES wurde schon als "Swiss Army Knife for Core War" bezeichnet, weil er sehr viele Funktionen in einem Programm vereint.

Dieses Handbuch beschreibt nicht jedes Detail von ARES, weil die Benutzerschnittstelle noch nicht endgültig festgelegt ist. Sobald ARES als Gammaversion vorliegt, wird hier auch eine Auflistung aller Funktionen veröffenlicht.

Das Handbuch besteht zur Zeit aus folgenden Abschnitten:

Kapitel des ARES-Handbuches
Seite Beschreibung
Hauptfenster Manipulation von Speicherinhalten, Schrittweise Ausführung der Instruktionen
Editor Bearbeiten von Quelltexten
Benchmark Vergleich eines Warriors mit einem Set von Referenzprogrammen
Experimental Mode Hermes und Bluecode
Konfiguration Der Optionendialog von ARES
Kompatibilität Details für Profis, Bekannte Fehler

Elemente des Hauptfensters

Warriors (A)

Im Bereich "Warriors" werden die Dateinamen der beiden Gegner festgelegt. Du kannst den Dateinamen direkt eintippen oder mit einer Datei-Auswahlbox festlegen, indem du die Eingabezeile doppelklickst. Ein Doppelklick auf einen der beiden Buchstaben, "A" bzw. "B", links neben der Eingabezeile, löscht deren Inhalt. "New" bzw. "Edit" öffnet den Editor.

Arena (B)

Compile + Run

Ein Klick auf diesen Button lädt die definierten Warriors in den Core und startet die Simulation. Wenn du die Programm nur übersetzen lassen willst, ohne sie zu starten, dann benutze die Funktion "Compile to Core" (Menu "File"), der voreingestellte Shortcut ist Strg-F9.

Tournament

In dem Popupfenster müssen zuerst einmal die gewünschten Kampfprogramme definiert werden. In der Datei-Auswahlbox unter "Add Competitors" ist eine Mehrfachauswahl möglich. Diese Funktion ist inzwischen veraltet und soll in Zukunft durch eine bessere Variante ersetzt werden.

Queues (C)

Dieser Bereich zeigt die Task Queues der laufenden Simulation. Wenn zwei Kämpfer definiert sind, wird die Simulation unterbrochen (BREAK), sobald eine Queue leer wird. Du kannst eine oder mehrere der eingetragenen Adressen durch Anklicken selektieren. Wenn dann der "STEP"-Button benutzt wird, läuft die Simulation solange weiter, bis einer der selektierten Prozesse ganz oben in der Queue steht. Ein Rechtsklick hebt alle Selektionen auf.

Debugger

Control Block (D)

Mit den Schalftflächen "Run", "Break", "Continue" und "Step" wird der Ablauf der Simulation gesteuert. Sofern noch keine Einträge in den Task Queues vorhanden sind, werden die Adressen aus den Texteingabefeldern des "Trace Block" (E) als Startwerte in die Queues übertragen, wenn man auf "Run" klickt. Adressen können in diese Felder direkt eingegeben oder mittels "Set Entry Point" aus dem Monitor (G) übernommen werden. Dabei wird die Adresse der zentrierten Zelle (roter Rahmen) in das Start-Adress-Eingebefeld übertragen, das mit dem Radio-Button links davon selektiert ist. Ist die jeweilige Checkbox "trace program A/B" aktiviert, so wird die Ausführung der Prozesse des jeweiligen Spielers im Monitor verfolgt (Siehe auch 1.3). Während einer Debug-Sitzung kann die Zelle mit der Adresse des aktuellsten Instruction Pointer im Monitor zetriert werden, indem man auf die freie Fläche zwischen Startadresse und "trace Program A/B" klickt. Ist das Programm in den Core compiliert worden, dann wird die Ausführung auch im Editor verfolgt; ist der Editor geöffnet, dann wird die aktuelle Instruktion markiert, wodurch man der Ablauf im Quelltext und im Monitor zugleich beobachten kann.

Monitor (G)

Dieser Bereich zeigt den Inhalt des Core in disassemblierter Form an. Die linke äußere Spalte zeigt die Break- (rot) und Tracepoints (gelb) an; ist eine Zelle hier markiert, dann wird die Simulation unterbrochen, sobald der Befehl in dieser Zelle ausgeführt werden soll. Tracepoints wirken nur, wenn der Prozess mittels Trace (E) verfolgt wird. Breakpoints können im Quelltext mittels ;break gesetzt werden. Break- und Tracepoints können durch Anklicken des Kreises im Monitor auch während der Simulation verändert werden. Wenn sich der Mauscursor über dem Monitor befindet, dann kann man mit dem Mausrad scrollen.

Die Farben der Zellen im Monitor zeigen die "Ownership" an - sie werden eingefärbt, nachdem ein Warrior darauf zugegriffen hat. Des Weiteren werden die aktuellen Schreib/Lese/Exekutionszugriffe visualisiert, indem die jeweiligen Zellen farbig hinterlegt werden. Zellen, die vom A-Operanden referenziert wurden, werden in einer helleren Abstufung der Spielerfarbe hinterlegt, Zellen des B-Operanden etwas dünkler. Der zuletzt ausgeführte Befehl wird mit dunkler Spielerfarbe, den nächste Befehl (Topmost queued IP) mit rötlicher Hintergrundfarbe markiert. Ein einfacher Klick mit der linken Maustaste zeigt A/B-Pointer und A/B-value der entsprechenden Zelle in der Statuszeile an.

Die Cursortasten, [Bild ↑] und [Bild ↓] sowie das Mausrad scrollen den Inhalt des Monitors. Man kann die zentrierte Zelle (roter Rahmen) editieren, nachdem man die [Return]-Taste gedrückt hat, Abstände dürfen dabei völlig entfallen. Mit der rechten Maustaste erhält man ein Kontextmenu, das die angeklickte Zelle als Startadresse im Control Block setzen, wie auch eine Zelle den Watches hinzufügen läßt.

Eine Funktionen ist nur über das Kontextmenu erreichbar: "Show Corresponding Line in Editor". Wenn die Zelle unter dem Maus-Cursor einen Befehl beinhaltet, der durch Übersetzung aus einer Datei entstanden ist, dann kann man die entsprechende Zeile im Quelltexteditor anzeigen lassen.

Watches (H)

Um besseren Überblick über die "Variablen" eines Programmes zu haben, kann man in diesem Bereich ausgesuchte Zellen des Core anzeigen lassen. Zellen, die hier angezeigt werden, können ebenfalls als Break Conditions verwendet werden; diese werden durch einen Klick auf die Schaltfläche "Edit Watch" festgelegt. So kann man ein Programm laufen lassen, bis eine bestimmte Situation eintritt. Bestimmte Instruktionen können im Quellcode mit der Direktive ;watch markiert werden. Bei der Assemblierung werden die entsprechenden Zellen des Core automatisch in die Watches-Liste eingetragen. Solange die Watches ausgeblendet sind, werden die Break Conditions ignoriert.

P-Space (I)

Hier wird der Inhalt der P-Spaces angezeigt.

Core View (J)

Der Core View bietet einen groben Überblick über die Geschehnisse im Speicher. Wenn man mit der Maus auf eine Zelle zeigt, wird der Inhalt dieser Zelle neben dem Mauscursor eingeblendet. Ein einzelner Klick in den Core View zeigt die entsprechende Zelle im Monitor, ein Doppelklick läßt den Inhalt der Zelle editieren. Ein Klick mit der Rechten Maustaste schaltet in den "fast mode" um, alle Anzeigen des Debuggers werden ausgeblendet und die Core-Animation der Speicherzugriffe wird vorrübergehend auf schnell gestellt. Ab der Zoom-Stufe 10x können zu den farbigen Quadraten auch Buchstaben angezeigt werden, die die Instruktion repräsentieren, die in der jeweiligen Zelle gespeichert ist. Die Hintergrundfarbe zeigt an, welches Programm zuletzt auf die Zelle zugegriffen hat, die Farbe des Buchstaben zeigt an, wer der "Owner" dieser Zelle ist: Ein Programm "besitzt" eine Zelle, wenn die Instrukion zum Übersetzten Programmcode gehört, oder eine Kopie mittels MOV aus diesem ursprünglichen Code ist. Wenn sich der Mauscursor über dem Core View befindet, und nur ein Ausschnitt des Core angezeigt wird, dann kann man mit dem Mausrad scrollen.

Statuszeile (K)

Während der Programmausführung wird hier angezeigt, wieviele Zyklen bereits durchlaufen wurden, wie auch die Anzahl der Prozesse, Kills und Suicides der jeweiligen Warriors. Des Weiteren wird am rechten Ende der Statuszeile angezeigt, welche Schalter ein- bzw. ausgeschaltet sind. Die Buchstaben entsprechen den voreingestellten Shortcuts. Großbuchstaben zeigen an, daß die Funktion aktiviert ist.

Editor

Editor

Menufunktionen

Während der Editor geöffnet ist, stehen einige Befehle des Hauptmenus nicht zur Verfügung, dafür sind die Funktionen des Menu "Edit" aktiviert. Zwei dieser Befehle sind besonders interessant, nämlich "Find Cell in Monitor" und "Send To Hill".

Find Cell in Monitor

Wenn der Warrior bereits in den Speicher compiliert wurde, kann über diese Funktion die erste Zelle des Core gefunden werden, die aus jener Zeile des Quellcodes erzeugt wurde, in der sich der Textcursor befindet.

Send to Hill

Die Option "Send To Hill" ist über das "Edit"-Menu erreichbar, sie steht daher nur zur Verfügung, wenn zuvor eine Datei zur Bearbeitung geöffnet wurde. Der Text wird aus dem Editor in das Send-To-Hill Fenster übertragen und kann dort vor dem Hinaufladen angepaßt werden. Um diese Funktion zu benutzen, müssen der SMTP-Host sowie eventuell nötige Logindaten im Optionen-Dialog festgelegt worden sein. Die eMail-Adresse des Hills und die Reply-Adresse können ebenfalls nachträglich angepaßt werden.

Es ist ratsam, seinen Warrior zuerst mit der Benchmark-Funktion gründlich zu prüfen, bevor man ihn an den Hill sendet, denn manche Hills geben den hinaufgeladenen Kampfprogrammen eine maximale Lebenszeit (Anzahl der Konfrontationen). Wenn man seinen Warrior immer wieder verbessert und hinauflädt, dann steigt das Alter der anderen Programme unnötig, und sie könnten sogar vom Hill geworfen werden.

Weitere Funktionen

Wenn der Texteingabecursor auf einem Schlüsselwort steht, wird ein Kurzhilfetext in der Statuszeile angezeigt.

Die größe des Editors kann geändert werden, indem man den Button mit der Beschriftung ">" rechts oben anklickt, oder den Shortcut für "ExpandEd" benutzt. Dieser ist auf F2 voreingestellt.

Benchmark

Browser und Tutorial

ARES benutzt eine Browser-Komponente, um Webseiten anzuzeigen. Leider wird damit automatisch der Internet Explorer geladen, auch wen im System ein anderer Standardbrowser eingetragen ist. Bookmarks, die eine Seite im externen Browser öffnen, laden diese Seite aber mit dem Standardbrowser.

Interaktives Tutorial

ARES ermöglicht die Programmierung eines interaktiven Tutorials. Er benutzt dazu gewöhnliche HTML-Seiten, die besondere Befehle in einem auskommentierten Bereich des Quellcodes beinhalten. Derzeit sind nur zwei kleine Test-Tutorials eingebaut, die einen groben Überblick über die Benutzerschnittstelle von ARES vermitteln. Das Tutorial funktioniert nur im integrierten Browser.

Web-Browser

Über das Bookmark-Menu können verschiedene Internet-Seiten aufgerufen werden. Manche davon werden in den integrierten Browser von ARES geladen, andere öffnen die Seite im Standardbrowser des Betriebsystems. Die Bookmarks können im Optionen-Dialog angepaßt werden. Man kann frei wählen, ob eine Seite mit dem Standardbrowser oder in ARES geöffnet werden soll.

Zusätzliche Funktionen

In der Titelleiste des Browsers befinden sich die Schaltflächen "close", "back" und "open in browser". Ein Klick auf "open in browser" lädt die aktuelle Seite mit dem Standardbrowser des Betriebsystems. Diese Funktionen sollen demnächst in das Menu übersiedelt werden ("Browser" anstelle von "Bookmarks").

Benchmark

Wenn man ein Kampfprogramm geschrieben hat, dann möchte man sicherlich etwas über seine Stärke wissen. Dazu kann man die (alte und langsame) Tournament-Funktion von ARES verwenden, aber eine Prüfung gegen eine Liste mit Referenz-Kampfprogrammen ist sinnvoller mit dem Programm pmars, denn es läuft ohne grafische Ausgabe im Hintergrund und ist dadurch sehr viel schneller. Um den Benchmark mit pmars bequem ausführen zu können, wurde eine spezielle Schnittstelle in ARES integriert.

Drei unterschiedliche Sets von Benchamark-Sammlungen werden mitgeliefert, denn die Ergebnisse hängen von den Gegnern ab, mit denen man seine eigenen Warriors testen läßt. Wenn dein Warrior mit dem voreingestellten Set einen Punktewert von 75 bis 100 Punkte erreicht, dann hat er gute Chancen, es auf den Beginner's Hill von corewar.info zu schaffen.

Wird die Benchmark-Funktion vom Editor aus aufgerufen, so wird der Dateiname des gerade bearbeiteten Warriors übernommen, ansonsten muß zuerst einmal das zu testende Kampfprogramm gewählt werden (Schaltfläche "Select"). Um ein anderes Benchmark-Set zu wählen, kannst du das entsprechende Verzeichnis mittels "Select"-Button auswählen oder direkt eingeben (Der "Update List" Button lädt dann die Dateinamen). Der Benchmark wird nur gegen jene Programme durchgeführt, die in der mittleren Liste selektiert sind. Die Einstellungen des MARS können für den Benchmark angepasst werden, Experten können die pMARS-Kommandozeilenparameter direkt anpassen. Wenn pro Konfrontation nur 100 Runden (Voreinstellung) gewählt werden, dann ist das Ergebnis nicht besonders genau. Wer es wirklich ganz genau wissen will, sollte 300 bis 1000 Runden verwenden. Das Endergebnis wird in jedem Fall auf einen 100 Runden ensprechenden Wert gekürzt.

Durch Klicken auf die Schaltfläche "Start" tritt der Warrior der Reihe nach mehrmals gegen die bestimmte Anzahl der Benchmark-Kampfprogramme an, das Ergebnis wird in der Statuszeile des Benchmark-Fensters ausgegeben.

Experimental Mode

Experimental Mode

ARES ist nicht nur ein Debugger für Core War-Spieler, sondern auch ein Experimentier-Baukasten für Leute, die sich mit der grundlegenden Funktionsweise eines Computers auseinandersetzen wollen. Die Erweiterung heißt "Hermes" (eine Anspielung auf den Begriff HermeneutikExternal Link) und bietet zusätzliche Funktionen, die herkömmliche MARS-Programme nicht beinhalten. Vermutlich ist ARES nicht der einzige MARS, der einen erweiterten Befehlssatz kennt. Es gibt eine Sammlung von VorschlägenExternal Link für mögliche (und auch unmögliche) weitere Opcodes. Ich habe beschlossen, alle Befehle und Erweiterungen, die nicht für das Spiel Corewar gedacht sind, als "Bluecode" zu bezeichnen. Manche Bluecode-Befehle könnten zwar auch im Spiel benutzt werden, sind aber von pmars nicht implementiert und gehören daher nicht zum Extended-94 Standard.

ARES bietet im Experimenal-Modus folgende zusätzliche Möglichkeiten:
  • Absolute Adressierung (Adress mode indicator "&")
  • Stack (PSH, POP)
  • Subroutines (Unterprogrammaufrufe mittels JSR und RET)
  • Virtuelle Hardware ("Console": Tastaturprozessor und Text-Bildschirm)
  • mitgeliefertes "Mikro-Betriebsystem", das eingetippte Zeichen am Bildschirm anzeigt

Hermes Extension

Aktivieren

Die Funktionen der Hermes-Erweiterung stehen nur zur Verfügung, wenn man den Simulator in den "Experimental Mode" umschaltet. Das macht man im Optionen-Dialog unter MARS/MARS accepts/Bluecode. Nachdem man auf den Experimental Modus umgeschaltet hat und das Optionen-Fenster verläßt, wird das Consolenfenster (der virtuelle Bildschirm) geöffnet. Ein Klick auf "Compile + Run" bootet daraufhin das "Mikro-System". Sollte das Console-Fenster einmal abhanden kommen, kann man es über das "Window"-Menu wieder herbeischaffen (Voreingestellter Shortcut: Strg-F7). Um das ROM-Programm starten zu lassen, sollte man vor dem Umschalten die Einträge für die Warriors im Hauptfenster löschen: Sind beide Texteingabefelder leer, so wird der Name der ROM-Datei als "Warrior A" voreingestellt, nachdem man den Optionendialog schließt.

User-Programme

Man kann ein User-Programm als Warrior B angeben, welches zwar übersetzt, aber nicht gestartet wird, denn im Experimental Mode wird nur die Task-Queue des Spieler A verwendet. Das Hermes-ROM prüft beim "hochfahren", ob ein Userprogramm an Adresse &1000 vorhanden ist, und übergibt in diesem Fall die Kontrolle an dieses Programm. Natürlich kann man den Hermes auch ohne das ROM programmieren. Mit ARES werden zwei Bluecode-Programme ausgeliefert: Eines ist als Userprogramm für das Hermes-ROM gedacht, das andere kann alleine anstelle des ROMs geladen werden.

Besonderheiten

Wenn du den Simulator auf "Experimental Mode" schaltest, werden die letzten 400 Speicherzellen des Core als Video-RAM betrachtet: Zahlen, die dort gespeichert werden bewirken, daß die "Grafikkarte" entsprechende Farben und Zeichen anzeigt. Der Speicherbereich des "VRAM" wird im Monitor-View blau angezeigt. Oberhalb des VRAM befinden sich an den Adressen VRAM_START-1 und VRAM_START-2 die Register der Video-Hardware. Zahlen, die dorthin gespeichert werden, bestimmen die Hintergrundfarbe der Console, die Farbe des Rahmens und Position sowie Sichbarkeit des Texteingabe-Cursors. Diese beiden Speicherzellen werden im Monitor grün dargestellt. An der Adresse VRAM_START-3 befindet sich der Port zum Tastaturprozessor. Wird im Console-Fenster eine Taste gerückt, so wird das Zeichen von einem virtuellen Tastaturprozessor zwischengespeichert. Das erste Zeichen wird in den Hauptspeicher übertragen (Adresse VRAM_START-3), weitere Zeichen werden in einem Tastaturpuffer zwischengespeichert. Wenn ein Programm das Zeichen aus dem Tastaturport ausgelesen und verarbeitet hat, muß es den Zahlenwert 0 in die Speicherzelle des Tastaturports schreiben, was den Tastaturprozessor anweist, das nächste Zeichen aus seinem Puffer in den Port zu übertragen.

Bluecode

Damit die Programme für den Experimental-Mode etwas näher an die Wirklichkeit echter Computer herankommen, wurden dem Prozessor besondere Eigenschaften hinzugefügt, die es im Spiel Core War nicht gibt. Es handelt sich hierbei um Absolute Adressierung (der adress mode indicator ist "&"), sowie "Stack"-Funktionen, die vom MARS-Prozessor unterstützt werden. Für Programmierer stehen somit die Befehle PSH, POP, JSR und RET zur Verfügung. Der Stack-Bereich beginnt eine Zelle oberhalb der ersten Instruktion des Programmes und wird rückwärts gezählt. Userprogramme werden deshalb an die Adresse &1000 geladen, damit Werte, die auf den Stack "gepushed" werden, nicht mit dem Video-RAM kollidieren. Die Maximale Stackgröße ist darum auch 1000 Zellen. Das wird jedoch nicht von der Hardware geprüft, denn dem Prozessor fehlt ein Statusregister. Weitere Informationen können dem kommentierten Quellcode des ROMs entnommen werden.

HERMES_mini.rom

Das Hermes-Mini-ROM ist nur ein Programmrumpf und dafür vorgesehen, von einem Lernenden erweitert zu werden. Es beinhaltet einige Unterprogramme, die Zeichenketten in der Console ausgeben oder den Bildschirm löschen. Des Weiteren gibt es ein besonderes OS-CALL Unterprogramm, das von Userprogrammen benutzt werden soll, um Funktionen des ROMs kontrolliert aufzurufen. Derzeit bietet das ROM-System nur 5 Betriebsystem-Funktionen (CLRSRC, SCROLLUP, HOME, PRNTSTR, und READKEY). Der Aufbau mag übertrieben kompliziert erscheinen, man stelle sich aber vor, das System würde 100 Funktionen bereitstellen, dann versteht man den Sinn der Funktion OSCALL.

Eine Aufgabe

Das Mini-ROM kann zwar eingegebene Zeichen auf den Bildschirm übertragen, es sorgt sogar für Scrolling, jedoch erkennt es weder die Cursror-Tasten noch Return oder TAB. Die Stelle, an der Erweiterungen für diese Tasten eingefügt werden sollen, befindet sich in Zeile 95 der Datei "HERMES_mini.rom" und ist mit einem Kommentar gekennzeichnet.

Kompatibilität

Konfiguration

Der Optionendialog wird über das Simulator-Menu aufgerufen. Er besteht aus acht Seiten ("Tabs"). Die Screenshots auf dieser Seite stammen von ARES v2.33β, das Aussehen wird sich in Zukunft noch etwas verändern. Die Erklärungen beziehen sich auf v2.34β.

Wenn ARES das erste Mal aufgerufen wird, dann werden die Verzeichnisse "ares_system" und "manual" sowie die Datei "bookmarks.txt" angelegt, sofern sie nicht ohnehin schon Vorhanden sind. ARES sollte in einem eigenen leeren Unterverzeichnis gespeichert werden, wie z. B. C:\Programme\ARES oder D:\ARES. Des Weiteren wird geprüft, ob der Font "Lucida Console" im System installiert ist. Wenn ARES diesen Font nicht finden kann, erscheint eine Dialogbox, die den Benutzer fragt, ob dieser Font installiert werden soll. Die Schriftarten für Editor und Monitor sind auf FixedSys voreingestellt, es wird aber empfohlen, Lucida Console mit 9pt zu verwenden. Manche Funktionen, wie das Installieren des Fonts funktionieren natürlich nur, wenn die entsprechenden Dateien auch vorhanden sind, ARES sollte aber auch weitgehend funktionieren, wenn nur die Datei ares.exe vorhanden ist.

Optionen

ARES bietet einige Optionen, die im Folgenden erklärt werden:

Abschnitte des Optionen Dialoges
Tab Beschreibung
MARSEinstellungen des Simulators - die "Spielregeln"
ConversionVoreingestellte Opcode Modifiers
CompilerEinstellungen zur Übersetzung von Kampfprogrammen und zur Initialisierung des Speichers
ZeusSteuerung der Oberfläche, Optionen des Debuggers
ShortcutsAlle Menubefehle können mit Tastaturkürzeln aufgerufen werden
KotHE-Mail einstellungen für die "Send-To-Hill" Funktion
BookmarksLesezeichen für Internetseiten
OthersEinstellungen, die keiner bestimmten Kategorie zuzuordnen sind

MARS

Hier kann man den Simulator konfigurieren:
  • Core Size: Größe des virtuellen Speichers (Core). Die Zahl der Instruktionen, die der MARS-Speicher aufnehmen kann.
  • Automatically copy input to limits: Wenn angewählt, dann wird der Wert für Core Size bei Änderung automatisch in die Felder Read Limit und Write Limit übertragen. Im Normalfall wird man das so wollen. Die Limits können nachträglich verändert werden.
  • Read Limit, Write Limit, visualize: Read- und Writelimit sind eine zusätzliche Erschwernis: Speicherzugriffe können nur auf Zellen erfolgen, die nicht weiter von der ausführenden Instruktion entfernt sind, als das Limit erlaubt. Man kann unterschiedliche Limits für lesende und schreibende Zugriffe festlegen, und diese im Core View anzeigen lassen ("visualize"). Aufgrund der Berechnung der Zugriffe müssen die Limits ein Teiler von Core Size sein.
  • Max. Tasks: Größe der Task-Queues. Beschreibt, wieviele Prozesse ein Kampfprogramm mit SPL erzeugen darf. Versucht ein Programm einen Task zu erzeugen, wenn die Queue schon voll ist, so wird der Versuch ignoriert.
  • P-Space Size: Ebenfalls eine Spielerweiterung. Gibt an, wieviel Privatspeicher den Programmen zur Verfügung steht (Erklärungen dazu finden sich im ICWS-94-Draft). Man kann die Größe des P-Space automatisch aus der Core Size ableiten lassen oder direkt eingeben.
  • Max. Cycles: Legt fest, wann ein Unentschieden vorliegt. Ein Cycle ist entweder ein oder zwei Ticks, wobei mit Tick die Ausführung einer Instruktion gemeint ist. Bei nur einem Spieler sind Ticks und Cycles gleichbedeutend, der Wert zeigt an, wieviele Befehle schon ausgeführt wurden. Sind zwei Programme im Speicher, so wird pro Zyklus je eine Instruktion aus beiden Task Queues ausgeführt, die Anzahl der Ticks pro Zyklus ist in diesem Falle immer doppelt so hoch, wie die Anzahl der Zyklen. Man kann die Max. Cycles automatisch auf ein Vielfaches der Core Size setzen lassen.
  • 'MOV #' creates DAT: Damit ARES zu ICWS-88 kompatibel ist, kann man einschalten, daß automatisch ein DAT-Befehl an der Zieladresse erzeugt wird, wenn ein MOV einen A-operand mit immediate adress mode ausführt. Es wird empfohlen, diese Option abzuschalten, wenn man nicht nach ICWS-88 spielt.
  • MOV copies Breakpoint: Eine Debugger-Option. Wenn eine Zelle durch eine MOV-Instruktion kopiert wird, dann wird der Breakpoint in der überschriebenen Zelle für gewöhnlich nicht verändert. Ist diese Option eingeschaltet, so wird beim Kopieren auch die Einstellung für einen eventuell gesetzten Haltepunkt mitübertragen. Das ist sinnvoll, wenn man einen Replikator programmiert hat und die Simulation ebenfalls dann unterbrochen werden soll, wenn eine Kopie des Basisprogramms eine Bestimmte Instruktion ausführt.
  • JMN.F / DJN.F behaviour like pmars: Leider ist der MARS von pmars nicht genau nach den Angaben des ICWS-94 programmiert. Das betrifft allerdings nur die Befehle JMN.F, JMN.X, JMN.I, DJN.F, DJN.X und DJN.I. Alle diese Befehle sollen einen Sprung ausführen, wenn die Werte der angegebenen Zelle Null sind. Für Operationen, die beide Felder (A und B) auf Null prüfen, schreibt ICWS-94 vor, daß der Sprung durchzuführen ist, wenn beide Werte ungleich Null sind. Das Programm pmars springt aber, wenn auch nur ein Wert ungleich Null ist. Es wird dringend empfohlen, diese Option zu aktivieren, wenn man keinen triftigen Grund hat, ICWS-94 konformes Verhalten zu benutzen.
  • MARS accepts: Hier wird festgelegt, welche Opcodes man in den Core eingeben darf (Das betrifft auch den Assembler). Wird "Bluecode" gewählt, so werden die funktionen der Hermes-Einheit aktiviert. Extended Redcode erlaubt die Verwendung der indirekten Adressmodi { und SEQ (Ein Synonym für SNE, LDP und Es wird empfohlen, die Option "Extended Redcode" zu wählen, wenn man keinen triftigen Grund hat, "Strictly Redcode (Draft 94)" zu verwenden. Ein solcher Grund könnte sein, daß man nach den Regeln des ICWS-88 spielt und man verhindern will, daß irrtümlicherweise Warriors in den Speicher geladen werden, die diese Erweiterungen benutzen.
  • Evaluation of Pre/Post In/Decrement: Wenn ein Befehl wie z. B. MOV <0, <0 ausgeführt werden soll, dann ist nicht sofort klar, was genau wohin kopiert wird: Zuerst werden ja die Operanden ausgewertet, in diesem Fall wird <0 ausgeführt, wobei der zweite Wert auf <-1 geändert wird.

    Danach stellt sich die Frage, ob die Zieladresse nun aus <0 oder aus <-1 berechnet werden soll. Nachdem bei der Auswertung von Operand A der Operand B (in diesem Beispiel derselben Zelle) um eins verkleinert wurde, gibt es zwei Möglichkeiten:

    In Memory Evaluation

    Der Prozessor arbeitet prinzipiell mit den Werten, die sich im Speicher befinden. Hier würde bei der Auswertung von Operand B bereits ein Wert von <-1 vorliegen und das Ziel der Transaktion hängt davon ab, welcher B-Wert in der Zelle darüber gespeichert ist. Es wird also die Zelle darüber an eine Adresse kopiert, die durch den Wert der Zelle darüber festgelegt wird, wie bei einem MOV -1, @-1.

    In Register Evaluation

    Bevor ein Befehl ausgeführt wird, wird er in dem Prozessor-Register "Current Instruction" zwischengespeichert. Die Evaluation der Operatoren erfolgt, Ergebnisse werden in den Core geschrieben. In unserem Beispiel wird bei der Auswertung des Operanden A der B-Wert dieses Befehls im Core dekrementiert. Danach wird Operand B (aus dem Register "Current Instruction") ausgewertet. Im Register steht immer noch <0, weshalb wiederum der B-Wert der aktuellen Zelle (+0) verkleinert wird. Es wird in diesem Falle die Zelle vor der aktuellen Instruktion über sich selbst kopiert (Wie ein MOV -1, -1). Es wird dringend empfohlen, "In Register Evaluation" zu verwenden.
  • Core Presets: Man kann verschiedene Sets von MARS-Einstellungen speichern, wenn man bestimmte Einstellungen immer wieder benutzt.

Conversion

Es gibt Unterschiede, auf welche Felder sich bestimmte Instruktionen beziehen, wenn man nach den Regeln ICWS-86, ICWS-88 oder ICWS-94 spielt. Vor ICWS-94 gab es keine Opcode Modifier, jeder Befehl hat automatisch die passenden Felder angesprochen.

Wird in Redcode-94 kein Modifier angegeben, dann wird er aus dieser Tabelle ermittelt. Es gibt Auswahlmöglichkeiten für ICWS-86, ICWS-88, Bluecode und "Custom". Es wird empfohlen, die Einstellung "Bluecode" zu verwenden, sie ist auch für die Regeln unter ICWS-94 gültig.

Compiler

Hier kann man das Übersetzen von Quellcodes beeinflussen:
  • ARES parser accepts white space as field divider: Manche MARS-Programme erlauben es, Operator A und B mittels Leerzeichen zu trennen. Hierbei kann es zu Konflikten mit Ausdrücken kommen, wenn sie Leerzeichen beinhalten, wie z. B. JMP loop + 1. Ist diese Option eingeschaltet, würde der ARES-Parser das als JMP loop, +1 interpretieren. Das kann sich besonders bei komplexeren Ausdrücken auswirken, etwa wenn eine Ausdruck wie (loop + delta) Leerzeichen beinhaltet, um die Lesbarkeit zu erhöhen. Es wird empfohlen, diese Option auszuschalten.
  • External Assembly via pmars.exe: Der ARES-Parser arbeitet nicht genau gleich wie der von pmars. Wenn es zu Problemen kommt, kann man das Übersetzen durch die mitgelieferte pmars.exe aktivieren. Dadurch ist sichergestellt, daß die Übersetzung kompatibel mit pmars und den Hills ist. Diese Option ist nur dann sinnvoll, wenn die Quellcodes exzessiven Gebrauch von EQU und FOR machen. ARES v2.34 ignoriert mit externem Assembler die Direktiven ;break und ;watch.
  • Silent Assembly: Wenn das Popup-Fenster "Assembler Messages" nervt, kann man es hier abschalten. Es erscheint dann nur noch, wenn Fehler aufgetreten sind. Man kann dieses Fenster jedoch nachträglich über das Menu "Window" abrufen.
  • Fixed Entry Point for Warrior B: Eine Debug-Option. Man kann damit die Startadresse des zweiten Programmes erzwingen.
  • Clear Memory Before Compiling: Ist diese Option deaktiviert, so wird er Speicher zwischen den Runden nicht gesäubert. Man kann sich dann nicht darauf verlassen, daß eine Leere Zelle an ihrem Inhalt erkennbar wäre.

    Üblicherweise wird der Speicher aber geleert, man hat mit ARES die Wahl, womit er dabei gefüllt werden soll:
    • DAT $0, $0
    • Zufallswerte ("Random Values")
    • Ein voreingestellter Wert ("Cusotm Value")
    Es wird empfohlen, den Speicher mit DAT $0, $0 löschen zu lassen.
  • Min. Distance/Max. Length: Der minimale Abstand zwischen zwei Warriors und maximal erlaubte Länge beim Laden. 2 * MINDISTANCE + 2 * MAXLENGTH darf natürlich nicht größer sein als CORESIZE.
  • pMARS version number: Hier kann die Core-Variable VERSION voreingestellt werden. Sobald sichergestellt ist, daß ARES zu einer bestimmten Version von pmars kompatibel ist, wird dieses Feld mit der entsprechenden Zahl vorbelegt sein. Für die Versionsnummer pmars 0.9.2 ist 092 einzugeben.
  • Include Core Variables as EQUs: Ist diese Option eingeschaltet, so werden beim Übersetzen automatisch einige EQU-Variablen vordefiniert. SiehePseudo-Instruktionen.
  • Complete Opcodes with: Wenn bei einer Instruktion kein B-Operator angegeben ist, dann wird dieser vorbestimmte Wert verwendet.

Zeus

Hier kann man das Aussehen und Verhalten der grafischen Oberfläche von ARES beeinflussen. Manche Optionen haben einen Shortcut (z. B. ^W für Animate...Access). In der Statusleiste des Hauptfensters wird der aktuelle Zustand dieser Optionen angezeigt: Großbuchstaben bedeuten "aktiv", Kleinbuchstaben "abgeschaltet". Die angezeigten Status-Buchstaben verändern sich nicht, wenn man den Funktionen andere Tasten zuweist.
  • Mouse Wheel Steps: Legt fest, wieviele Zellen im Monitor gescrollt werden soll, wenn man das Mausrad dreht.
  • Flash Cell Time: Wenn ein Befehl ausgeführt wird, dann wird die entsprechende Zelle im Core View kurz weiß hinterlegt dargestellt, damit man besser erkennt, wo gerade etwas passiert. Hiermit kann man festlegen, wie lange so ein "Aufblitzen" dauern soll. Dadurch wird die Ablaufgeschwindigkeit der Simulation beeinflußt, sofern sich ARES nicht gerade im "Fast Mode" befindet. (Diesen aktiviert man mit einem Rechtsklick im Core oder Strg-H.) Bei manchen Bildschirmen kann es vorkommen, daß ein sehr kleiner Wert (Unter 20) dazu führt, daß die Flashes nicht zu sehen sind.
  • Full Speed: Durch Betätigen dieser Schaltfläche werden die Einstellungen im Tab "Zeus" auf maximale Geschwindigkeit optimiert.
  • Animate Read/Write/Exec Access: Ist diese Option gewählt, dann werden im Monitor die Zellen markiert, die durch die aktuelle Instruktion adressiert wurden. Des Weiteren werden Lese/Schreibzugriffe im Core durch "flashen" visualisiert.
    Voreingestellter Shortcut: Strg-W
  • Flash Cells on Acess: Aktiviert/Deaktiviert das Flashing. Ein vorrübergehendes Abschalten beschleunigt die Simulation deutlich.
    Voreingestellter Shortcut: Strg-F
  • Gray Cells beyond Core Limits in Monitor: Wenn aktiviert, dann werden Zellen oberhalb der ersten Zelle (Adresse &000000) grau dargestellt, um den Wrap-Around zu visualisieren. Ebenso werden Zellen, die unterhalb der letzten Zelle (CORESIZE-1) angezeigt werden, ausgegraut.
  • Core Displays Opcode: Wenn diese Option eingeschaltet ist, dann zeigt die Core View bestimmte Buchstaben für die jeweils gespeicherten Instruktionen an. Wird die Option umgeschaltet, wird der aktuellerweise angezeigte Core nicht gleich angepaßt. Nur neu gezeichnete Zellen bekommen den Buchstaben, darum sollte man den Speicher löschen, damit die gesamte Core-Grafik erneuert wird.
  • Full Output in Monitor: Die Standardeinstellung ist "ausgeschaltet". Dadurch werden die Instruktionen im Monitor gekürzt ausgegeben. Opcode Modifiers werden nur gezeigt, wenn sie nicht dem Default entsprechen (siehe Tab "Conversion"). Weiters werden der adress mode indicator für direkte Adressierung ("$") nicht angezeigt, sowie unbenutze Felder (Wie bei yspan class="source">JMP) weggelassen. Diese Option ist für den Gebrauch im Unterricht gedacht, Einsteigern wird empfohlen, die volle Ausgabe zu aktivieren, damit die Wirkungsweise der opcode modifier deutlicher wird.
    Voreingestellter Shortcut: Strg-O
  • Display Signed Values: Da Zahlen im MARS-Speicher immer im Bereich 0..CORESIZE-1 gespeichert sind, kann die Anzeige von negativen Zahlen irritierend/unpassend sein. Mit dieser Option kann man wählen, ob die Zahlen so angezeigt werden, wie sie tatsächlich im Speicher vorliegen (ohne Vorzeichen), oder ob Zahlen n > CORESIZE/2 als CORESIZE-n dargestellt werden sollen.
    Voreingestellter Shortcut: Strg-I
  • Don't Show Used Cells in Core after Reset: Ein Speedup. Wenn der Core nicht automatisch gelöscht wird (Siehe Tab "Compiler"), dann werden die Zellen, die schon Instruktionen aus dem vorigen Kampf beinhalten grau dargestellt. Bei besonders großen Cores kann es dadurch etwas länger dauern, bis die Grafik für einen "empty core" fertiggestellt ist.
  • Animate "the Winner is": Legt fest, ob nach einem Kampf der herunterfallende Name des Gewinners angezeigt werden soll.
  • Core Zoom: Legt den Vergrößerungsfaktor für die Core View fest, wobei die Zahl für die Größe einer einzelnen Zelle in Pixel steht.
  • Show All Fonts: Legt fest, ob in den Dialog-Fenstern zur Schriftartenauswahl auch Schriften mit variabler Breite angezeigt werden sollen. Es wird empfohlen, nur Schriften mit fixer Breite zu verwenden
  • Editor/Monitor Font: Hier kannst du die Schriftarten wählen, mit denen die jeweiligen Objekte angezeigt werden sollen. Es wird empfohlen, Lucida Console mit 9pt Größe zu benutzen.

Shortcuts

Hier kannst du die Tastaturabkürzungen ("Shortcuts") für jeden Menupunkt festlegen. Manche Befehle können einen Shortcut haben, obwohl sie nicht über das Menu erreichbar sind.

KotH

Hier kannst du eingeben, wie ARES e-Mails an die Hills versenden soll.
  • Author Name: Dieser Text wird automatisch als Direktive eingefügt, wenn man eine neue Datei anlegt. (";author "). Dieser Name wird auch veröffentlicht, wenn man einen Warrior auf einen Hill schickt. Überlege gut, ob du hier wirklich eine e-Mail Adresse angeben willst.
  • Default Hill: Die Adresse, an die ein Warrior von der "Send-To-Hill" Funktion geschickt werden soll.
  • Reply-To: Hier sollte man unbedingt eine gültige e-Mail-Adresse angeben, damit man nach dem Einsenden Kontrolle über den hochgeladenen Warrior hat und damit der KotH-Server Statusmeldungen (z. B. ob der Warrior übersetzt werden konnte) an den Benutzer senden kann.
  • SMTP-Server: Der Post-Ausgangsserver. Meistens ein Name wie smtp.providername.at. Diese Angabe wurde einem vom eigenen Internet-Provider bekanntgegeben. Wenn diese Adresse nicht bekannt ist, dann kann man versuchen, in den Einstellungen seines Mailprogrammes nachzuschauen, oder man kann raten und probieren.
  • SMTP-Login: Falls der SMTP-Server eine Authentifizierung verlangt, muß man hier Username und Passwort eingeben, damit ARES e-Mail versenden kann.
Die Funktionen für den POP3 Server sind derzeit nicht implementiert. Es ist geplant, einen einfachen e-Mail Client in ARES einzubauen, der automatisch die Antworten der KotH-Server herunterladen soll.

Bookmarks

Hier kannst du den Inhalt des "Bookmarks" Menu bestimmen. Text nach einem Strichpunkt (";") wird vor der Auswertung entfernt. Leere Zeilen werden ignoriert. Eine Zeile, die nur ein Minus-Zeichen (Bindestrich, "-") beinhaltet, erzeuget eine Trennlinie im Menu. Alles andere wird als Link interpretiert. Solche Zeilen können aus bis zu drei Segmenten bestehen, wobei jeder Abschnitt mit einer Pipe ("|") getrennt wird. Der erste Abschnitt beinhaltet den URL (die "Internetadresse") der Seite. Der letzte Abschnitt definiert den Text für das Menu. Der mittlere Abschnitt kann entweder leer gelassen werden, oder er beinhaltet einen beliebigen Text. Ist ein Text vorhanden, dann wird die Seite beim Aufrufen in den Default-Browser des Betriebsystems geladen. Ist der Abschnitt leer, so wird die Seite in den eingebauten Browser von ARES geladen.

Beispiele:
www.google.com
www.google.com|
www.google.com||
www.google.com||Google
www.google.com|x
www.google.com|x|Google

Others

Hier sind alle Optionen, die keiner Kategorie zugeordnet sind.
  • Tournament Confrontations per Battle: Hier kann man festlegen, welcher Wert als Voreinstellung für die "Tournamen"-Funtkion gesetzt werden soll.
  • Register File Types: Durch Betätigung dieser Schaltfläche werden einige Registry-Keys in die Windows-Konfigurationsdatenbank eingetragen. Dadurch können *.red, *.blu und *.rom Dateien mit ARES assoziiert werden. Damit kann man bewirken, daß solche Dateien automatisch mit ARES geöffnet werden, wenn man sie doppelklickt. Besonders bequem ist das, wenn man Warriordateien im Internet anklickt. Der "Koenigstuhl"-Server bietet z. B. ein großes Archiv an .red Dateien an, die man dann gleich laufen lassen kann.
  • Ask Before Closing ARES: Die nervige "ARES wirklich beenden?"-Dialogbox abschalten.
  • Save Window Positions to INI: ARES merkt sich, wohin der Benutzer die einzelnen Fenster verschiebt. Diese Positionen kann man in der Konfigurationsdatei von ARES speichern lassen, damit sie beim nächsten Aufruf wieder genauso plaziert werden.
  • Display HTML help file after loading ARES: Legt fest, ob der integrierte Browser die Startseite anzeigen soll. Das wird man vermutlich recht bald abschalten wollen.
  • Warriors Directory: Legt fest, wo ARES primär nach Quelldateien suchen soll. Bestimmt den Ordner, den Open/Save-Dialoge beim Öffnen anzeigen.
  • Benchmark Directory: Hier kann man ein Verzeichnis angeben, in dem eine Sammlung von Referenz-Kampfprogrammen gespeichert ist. Man kann in der Benchmark-Funktion trotzdem einen anderen Ordner auswählen.

Kompatibilität

ARES sollte vollständig kompatibel zu den Vorgaben im erweiterten ICWS-94 DraftExternal Link (Version 3.3) sein, jedoch steht eine endgültige Verifikation noch aus.

Der aktuelle Standard wird durch das Programm pmars vorgegeben. ARES beherrscht fast alle Erweiterungen, wie sie in pmars implementiert sind, jedoch kennt er die Instruktion PIN (noch) nicht. Der Assingment-Operator und die Register A bis Z werden ebenfalls nicht unterstützt und es ist auch nicht geplant, diese Option in den ARES-Parser zu integrieren.

Die KoTH-Server verwenden pmars, und das Verhalten dieses Programmes wird von der Core War-Community als Standard betrachtet.

Unterschiedliches Verhalten von ARES gegenüber pmars

  • EQU statments dürfen keine weiteren EQUs einfügen, was mit pmars möglich ist.
  • EQUs sollten nicht innerhalb von FOR schleifen deklariert werden, weil pmars das nicht erlaubt.
  • NOP darf in pmars nicht ohne Operanden angeschrieben werden.
  • ARES entfernt im Laufe der Übersetzung FOR-Laufvariablen. Das geschieht vor der Behandlung von Sprungmarken, weshalb folgendes Konstrukt im Gegensatz zu pmars nicht übersetzt werden kann:
      L M FOR i 
    JMP 0
    ROF
    JMP M
    Der Parser von pmars übersetzt das obige Beispiel zwar, macht aber aus JMP M fälschlicherweise einen JMP 0. (Ein JMP L funktioniert mit beiden Programmen)
  • CURLINE funktioniert in verschachtelten FOR-Schleifen anders als bei pmars:
      FOR n 
    FOR CURLINE
    ...
    ROF
    ROF
    Der Assembler warnt.
  • Der pmars-Parser zählt CURLINE falsch, wenn man EQU und FOR wie folgt kombiniert:
      OPC EQU JMP 

    FOR n
    OPC CURLINE
    ROF

Tips für kompatible Quellcodes

Wer mit verschiedenen Core War-Simulatoren arbeitet, sollte keine verschachtelten FOR-Schleifen benutzen. Die Verwendung von CURLINE ist ebenfalls problematisch, besonders in Verbindung mit FOR-Schleifen und EQU Statements, die Opcodes ersetzen. EQU Statements sollten mit Vorsicht genossen werden, ganz besonders Multiline-EQU. Ich empfehle, EQU nur für konstante Zahlenwerte (oder natürlich mathematische Ausdrücke) zu verwenden und dabei auf die Verwendung auf CURLINE in diesen EQUs zu verzichten. Sollte auf kompliziertere Konstruktionen nicht verzichtet werden können (z. B. wenn man fremde Warriors übersetzt), dann sollte man die Ausgabe der Assembler genau auf Korrektheit prüfen. Wer seine Programme mit ARES entwickelt und den Verdacht hat, daß die Übersetzung durch pmars andere Ergebnisse liefert, sollte sein Programm vor dem Upload mit ARES ins Load File Format übersetzen lassen.

Vollständige pmars Kompatibilität

Solltest du sichergehen wollen, daß ARES die Programme genauso wie pmars übersetzt, kannst du die Option "External Assembly" benutzen. ARES benutzt dann pmars.exe, um den Programmcode übersetzen zu lassen, wodurch die Kompatibilität des Parsers auf 100.0% steigt ;-) Die Direktiven ;break und ;watch verlieren dadurch allerdings (derzeit noch) ihre Wirksamkeit, Bluecode kann natürlich auch nicht übersetzt werden. Derzeit wird ARES mit pmars 0.9.2 ausgeliefert.

Links

Diese Homepage behandelt nur die Grundlagen von Redcode. Um eigene Warriors auf den Hills zu platzieren, wirst du etwas tiefer in die Techniken von Core War eindringen müssen. Diese Linksammlung ist sicher nicht vollständig, der Schwerpunkt ist Weiterbildung nach dem ARES-Tutorial. Falls du gute Tutorials kennst, dann sende bitte eine Nachricht an mich.

Pflichtlektüre

http://corewar.co.uk/icws94.txtInternational Core War Standard from 1994 (Extended Version)
http://www.koth.org/i ... mars-redcode-94.txtExtensions to the draft, as they are implemented by pmars

Nachtisch

Nach dem ARES-Tutorial solltest du die folgenden Tutorials lesen:
http://corewars.jgutz ... scanners/index.htmlQuickScanners
http://www.corewar.in ... o/lexicon/paper.htmPapers (Replicators)

Hard Core

Wenn du die Tutorials bereits auswendig kennst, dann hilft nur noch das Studium fremder Kampfprogramme:
http://corewar.home.p ... orewarWarriors.htmlCollection of warriors, sorted by style
http://www.ociw.edu/~ ... AR/koenigstuhl.htmlKoenigstuhl - Huge Archive
http://corewar.home.p ... ne.com/corewarrior/Archive of CoreWarrior (a Core War newsletter)

Community

Wenn du mal nicht weiter weißt...
news:rec.games.corewarDie Newsgroup rec.games.corewar, unbedingt abonieren!
http://groups.google. ... games.corewar?gvc=2Die Newsgroup rec.games.corewar auf Google
http://harald.ist.org/ares/irc/Java-Client für IRC Chatroom - irc.koth.org#corewars

Software

http://www.koth.org/pmars/Executeables von pmars (nicht ganz aktuell)
http://sourceforge.ne ... s.php?group_id=3452pmars on SourceFourge
http://www.corewar.info/optimax/Redcode Optimizer

Interessante Websites

http://sal.math.ualberta.ca/KotH Server mit Beginner's Hill
http://www.koth.org/KotH Server, Links, Material, Downloads
http://www.corewar.info/Large site offering links, texts and downloads
http://corewar.co.uk/links.htmLinks and material
http://www.familie-ke ... hannes/cwcomics.txtCute Core War-Comic

The Imp Ring

The Core War Imp Ring
<PREV  LIST  RAND  NEXT>