     Wojny Rdzeniowe 
     Reguly gry wedlug standardu CWS'88 


     Spis tresci. 

1. Wprowadzenie. 
2. Reguly walki. 
3. Rdzen. 
4. Tryby adesowania. 
5. Rozkazy Redcode. 
6. Wykonywanie rozkazow. 
7. Skladnia kodu zrodlowego. 
8. Pseudoinstrukcje. 


     1. Wprowadzenie. 

     Redcode jest jezykiem asemlerowym, w ktorym pisane sa 
programy walczace w Wojnach Rdzeniowych. Redcode jest asemblerem 
wirtualnego procesora o nazwie MARS. Poniewaz MARS jako 
urzadzenie nie istnieje, jego dzialanie musi byc symulowane 
programowo (stad tez jego nazwa: Memory Array Redcode 
Simulator), tak wiec mianem tym okresla sie rowniez programy, 
ktore potrafia pobierac z wirtualnej pamieci rozkazy Redcode, 
dekodowac je i wykonywac, tak jak robilby to prawdziwy MARS. 


     2. Reguly walki. 

     Wojny Rdzeniowe polegaja na walce programow napisanych w 
Redcode. Walka toczy sie w pamieci MARS-a, zwanej rdzeniem. 
Przed rozpoczeciem bitwy MARS umieszcza walczacych wojownikow na 
rdzeniu dbajac o to, aby ich kody nie pokrywaly sie. W trakcie 
rozgrywek MARS w kazdym cyklu wykonuje po jednej instrukcji 
kazdego wojownika, zawsze w tej samej kolejnosci. Walka konczy 
sie, gdy jeden z walczacych programow zostanie pokonany, lub gdy 
zostanie wykonana okreslona liczba cykli. W drugim przypadku 
walka pozostaje nierozstrzygnieta i MARS oglasza remis. 
     MARS jest systemem wieloprocesowym. Kazdy wojownik w 
momencie uruchomienia dysponuje jednym procesem, ale w trakcie 
walki moze sie podzielic na wiele procesow. Procesy jednego 
wojownika tworza kolejke i wykonywane sa na przemian po jednej 
instrukcji. Jesli ktorys z procesow zostanie zniszczony, 
zostanie rowniez usuniety z kolejki. Wojownik przegrywa walke, 
gdy z jego kolejki procesow zostanie usuniety ostatni proces. 


     3. Rdzen. 

     Rdzen jest pamiecia, w ktorej sa umieszczane i wykonywane 
walczace programy. Pamiec ta jest zorganizowana w zamkniety 
pierscien komorek. Pojedyncza komorka sklada sie z pola 
operacji, pola argumentu A oraz pola argumentu B. Kazda 
instrukcja Redcode miesci sie dokladnie w jednej komorce 
pamieci. 
     Poniewaz rdzen nie ma ani poczatku, ani konca, niemozliwe 
jest przypisanie poszczegolnym komorkom jednoznacznych adresow. 
Wszystkie adresy sa wiec liczone wzgledem biezacej pozycji. Na 
przyklad adres 0 wskazuje biezaca instrukcje, adres -1 
instrukcje poprzednia, a adres 1 instrukcje nastepna.  
     Cykliczna struktura rdzenia powoduje rowniez, ze kazda 
komorka moze miec wiele roznych adresow. Jesli przyjmiemy 
rozmiar rdzenia za M, to biezaca instrukcje znajdziemy pod 
adresami {..., -2M, -M, 0, M, 2M, ...}, poprzednia pod {..., 
-1-2M, -1-M, -1, M-1, 2M-1, ...}, a nastepna pod {..., 1-2M, 
1-M, 1, M+1, 2M+1, ...}. 
     Przed walka rdzen jest inicjowany przez wpisanie instrukcji 
DAT 0, 0 do kazdej jego komorki. 
Pola argumentow moga zawierac liczby calkowite z zakresu 
[0..M-1]. 


     4. Tryby adresowania. 

     Redcode dopuszcza stosowanie czterech trybow adresowania. 
Tryb adresowania poszczegolnych argumentow okresla sie poprzez 
odpowiednie oznaczenie ich typow. Dopuszczalnymi trybami 
adresowania sa: 
     Tryb natychmiastowy, oznaczany symbolem #. Jego wartosc 
znajduje sie w biezacej instrukcji. Jezeli argument A jest 
natychmiastowy, jego wartosci nalezy szukac w polu argumentu A, 
zas jesli argument B jest natychmiastowy, wartosc znajduje sie w 
polu argumentu B. 
     Tryb bezposredni, przyjmowany domyslnie, gdy nie ma zadnego 
