Click Here To Go To The Your Computer Archive

Written By Jon Cottrell

Cover Art
Click Here To Enlarge Loading Screen

Loading Screen
Click Here To Enlarge Opening Screen

Opening Screen
Click Here To Enlarge Screenshot

Game Screenshot

Core Wars

Jon Cottrell makes war, not love, on his BBC

Most programmers seem to play arcade games in their spare hours, but now, from America, the land of hacking, the Big Mac and War Games comes the new line in strategy games - Core Wars.

I first came across the idea of Core Wars in, unsurprisingly, an American magazine, Scientific American, a few months ago. The idea seemed excellent, so I dug out my Advanced User Guide, and put together this half-Basic, half machine code version of Core Wars, to the best of my knowledge, the first version in this country.

The game scenario is original - the Americans have succeeded in penetrating a giant Russian mainframe, and each player is trying to cause his opponent's program to crash, by the simple means, familiar to all machine code junkies, of making it execute an illegal instruction.

In Core Wars, two programs try to deliberately corrupt each other. A number of techniques have been developed so far; there are the simple methods like overwriting sections of memory at random, or alternatively, "intelligent" methods involving searching out the opposing program. Most people who have written machine code will be familiar with the problem - an uncautiously calculated jump, and the processor is trying to execute data rather than a program, with unpredictable and surprising results.

This means the game is played by two players. Each writes a program, of maximum length 100 lines, in a specially-developed language called Redcode, which looks rather like a stripped-down assembly language. These programs are then assembled; play takes place in a memory space of the 3000 locations in the "core" from which the game takes its name - core is a now obsolete term once used to describe a computer's memory space - and each location can contain either a single Redcode instruction, or a piece of data.

If you try to execute data as an instruction, the program will not recognise it, and the opposing one will win! The core is organised slightly differently from a present-day memory, in that core location 2999 "wraps round" and is next to location 0. This means that attempting to access location 3000 results in accessing location 0, and so on. Then play starts for real - the computer executes one instruction from each player's program in turn, rather in the manner of a time-sharing system. Play continues until one program is forced to crash by executing an invalid instruction, and so the other player wins.

Obviously, one simple tactic is just to write a program that overwrites various areas of memory at random, hoping to score a 'hit' on a vital part of the other program, making it crash when it tries to execute that part. But an experienced player could find a way round such an attack - if he set up a block of memory with known contents, which was continuously checked, as soon as a byte was corrupted, he could arrange for his program to transfer itself somewhere in memory less vulnerable, and start a counter-attack.

Easy To Pick Up

The sort of instructions used will be immediately familiar to anyone who has dabbled in machine code, but don't despair if you never adventured beyond Basic - the limited instruction set will be easy to pick up, and will leave you with some knowledge of the techniques used in genuine machine code. At this point it seems appropriate to describe briefly the instruction set and some details of how the instructions are assembled, and then run through how to use the program.

Each instruction has a code, as for any assembly language, also a mnemonic. The mnemonic is what you might consider the 'real' instruction - it is the mnemonic that you type in to the computer as a line of your Redcode program. The instructions are dealt with in numerical order of code.

Each instruction has either one or two "arguments" - the numbers that follow the mnemonic. The largest argument allowed is 3000 - anything larger will be reduced modulus 3000. Negative arguments are assembled in "complement form"; i.e. an argument of -1 becomes 2999, since subtracting 1 has the same effect as adding 2999 in a core of 3000 contiguous locations.

0 - DAT - Used for putting data in a location. Although this instruction has a code of 0, it is a special case, as it assembles as an unexecutable instruction followed by the byte of data. Strictly speaking, any location in the core containing an unexecutable statement followed by data could be regarded a data statement, but DAT is provided to actually store data in specified locations. It takes one argument.
1 - MOV - Moves data from one location to another. It takes two arguments, so the format is MOV a,b. It will move the contents of location a to location b, without affecting the contents of a.
2 - ADD - This instruction takes two arguments, which makes its format ADD a,b. It adds the contents of location a to the contents of location b, leaving the result as the new contents of b, and leaving the contents of a unchanged.
3 - SUB - Takes two arguments, so its format is SUB a,b. The effect is to subtract the contents of location a from location b, leaving the contents of a unchanged.
4 - JMP - Has but one argument, which makes its format JMP a. It is an unconditional jump to location a - used for the same purpose as Goto in Basic, but is relative rather than absolute, as explained below.
5 - JMZ - This is a conditional jump instruction which takes two arguments, making its format JMZ a,b. If the contents of location b is 0, the program will jump to relative location a - otherwise it will carry on with the next instruction after the JMZ.
6 - JMG - As above, except that a jump occurs if the contents of location b is greater than zero.
7 - DJZ - Takes two arguments, making the format DJZ a,b. The effect is to decrement the contents of location b by one, then jump to location a if the contents of location b is now 0.
8 - CMP - this also has two arguments, so its format is CMP a,b. It compares the contents of location a and location b, and if they are not equal to each other, the computer will skip the next instruction, and continue execution with the one after.

