[alt.sources] Core Wars

c60a-3ci@web-3f.berkeley.edu (An Existentialist) (01/21/89)

Here's a copy of the COREWARS programs.  To extract, save to a file, delete
this headers, and type:

%sh < <filename>

The MANIFEST file should have a listing of ALL the files included with this
package.
Na Choon Piaw
c60a-3ci@wolf.berkeley.edu
(soon to be changed)
301P, 2650 Durant,
Berkeley, CA 94720.
P.S.  There is also a "bitmapped" display version that works ONLY for the
      SUN.  You must have a SUN 3/50 or better and the pixrect library for
      it to work.  Mail me for the source.
-----------------------        cut here        -------------------------
echo x - MANIFEST
cat >MANIFEST <<'!E!O!F!'
CoreWars public distribution package, by Na Choon Piaw.

You should have the following files:

Assembler:
assem.h		-	assembler data structures
assem.c		-	assembler function
disassemble	-	disassembler
lookup.c	-	does symbol matching
amain.c		-	"main" for the assembler
aoutput.c	-	output function for the assembler
parse.c		-	parser for the assembler
tokenize.c	-	tokenizer for the assembler
MakeAss		-	Makefile for the assembler

Interpreter:
interp.h	-	interpreter data structures
inst.c		-	implementation of simulator
load.c		-	loader
main.c		-	"main" for the interpreter
output.c	-	output functions for the interpreter
MakeInt		-	Makefile for the interpreter

*.rc		-	redcode programs (sample)

MANIFEST	-	this file
README		-	documentation file

To install, type:
%make -f MakeAss
%make -f MakeInt
%assem *.rc

Please send all bug reports and modifications as well as other interesting
notes to:

Na Choon Piaw
c60a-3ci@web.berkeley.edu
301P, 2650 Durant,
Berkeley, CA 94720.
(to be changed later)

Note:	You are now a "registered user".  Doesn't mean anything, means I've
	got a list of people to send updates to.
!E!O!F!
echo x - MakeAss
cat >MakeAss <<'!E!O!F!'
all: assem disassem
assem: amain.o tokenize.o parse.o lookup.o aoutput.o assem.o
	cc amain.o tokenize.o parse.o assem.o lookup.o aoutput.o -o assem
disassem: disassem.c assem.h
	cc -O disassem.c -o disassem

main.o: amain.c assem.h
	cc -O -c amain.c -o main.o
tokenize.o: tokenize.c assem.h
	cc -O -c tokenize.c -o tokenize.o
parse.o: parse.c assem.h
	cc -O -c parse.c -o parse.o
lookup.o: lookup.c assem.h
	cc -O -c lookup.c -o lookup.o
output.o: aoutput.c assem.h
	cc -O -c aoutput.c -o aoutput.o
assem.o: assem.c assem.h
	cc -O -c assem.c -o assem.o

!E!O!F!
echo x - MakeInt
cat >MakeInt <<'!E!O!F!'
CFLAGS = -DSMALL

interp: main.o load.o output.o play.o inst.o
	cc -o interp  main.o load.o output.o play.o inst.o -lcurses -ltermcap

main.o:	interp.h main.c MakeInt
	cc -c $(CFLAGS) main.c -o main.o

load.o: interp.h load.c
	cc -c $(CFLAG) load.c -o load.o

output.o: interp.h output.c MakeInt
	cc -c $(CFLAGS) output.c -o output.o

play.o: play.c interp.h MakeInt
	cc -c $(CFLAGS) play.c -o play.o

inst.o: inst.c interp.h
	cc -c $(FLAGS) inst.c -o inst.o
!E!O!F!
echo x - README
cat >README <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

This is a new version of corewars I'm working on.  It will feature
ONLY 2 player games (I hate 3 or more players on corewars), some limited
amounts of display (using curses) though that will be implemented only
after the non-display functions work.  And an independent, full featured
assembler.  The interpreter will load "binary" code only.

Full featured -- 1 physical pass, 2 "in memory" passes.
		 symbolic memory addresses, immediate, direct, and indirect
		 addressing modes.

COREWARS is a where the players each write an assembly language program
that battle each other in the memory of a computer called MARS.  (Memory
Array Redcode Simulator)  The game ends when (1) all the streams of execution
on one side have died out, or (2) when the time limit is up.  In (1) the
winner is the side which still has surviving execution streams.  In (2)
the game is a draw.  Once we are finished with implementing this game,
maybe we can schedule a corewars tournament.

My version of corewars implements (hopefully in a short while) the following
instructions from World 7 --- The First Corewars Tournament (with some
modifications) (Note: the first two articles have slightly differing
definitions):

INSTRUCTION	MNEUMONIC	CODE	ARGUMENTS	EXPLANATION
Move		mov		1	A B		move contents of
							address A to address B
Add		add		2	A B		add contents of address
							A to address B
Subtract	sub		3	A B		subtract contents of
							address A from address
							B
Jump		jmp		4	  B		Transfer control
							to address A
Jump if zero	jmz		5	A B		Transfer control
							to address A if
							contents of B are
							greater then 0
Jump if not 0	jmn		6	A B		transfer control to
							address B if contents
							of B is != 0
Decrement jump	djn		7	A B		Subtract 1 from
if not zero						contents of address
							B and transfer
							control to
							address A if contents
							of address B is != 0
Compare		cmp		8	A B		compare contents of
							addresses A and B; if
							they are unequal,
							skip the next
							instruction.
Split		spl		9	  B		split execution into
							next instruction and
							the instruction at A
Data statment	dat		0	  B		A nonexecuatble
							statement; B is the
							data value.


Addressing modes:-
immediate mode:		#argument
direct mode:		symbol
indirect mode:		@symbol

Expected other differences -
1)	When +/- are conducted to an address that contains other than a
	data instruction, parameter B is always modified (even in spl
	instructions).  There is no way and add or sub instruction can
	change one type of instruction to another, so the CODE part of
	the table is really rather worthless.
2)	The assembler will feature the "real" symbol.  In other words,
	you do not have to convert all your symbols into numbers before
	feeding your program to the assembler.
	There will be a reserved symbol, though, called "start" and this
	will be where your code will start executing.
	All symbols are characterized by a ':' character at the end, e.g.
	"start:"
3)	Separate assembly.  In the interest of modularity and ease of
	debugging (for my code, of course, not for the redcode assembly
	program, so I still don't suggest writing 200 instruction redcode
	programs, even though the assembler permits it), I have decided
	to separate the assembler from the interpreter.  The interpreter
	will have to be fed the preassembled programs, etc. in a special
	format.  (produced by the assembler).

4)	Will not implement stuff like the "<" or the ">".  i.e., decrease
	stuff pointed to by operand, then get that address.

5)	Randomized starting memory for the redcode programs.
	Make corewars more deterministic - predictable.
	Can be implemented later if demand arises.

Other expected differences -

BUGS!!!

BUG REPORT SECTION:-

assem/main.c	- The program will not accept files with ".e" anywhere
		  in the last portion of it (i.e., the extension)
		  These files will be rejected.
		  e.g.	"mice.eecs" will be rejected

tokenize.c	- The program will not be able to handle comments without
		  any delimiters between them and the code.
		  e.g.  "mov 0 1;;help" will be tokenized into:
			"mov" "0" "1;;help" which is not acceptable, since
			the later functions like lookup will not be able
			to handle it.

