Skip to content
This repository was archived by the owner on Mar 12, 2025. It is now read-only.

Redcode

PietHelzel edited this page Nov 22, 2024 · 13 revisions

Important Links

Guide

Game Structure

CoreWar is played by two or more players. Each player submits a program written in the RedCode programming language. These programs are then loaded into a shared memory and executed in turns. Using RedCode, programs can modify themselves and other programs.

The Memory Core

The memory core holds all instructions of all programs. It is structured as a circular array, with the smallest unit of memory being a single instruction. Due to the circular nature, all addresses can be seen as being modulo the core size. For example, if a write happens to absolute address 8010, with the core having a size of 8000, the write will actually happen at absolute address 10.

Relative addressing

Addresses in RedCode are actually written as a relative index from the currently executing instruction. If an instruction sits at absolute index 10 in the memory core, and it writes to address 2, it writes to the address 2 spaces further ahead in memory, which would be absolute index 12. Similarly, it an instruction at absolute address 5 reads from instruction -3, it reads 3 spaces behind it in memory, that being absolute index 2.

Processes

Every player starts with a single process at the first instruction of their submitted program. After an instruction is executed, the next instruction is added to the process queue. In case of specific instructions like the jump instruction (JMP) for example, instead of the next instruction being queued, the jump target is queued. Using the split instruction (SPL), a player can create a new process at a specific location. This new process is queued in addition to the next instruction, but AFTER it.

Killing processes

Some instructions, namely the DAT instruction as well as DIV and MOD in case the divisor is zero, do not queue any next instruction, effectively killing the process. When a player loses all processes, they lose.

Instructions

An instruction looks like the following:

MOV.AB @-2, #3