Assumes Direct Mode

As I have said, arguments lie in the range 0-2999 when assembled, but in addition, each can take one of three modes, called addressing modes. These modes are immediate, direct and indirect. Unless you specify otherwise, the computer assumes the argument is in direct mode. Prefixing the argument with # means it is immediate, and prefixing with @ means it it indirect. Taking as an example the complete instruction MOV 0,1 the full effect would be:

  1. The computer finds neither argument has a prefix, so assumes both are direct.
  2. It therefore adds the first argument onto the current address to find out where to get the data to be moved from, and then adds the second argument onto the current address to find out where to put it.

The instruction therefore moves the data 0 locations beyond the instruction - i.e. the instruction itself - to a position 1 location beyond the instruction, i.e. in the next location. Incidentally, this is an example of a complete one-line program - when the instruction has been executed, the computer wil look for the next instruction, and find the instruction "MOV 0,1" that has been put there by the previous one, and execute this, and this will continue, leaving a trail of MOV 0,1's throughout the core.

Machine code programmers will have noticed that the direct mode, as are all modes, is relative - there is no way for a program to know its absolute address in the core, and the start address for each program within the core is assigned randomly on assembly.

The other two modes I mentioned work as follows - first immediate mode. This causes the argument to be interpreted as a number, rather than an address in core. This means that MOV#3,2 will place the number 3 in the location 2 places further on from the present one, as opposed to MOV3,2, which would copy the contents of the location three places on from the present one into the location two on from the present one.

Indirect mode is rather more complicated, and is best explained by an example.

ADD @2,4 means "read the contents on location 2. Add this number on to the current location. Now read the contents of this location, and that is the value to be added to that four locations on from the current one."

So, if you're still with me, type in the program, save it and type Run. After the title page press space and you should see the second screen, headed 'Phase 1 - Redcode entry', and below that, the heading 'Player 1' and an input prompt, '0>'. Here's where you use those instructions I just explained, and type in your program.

Remember, you have a maximum of 100 lines of program each, and each prompt is numbered to show you how you're doing for space.

When you finish your program, press Return in response to the prompt, and then the other player will be given the chance to input his program. After each player enters his program, he is asked to specify which instruction he wishes execution to commence with; in response to the question, enter the number of the instruction you wish to execute first.

When both players have finished, you will be asked to enter the number of turns of execution - i.e. how many instructions the computer is going to execute from each program before it announces a draw.

When both players have entered their code, it is assembled. If an error such as an illegal instruction or format is encountered, the appropriate player has to re-input his code. Assuming no errors are present in either program, however, the game starts in earnest.

This is where I made a major change to the game as I discovered it. Apart from the fact that the original game had no assembler, and the code had to be put together by hand, I have stuck to the original guidelines. But the game as it stood wasn't exactly visual - each player typed in a mass of numbers, sat back and waited. This was all very well in the early days, but now people expect something more in the way of a display.

Semi-Graphic Display

So to that end, I added a semi-graphic display, which takes the form of an array of graphic blocks, 20 blocks wide by 10 high. Each block represents 15 locations in the core, numbered across the screen, so that top left hand block represents locations 0-14, the top right hand locations 295-299, the bottom left 2700-2714 and the bottom right 2985-2999. When the game starts, the area occupied by player one's code is shown in red, and that occupied by player two's in green.

This is the first time either player can know where their code is in memory. After that, whenever a player's program changes the contents of a location in the core, the block representing that location changes to the appropriate colour, so, if a program is overwriting areas of memory as an attack, it is possible to see how close the attack is to a vital point.

The game ends when one program or the other tries to execute an illegal instruction, or when the set number of turns has elapsed.