

/*	COREWARS 2.C	The game of Core Wars, as described by
**			A.K. Dewdney in the May 1984 issue of
**			Scientific American. 
**
**			Note: Output redirection will effect detailed
**			      listings ONLY! (And it'll probably be a
**			      BIG file.)
**
**	Date: 28 May 1984
**	Author: Kevin A. Bjorke
**
**	Copyright (C) 1984 Kevin A. Bjorke
**	Released to the Public Domain for Non-Commercial Use Only
**
**      7-June-1984 Bob Green
**          Converted to MS-DOS 2.xx   -  Computer Innovations  C86
**          and A TeleVideo 950 terminal from CP/M and Small-C 2.03  
**
**	28-February-1988 	Chad The_Walrus Netzer
**
**		Ported the above version to the Amiga computer.  Used
**	Manx Aztec C version 3.4a to develop and compile.  This is
**	a "no frills" version.  This version is freely redistributable,
**	but not for commercial use.  Feel free to add/modify/improve it.
**	In fact, I'm sure there are parts of the user interface you'll want
**	to improve.  If so, DO so!!!  And then release it for everyone else's
**	pleasure.  Please include all source code with ANY executable you
**	distribute!  Others will thank you, and so will I.
**		PS.  Sorry, for the messy code, in places, but that's what
**	comes from porting...  I didn't bother keeping track of my code,
**	and the old code.  So have fun figuring it out! (I did.)
** 
**		I intend to add to this program in my spare-spare time...
**	But don't wait for me to do all the work.  This program could
**	use a couple notable additions (better command line editing,
**	I/O redirection, comments in program listings, support for unattended
**	'tournament' play, and better listing of memory comes to mind).  Hope 
**	you enjoy fiddling with it.  You can contact me on Usenet at:
**
**		portal!cup.portal.com!Chad_The-Walrus_Netzer (I think).
**	Look for me in the comp.sys.amiga section.  Or call BBS-JC at:
**
**		415-961-7250	(Mountain View, Ca.)
**	and look for Chad Netzer on the board...
**
*/

#include "stdio.h"
#include "sgtty.h"

#include "corewar.h"	/* header file for Amiga IO routines */

#define MAXSIZE 1000		/* Dewdney has 8000 - change to your taste */
#define VERSION "1.01"
#define CLRSCR "\033*"		/* For Telcon, Heath, Etc. */
#define ADRCUR "\033="		/* Likewise */
#define BIAS 31			/* Ditto */
#define INDEXED 2		/* Modes (Indexed = Indirect) */
#define DIRECT 1
#define IMMEDIATE 0
#define CUTOFF 2000		/* Max. number of instructions executed */
#define BLANK ' '
#define CR 0x0d
#define LF 0x0a
#define ENQ 0xB
#define FULLIST 		/* List what's going where on MOV, ADD, etc */

#define ERROR	-1
#define BELL	0x7

void	querymem();

/*
**********************
** Global Variables **

**********************
*/

char proga[80];			/* names of the battle programs */
char progb[80];

int pc[2];			/* Redcode program counters */
char instr;			/* Current instruction */
int modea, modeb;		/* Current addressing modes -- see below */
int now;			/* programs 0 and 1 (pc index) */
char line[12];			/* large enough to accept anything required */
char show;			/* used to differentiate between showing and */
				/* executing code */

char code[MAXSIZE];		/* Redcode Instructions */
int  arga[MAXSIZE];		/* Battle Prog Arguments */
int  argb[MAXSIZE];		/* Total: 5 bytes per "location" */


/*
** Since the Redcode Instruction set has only 9 instruction and 3 addressing
** modes, the code is split into three fields:
**
**	Bit:	7 6 5 4 3 2 1 0
**		a a b b i i i i
**
** where a = mode of argument a
**       b = mode of argument b
**       i = instruction (0-8 -- 9-15 treated as 0, or invalid)
**
** modes: 0 - immediate
**        1 - direct
**        2 - indirect
*/

/*
*************************
** Main Program Driver **
*************************
*/

main()	
{
	while (TRUE) {
		mysetup();
		logo();
		clean();
		prepare();
		fight();
		querymem();
		myputs("\n\nPlay Again (Y/N) ? ");
		if (toupper(mygetc()) != 'Y')
			break;
	}
	puts("\n\n    Thththththat\'s all, folks....\n");
	myexit(0);
}