play.c/main.c:	- The BIG option (to fully bit map the memory array,
		  doesn't work.   I wonder why?)

Note:
A.K. Dewdney's THE ARMCHAIR UNIVERSE is available from Freeman (publisher).
I highly recommend buying a copy and reading it.  It's a whole lot
of fun and teaches a lot of great programming techniques.
It's moderately priced at around $13.25, and, in my opinion, worth every
penny.

Choon Piaw


Assembler portion of corewars
Usage:
assem <file1> <file2> .....

All output files will have an additional extension ".e"
for instance, if the input file was "help.rc" the ouput file will be "help.e"

Assembler limits: 200 instructions
		  100 symbols
		  255 characters in a string

Comments are indicated by a ;
Assembler is not case sensitive.
Remember that this does not conform to ALL the A.K. Dewdney standards
in the implementation of OPCODES/PARAMETERS, but as long as you write
code that does not depend on changing other's opcode, you should be
all right.  The instruction set is standard, however.

BUGS:
	Any file starting with a .e extension is rejected automatically
	Any character is recognized as a symbol.  Therefore, in
	mov	0	1;this is a comment
	1;this will be interpreted as a symbol instead of a number with
	a comment following it.  The proper form would be:
	mov	0	1	;this is a comment



Corewars interpreter
- started 12/14/88 by Na Choon Piaw (The Existentialist)

usage:

interp cycles file1 file2

cycles		-number of machine cycles
file1, file2	-preassembled corewars programs

Programmer's documentation for Redcode interpreter

Goal:

To provide a visual version of corewars that is fast, standard and portable.

Modules

1.	int loader(int position)
	-	load up files into main memory to position.
	-	setup the starting pcs

2.	eval
	-	input the memory element + a host of other info
		(including the list of streams)
	-	each of the instructions will be handled by a separate
		subroutine that will be passed just sufficient info to carry
		it out.

3.	control
	-	responsible for keeping track of who's what and passing the
		correct instruction to the evaluator

4.	Display
	-	Display in a visual format (to be decided) what's going on
		in memory at that time.

Data Structures

Streams of execution are kept as circularly linked list, with split
instructions adding to the linked list, and with trying to execute data
instructions.

All parameters will range from 0 to +8000.  Excess will be trimmed off,
negatives will be wrapped back around.

Credit where credit is due:

Case Larsen wrote and debugged the SUN graphics function interface2.c
Adrain Ho wrote and debugged the loader load.c
I wrote and debugged the rest.

!E!O!F!
echo x - assem.h
cat >assem.h <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* MARS redcode assembler:
	Header file
	Restarted in Novemeber '88
	Na Choon Piaw		*/

/* note that tag is just a generic name for something that I can't think
   of a name for.                            ---- CP                 */

/* define instruction set */
typedef enum { dat, mov, add, sub, jmp, jmz, jmn, djn, cmp, spl } instr;

/* define addressing modes */
typedef enum { immed, direct, indirect } mode;

/* define structure of an instruction */
/* NOTE: this will probably differ from the interpreter's version */
typedef struct
{
	instr	inst;		/* instruction */
	int	para1,para2;	/* first parameter, second parameter */
	mode	m1,m2;		/* addressing modes for parameters */
} memory;	/* memory element */

/* define compiler limits */
#define MAXINST	200	/* maximum number of instructions */
#define SYMBOLS 100	/* maximum number of symbols in symbol table */
#define MAXBUFFER 256	/* maximum size of string buffer */
#define COMMENT ';'	/* comment character */

/* linked list of tokens for tokenizer and assembler to work on */
typedef struct tag0
{
	char		*token;		/* token as a string */
	struct tag0	*next;		/* next token */
} tokenlist;

/* symbol table structure */
typedef struct
{
        char    *symbol;                /* pointer to string of symbol */
        int     position;               /* position the symbol belongs to */
} tag1;

/* now to define the strings that the assembler recognizes */
#define MOV	"MOV"
#define ADD	"ADD"
#define SUB	"SUB"
#define JMP	"JMP"
#define JMZ	"JMZ"
#define JMN	"JMN"
#define DJN	"DJN"
#define CMP	"CMP"
#define SPL	"SPL"
#define DAT	"DAT"
!E!O!F!
echo x - interp.h
cat >interp.h <<'!E!O!F!'
/* MARS redcode interpreter:
	Header file
	Started December '88
	Na Choon Piaw		*/

/* instruction set */
typedef enum { dat, mov, add, sub, jmp, jmz, jmn, djn, cmp, spl } instr;

/* addressing modes */
typedef enum { immed, direct, indirect } mode;

/* this is the new definition of a memory cell */
typedef struct
{
	instr	inst;		/* instruction */
	int	para1, para2;	/* first parameter, second parameter */
	mode	m1, m2;		/* addressing modes */
	int	lastmod;	/* last modified by */
} cell;

/* old assembler definition of memory cell */
typedef struct
{
	instr	inst;
	int	para1, para2;
	mode	m1, m2;
} memory;

/* doubly circularly linked list for streams of execution */
typedef struct tag0
{
	int		pc;	/* program counter */
	struct tag0	*next, *prev;
} stream;

/* interpreter limits */
#define MAXINST	200		/* maximum number of instructions */
#define SIZE	8000		/* size of array */
#define MAXPLAY	3		/* maximum number of players */
#define RANDIVISOR 33
!E!O!F!
echo x - amain.c
cat >amain.c <<'!E!O!F!'
/* this module contains the main function, which settles open/close
   file i/o as well as little trivial details like adding an extension
   and stuff like that.  It has been debuged (except for BUG NOTES)
   so it's safe to trust. There is a little bit of inefficiency, but
   that's justified since I want a more easily readable program */

#include "assem.h"
#include <stdio.h>
#include <malloc.h>
#include <strings.h>

char	*outname(str)
char	*str;
/* accepts an input string and outputs a proper output file with ".e"
   extension.  If already has .e, as an extension, produce an error. 
   BUG NOTE: even if it's as innoculous as .eex, etc (as long as the
             extension starts with .e) it will still produce an error
   Otherwise, remove current extension
   and add .e extension.
   returns pointer to new output name                                       */
{
	char	*newstr;
	char	*dot;		/* position of '.' */

	if (!(newstr =(char *) malloc( (unsigned) strlen(str) + 3)))
	{
		printf("not enough memory --- outname\n");
		exit(1);
	}

	strcpy(newstr,str);

	if (!(dot = rindex(newstr,'.')))
		strcat(newstr,".e");	/* no extenstion */
	else if (*(dot + 1) == 'e')	/* same extension as output? */
	{
		printf("wrong input file name: %s\n", newstr);
		printf("try moving to non .e extension --- outname\n");
		exit(1);
	}
	else				/* perform surgery */
	{
		(*(dot + 1)) = 'e';
		(*(dot + 2)) = '\0';
	}

	return newstr;
}


/* main -- Open input and output files, giving default names if
           necessary.  Detects errors like not being able to open files
	   etc.
*/
main(argc, argv)
int	argc;
char	*argv[];
{
	FILE	*f1,*f2;		/* input file, output file */
	char	*outfile = "NONAME",	/* default output file */
		flag = 0;		/* standard input */

	if (argc == 1)		/* no arguments */
	{
		flag = 1;	/* read from standard input */
		argv[1] = outfile;
		argc = 2;	/* one file */
	}

	for (;argc > 1; argc--)
	{
		if (flag)
			f1 = stdin;	/* set file to standard input */
		else
			/* open input file */
			if (!(f1 = fopen(argv[argc - 1],"r")))
			{
				printf("oops cannot open file %s",
					argv[argc - 1]);
				printf("\n-- in main\n");
				exit(1);	/* error status 1 */
			}

			/* open output file */
			if (!(f2 = fopen(outname(argv[argc - 1]), "w")))
			{

				printf("cannot open write file %s",
					outname(argv[argc - 1]));
				printf("\n --- in main\n");
				exit(1);
			}

		printf("%s:\n", argv[argc - 1]);
		assemble(f1,f2);	/* call assembler */

		if (!flag)	/* close file */
			fclose(f1);

		fclose(f2);
	}
}

/* debugging version of assemble */
/* commented out because this module is now fully debugged */
/* --- Na Choon Piaw, 11/14 */
/*
assemble(infile,outfile)
FILE	*infile, *outfile;
{
	int	c;

	while ((c = fgetc(infile)) != EOF)
		fputc(c, outfile);
}
*/
!E!O!F!
echo x - aoutput.c
cat >aoutput.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* output.c --- output routine for the asssembler.
   11/25/88  --- NCP                             */

/* algorithm:
   1.	write number of instructions into file
   2.	write code number of start instruction
   3.	while there are still instructions do
   4.		write instruction n
   5.   end
*/
#include "assem.h"
#include <stdio.h>

#define IO(i,j) if ((i) < (j)) { printf("error in writing file  --- output\n" \
				 ); }