symbolu okreslajacego typ argumentu. W trybie bezposrednim pole 
argumentu jest adresem, ktory wskazuje komorke bedaca wartoscia 
argumentu. Adres jest liczony wzgledem aktualnie wykonywanej 
instrukcji. Niektore MARS-y zezwalaja na oznaczanie argumentow 
bezposrednich symbolem $. 
     Tryb posredni, oznaczany symbolem @. Tutaj, podobnie jak w 
trybie bezposrednim, pole argumentu jest adresem wskazujacym 
jakas komorke, jednak jest ona nie wartoscia argumentu, lecz 
wskaznikiem do niej. Adres komorki bedacej wartoscia argumentu 
znajduje sie w polu argumentu B wskaznika i jest on liczony 
wzgledem pozycji wskaznika. Na przyklad: 

x1 DAT #0, #0  ;komorka docelowa 
x2 DAT #0, #-1 ;wskaznik (adres posredni) 
x3 MOV 0,  @-1 ;kopiuje siebie pod adres x1 

     Tryb posredni zmniejszany, oznaczany symbolem <. Wyznaczany 
jest niemal dokladnie w tak samo, jak tryb posredni, jedynie 
wskaznik (scislej: wartosc jego pola argumentu B) przed uzyciem 
jest zmniejszany o jeden. Na przyklad: 

x1 DAT #0, #0  ;komorka docelowa 
x2 DAT #0, #0  ;wskaznik (adres posredni) 
x3 MOV 0,  <-1 ;kopiuje siebie pod adres x1 


     5. Rozkazy Redcode. 

     DAT A, B 

     Rozkaz DAT (data) ma dwa zastosowania. Po pierwsze, 
umozliwia przechowywanie w swoich polach argumentow roznego 
rodzaju danych: licznikow, adresow, wskaznikow itp. Po drugie, 
proces, ktory wykona te instrukcje, zostaje usuniety z kolejki 
procesow. Gdy wszystkie procesy wojownika zostana usuniete z 
kolejki, wojownik przegrywa walke. 
     W rozkazie DAT dopuszczalne sa tylko dwa typy argumentow: 
natychmiastowy i posredni zmniejszany. Efekt zmniejszenia 
wskaznika podczas wyznaczania argumentu posrednego zmniejszanego 
wystepuje rowniez przy rozkazie DAT. Proces jest bowien usuwany 
dopiero po wyznaczeniu obu argumentow. 
     Argument A moze byc pominiety, w jego miejsce zostanie 
wstawiony #0. 

     MOV A, B 

     Rozkaz MOV (move) kopiuje A do B. Jesli argument A jest 
natychmiastowy, jego wartosc jest wstawiana do pola B komorki 
wskazywanej przez argument B. Jesli argument A jest innego typu, 
cala komorka wskazywana przez argument A jest kopiowana do 
komorki wskazywanej przez argument B. 
     Argument B nie moze byc natychmiastowy. 

     ADD A, B 

     Rozkaz ADD dodaje A do B i umieszcza wynik w B. Jesli 
argument A jest natychmiastowy, jego wartosc jest dodawana do 
pola B komorki wskazywanej przez argument B. Jesli argument A 
jest innego typu, pole A i pole B komorki wskazywanej przez 
argument A sa dodawane odpowiednio do pola A i pola B komorki 
wskazywanej przez argument B. 
     Argument B nie moze byc natychmiastowy. 

     SUB A, B 

     Rozkaz SUB (subtract) odejmuje A od B i umieszcza wynik w 
B. Dzialanie tego rozkazu jest identyczne, jak rozkazu ADD, 
jedynie zamiast operacji dodawania jest odejmowanie. 
     Argument B nie moze byc natychmiastowy. 

     JMP A, B 

     Rozkaz JMP (jump) skacze do A. Skok jest wykonywany do 
instrukcji wskazywanej przez argument A. Argument B moze byc 
pominiety, w jego miejsce zostanie wstawiony #0. 
     Argument A nie moze byc natychmiastowy. 

     JMZ A, B 

     Rozkaz JMZ (jump if zero) wykonuje skok do A, gdy B jest 