/*
** Advertise the program
*/
logo()
{
	myputs("^[c");
	myputs("\n\n    ****************************************\n");
	myputs("    **                                    **\n");
	myputs("    **         C O R E    W A R S         **\n");
	myputs("    **                                    **\n");
	myputs("    ****************************************\n\n\n");
	myputs("    MARS Version ");
	myputs(VERSION);
	myputs("   Kevin Bjorke 5/28/84\n\n");
}

/*
********************
** Input Routines **
********************
*/

/*
** Read in both programs
*/
prepare()
{
	int top,bot;
	char *p;
	FILE *fp1,*fp2;			/* possible file pointer */

	myputs("\n    Enter the name of Battle-Program A: ");
	mygets(proga,80);
	myputs("\n    Enter the name of Battle-Program B: ");
	mygets(progb,80);
	for (p = &progb[0]; *p; ++p)
		*p = toupper(*p);
	for (p = &proga[0]; *p; ++p)
		*p = toupper(*p);
	bot = 0;
	bot = getinitial(proga,bot);	/* find out where to initially put the program */
	if ((fp1 = fopen(proga,"r")) == NULL) {
		myputs("^[c");
		myputs("\n\nPlease enter code for ");
		myputs(proga);
		myputs(":\n\n");
		top = getprog(proga,bot);
	} else
		top = fgetprog(fp1,bot);
	myprintf("\nNow listing Program %s:\n\n",proga);
	showmem(bot,top);
	pc[0] = bot;
	getpc(0);
	bot = MAXSIZE/2;
	bot = getinitial(progb,bot);	/* find out where to initially put the program */
	if (ABS(bot - pc[0]) < MAXSIZE/8)  {
		bot = pc[0] + MAXSIZE/8;
		bot = reladr(bot,0);
		myputs("I have to re-adjust your starting position!\n");
		myprintf("Your new location is %d.",bot); 
	}
	if ((fp2 = fopen(progb,"r")) == NULL) {
		myputs("^[c");
		myputs("\n\nNow, please enter code for ");
		myputs(progb);
		myputs(":\n\n");
		top = getprog(progb,bot);
	} else
		top = fgetprog(fp2,bot);
	myprintf("\nNow listing Program %s:\n\n",progb);
	showmem(bot,top);
	pc[1] = bot;
	getpc(1);
}

/*
** get a program from the keyboard
*/
getprog(prg,ct) char *prg; int ct;
{
	int i,row; 
        char *pt;
	row = 10;
	myputs("Enter program one line at a time.\n");
	myputs("Pressing RETURN between arguments.\n");
	myputs("    Use END to finish\n\n");
	header(9);
	while (TRUE) {
		modea = modeb = DIRECT;
		cursor(row,1);
		stout(ct);
		cursor(row,10);
		mygets(line,12);
		for (i = 0; line[i]; ++i) {
			if (('\n' == line[i]) || ('\t' == line[i]) || (' ' == line[i]))
				line[i] = '\0';
			line[i] = toupper(line[i]);
		}
		if ((i = getinst(ct)) == NULL)
			break;
		if (i == ERROR) {
 			cursor(row,10);
			myputs("\007???\n");
			continue;
		}
		cursor(row,50);
		myputs(line);
		if (code[ct] != 0) {
			cursor(row,20);
			mygets(line,12);
			pt = &line[0];
			cursor(row,60);
			if (*pt == '@') {
				myputs("Indirect");
				modea = INDEXED;
				++pt;
			} else if (*pt == '#') {
				myputs("Immediate");
				modea = IMMEDIATE;
				++pt;
			}  else
				myputs("Direct");
			i = stoi(pt);
			arga[ct] = i;
		}
		if (code[ct] != 4) {
			cursor(row,30);
			mygets(line,12);
			pt = &line[0];
			cursor(row,70);
			if (*pt == '@') {
				myputs("Indirect");
				modeb = INDEXED;
				++pt;
			} else if (*pt == '#') {
				myputs("Immediate");
				modeb = IMMEDIATE;
				++pt;
			} else
				myputs("Direct");
			i = stoi(pt);
			argb[ct] = i;
		}
		modea *= 64;
		modea &= 192;
		modeb *= 16;
		modeb &= 48;
		code[ct] += (modea + modeb);
		++ct;
		if ((++row) > 23) {
			row = 2;
			myputs("^[c");
			header(1);
		}
	}
	myputs("\n    Code for ");
	myputs(prg);
	myputs(" completed.\n");
	return(ct);
}