output(f, table, code, no)
FILE	*f;		/* file to output to */
tag1	table[];	/* table of symbols */
memory	code[];		/* code itself */
int	no;		/* number of instructions */
{
	int	check,	/* check on how many bytes have been written */
		start;	/* starting instruction */

	check = fwrite(&no, sizeof(int), 1, f);
	IO(check,1)

	start = getsym("START",table);
	check = fwrite(&start, sizeof(int), 1, f);
	IO(check,1);

	check = fwrite(code, sizeof(memory), no, f);
	IO(check,no);
}
!E!O!F!
echo x - assem.c
cat >assem.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* assembler portion of the program */
/* 11/14 '88
  Implementing a one-pass "text in memory" assembler that handle tokens
  as well.  Symbol table is a linear table.
   Algorithm:
	1)	Tokenize the input file (i.e. produce a list of symbols in mem)
	2)	Run through once, parsing instructions but not turning
		into machine code yet.  Insert all new symbols into symbol
		table.
	3)	Run through second time,  turning it into machine code
		and looking up symbols.  -- the real error phase.
*/
#include <stdio.h>
#include <malloc.h>
#include "assem.h"

/* symbol table */
static tag1	table[SYMBOLS];

/* instructions list */
static memory	elements[MAXINST];

/* declaring the functions */
tokenlist	*tokenize();

/* top level connections for assembler hooks up tokenize, parse, lookup
   and output so that they can be written independently (no global
   variables in this program                                         */
assemble(infile, outfile)
FILE	*infile,*outfile;
{
	tokenlist	*head;
	int		i;		/* number of instructions */

	head = tokenize(infile);
	parse(head, table);
	i = lookup(head, table, elements);
	output(outfile, table, elements, i);
}

!E!O!F!
echo x - disassem.c
cat >disassem.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

#include <stdio.h>
#include "assem.h"

/* dissassembler for red code programs.
   NOTE:  I'm not bothering with writing code to REALLY disassemble
          it, just output starting position, number of instructions,
	  opcode, and operands.
   11/25 ---- NCP                           */
#define IO(i,j) if ((i) < (j)) {printf("error reading file\n");exit(1);}


main(argc, argv)
int	argc;
char	*argv[];
{
	FILE	*f;
	int	i,j,k;
	memory	elements[MAXINST];

	if (argc < 1)
	{
		printf("usage: disassem <file>\n");
		exit(1);
	}

	if (!(f = fopen(argv[1],"r")))
	{
		printf("cannot open file %s\n", argv[1]);
		printf("------ main\n");
		exit(1);
	}

	i = fread(&j, sizeof(int), 1, f);
	IO(i,1)
	printf("Number of instructions: %d\n", j);

	i = fread(&k, sizeof(int), 1, f);
	IO(i,1)
	printf("Starting instruction: %d\n", k);

	i = fread(elements, sizeof(memory), j, f);
	IO(i,j)

	printf("NO.\tOPCODE\tFIRSTPARA\tSECONDPARA\n");
	for (i = 0; i < j;i++)
	{
		printf("%d\t", i);
		printf("%d\t", (int) elements[i].inst);
		if (elements[i].m1 == immed)
			putchar('#');
		else if (elements[i].m1 == indirect)
			putchar('@');
		printf("%d\t\t", (int) elements[i].para1);
                if (elements[i].m2 == immed)
                        putchar('#');
                else if (elements[i].m2 == indirect)
                        putchar('@');
                printf("%d\n", (int) elements[i].para2);
	}

	fclose(f);
}
!E!O!F!
echo x - inst.c
cat >inst.c <<'!E!O!F!'
/* execution code for the machine */

/*	This is a replacement for the screwed up code in "execute.c"
	Each instruction is a function, and accepts just enough information
	to execute that instruction.
*/

#include "interp.h"
#include <malloc.h>
#include <stdio.h>

extern	cell	a[];		/* core */

correct(thing)
int	*thing;
{
	if (*thing >= 0)
		*thing %= SIZE;
	else if (*thing < 0)
	{
		*thing %= SIZE;
		*thing = SIZE + *thing;
	}
}

int	getdirect(ins, off)
/* get position of direct parameter */
int	ins;	/* current position */
int	off;	/* offset of instruction */
{
	int	temp;

	temp = ins + off;
	correct(&temp);
	return (temp);
}

int	getindirect(ins, off)
/* get position of indirect parameter */
/* Note :-
A.K. Dewdney's specifications say that the pointer points relative to its
current position, NOT the instruction's position. */

int	ins;
int	off;
{
	int	temp;	/* temporary variable */

	correct(&off);
	temp = getdirect(ins, off);		/* get direct variable */
	correct(&temp);				/* correct it */
	temp = getdirect(temp, a[temp].para2);	/* This time it's for real */
	correct(&temp);
	return (temp);
}

int	getpara(ins, m, p)
/* Note:- This routine is designed only to work with indirect and direct
   operations.  NOT with immediate mode operations. Returns the address
   of the parameter by calling getdirect or getindirect			*/
int	ins;	/* location of current instruction */
mode	m;	/* mode */
int	p;	/* parameter */
{
	if (m == direct)
		return(getdirect(ins, p));
	else if (m == indirect)
		return(getindirect(ins, p));

	/* else */
	printf("getpara passed wrong parameter --- getpara\n");
	exit(1);
}

Mov	(ins, pc, pid)
/* MOV instruction :
sets the lastmod flag to the process id					*/
int	ins;	/* position of instruction to execute */
int	*pc;	/* program counter */
int	pid;	/* process ID */
{
	int	realpos;
	cell	temp;		/* store item to be moved */

	if (a[ins].m1 == immed)
	{
		temp.inst = dat;
		temp.para1 = 0;
		temp.para2 = a[ins].para1;
		temp.m1 = temp.m2 = immed;
	}
	else
	{
		realpos = getpara(ins, a[ins].m1, a[ins].para1);
		temp = a[realpos];
	}

	if (a[ins].m2 == immed)
	{
		printf("Tried to mov to immediate parameter\n");
		printf("--- mov\n");
		exit(1);
	}
	else
		realpos = getpara(ins, a[ins].m2, a[ins].para2);

	a[realpos] = temp;
	a[realpos].lastmod = pid;
	(*pc)++;
	correct(pc);
}

Add(ins, pc, pid)
/* ADD instruction */
int	ins;
int	*pc;
int	pid;
{
	int	x;		/* parameter A */
	int	realpos;	/* real position of B */

	if (a[ins].m1 == immed)
		x = a[ins].para1;
	else
		x = a[getpara(ins, a[ins].m1, a[ins].para1)].para2;

	if (a[ins].m2 == immed)
	{
		printf("Trying to add to immediate address\n");
		printf("--- ADD\n");
		exit(1);
	}
	else
		realpos = getpara(ins, a[ins].m2, a[ins].para2);

	a[realpos].para2 += x;
	a[realpos].lastmod = pid;
	correct(&(a[realpos].para2));
	(*pc)++;
	correct(pc);
}