It is made up of the following parts:

  • Opcode (e.g. MOV): The instruction name.
  • Modifier (e.g. AB): Modifies the instruction by specifying which operands it works on.
  • Source field (e.g. @-2 and destination field (e.g. #3):
    • Each made up of an address mode (like @ or #) followed by a number.
    • The comma separator is necessary!

Address modes

Each instruction can have an address mode for each of its fields. The following is a list of all possible address modes:

  • $ - direct addressing
  • # - immediate
  • * - a-field indirect addressing
  • @ - b-field indirect addressing
  • { - a-field indirect addressing with predecrement
  • < - b-field indirect addressing with predecrement
  • } - a-field indirect addressing with postincrement
  • > - b-field indirect addressing with postincrement

Direct Addressing

Direct addressing directly points to the instruction specified by the number. For example, a MOV $0, $1 copies the 0th instruction (the MOV command itself) one space ahead in memory. The dollar sign is optional, if no address mode is specified, direct addressing is used automatically.

Immediate

A literal number. If decoded as an address, it always points to address 0.

Indirect addressing

Indirect addressing looks into the a- or b-field (whichever is chosen by the specific address mode) of the nth instruction. This value is then used as a direct pointer to the instruction that will actually be used. Here is an example:

-1 | DAT #0, #4 ;  ---.
0  | MOV 0, @-1 ;     |
1  | ...        ;     | +4
2  | ...        ;     |
3  | MOV 0, @-1 ; <---'

When the MOV is executed, it looks at the b-field of instruction -1 (the DAT instruction). The value is 4. It then uses that as a pointer from the DAT instruction (not from the MOV!) to reach the actual destination instruction. In conclusion, the MOV copies itself to instruction 3 (4 ahead of the DAT).

Predecrement and Postincrement

If a predecrement or postincrement addressing mode is used, the intermediate value is either

  • decremented and then used as a pointer in the case of a predecrement.
  • used as a pointer and then incremented in the case of a postincrement.

Modifier

Modifiers change the operators an instruction works on.

  • A: Operates on the a-field of the source-instruction and the a-field of the destination-instruction.
    • Example: ADD.A $1, $2 adds the a-field of instruction 1 to the a-field of instruction 2. The result is saved in the a-field of instruction 2.
  • B: Operates on the b-field of the source-instruction and the b-field of the destination-instruction.
    • Example: ADD.B $1, $2 adds the b-field of instruction 1 to the b-field of instruction 2. The result is saved in the b-field of instruction 2.
  • AB: Operates on the a-field of the source-instruction and the b-field of the destination-instruction.
    • Example: ADD.AB $1, $2 adds the a-field of instruction 1 to the b-field of instruction 2. The result is saved in the b-field of instruction 2.
  • BA: Operates on the b-field of the source-instruction and the a-field of the destination-instruction.
    • Example: ADD.BA $1, $2 adds the b-field of instruction 1 to the a-field of instruction 2. The result is saved in the a-field of instruction 2.
  • F: Operates on both the a-field of both instructions and the b-field of both instructions respectively. Basically combines the A and B modifiers.
    • Example: ADD.F $1, $2 adds the a-field of instruction 1 to the a-field of instruction 2 as well as the b-field of instruction 1 to the b-field of instruction 2. The result is saved in the respective field.
  • X: Basically combines the AB and BA modifiers. Works like F, but with opposing fields.
    • Example: ADD.X $1, $2 adds the a-field of instruction 1 to the b-field of instruction 2 and saves the result in the b-field of instruction 2. Then it adds the b-field of instruction 1 to the a-field of instruction 2 and saves the result in the a-field of instruction 2.
  • I: Operates on the whole instruction. Only used with MOV and comparison instructions (SEQ, SNE). The specifics depend a bit on the instruction itself.
    • Example: MOV.I $1, $2 moves the entire instruction 1 including opcode and address modes to instruction 2.

Instruction Set

DAT

Kills the process. When all processes of a player are killed, they lose.

MOV (Move)

Copies data from the source address to the destination address. The specific data is determined by the modifier. For example, the AB modifier copies the a-field from the source instruction to the b-field of the destination instruction. The I modifier copies the entire instruction.

Arithmetic Instructions (ADD, SUB, MUL, DIV, MOD)

ADD, SUB, MUL, DIV and MOD are all very similar, just computing the specified mathematical operation on the arguments specified by the modifier. For example, the ADD.AB instruction would add the a-field of the source instruction and the b-field of the destination instruction and save it to the b-field of the destination instruction. The I and the F modifiers act the same, computing the operation of the a-fields and the b-fields, then saving the result in the respective field in the destination instruction.

Special cases

Division by zero using the DIV or MOD instructions acts like a DAT instruction, killing the process and leaving the destination unchanged. If two divisions are done using the F or X modifiers, and one is a divide by zero, the other division is still performed.

JMP (Jump)

Jumps to (queues) the instruction pointed at by the a-field.

JMZ (Jump if zero)

This instruction works similar to JMP in that it jumps to (queues) the instruction pointed at by the a-field. However, it only does that after looking at the instruction pointed to by the b-field. The jump is only done if the a- or b-field (or both) of this instruction is zero. Which fields are looked at is determined by the modifier:

  • A, BA: Jumps if the a-field of the instruction is zero
  • B, AB: Jumps if the b-field of the instruction is zero
  • I, X, F: Jumps only if both fields of the instruction are zero

JMN (Jump if not zero)

Works like JMZ, but jumps if the tested value is not zero. If two values are tested (I, X, F modifiers), then the jump is executed if either of the fields are non-zero.

DJN (Decrement, then jump if not zero)

Works like JMN, but the a- and/or b-fields of the instruction pointed at by the b-field are decremented by one before testing.

SPL (Split)

Creates a new process that starts at the instruction pointed to by the a-field. This new process is queued after the executing the SPL instruction. So the new process will be executed after the current one has been executed again.

SEQ (Skip if equal) / CMP (Compare)

Note: CMP is the old name for the instruction before SNE was created. In modern standards, SEQ should be used.

Compares two instructions and skips the next instruction if they are equal. The fields being compared are dependent on the modifier. The I modifier compares the opcode and modifier as well, but ignores the address modes. If two fields are tested, both have the be equal for the skip to occur.

SNE (Skip if not equal)

Works like SEQ, but skips the next instruction if the tested instructions are not equal. If two fields are tested, it is sufficient for any pair of them to be different for the skip to occur.

SLT (Skip if less than)

Skips the next instruction if field(s) in the address pointed at by the a-field are less than the field(s) in the address pointed at by the b-field. The tested fields are specified by the modifier. If the modifier causes two comparisons (I, F, X), both need to be true for the next instruction to be skipped. The I modifier acts like the F modifier, since opcodes do not have an order to them.

NOP (No operation)

Does nothing.

Common extensions

P-Space

The p-space (private space) extension implements a memory space for each program. A p-space can only be accessed by the respective program, and its data persists over a multi-round game. Contrary to the normal memory core, the p-space stores only integers, not full instructions. However, it is still circular.

Instructions

STP (Store private)

Stores the value referenced by the a-field into the p-space memory location referenced by the b-field. For example:

STP.B  2, 3
...
DAT    #0, #10
DAT    #0, #7

This code would store the number 10 into the p-space memory location 7.

LDP (Load private)

Loads the value from the p-space memory location referenced by the a-field into the instruction referenced by the b-field. It generally works similar to the STP instruction.

Quirks

P-space instructions cannot store or load two values at the same time. Because of this, the I, F and X modifiers act like the B modifier.

PIN (P-space Identification Number)

PIN is used very rarely. PIN can identify a p-space, allowing multiple processes with the same PIN to share it.

FOR loops

The FOR-ROF construct can be used to repeat code without copy-pasting. It looks like this:

FOR 5
MOV 0, 1
ROF

This gets expanded to:

MOV 0, 1
MOV 0, 1
MOV 0, 1
MOV 0, 1
MOV 0, 1

This can become an issue when labels are used, as it will result in multiple definitions for the same label. In that case, another feature can be used:

N FOR 3
label&N MOV label&n, label&n+10
ROF

This gets expanded to:

label01 MOV label01, label01+10
label02 MOV label02, label02+10
label03 MOV label03, label03+10

Clone this wiki locally