zerem. Jesli argument B jest natychmiastowy, badana jest jego 
wartosc, natomiast jesli jest innego typu, badana jest wartosc 
pola B komorki wskazywanej przez argument B. Ewentualny skok 
wykonywany jest do instrukcji wskazywanej przez argument A. 
     Argument A nie moze byc natychmiastowy. 

     JMN A, B 

     Rozkaz JMN (jump if non-zero) wykonuje skok do A, gdy B 
jest rozne od zera. Dzialanie tego rozkazu jest identyczne (za 
wyjatkiem warunku skoku), jak rozkazu JMZ. 
     Argument A nie moze byc natychmiastowy. 

     DJN A, B 

     Rozkaz DJN (decrement and jump if non-zero) zmniejsza B i 
skacze do A, gdy B po zmniejszeniu jest rozne od zera. Jesli 
argument B jest natychmiastowy, zmniejszana (i pozniej badana) 
jest wartosc argumentu B. Jesli argument B jest innego typu, 
zmniejszana (i pozniej badana) jest wartosc pola B komorki 
wskazywanej przez argument B. Ewentualny skok wykonywany jest do 
instrukcji wskazywanej przez argument A. 
     Argument A nie moze byc natychmiastowy. 

     CMP A, B 

     Rozkaz CMP (compare, skip if equal) porownuje A z B, po 
czym przeskakuje nastepna instrukcje, jesli A i B sa rowne. 
Jesli argument A jest natychmiastowy, porownywana jest wartosc 
argumentu A z wartoscia pola B komorki wskazywanej przez 
argument B. Jesli argument A jest innego typu, porownywane sa 
cale komorki (tzn. zarowno ich pola operacji, jak i pola 
argumentow) wskazywane przez argument A i argument B. 
     Argument B nie moze byc natychmiastowy. 

     SPL A, B 

     Rozkaz SPL (split) uruchamia pod A nowy proces. 
Nowopowstaly proces wykonywany jest od instrukcji wskazywanej 
przez argument A. Bezposrednio po wykonaniu rozkazu SPL 
wykonywana jest jednak ta sama instrukcja, jaka bylaby wykonana, 
gdyby ow nowy proces nie powstal (nowy proces dodawany jest 
bowiem na koncu kolejki procesow). Jesli kolejka procesow jest 
pelna, SPL nie tworzy nowego procesu. 

x1 SPL 0 
x2 JMP 0 

     W powyzszym przykladzie kolejnosc wykonywania 
poszczegolnych procesow jest nastepujaca: 

[proces 1]     x1 SPL 0  ;proces 1 tworzy proces 2 pod x1 
[przeciwnik]             ;wykonywany jest kod przeciwnika 
[proces 1]     x2 JMP 0  ;proces 1 zapetla sie 
[przeciwnik] 
[proces 2]     x1 SPL 0  ;proces 2 tworzy proces 3 pod x1 
[przeciwnik] 
[proces 1]     x2 JMP 0 
[przeciwnik] 
[proces 2]     x2 JMP 0  ;proces 2 zapetla sie 
[przeciwnik] 
[proces 3]     x1 SPL 0  ;proces 3 tworzy proces 4 pod x1 
[przeciwnik] 
[proces 1]     x2 JMP 0 
[przeciwnik] 
[proces 2]     x2 JMP 0 
[przeciwnik] 
[proces 3]     x2 JMP 0  ;proces 3 zapetla sie 
[przeciwnik] 
[proces 4]     x1 SPL 0  ;proces 4 tworzy proces 5 pod x1 
(...)                    ;itd... 

     Argument B moze byc pominiety, w jego miejsce zostanie 
wstawiony #0, argument A nie moze byc natychmiastowy. 

     SLT A, B 

     Rozkaz SLT (skip if less than) porownuje A z B, po czym 
przeskakuje nastepna instrukcje, jesli A jest mniejsze niz B. 
Dzialanie rozkazu jest analogiczne do CMP. 
     Argument B nie moze byc natychmiastowy. 

     Redcode, podobnie jak inne jezyki asemblerowe, wprowadza 
pewne ograniczenia w uzywaniu argumentow natychmiastowych. Nie 
wolno ich uzywac tam, gdzie rozkaz spodziewa sie adresu, np. 
przy skokach. Niedopuszczalne sa zatem ponizsze konstrukcje (? 
oznacza dowolny typ argumentu): 

     MOV ?A, #B 
     ADD ?A, #B 
     SUB ?A, #B 
     CMP ?A, #B 
     SLT ?A, #B 
     JMP #A, ?B 
     JMZ #A, ?B 
     JMN #A, ?B 
     DJN #A, ?B 
     SPL #A, ?B 

     Wyjatkiem jest tu rozkaz DAT, ktory w obu argumentach 