Sub(ins, pc, pid)
/* SUB instruction */
int	ins;
int	*pc;
{
	int	x;		/* parameter A */
	int	realpos;	/* real position of B */

	if (a[ins].m1 == immed)
		x = a[ins].para1;
	else
		x = a[getpara(ins, a[ins].m1, a[ins].para1)].para2;

	if (a[ins].m2 == immed)
	{
		printf("Trying to subtract from immediate address\n");
		printf("--- SUB\n");
		exit(1);
	}
	else
		realpos = getpara(ins, a[ins].m2, a[ins].para2);

	a[realpos].para2 -= x;
	a[realpos].lastmod = pid;
	correct(&(a[realpos].para2));
	(*pc)++;
	correct(pc);
}

Jmp(ins,pc)
int	ins;
int	*pc;
{
	if (a[ins].m2 == immed)
	{
		printf("attempt to jump to immediate address\n");
		printf("-- JMP\n");
		exit(1);
	}
	else
		*pc = getpara(ins, a[ins].m2, a[ins].para2);

	correct(pc);
}

Jmz(ins, pc)
int	ins;
int	*pc;
{
	int	value;	/* value of first parameter */

	if (a[ins].m2 == immed)
		value = a[ins].para2;
	else
	{
		value = getpara(ins, a[ins].m2, a[ins].para2);
		correct(&value);
		value = a[value].para2;
	}

	correct(&value);

	if (value)
		(*pc)++;
	else
		*pc = getpara(ins, a[ins].m1, a[ins].para1);

	correct(pc);
}

Jmn(ins,pc)
int	ins;
int	*pc;
{
	int	value;

	if (a[ins].m2 == immed)
		value = a[ins].para2;
	else
	{
		value = getpara(ins, a[ins].m2, a[ins].para2);
		correct(&value);
		value = a[value].para2;
	}

	if (!value)
		*pc = getpara(ins, a[ins].m1, a[ins].para1);
	else
		(*pc)++;
	correct(pc);
}

Djn(ins, pc, pid)
int	ins;
int	*pc;
{
	int	temp;	/* position to decrement */

	if (a[ins].m2 == immed)
	{
		printf("tried to decrement immediate address\n");
		printf("--- DJZ\n");
		exit(1);
	}
	else
		temp = getpara(ins, a[ins].m2, a[ins].para2);

	(a[temp].para2)--;
	correct(&(a[temp].para2));
	a[temp].lastmod = pid;

	if (!a[temp].para2)
		(*pc)++;
	else
		*pc = getpara(ins, a[ins].m1, a[ins].para1);

	correct(pc);
}

Cmp(ins, pc)
int	ins;
int	*pc;
{
	int	value1, value2;

	if (a[ins].m1 == immed)
		value1 = a[ins].para1;
	else
		value1 = a[getpara(ins, a[ins].m1, a[ins].para1)].para2;

	if (a[ins].m2 == immed)
		value2 = a[ins].para2;
	else
		value2 = a[getpara(ins, a[ins].m2, a[ins].para2)].para2;

	correct(&value1);
	correct(&value2);
	if (value1 == value2)
		(*pc) += 2;	
	else
		(*pc)++;

	correct(pc);
}

Spl(ins, pc, mem)
int	ins;
int	*pc;
stream	*mem;		/* pointer to structure of current pc */
{
	int	newpc;
	stream	*newmem;

	if (a[ins].m2 == immed)
	{
		printf("Tried to split into immediate address\n");
		printf("--- SPL\n");
		exit(1);
	}
	else
		newpc = getpara(ins, a[ins].m2, a[ins].para2);

	if (!(newmem = (stream *) malloc(sizeof(stream))))
	{
		printf("no more memory!!\n");
		printf("--- SPL\n");
		exit(1);
	}

	correct(&newpc);
	newmem -> pc = newpc;
	newmem -> next = mem -> next;
	mem -> next = newmem;
	newmem -> prev = mem;
	newmem -> next -> prev = newmem;

	(*pc)++;
	correct(pc);
}

extern	stream	*exe[];

Dat(i)
/* kill stream */
int	i;
{
	stream	*curr = exe[i];		/* current */

	if (curr -> next == curr)
	{
		exe[i] = NULL;
		free(curr);
		return;
	}

	exe[i] = curr -> next;
	curr -> next -> prev = curr -> prev;
	curr -> prev -> next = curr -> next;

	free(curr);
}
!E!O!F!
echo x - interp.c
cat >interp.c <<'!E!O!F!'
!E!O!F!
echo x - load.c
cat >load.c <<'!E!O!F!'
/* program loader for MARS
   12/16/88    --- NCP   */

#include "interp.h"
#include <stdio.h>
#include <memory.h>
#include <malloc.h>

#define IO(i,j) if((i) < (j)){printf("error reading file\n");exit(1);}
extern	cell	a[];
extern	stream	*exe[];

/* generates starting position */
/* debugging non random version */
int	startposition()
{
/*  debugging code:
	static int	i = 0;

	if (i == 0)
	{
		i = 1;
		return(0);
	}
	else
		return((int) SIZE / 2)
*/
	return(rand()%SIZE);
}


load(f, no)
FILE	*f;		/* file */
int	no;		/* player number */
{
	memory	ele[MAXINST];
	int	i;		/* number of instructions */
	int	test;		/* test instructions read */
	int	start;		/* starting instruction */
	int	position;	/* loading position */
	int	counter;	/* general-purpose counter */

	test = fread(&i, sizeof(int), 1, f);
	IO(test, 1)
	printf("Found %d/%d instructions\n", i, MAXINST);
	if (i > MAXINST) {
		printf("load: Oops!  Too large!!\n");
		return(1);	/* oops - too large */
	}

	test = fread(&start, sizeof(int), 1, f);
	IO(test, 1)

	test = fread(ele, sizeof(memory), i, f);
	IO(test, i)

	/* get a new starting position */
	do {
		position = startposition();
		printf("Trying position %d\n", position);
	} while (testpos(position,i));

	/* and load the code there */
	printf("Loading %d instructions at location %d\n", i, position);
	for (counter = 0; counter < i; counter++) {
		memcpy(&a[(counter+position)%SIZE],&ele[counter],sizeof(memory));
#ifdef DEBUG
		if (!(((counter+position)%SIZE)%10)) {
			printf("%d%",(counter+position)%SIZE);
		} else {
			putchar('.');
		}
#endif
		a[(counter+position)%SIZE].lastmod = no;
		printf("Contents of location %d: %d %d %d %d %d %d\n",
			(counter+position)%SIZE,
			a[(counter+position)%SIZE].inst,
			a[(counter+position)%SIZE].para1,
			a[(counter+position)%SIZE].para2,
			a[(counter+position)%SIZE].m1,
			a[(counter+position)%SIZE].m2,
			a[(counter+position)%SIZE].lastmod);
	}

	if ((exe[no] = (stream *) malloc(sizeof(stream))) == NULL) {
		printf("load: Can't malloc the PC - aborting\n");
		exit(1);
	} else {
		exe[no]->pc = (position + start) % SIZE;
		exe[no]->next = exe[no]->prev = exe[no];
#ifdef DEBUG
		printf("Starting PC: %d %d %d\n", exe[no]->pc, exe[no]->next->pc, exe[no]->prev->pc);
#endif
	}

	return(0);	/* all OK */
}