/*
** get a program from a file
*/
fgetprog(prg,ct)
 FILE	*prg;
 int	ct;
{
	int i; char *pt;
	myputs("\nNow reading program file...\n\n");
	while (TRUE) {
		modea = modeb = DIRECT;
		if (nextword(prg) == FALSE)
			break;
		if ((i = getinst(ct)) == NULL)
			break;
		if (i == ERROR) {
			myputs("\007\nError - file fouled up!!!\n");
			myexit(0);
		}
		if (code[ct] != 0) {
			nextword(prg);
			arga[ct] = rdarg(&modea);
		}
		if (code[ct] != 4) {
			nextword(prg);
			argb[ct] = rdarg(&modeb);
		}
		modea *= 64;
		modea &= 192;
		modeb *= 16;
		modeb &= 48;
		code[ct] += (modea + modeb);
		++ct;
	}
	fclose(prg);
	return(ct);
}

/*
** select an instruction from the vaule in line[]
*/
getinst(indx)
int indx;
{
	if (strcmp(line,"MOV") == NULL)
		code[indx] = 1;
	else if (strcmp(line,"ADD") == NULL)
		code[indx] = 2;
	else if (strcmp(line,"SUB") == NULL)
		code[indx] = 3;
	else if (strcmp(line,"JMP") == NULL)
		code[indx] = 4;
	else if (strcmp(line,"JMZ") == NULL)
		code[indx] = 5;
	else if (strcmp(line,"JMG") == NULL)
		code[indx] = 6;
	else if (strcmp(line,"DJZ") == NULL)
		code[indx] = 7;
	else if (strcmp(line,"CMP") == NULL)
		code[indx] = 8;
	else if (strcmp(line,"DAT") == NULL)
		code[indx] = 0;
	else if (strcmp(line,"END") == NULL)
		return(NULL);
	else 
		return(ERROR);
	return(TRUE);
}

/*
** get an argument value from line[]
*/
rdarg(md) char *md;
{
	char *pt; int i;
	pt = &line[0];
	if (*pt == '@') {
		*md = INDEXED;
		++pt;
	} else if (*pt == '#') {
		*md = IMMEDIATE;
		++pt;
	}
	i = stoi(pt);
	return(i);
}

/*
** set the program counters to their initial locations
*/
getpc(i)
int i;
{
	myprintf("\nStart execution at what location (default = %d )?: ",pc[i]);
	mygets(line,12);
	if (line[0] == '\0')	/* set to default if no input */
		return;
	pc[i] = stoi(line);
	pc[i] = reladr(pc[i],0);
}

/*
** print programming header on screen
*/
header(rw)
int rw;
{
	cursor(rw,1);
	myputs("Addr");
	cursor(rw,10);
	myputs("Instr");
	cursor(rw,20);
	myputc('A');
	cursor(rw,30);
	myputc('B');
	cursor(rw,55);
	myputs("Modes:");
}

/*
** Get the next word from a TEXTfile, making sure to capitalize
*/
nextword(fil)
FILE *fil;
{
	char *pt, c;
	pt = &line[0];
	c = '\t';
	while ((c == '\t') || (c == BLANK) || (c == CR) || (c == LF)) {
		if ((c = toupper(getc(fil))) == EOF)
			return (FALSE);
	}
	while ((c != '\t') && (c != BLANK) && (c != CR) && (c != LF)) {
		*pt++ = c;
		if ((c = toupper(getc(fil))) == EOF)
			return (FALSE);
	}
	*pt++ = '\0';
	return(TRUE);
}



/*
** Show memory
*/
showmem(start,finish)
int start, finish;
{
	int ct,c;
	myprintf("Addresses %4d through %4d.\n",start,(finish-1));
	show = TRUE;
	myputc('\n');
	for (ct = start; ct < finish; ++ct) {
		readloc(ct);
		myputc('\n');
		if((c = mygetchar()) == EOF)
			myexit(0);
		if((c & 0x7f) == ENQ)
			break; 
	}
}
	
/*
****************************
** MASTER BATTLE ROUTINES **
****************************
*/