dopuszcza wylacznie adresowanie natychmiastowe (#) i posrednie 
zmniejszane (<). 
     Niektore MARS-y umozliwiaja obejscie tego ograniczenia, 
przyjmujac adres argumentow natychmiastowych rowny 0.  


     6. Wykonywanie rozkazow. 

     Ten punkt zawiera opis techniczny przeznaczony dla 
projektantow MARS-ow oraz zaawansowanych graczy. Jego zadaniem 
jest jak najdokladniejsze zdefiniowanie regul, jakimi rzadza sie 
Wojny Rdzeniowe oraz rozwianie wszelkich mozliwych niejasnosci. 

     MARS, aby poprawnie zinterpretowac instrukcje Redcode, 
korzysta z nastepujacych wewnetrznych rejestrow: 

RR   - rejestr rozkazu 
RA   - rejestr argumentu A 
RB   - rejestr argumentu B 
PC   - licznik programu (adres aktualnie wykonywanej 
       instrukcji) 
AdrA - adres argumentu A 
WskA - wskaznik argumentu A 
AdrB - adres argumentu B 
WskB - wskaznik argumentu B 
A    - wartosc argumentu A 
B    - wartosc argumentu B 

     Rejestry RR, RA i RB sluza do przechowywania calych komorek 
pamieci, mozna wiec w nich wyodrebnic trzy pola: Op, A i B 
(odpowiednio: pole operacji, pole argumentu A i pole argumentu 
B). Pozostale rejestry moga przechowywac liczby calkowite z 
zakresu [0..M-1], gdzie M jest wielkoscia rdzenia. Wszystkie 
operacje arytmetyczne MARS wykonuje modulo M. 
     Instrukcje wykonywane przez MARS-a posluguja sie wylacznie 
adresami wzglednymi, jednak MARS musi miec mozliwosc 
bezwzglednego adresowania rdzenia. Rejestr PC przechowuje zatem 
bezwzgledny adres wykonywanej instrukcji. Rejestry AdrA, WskA, 
AdrB i WskB przechowuja bezwzgledne adresy argumentow lub 
wskaznikow liczone wzgledem adresu umieszczonego w PC. 

     Pierwszym krokiem MARS-a jest odczytanie instrukcji do 
wykonania i zapamietanie jej w rejestrze RR. Instrukcja ta 
znajduje sie na rdzeniu pod bezwzglednym adresem przechowywanym 
w rejestrze PC: 

RR = Rdzen[PC] 

     Kolejnym krokiem jest opracowanie argumentow. Tak 
wyznaczany jest natychmiastowy argumentu A: 

AdrA = PC 
RA = Rdzen[AdrA] 
A = RA.A 

     Bezposredni argument A: 

AdrA = (PC + RR.A) mod M 
RA = Rdzen[AdrA] 
A = RA.B 

     Posredni argument A: 

WskA = (PC + RR.A) mod M 
RA = Rdzen[WskA] 
AdrA = (WskA + RA.B) mod M 
RA = Rdzen[AdrA] 
A = RA.B 

     Posredni zmniejszany argument A: 

WskA = (PC + RR.A) mod M 
RA = Rdzen[WskA] 
Rdzen[WskA].B = (Rdzen[WskA].B + M - 1) mod M 
AdrA = (WskA + RA.B) mod M 
AdrA = (WskA + M - 1) mod M 
RA = Rdzen[AdrA] 
A = RA.B 

     Argument B wyznaczany jest podobnie, jedynie zamiast AdrA, 
WskA i RR.A uzywane sa odpowiednio AdrB, WskB i RR.B, a zamiast 
wartosci A wyliczana jest wartosc B: 

B = RB.B 

     Po wyznaczeniu obu argumentow MARS moze przystapic do 
wykonywania instrukcji umieszczonej w RR.Op. Dzialanie 
wszystkich rozkazow zostalo opisane w punkcie 5, nalezy jedynie 
pamietac o nastepujacych zasadach: 
 - MOV albo wpisuje A do Rdzen[AdrB].B, albo RA do Rdzen[AdrB], 
 - ADD i SUB z natychmiastowym argumentem A operuja na A i B, 
wynik umieszczaja w Rdzen[AdrB].B, 
 - ADD i SUB z adresowym argumentem A operuja na RA.A i RB.A 
oraz na RA.B i RB.B, wyniki umieszczaja odpowiednio w 
Rdzen[AdrB].A i Rdzen[AdrB].B, 
 - skoki i SPL operuja na rejestrze AdrA, 
 - skoki warunkowe testuja rejestr B, 
 - DJN zmniejsza Rdzen[AdrB].B, 
 - CMP operuje albo na rejestrach A i B, albo na RA i RB, 
 - SLT zawsze operuje na rejestrach A i B, 
 - jesli rozkaz nie wykonal skoku, to PC = (PC + 1) mod M 


     7. Pseudoinstrukcje. 

     Obok jedenastu rozkazow Redcode dopuszcza stosowanie dwoch 
pseudoinstrukcji: END i EQU. Pseudoinstrukcje nie sa rozkazami 
Redcode, a jedynie dyrektywami kompilatora. 

END <etykieta> 

     Pseudoinstrukcja END sluzy do oznaczania punktu startowego 
oraz konca programu. Punktem startowym programu staje sie 
instrukcja wskazana etykieta podana jako argument. Argument jest 
opcjonalny i mozna go opuscic, punktem startowym bedzie wowczas 
pierwsza instrukcja w programie. Wszystko, co w kodzie zrodlowym 
znajduje sie po pseudoinstrukcji END, jest przez kompilator 
ignorowane. 

<etykieta> EQU <A> 

     Pseudoinstrukcja EQU (equate) powoduje podmienianie kazdego 
wystapienia etykiety <etykieta> napisem <A>. Na przyklad: 

x1   DAT #0, #x1 
     DAT #0, #x1 
     DAT #0, #x1 

znaczy to samo, co 

x1   DAT #0, #0 
     DAT #0, #-1 
     DAT #0, #-2 

ale 

x1   EQU0 
     DAT #0, #x1 
     DAT #0, #x1 
     DAT #0, #x1 

znaczy tyle, co 

     DAT #0, #0 
     DAT #0, #0 
     DAT #0, #0 


     8. Skladnia kodu zrodlowego. 

     Program napisany w Redcode sklada sie z wierszy o 
nastepujacym formacie: 

<etykieta>     <instrukcja>   <komentarz> 

     Elementy te sa opcjonalne, zatem pojedynczy wiersz nie musi 
zawierac ich wszystkich. Dopuszczalne tez sa puste wiersze. 
Znakami rozdzielajacymi sa spacja i znak tabulacji. Etykieta 
jest ciagiem liter i cyfr zaczynajacym sie litera. Male i duze 
litery sa nierozroznialne, do liter zalicza sie rowniez kreska 
podkreslenia. Etykieta moze miec dowolna dlugosc, ale tylko 
pierwsze osiem znakow jest znaczacych. Komentarz jest ciagiem 
dowolnych znakow zaczynajacym sie srednikiem i konczacym sie 
wraz z koncem wiersza. 

     Instrukcja sklada sie z rozkazu i dwoch argumentow, przy 
czym kazdy z argumentow moze byc poprzedzony symbolem 
okreslajacym jego typ: 

<rozkaz>  <typ A><argument A>, <tyb B><argument B> 

Dopuszczalnymi rozkazami sa DAT, MOV, ADD, SUB, JMP, JMZ, JMN, 
DJN, CMP, SPL, SLT, END oraz EQU. Dopuszczalnymi typami 
argumentow sa: # (adresowanie natychmiastowe), @ (adresowanie 
posrednie) i < (adresowanie posrednie zmniejszane). Oznaczenie 
typu argumentu mozna pominac, spowoduje to przyjecie typu 
bezposredniego. Argumenty sa wyrazeniami arytmetycznymi 
skladajacymi sie z liczb calkowitych, etykiet oraz operatorow 
dodawania (+), odejmowania (-), mnozenia (*) oraz dzielenia 
calkowitego (/). Dopuszczalne jest stosowanie nawiasow w 
wyrazeniach. 
     CWS'88 w oryginale nie zezwala na oddzielanie argumentow 
przecinkami, podobnie jak nie zezwala na oddzielanie spacjami 
skladnikow wyrazen arytmetycznych. Obecnie jednak wiekszosc 
MARS-ow omija te ograniczenia. 


---------------------------------------------------------------- 
Opracowal Adam Ryba na podstawie: 
[1] International Core War Society, "Core Wars Standard of 
    1988", 1988. 
[2] Mark A. Durham, "Introduction to Redcode", 1991. 
[3] Mark A. Durham, "EMI'88, Execute MARS Instruction a'la 
    ICWS'88", 1991. 

Wroclaw, 1995.03.05 