/* tests for empty segment of *no* bytes starting at *start* */
testpos(start,no)
int	start,no;
{
	int	counter;	/* general-purpose counter */

	for (counter = 0; counter < no; counter++) {
#ifdef DEBUG
		if (!(((counter+start)%SIZE)%10)) {
			printf("%d%",(counter+start)%SIZE);
		} else {
			putchar('.');
		}
#endif
		if (a[(counter+start)%SIZE].lastmod) {
			printf("testpos: Oops -- something at %d\n",(counter+start)%SIZE);
			return(1);  /* oops - clash */
		}
	}
	return(0);	/* all clear - segment is free */
}
!E!O!F!
echo x - lookup.c
cat >lookup.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* lookup   ---- this is the real core of the assembler.  It will
		 perform functions such as matching symbols to instructions,
		 matching symbolic references to absolute code (and performing
		 whatever calculations necessary).                         */

#include "assem.h"
#include <malloc.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>

/* declare external functions */
int		symbol();
int		instruction();
instr		getin();
int		para();
int		getpara();
mode		getmode();

/* algorithm :-
	1)	start from beginning of the list.
	2)	process element.
	3)	go on to next element.
	4)	return number of instructions
*/

int lookup(head, table, elements)
tokenlist	*head;
tag1		table[];
memory		elements[];
{
	int	i = 0,			/* instruction pointer */
		j = 0,			/* number of parameters */
		k = 0;			/* current parameters */

	initialize(elements);		/* initialize table */
	while (head && symbol(head))	/* look for first instruction */
		head = head -> next;
	while (head)
	{
		/* expect instruction */
		if (!instruction(head))
		{
			printf("%s is not instruction\n", head -> token);
			printf(" --- lookup\n");
			exit(1);
		}

		elements[i].inst = getin(head);

		/* expect number of parameters */
		j = para(head);
		k = 0;
		while (k < j)
		{
			head = head -> next;
			if (k == 0 && j == 2)	/* if single para, put in B */
			{
				elements[i].para1 = getpara(head,table,i);
				elements[i].m1 = getmode(head);
			}
			else
			{
				elements[i].para2 = getpara(head,table,i);
				elements[i].m2 = getmode(head);
			}
			k++;
		}

		if (head)
			head = head -> next;	/* next instruction, if any */

		while (head && symbol(head))	/* skim symbols */
			head = head -> next;

		i++;
	}	/* while */
	return i;
}

/* set all instructions to init */
initialize(elements)
memory	elements[];
{
	memory	ele;		/* set all to this initialized variable */
	int	i;

	ele.inst = dat;
	ele.para1 = ele.para2 = 0;
	ele.m1 = ele.m2 = immed;

	for (i = 0; i < MAXINST; i++)
		elements[i] = ele;
}

/* get the instr part of an instruction */
instr	getin(ptr)
tokenlist	*ptr;
{
	char	*t;		/* string */
	instr	x;		/* return value */

	t = ptr -> token;

	if (!strcmp(t, DAT))
		x = dat;
	else if (!strcmp(t, MOV))
		x = mov;
	else if (!strcmp(t, ADD))
		x = add;
	else if (!strcmp(t, SUB))
		x = sub;
	else if (!strcmp(t, JMP))
		x = jmp;
	else if (!strcmp(t, JMZ))
		x = jmz;
	else if (!strcmp(t, JMN))
		x = jmn;
	else if (!strcmp(t, DJN))
		x = djn;
	else if (!strcmp(t, CMP))
		x = cmp;
	else if (!strcmp(t, SPL))
		x = spl;
	else
	{
		printf("%s is not an instruction\n", t);
		printf("--- getin\n");	
		exit(1);
	}

	return x;
}	/* getin */

/* get the actual parameter (not the symbolic one)
Algorithm:
	1)	check if symbol
	2)	if symbol then look up, compare with current instruction,
		and calculate what the actual parameter should be.
	3)	if not symbol, then check that it is a numeric. if not, error.
	4)	otherwise, return atoi
*/
int	getpara(ptr, table, curr)
tokenlist	*ptr;		/* parameter instruction */
tag1		table[];	/* symbol table */
int		curr;		/* current instruction */
{
	char	*t;		/* token string */

	t = ptr -> token;

	if (*t == '@' || *t == '#')	/* ignore these */
		t++;

	if (number(t))
		return(atoi(t));
	else				/* must be symbol */
		return(getsym(t, table) - curr);
}

/* return the absolute location (from the beginning of the program) of
   a symbol                                                         */
int	getsym(str, table)
char	*str;
tag1	table[];
{
	int	i = 0;
	char	s[MAXBUFFER];

	/* add colon for strcmp */
	strcpy(s,str);
	strcat(s,":");

	for (; (i < SYMBOLS) && (table[i].symbol != NULL); i++)
		if (!strcmp(table[i].symbol, s))
			return(table[i].position);

	/* out here, not symbol */
	printf("symbol %s undefined\n", str);
	printf("--- getsym\n");
	exit(1);
}

/* checks that every element in string is a digit */
int	number(str)
char	*str;
{
	int	i = 1;

	if (*str == '+' || *str == '-')		/* positive or negative */
		str++;

	while (*str && i)
	{
		if (!isdigit(*str))
			i = 0;
		str++;
	}

	return i;
}

mode	getmode(ptr)
tokenlist	*ptr;
{
	char	*t;		/* token string */

	t = ptr -> token;

	if (*t == '@')
		return(indirect);
	else if (*t == '#')
		return(immed);
	else
		return(direct);
}

/* special debugging portion */

/* declared debugged (phew! this one was tough) on 11/23/88 by CP */
/*
tokenlist	*tokenize();
tag1 		table[SYMBOLS];
memory		elements[MAXINST];
extern tokenlist *tokenize();

main(argc, argv)
int	argc;
char	*argv[];
{
	tokenlist 	*head;
	FILE		*f;
	int		i;

	printf("loaded\n");
	f = fopen(argv[1],"r");
	printf("%s file opened\n", argv[1]);
	head = tokenize(f);
	printf("tokenized\n");
	parse(head, table);
	printf("parsed\n");
	i = lookup(head, table,elements);
	printf("%d instructions\n", i);

	printf("OPCODE\tFIRSTPARA\tSECONDPARA\n");
	for (i = 0; i < 20; i++)
	{
		printf("%d\t",(int) elements[i].inst);
		if (elements[i].m1 == indirect)
			printf("@");
		else if (elements[i].m1 == immed)
			printf("#");
		printf("%d\t\t", elements[i].para1);
                if (elements[i].m2 == indirect)
                        printf("@");
                else if (elements[i].m2 == immed)
                        printf("#");
		printf("%d\n", elements[i].para2);
	}
	fclose(f);
}

printsymbols(head)
tokenlist	*head;
{
	while (head != NULL)
	{
		printf("%s\n", head -> token);
		head = head -> next;
	}
}

printable(t)
tag1	t[];
{
	int	i;
	printf("%s\t%s\n","SYMBOL","POSITION");
	for (i = 0; table[i].symbol != NULL; i++)
		printf("%s\t%d\n", table[i].symbol,table[i].position);
}

*/
!E!O!F!
echo x - main.c
cat >main.c <<'!E!O!F!'
/* main program for MARS interpreter
   As usual, this does not do anything important.
   Just calls all the proper subroutines in the right order and
   opens files.
   -- 12/16/88         --- NCP        */

#include "interp.h"
#include <ctype.h>
#include <stdio.h>
#include <memory.h>
#include <curses.h>

long	atol();

/* memory array -- called "a" for easy typing */
cell	a[SIZE];