/*
** Fight it out
*/
fight()
{
	int ct, i;
	show = FALSE;
	myputs("^[c");
	myputs("    PRESS ANY KEY TO BEGIN ");
	mygetc();	/* wait for a keypress */
	myputs("\n\nBeginning Battle...\n\n");
	myputs("    > ^K to Abort <\n\n");
	myprintf("\t%8s\t\t\t%8s\n\n",proga,progb);
	for (ct = 0; ct < CUTOFF; ++ct) {
		if ((ct % 5) == 0)
			myputc('\n');
		stout(ct);
		now = 0;
		if (readloc(pc[0]) == NULL)
			break;
		myputs("    ");
		now = 1;
		if (readloc(pc[1]) == NULL)
			break;
		myputc('\n');
		if ((mygetchar() & 0x7f) == ENQ)
			break;
		for (i = 0; i <2; ++i)
			pc[i] = reladr(pc[i],0);
	}
	myputc(BELL);
	myprintf("\nBattle Completed after %d instruction cycles!\n",ct);
	if (ct == CUTOFF)
		myputs("\>Draw<\n");
}	

/*
** read a location, print the mnemonic, and perform it
*/
readloc(loc)
int loc;
{
	char ma, mb; int res;
	instr = modea = modeb = code[loc];
	instr &= 15;		/* or 0x0F */
	modea = (modea / 64) & 3;
	modeb = (modeb / 16) & 3;
	res = -1;
	if (instr == 1)			/* can't use switch with char arg */
		mov();
	else if (instr == 2)
		add();
	else if (instr == 3)
		sub();
	else if (instr == 4)
		jmp();
	else if (instr == 5)
		jmz();
	else if (instr == 6)
		jmg();
	else if (instr == 7)
		djz();
	else if (instr == 8)
		cmp();
	else {
		myputs("DAT");
		res = NULL;
	}
	myprintf("    %c%3d    %c%3d [%3d]",mnem(modea),arga[loc],mnem(modeb),argb[loc],loc);
	return(res);
}

/*
*********************************************
** functions to perform Redcode operations **
*********************************************
*/

/*
** Redcode MOV instruction
*/
mov()
{
	int a,b;
	myputs("MOV");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
	if (modea == IMMEDIATE) {
		code[b] = 0;			/* make it a DAT */
		argb[b] = arga[pc[now]];
#ifdef FULLIST
		myprintf("|#%6d>%3d|",arga[pc[now]],b);
#endif
	} else {
		a = findadr(pc[now], &arga[0], modea);
#ifdef FULLIST
		myprintf("|%3d>%3d|",a,b);
#endif
		code[b] = code[a];
		arga[b] = arga[a];
argb[b] = argb[a];
	}
	++pc[now];
}

/*
** Redcode ADD instruction
*/
add()
{
	int a,b;
	myputs("ADD");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
	if (modea == IMMEDIATE) {
		argb[b] += arga[pc[now]];
#ifdef FULLIST
		myprintf("|#%6d+%3d|",arga[pc[now]],b);
#endif
	} else {
		a = findadr(pc[now], &arga[0], modea);
#ifdef FULLIST
		myprintf("|%3d+%3d|",b,a);
#endif
		argb[b] += arga[a];
	}
	++pc[now];
}

/*
** Redcode SUB instruction
*/
sub()
{
	int a,b;
	myputs("SUB");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
	if (modea == IMMEDIATE) {
		argb[b] -= arga[pc[now]];
#ifdef FULLIST
		myprintf("|%3d-#%6d|",b,arga[pc[now]]);
#endif
	} else {
		a = findadr(pc[now], &arga[0],modea);
#ifdef FULLIST
		myprintf("|%3d-%3d|",b,a);
#endif
		argb[b] -= arga[a];
	}
	++pc[now];
}

/*
** Redcode JMP Instruction
*/
jmp()
{
	int a;
	myputs("JMP");
	if (show)
		return(NULL);
	pc[now] = findadr(pc[now], &arga[0], modea);
}

/*
** Redcode JMZ instruction
*/
jmz()
{
	int b;
	myputs("JMZ");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
#ifdef FULLIST
	myprintf("|%3d|",b);
#endif
	if (argb[b] == 0)
		pc[now] = findadr(pc[now], &arga[0], modea);
	else
		++pc[now];
}


/*
** Redcode JMG Instruction
*/
jmg()
{
	int b;
	myputs("JMG");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
#ifdef FULLIST
	myprintf("|%3d|",b);
#endif
	if (argb[b] != 0)
		pc[now] = findadr(pc[now], &arga[0], modea);
	else
		++pc[now];
}
	
