Learning path · Step 2 of 5
2026-07-18 · 6 min read

Understanding Redcode

AI notice: This text is created with the support of AI systems; it is reviewed editorially and taken responsibility for before publication.

In step 1 you saw two warriors and roughly understood what they do. Now let us look at what a Redcode instruction actually consists of.

The language has few instructions. The depth is somewhere else — in a place that many introductions treat as a footnote.

The anatomy of an instruction

Every cell in the core holds exactly one instruction, and it always looks like this:

MOV.I   $3, @7
│   │    │   │
│   │    │   └─ B-operand
│   │    └───── A-operand
│   └────────── modifier
└────────────── opcode

Four parts, always all present. If you leave out the modifier in the source, the assembler fills one in according to fixed rules — so it is never really absent, just invisible. That is the most common source of surprises for beginners.

Both operands have an addressing mode (the $ and the @ above) and a value. Values are always relative to the current cell: $3 means "three cells along", not "address 3".

The instructions

There are 16 opcodes, grouped by purpose:

Moving data and arithmetic

Instruction Meaning
DAT Data. Kills the process that runs onto it.
MOV Copies A to B.
ADD SUB Adds or subtracts A onto B.
MUL DIV MOD Multiplies, divides, remainder.
NOP Does nothing, but costs a cycle.

Jumping

Instruction Meaning
JMP Jumps to A.
JMZ JMN Jumps if B is zero, or not zero.
DJN Subtracts one from B, then jumps if B is not zero.
SPL Starts an additional process at A.

Comparing

Instruction Meaning
SEQ (CMP) Skips the next instruction if A equals B.
SNE The same, if they differ.
SLT The same, if A is less than B.

Memory between roundsLDP and STP access p-space, a small memory area that survives a round. It lets a warrior learn from lost rounds. That is an advanced topic; under the standard '94nop rule set used here, both are disabled.

That was the entire language. If it strikes you as thin: quite right. The art is not in the number of instructions.

The addressing modes

An operand says not only which cell is meant but also how to get there. There are eight modes. The four most important first:

Mode Name Meaning
# immediate The value itself, not a cell. #5 is the number 5.
$ direct The cell that many steps away. $5 is five cells along.
@ B-indirect Go to $5, read the B-field there, go that far again.
* A-indirect The same, but via the A-field.

The indirect modes are why Core War works at all: a warrior can advance a pointer and work on the whole memory without growing itself. That is exactly what the dwarf from step 1 does with @bomb.

Then there are four modes that alter the pointer in passing:

Mode Meaning
< Subtract one from the B-field first, then go indirect through it.
> Go indirect through B, then add one afterwards.
{ Like <, but via the A-field.
} Like >, but via the A-field.

These four are extremely handy — you get loop counters for free — and at the same time the biggest source of errors in the language. More on that in a moment.

The modifier matters more than the instruction

Now the point this chapter is really about.

An instruction has two fields, A and B. The modifier determines which fields are touched at all:

Modifier Effect
.A A to A only.
.B B to B only.
.AB A-field of the source into the B-field of the target.
.BA B-field of the source into the A-field of the target.
.F Both fields, A to A and B to B.
.X Both, crossed over.
.I The whole instruction, including opcode and modes.

This sounds like a detail. It is the difference between a working warrior and a dead one.

Take the imp from step 1 and change nothing but the modifier. 2000 rounds against the dwarf in each case:

Variant Wins Losses Ties Score
MOV.I $0, $1 0 510 1490 74.5
MOV.A $0, $1 0 2000 0 0.0
MOV.B $0, $1 0 2000 0 0.0
MOV.F $0, $1 0 2000 0 0.0
MOV.X $0, $1 0 2000 0 0.0
MOV.AB $0, $1 0 2000 0 0.0
MOV.BA $0, $1 0 2000 0 0.0

Two characters changed, and a warrior that survives three quarters of its fights becomes one that loses every single one of 2000 rounds.

The reason is simple once you have seen it. The imp lives by copying itself — and it must copy itself completely, including the MOV opcode. Only .I carries the opcode across. With any other modifier, just a number travels into the neighbouring cell. The opcode there remains what it was: DAT. So the imp writes its own grave and walks into it on the next cycle.

Remember this: MOV.I and MOV.A are not two variants of the same instruction. They are two different tools that happen to share a name.

Simulators reflect this in their construction, incidentally: opcode and modifier are fused internally into one value, because together they form a single instruction. Anyone who reads only the opcode has not read the instruction.

The trap with < and >

One more peculiarity, and it causes more warrior bugs than anything else.

The modes < > { } alter a pointer. They do so even when the operand is not needed for the actual computation.

JMZ.B   $2, <5

JMZ jumps if the B-field is zero. Whether it jumps or not — the < has already subtracted one from the B-field of cell $5 in either case. The side effect happens when the address is resolved, not when the instruction executes.

On top of that comes an ordering you need to know: the A-operand is resolved first, the B-operand afterwards. So if both point at the same cell and both modify it, the B-operand sees the A-operand's change — but not the other way around.

That is not harassment but a tool: experienced authors build counters with it that cost no instruction of their own. For beginners the main lesson is: if a warrior behaves inexplicably, first check whether there is a < or > somewhere you did not think about.

An example to read

With that knowledge the dwarf can now be read line by line:

step    EQU     4
start   ADD.AB  #step, $bomb
        MOV.I   $bomb, @bomb
        JMP.A   $start
bomb    DAT.F   #0, #0
  • ADD.AB #step, $bomb#step is the number 4 itself. .AB means: A of the source onto B of the target. So it adds 4 to the B-field of bomb.
  • MOV.I $bomb, @bomb.I copies the whole instruction. The target is @bomb, that is, indirect through the B-field of bomb — precisely the pointer the previous line just incremented.
  • JMP.A $start — back to the beginning.
  • bomb DAT.F #0, #0 — the bomb. It is simultaneously ammunition and pointer storage: the B-field serves as an address counter while the rest of the cell is shipped through the core as a lethal payload.

This double duty — one cell being data and pointer and weapon — is typical of Redcode. Space is scarce, and every cell you save is one less cell the opponent can hit.

What you now know

  • An instruction consists of opcode, modifier and two operands, each with an addressing mode.
  • There are 16 instructions; the language is deliberately small.
  • # is a value, $ is a cell, @ and * are pointers.
  • < > { } alter pointers — even when the operand goes unused.
  • The modifier decides the semantics. MOV.I versus MOV.A is the difference between 74.5 and 0.0 points.

In the next step we look at the strategies that grew out of these few instructions — and why they beat each other in a rock-paper-scissors pattern.


All figures on this page were computed with pMARS under the standard '94nop rule set over 2000 rounds each.



← Core War overview