/* next execution for each player */
stream	*exe[MAXPLAY];

/* returns which player won */
int	play();

usage()
{
	printf("usage: interp cycles file1 file2\n");
	printf("--- main\n");
}

/* checks that every element in string is a digit */
int	number(str)
char	*str;
{
	int	i = 1;

	while (*str && i)
	{
		if (!isdigit(*str))
			i = 0;
		str++;
	}

	return i;
}

initialize()
{
	int	counter;	/* general-purpose counter */

	counter = (int) time(0);
#ifndef DEBUG
	initscr();		/* for "curses" library */
	scrollok(stdscr, 0);
	nl();
	clear();
	refresh();
#endif

	srand(counter);
	for (counter = 0; counter++; counter < SIZE)
		memset(a[counter],0,sizeof(cell));
}

main(argc, argv)
int	argc;
char	*argv[];
{
	FILE	*f;
	int	errcode, result;

	initialize();		/* initialize all global variables */

	if (argc != 4)		/* too many or too few */
	{
		usage();
		exit(1);
	}

	if (!(number(argv[1])))
	{
		usage();
		exit(1);
	}

	if ((f = fopen(argv[2], "r")) == NULL)
	{
		printf("%s cannot be opened\n", argv[2]);
		exit(1);
	}

	errcode = load(f, 1);
	fclose(f);
	if (errcode == 1) {
		printf("main: Sorry, but %s is too large to load\n",argv[2]);
		exit(1);
	}


	if ((f = fopen(argv[3], "r")) == NULL)
	{
		printf("%s cannot be opened\n", argv[3]);
		exit(1);
	}

	errcode = load(f,2);
	fclose(f);
	if (errcode == 1) {
		printf("main: Sorry, but %s is too large to load\n",argv[3]);
		exit(1);
	}

#ifndef DEBUG
	clear();
#endif

	result = play(atol(argv[1]));

#ifndef DEBUG
	output(0);
	move(21, 0);
	if (!result)
		printw("nobody won!");
	else
		printw("%s won!", argv[result + 1]);
	move(22, 0);
	printw("Hit any key to continue...");
	refresh();
	getch(errcode);
	endwin();
#endif
}
!E!O!F!
echo x - output.c
cat >output.c <<'!E!O!F!'
/** Output function **/

#include <stdio.h>
#include <curses.h>

#include "interp.h"

extern	cell	a[];

#if DEBUG
output()
{
	int	i;

	for (i = 0; i < SIZE; i ++)
	{
		if (a[i].lastmod == 0)
			putchar('0');
		else if (a[i].lastmod == 1)
			putchar('1');
		else if (a[i].lastmod == 2)
			putchar('2');
		else
		{
			printf("\nerror lastmod == %d", a[i].lastmod);
			printf("i == %d", i);
			exit(1);
		}
	}

	putchar('\n');
}
#endif

#ifdef SMALL
/* assume large, 80 col screen */

output(cycles)
long	cycles;
{
	int	map[SIZE / 5];	/* map to one fifth the size */
	int	i, j, k = 0;

	move(0,0);		/* start from the top */

	domap(map);

	for (i = 0; i < SIZE/5;)
	{
		move(k, 0);
		for (j = 1; j < COLS; i++, j++)
		{
			if (SIZE / 5<= i)
				break;

			if (map[i] == 0)
				addch('0');
			else if (map[i] == 1)
				addch('1');
			else if (map[i] == 2)
				addch('2');
		}

		k++;
	}
	move(23, 0);
	printw("Cycles left: %10d", cycles);
	refresh();
}

domap(arr)
int	arr[];
{
	int	i, j, pid1, pid2, pid3;

	for (j = 0; j < SIZE/5; j++)
	{
		pid1 = pid2 = pid3 = 0;
		for (i = j * 5; i < (j+1) * 5; i++)
		{
			if (SIZE <= i)
				break;
			if (a[i].lastmod == 0)
				pid1++;
			else if (a[i].lastmod == 1)
				pid2++;
			else if (a[i].lastmod == 2)
				pid3++;
			else
			{
				printf("invalid modification detected\n");
				printf("--- domap\n");
				exit(1);
			}
		}
		arr[j] = max(pid1, pid2, pid3);
	}
}

max(i, j, k)
int	i, j, k;
{
	if (i > j && i > k)
		return 0;
	else if (j > i && j > k)
		return 1;
	else
		return 2;
}
#endif

#ifdef BIG
output(cycles)
int	cycles;
{
	int	i, j, k;

	/* perform detailed mapping */
	for (i = 0, k = 0; i < SIZE; k++)
	{
		move (k, 0);
		for (j = 0; j < COLS; i++, j++)
		{
			if (i >= (SIZE - 1))
				break;

			/* else */
			if (a[i].lastmod == 0)
				addch('0');
			if (a[i].lastmod == 1)
				addch('1');
			else
				addch('2');
		}
	}
	move (k + 2, 0);
	printw("Cycles left: %10d\n", cycles);
	refresh();
}
#endif
!E!O!F!
echo x - parse.c
cat >parse.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* parser portion of the assembler.
   11/15/88         - NCP        */

#include <strings.h>
#include <stdio.h>
#include "assem.h"

/* parser:
  Algorithm:
	1)	initialize symbol table
	2)	Scan first element in token list
	3)	if symbol declaraion (e.g. "start:") then lookup and enter into
		symbol table
	4)	if instruction, skip forward number of parameters
		that instruction accepts
	5)	otherwise error (not instruction or symbol)
	6)	repeat 3-6 until no more tokens.
*/
parse(tokens,table)
tokenlist	*tokens;	/* list of tokens */
tag1		table[];	/* symbol table */
{
	int	i = 0;		/* instructon counter */

	init(table);		/* initialize table */

	while (tokens)
	{
		if (symbol(tokens))
		{
			insert(tokens,table,i);
			tokens = tokens -> next;
		}
		else if (instruction(tokens))
		{
			int	j = 0, k = para(tokens);

			/* move up to next instruction:
			   i.e., move from instruction + number of parameters
			   if instruction is one parameter, move twice, etc */
			while (j <= k)
			{
				tokens = tokens -> next;
				j++;
			}

			i++;		/* next instruction */
		}
		else			/* not instruction or symbol */
		{
			printf("%s not symbol or instruction\n",
				tokens -> token);
			printf(" --- parse\n");
			exit(1);
		}
	}	/* while */

	/* test for too many instructions */
	if (i > MAXINST)
	{
		printf("too many instructions\n");
		printf("---- parse\n");
		exit(1);
	}
} /* parse */

init(table)		/* function to initialize symbol table (set to 0 */
tag1	table[];
{
	int	i;

	for (i = 0; i < SYMBOLS; i++)
	{
		table[i].symbol = NULL;
		table[i].position = 0;
	}
}

int symbol(tok)
/* identifies a symbol:
	A symbol is just a token that ends with a ':'	*/
tokenlist	*tok;
{
	char	*t;		/* token string */

	t = tok -> token;
	if ((t[strlen(t) - 1]) == ':')	/* address last character */
		return 1;		/* is symbol declaration */
	else
		return 0;
}

/* inserts a token into a symbol table (without stripping the ':') -
   Algorithm:
	1)	search until symbol table reads a NULL string or
		the new symbol is equal to and old one.
	2)	if the new symbol is not a NULL, then error
		otherwise, insert
	3)	if out of space, error
*/
int insert(tok, table, no)
tokenlist	*tok;			/* token */
tag1		table[];		/* symbol table */
int		no;			/* instruction number */
{
	char	*t;		/* token string */
	int	i = 0;		/* index on table */

	t = tok -> token;

	/* search for empty place in table */
	for (; i < SYMBOLS && table[i].symbol != NULL &&
	       (strcmp(table[i].symbol,t) != 0); i++)
		;

	if (table[i].symbol != NULL)
	{
		printf("symbol %s already declared\n", t);
		printf("--- insert\n");
		exit(1);
	}

	table[i].symbol = t;
	table[i].position = no;
}

