Redcode

Podstawą Wojen Rdzeniowych jest asembleropodobny język programowania o nazwie Redcode. Napisany przeze mnie prosty samouczek może być pomocny dla początkujących rycerzy, jednak pisany był z myślą pokazania wszystkiego w praktyce, podczas gdy poniżej znajduje się kompilacja najważniejszych rzeczy w Redcode. Zgodnie z jego składnią każda komórka rdzenia składa się z pola instrukcji, pola argumentu A oraz pola argumentu B. W praktyce wygląda to tak:

etykieta trzyliterowa_instrukcja pole_argumentu_A, pole_argumentu_B

Etykietą może być dowolny ciąg znaków alfanumerycznych (czyli liter lub cyfr oraz znaku pokreślenia "_") zaczynający się literą bądź tymże znakiem "_".

W zależności od standardu różnie wygląda zestaw instrukcji. W świecie używany jest (w szczególności na serwerze KOTH), standard zastosowany w środowisku pMARS. Składa się on z następujących instrukcji:

DAT [A,] B  
- instrukcja niewykonywalna, zabija proces przy próbie wykonania, służy do przechowywania danych, przed walką rdzeń wypełniany jest instrukcjami DAT $0, $0
MOV  A,  B  
- instrukcja kopiowania z A do B
ADD  A,  B  
- dodawanie A do B, wynik zapisywany w B
SUB  A,  B  
- odejmowanie A od B, wynik zapisywany w B
MUL  A,  B  
- mnożenie B przez A, wynik zapisywany w B
DIV  A,  B  
- dzielenie całkowite B przez A, część całkowita wyniku zapisywana w B, dzielenie przez zero powoduje śmierć procesu
MOD  A,  B  
- dzielenie modulo B przez A, reszta z dzielenia zapisywana w B, dzielenie przez zero powoduje śmierć procesu
JMP  A[, B] 
- skok bezwarunkowy do komórki A
JMN  A,  B  
- skok do A, jeśli B jest różne od zera
JMZ  A,  B  
- skok do A, jeśli B jest równe zero
DJN  A,  B  
- zmniejszenie B o 1 i skok do A, jeśli B jest różne od zera
SPL  A[, B] 
- uruchomienie równoległego procesu w komórce A
STL  A,  B  
- jeśli A mniejsze od B, to omiń następną instrukcję
CMP  A,  B  
- jeśli A jest równe B, to omiń następną instrukcję
SEQ  A,  B  
- to samo co CMP
SNE  A,  B  
- odwrotnie niż CMP: jeśli A i B są różne, to omiń następną instrukcję
NOP [A,  B] 
- instrukcja pusta (działa tak, jak JMP $1)
LDP  A,  B  
- załaduj komórkę A P-przestrzeni do komórki B
STP  A,  B  
- zapisz A do komórki B P-przestrzeni

Oprócz instrukcji asemblera Redcode zawiera również pseudoinstrukcje, czyli takie instrukcje, które nie stanowią części programu-wojownika, a sterują jego kompilacją. Oto one:

[etykieta] EQU tekst 
- zamienia każdą etykietę w programie na tekst
ORG etykieta 
- ustala początkową instrukcję programu-wojownika
END [etykieta] 
- koniec kompilacji (opcjonalna etykieta oznaczająca początkową instrukcję programu-wojownika)
[zmienna] FOR n
          instrukcje 
          ROF
- pętla, instrukcje są n razy wstawiane do kodu, można używać w nich wyrażenia &zmienna, które zawiera aktualną wartość zmiennej
PIN liczba 
- numer identyfikacyjny P-przestrzeni, programy-wojownicy z tym samym PINem współdzielą tę samą P-przestrzeń

Do każdej instrukcji asemblera można dodać (po kropce bezpośrednio po instrukcji, a przed polami argumentów) modyfikatory:

.A  
- instrukcja operuje wyłącznie na polach A
.B  
- instrukcja operuje wyłącznie na polach B
.AB 
- instrukcja czyta z pola A instrukcji A i pola B instrukcji B, a zapisuje do pól B
.BA 
- instrukcja czyta z pola B instrukcji A i pola A instrukcji A, a zapisuje do pól A
.F  
- instrukcja czyta i pisze korzystając z obu pól: A i B
.X  
- instrukcja czyta z obu pól i zapisuje do obu, ale zamieniając je miejscami
.I  
- instrukcja operuje na całej komórce, czyli na polu rozkazu i na polach obu argumentów

W Redcode adresowanie jest względne. Oznacza to, że komórka rdzenia z obecnie wykonywanym procesem jest widziana przez ten proces jako komórka o adresie 0, następna po niej posiada adres 1, a poprzednia -1. Ponadto cały rdzeń jest zapętlony. Jeśli przyjmiemy, że rdzeń posiada N komórek, to komórka o adresie N to ta sama komórka, co ta o adresie 0. Przyjęto konwencję, że wszystkie liczby występujące w programie-wojowniku po kompilacji i w trakcie walki zawierają się w przedziale od -N/2+1 do N/2 włącznie. Oczywiście pisząc program nie musimy o to dbać, kompilator odwali całą tę mokrą robotę z nas :) Niemniej trzeba o tym pamiętać, zwłaszcza przy porównywaniu liczb. Istnieją następujące tryby adresowania:

 # 
- natychmiastowy (wartość liczbowa)
 $ 
- bezpośredni (adres)
 @ 
- pośredni przez pole B (adres właściwego adresu znajdującego się w polu B wskazanej bezpośrednio komórki)
 < 
- pośredni "przed-zmniejszany" przez pole B (podobnie jak wyżej, ale pole B jest zmniejszane o 1 przed wykonaniem instrukcji)
 > 
- pośredni "po-zwiększany" przez pole B (jak wyżej, tylko pole B jest zwiększane o 1 po wykonaniu instrukcji)
 * 
- pośredni przez pole A
 { 
- pośredni "przed-zmniejszany" przez pole A
 } 
- pośredni "po-zwiększany" przez pole A

Trybem domyślnym adresowania jest tryb bezpośredni. Oznacza to, że zapis MOV 0, 1 jest równoważny MOV $0, $1 (tak zresztą wygląda po skompilowaniu).