/*
** Redcode DJZ Instruction
*/
djz()
{
	int b;
	myputs("DJZ");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
#ifdef FULLIST
	myprintf("|%3d|",b);
#endif
	if ((--argb[b]) == NULL)
		pc[now] = findadr(pc[now], &arga[0], modea);
	else
		++pc[now];
}

/*
** Redcode CMP Instruction
*/
cmp()
{
	int a,b;
	myputs("CMP");
	if (show)
		return(NULL);
	if (modea != IMMEDIATE) {
		a = findadr(pc[now], &arga[0], modea);
		a = argb[a];
	} else
		a = arga[pc[now]];
	if (modeb != IMMEDIATE) {
		b = findadr(pc[now], &argb[0], modeb);
		b = argb[b];
	} else
		b = argb[pc[now]];
	++pc[now];
	if (a != b)
		++pc[now];
}

/*
*************************
** Addressing Routines **
*************************
*/

/*
** find an address via direct or indirect
*/
findadr(orig,pab,mod) int orig, *pab; char mod;
{
	int p;
	if (mod == IMMEDIATE)
		return(orig);
	p = reladr(orig,pab[orig]);
	if (mod == INDEXED)
		p = reladr(p,argb[p]);
	return(p);
}


/*
** return an absolute address in the circular arena from a relative one
*/
reladr(abs,rel) int abs,rel;
{
	int j;
	if ((j = abs + rel) >= MAXSIZE)
		j = reladr((j - MAXSIZE),0);
	else if (j < 0)
		j = reladr((j + MAXSIZE),0);
	return(j);
}

/*
****************************
** Miscellaneous routines **
****************************
*/

/*
** Start with a "clean slate" in the battle-code arena
*/
clean()
{
	int i;
	for (i = 0; i < MAXSIZE; ++i) {
		code[i] = '\0';
		arga[i] = NULL;
		argb[i] = NULL;
	}
}

/*
** return appropriate character for addressing modes
*/
mnem(c) char c;
{
	c &= 255;
	if (c == IMMEDIATE)
		return('#');
	else if (c == INDEXED)
		return('@');
	else
		return('.');
}

/*
** return an integer from a string
*/
stoi(str) char *str;
{
	int i,s;
	if (*str == '-') {
		s = -1;
		++str;
	} else
		s = 1;
	i = 0;
	while ((*str >= '0') && (*str <= '9'))
		i = (10 * i) + (*str++ - '0');
	i *= s;
	return (i);
}

/*
** print a string to stderr from an integer <= 9999
*/
stout(i) int i;
{
	if (i < 0)
		myputc('-');
	myputc((i / 1000 + '0'));
	i %= 1000;
	myputc((i / 100 + '0'));
	i %= 100;
	myputc((i / 10 + '0'));
	i %= 10;
	myputc((i + '0'));
	myputs(":  ");
}

/*
** Address the cursor (Currently set for Heath/Telcon)
*/
cursor(row,col)
int row,col;
{
	myprintf("^[[%d;%dH", row, col);
}

/* prompts user to specify where program should be located in memory */
getinitial(progname,start)
char	progname[];
int	start;
{
	char buffer[12];
	int i;

	myprintf("\n\nWhere should the program called (%s) be located (default = %d )?:  ",progname,start);
	mygets(buffer,12);
	if (buffer[0] == '\0') {	/* set to default if no input */
		i = start;
		i = reladr(i,0);
		return(i);
	}
	i = stoi(buffer);
	i = reladr(i,0);
	return(i);
}

void querymem()
{
	int start,end;
	char buffer[12];

	while(1){
		myputs("\nList Memory? ");
		if (toupper(mygetc()) != 'Y')
			break;
		myputs("\n\nEnter starting address to display (default = 0): ");
		mygets(buffer,12);
		if (buffer[0] == '\0')	/* set to default if no input */
			start = 0;
		else {
			start = stoi(buffer);
			start = reladr(start,0);
		}
		myputs("\n\nEnter ending address to display (default = MAXSIZE): ");
		mygets(buffer,12);
		if (buffer[0] == '\0')	/* set to default if no input */
			end = MAXSIZE;
		else {
			end = stoi(buffer);
			end = reladr(end,0);
			if (start > end) {
				myputs("\nStart address has to be smaller than ending address\n");
				continue;
			}
		}
		showmem(start,end);
	}
}

/* end of CORE WARS 2 */