/* tests whether instruction */
int instruction(tok)
tokenlist	*tok;
{
	char	*t;		/* token string */

	t = tok -> token;
	/* note that due to a quirk in strcmp (it returns a zero if the
	   strings are equal) this looks particularly convoluted, but
	   the logic is simple.                                        */
	return(! (strcmp (t,MOV) && strcmp (t,ADD) && strcmp (t,SUB) &&
		  strcmp (t,JMP) && strcmp (t,JMZ) && strcmp (t,JMN) &&
		  strcmp (t,DJN) && strcmp (t,CMP) && strcmp (t,SPL) &&
		  strcmp (t,DAT)));
}

/* return the number of parameters an instruction has */
int para(tok)
tokenlist	*tok;
{
	char	*t;		/* token string */
	int	i = 0;		/* return value */

	t = tok -> token;

	/* use a multiple if-elseif statement */
	if (!strcmp(t, MOV))
		i = 2;
	else if (!strcmp(t, ADD))
		i = 2;
	else if (!strcmp(t, SUB))
		i = 2;
	else if (!strcmp(t, JMP))
		i = 1;
	else if (!strcmp(t, JMZ))
		i = 2;
	else if (!strcmp(t, JMN))
		i = 2;
	else if (!strcmp(t, DJN))
		i = 2;
	else if (!strcmp(t, CMP))
		i = 2;
	else if (!strcmp(t, SPL))
		i = 1;
	else if (!strcmp(t, DAT))
		i = 1;
	else	/* unrecognized instruction */
	{
		printf("%s is not an instruction\n", t);
		printf("--- para\n");
		exit(1);
	}

	return(i);
}	/* para */




/* debugging section of parse.c */
/* code commented out --- given a clean bill of health by Dr. Na Choon Piaw
   --- 11/15/88                                                           */
/* declare tokenize */
/* extern tokenlist *tokenize(); */
/* declare symbol table */
/* tag1	table[SYMBOLS]; */

/* main(argc,argv)
int	argc;
char	*argv[];
{
	tokenlist	*head;
	FILE		*f;
	int		i;

	f = fopen(argv[1], "r");
	head = tokenize(f);
	parse(head, table);

	printf("%s\t%s\n", "SYMBOL", "POSITION");
	for (i = 0; table[i].symbol != NULL; i++)
		printf("%s\t%d\n", table[i].symbol, table[i].position);
}  */
!E!O!F!
echo x - play.c
cat >play.c <<'!E!O!F!'
/* play.c ---- main program for the interpreter.
   1.	play player 1
   2.	play player 2
   3.   output
   4.   check deaths
   5.   repeat

   --- 12/17/88   NCP                             */
#include <stdio.h>
#include "interp.h"

extern	stream *exe[];
extern  cell	a[];

int	dead(i)
int	i;
{
	if (exe[i])
		return 0;
	else
		return 1;
}


int	alive()
{
	int	i, j = 1;

	for (i = 1; i < MAXPLAY; i ++)
	{
		if (!exe[i])
		{
			j = 0;
			break;
		}
	}

	return(j);
}

int play(cycles)
long cycles;
{
	int	i;

	while (alive() && cycles)
	{
		for ( i = 1; i < MAXPLAY; i++)
		{
			execute(i);	/* play player n */
#ifdef DEBUG
			output();
#endif
#ifdef BIG
			output(cycles);
#endif
		}
#ifdef SMALL
		if (!(cycles % 100))
			output(cycles);
#endif
		cycles --;
	}

	if (dead(1) && !dead(2))
		return(2);		/* process 2 won */
	else if (dead(2) && !dead(1))
		return(1);		/* process 1 won */
	else
		return(0);		/* nobody won */
}

execute(i)
int	(i);
{
	instr	temp;			/* instruction */
	int	x;			/* cell to execute */
	int	pc;

	if (exe[i] == NULL)
		return;

	correct(&(exe[i] -> pc));
	x = pc = exe[i] -> pc;
	temp = a[x].inst;

	/* this really should have been a switch-case, but I'm using an
	   elongated if-else because the compiler doesn't accept it.   */

	if (temp == dat)
		Dat(i);
	else if (temp == mov)
		Mov(x, &pc, i);
	else if (temp == add)
		Add(x, &pc, i);
	else if (temp == sub)
		Sub(x, &pc, i);
	else if (temp == jmp)
		Jmp(x, &pc);
	else if (temp == jmz)
		Jmz(x, &pc);
	else if (temp == jmn)
		Jmn(x, &pc);
	else if (temp == djn)
		Djn(x, &pc, i);
	else if (temp == cmp)
		Cmp(x, &pc);
	else if (temp == spl)
		Spl(x, &pc, exe[i]);
	else
	{
		printf("Instruction not recognized\n");
		printf("--- execute\n");
		printf("opcode: %d", temp);
		printf(" executing no: %d", pc);
		printf(" process: %d\n", i);
		exit(1);
	}

	if (temp != dat)
		exe[i] -> pc = pc;

	if (exe[i] && (temp != dat))
		exe[i] = exe[i] -> next;
}
!E!O!F!
echo x - test.c
cat >test.c <<'!E!O!F!'
main()
{
	int	i, j;

	for (i = 0; i <8000;)
	{
		for (j = 0; j < 10; j++, i++)
			printf("%d", j);
	}
}
!E!O!F!
echo x - tokenize.c
cat >tokenize.c <<'!E!O!F!'
/*	Copyrighted (C) 1989 by Na Choon Piaw.  All rights reserved       */



/*	This program and documentation is Public Domain, and may be	  */
/*	distributed and copied by everyone provided this header		  */
/*	remains intact							  */

/* tokenize ---- tokenizer for the assembler.  It splits up all of the
   tokens in the given input file, generate a linked list of such tokens,
   and outputs the pointer to that linked list.
  11/14                 ----- NCP                                    */
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <malloc.h>
#include "assem.h"

/* tokenize function:
   separate input stream (infile) into tokens.
   algorithm:
	1)	call nextoken to get next token.
	2)	if "null" then return the first pointer
	3)	otherwise, add the newtoken to the token list
	4)	go back to 1.
*/
tokenlist *tokenize(infile)
FILE	*infile;
{
	tokenlist	*head, *tail, *newtail;
	char		*nextoken(), *newtoken;

	head = tail = NULL;
	while ((newtoken = nextoken(infile)) != NULL)
	{
		if (!(newtail = (tokenlist *) 
		                 malloc((unsigned) sizeof(tokenlist))))
		{
			printf("ran out of space for tokens: %s\n", newtoken);
			printf("------  tokenize\n");
			exit(1);
		}

		/* otherwise , set old stuff to this and move tail one up*/
		newtail -> token = newtoken;
		newtail -> next = NULL;
		if (tail)			/* tail already defined */
		{
			tail -> next = newtail;		/* set previous ptr */
			tail = tail -> next;		/* move up list */
		}
		else
			head = tail = newtail;
	}	/* end while */

	return (head);	/* return function value */
}

/* function next token:
   return next token in the file.
   Algorithm:
	1)	read until start of next token or EOF
	2)	if EOF then return NULL pointer
	3)	read new token into buffer
	4)	malloc new string
	5)	put next token into new string
	6)	change all characters into upper case
	7)	return pointer to string created in (4)      */
/* BUG NOTE:
   Due to the way it is written, the assembler will not process things like
   "mov0 1" correctly, and neither will "mov 0 1;imp" work.
   this is because the tokenize breaks everything down that isn't a
   delimiter, and a ";" is not a delimiter, even though it's not in
   the instruction set.                                   NCP - 11/15/88 */
char	*nextoken(infile)
FILE	*infile;
{
	char	buffer[MAXBUFFER],	/* string buffer */
		*newtoken;		/* new token pointer */

	int	c,			/* character we read in one by one */
		i = 0;			/* integer counter */

	while ((c = fgetc(infile)) != EOF)
	{
		if (!isspace(c))
			break;		/* not space, so process */
	}

	if (c == EOF)
		return(NULL);		/* no more!! */

	if (c == COMMENT)		/* handle comments */
	{
		while (((c = fgetc(infile)) != '\n') && c != EOF)
			;		/* read until end of the line */

		if (c == EOF)
			return(NULL);

		/* process the rest of the file:
		   actually, we could have done this by using a goto
		   the beginning of this function, but I think this
		   is a lot more elegant                     --- CP */
		return(nextoken(infile));
	}

	while ((!isspace(c)) && (c != EOF))	/* read until next space */
	{
		if (i >= MAXBUFFER)	/* buffer out */
		{
			printf("buffer over extended\n");	
			printf("--- nextoken\n");
			exit(1);
		}

		buffer[i++] = c;

		c = fgetc(infile);
	}	/* end while */
	buffer[i] = '\0';		/* terminate with a null */

	if (!(newtoken = (char *) malloc( (unsigned) strlen(buffer) + 1)))
	{
		printf("not enough memory for token %s\n", buffer);
		printf(" ------- nextoken\n");
		exit(1);
	}

	strcpy(newtoken, buffer);

	upcase(newtoken);

	return(newtoken);
}	/* end of nextoken */

/* upcase function -- translate string to upper case (don't trust toupper) */
upcase(str)
char	*str;
{
	while (*str)
	{
		if ((*str <= 'z') && (*str >= 'a'))
			*str -= 'a' - 'A';
		str++;
	}
}

/* special main to test the above */
/* section commented out because code has been fully debugged
   and given a clean bill of health			CP - 11/14/88 
main(argc,argv)
int	argc;
char	*argv[];
{
	FILE	*f;
	tokenlist	*head;

	f = fopen(argv[1],"r");
	head = tokenize(f);
	while (head != NULL)
	{
		printf("%s\n", head -> token);
		head = head -> next;
	}
} */

!E!O!F!
echo x - bomb.rc
cat >bomb.rc <<'!E!O!F!'
start:
mov	#0	@ptr
add	#1	ptr
jmp	start
ptr:	dat	#1
!E!O!F!
echo x - chang1.rc
cat >chang1.rc <<'!E!O!F!'
; chang 1 corewars program
	mov	#0	-1
	jmp	-1 ;;nothing
	dat		+9	;complete tester
start:	spl	-2
	spl	4
	add	#-16	-3	; nothing
	mov	#5	@-4
help:	jmp	-4
	spl	2
	jmp	-1
finish: next: odd:	mov	0	1
finito:
!E!O!F!
echo x - commando.rc
cat >commando.rc <<'!E!O!F!'
;;; an implementation of a k dewdney's commando program

count:	dat	#14
ptr:	dat	#200
imps:	mov	#0	-1	; imp stomper
	jmp	imps
	mov	#14	count
	mov	#100	ptr
start:	mov	imp	1000
	spl	999
loop:	mov	@count	@ptr
	sub	#1	ptr
	djn	loop	count
	add	#4	ptr
	spl	@ptr
	jmp	imps
imp:	mov	0	1
!E!O!F!
echo x - dwarf.rc
cat >dwarf.rc <<'!E!O!F!'
 dat	-1
start: add	#5	-1
mov	#0	@-2
JMP	-2
!E!O!F!
echo x - dwarfgun.rc
cat >dwarfgun.rc <<'!E!O!F!'
;;; dwarfgun program.
;;; Choon Piaw
stomp:	mov	#0	-1
	jmp	stomp
start:	spl	init
	jmp	stomp
count:  dat	#4
ptr:	dat	#500

;;;; dwarf part

dwf:	dat	#5000
loop:	mov	#0	@dwf
	djn	loop	dwf
;;; initialization
init:	mov	#-200	ptr2
	mov	#0	count2
	mov	#500	ptr
	mov	#4	count
;;; dwarf copy part
dwfcp:	mov	@count	@ptr
	sub	#1	ptr
	djn	dwfcp	count
	add	#3	ptr
	spl	@ptr		; leave dwarf running.
;;;; copy self upstream
selfcp:	mov	@count2	@ptr2
	sub	#1	count2
	sub	#1	ptr2
	cmp	count2	#-27
	jmp	selfcp
	add	#3	ptr2
	jmp	@ptr2
ptr2:	dat	#-200
count2:	dat	#0
!E!O!F!
echo x - gemini.rc
cat >gemini.rc <<'!E!O!F!'
dat	#0
dat	#99
start:	mov	@-2	@-1
cmp	-3	#9
jmp	4
add	#1	-5
add	#1	-5
jmp	-5
mov	#99	93
jmp	93
!E!O!F!
echo x - imp.rc
cat >imp.rc <<'!E!O!F!'
start: mov	0	1
!E!O!F!
echo x - impstomp.rc
cat >impstomp.rc <<'!E!O!F!'
start:	mov	#0	-1
	jmp start
!E!O!F!
echo x - mice.rc
cat >mice.rc <<'!E!O!F!'
; mouse program --- The First Core War tournament

ptr:	dat	#0		;hell
start:	mov	#9	ptr	;you
loop:	mov	@ptr	@7	;know
	sub	#1	6
	djn	loop	ptr	;i hate testing
	add	#1	4
	spl	@3		;assemblers
	add	#653	2	;like
	jmz	-7	-8	;this
	dat	833
!E!O!F!
echo x - selfcpy.rc
cat >selfcpy.rc <<'!E!O!F!'
imps:	mov	#0	-1
	jmp	imps
start:	spl	imps
	jmp	loop
count:	dat	#7
ptr:	dat	#57
	mov	#7	count
	mov	#57	ptr
loop:	mov	@count	@ptr
	sub	#1	ptr
	djn	loop	count
	jmp	46
!E!O!F!
echo x - sit.rc
cat >sit.rc <<'!E!O!F!'
start: jmp start
!E!O!F!
echo x - testspl.rc
cat >testspl.rc <<'!E!O!F!'
start: spl after
	mov	#0	-3
	jmp -1
after: mov 0 1
!E!O!F!

An Existentialist
c60a-3ci@wolf.berkeley.edu
%flames >/dev/null
"Do you think that if I stopped thinking so much I'd have less trouble?"

blojo@soda.berkeley.edu (05/21/91)

An X11r4 version of Core War is available for ftp from soda.berkeley.edu
(128.32.131.179).  It currently requires a workstation running X11r4 with
something like 8-plane color to run; black-and-white support may be added
in future.

For those of you without color, a constantly-updated Redcode archive will
be kept at the same site.  Please mail me any code not already contained
in the collection.

The program source lives on soda in pub/corewar/corewar.tar.Z; the constantly
updated Redcode is (bundled together) in pub/corewar/redcode.tar.Z, and
(separate) in pub/corewar/redcode/.

Prior to putting together this package, I searched far and wide through
various ftp sites for prior implementations of Core War; I can safely say
that this is by far the most excellent version of Core War I've seen.

 - Jon (blojo@soda.berkeley.edu)