

/*	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  
**
*/

#include "local.h"

#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 0x05
#define FULLIST 		/* List what's going where on MOV, ADD, etc */
/*
**********************
** 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) {
		logo();
		clean();
		prepare();
		fight();
		fputs("\nList Memory? ",stderr);
		if (toupper(flgetc()) == 'Y')
			showmem(0,MAXSIZE);
		fputs("\n\nPlay Again (Y/N) ? ",stderr);
		if (toupper(flgetc()) != 'Y')
			break;
	}
	fputs("\n\n\tThththththat\'s all, folks....\n",stderr);
}

/*
** Advertise the program
*/
logo()
{
	fputs(CLRSCR,stderr);
	fputs("\n\n\t****************************************\n",stderr);
	fputs("\t**                                    **\n",stderr);
	fputs("\t**         C O R E    W A R S         **\n",stderr);
	fputs("\t**                                    **\n",stderr);
	fputs("\t****************************************\n\n\n",stderr);
	fputs("\tMARS Version ",stderr);
	fputs(VERSION,stderr);
	fputs("   Kevin Bjorke 5/28/84\n\n",stderr);
}

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

/*
** Read in both programs
*/
prepare()
{
	int top,bot; char *p;
	int fp;			/* possible file pointer */
	fputs("\n\tEnter the name of Battle-Program A: ",stderr);
	gets(proga,80,stdin);
	fputs("\n\tEnter the name of Battle-Program B: ",stderr);
	gets(progb,80,stdin);
	for (p = &progb[0]; *p; ++p)
		*p = toupper(*p);
	for (p = &proga[0]; *p; ++p)
		*p = toupper(*p);
	bot = 0;
	if ((fp = fopen(proga,"r")) == NULL) {
		fputs(CLRSCR,stderr);
		fputs("\n\nPlease enter code for ",stderr);
		fputs(proga,stderr);
		fputs(":\n\n",stderr);
		top = getprog(proga,bot);
	} else
		top = fgetprog(fp,bot);
	printf("\nNow listing Program %s:\n\n",proga);
	showmem(bot,top);
	pc[0] = bot;
	getpc(0);
	bot = MAXSIZE / 2;
	if ((fp = fopen(progb,"r")) == NULL) {
		fputs(CLRSCR,stderr);
		fputs("\n\nNow, please enter code for ",stderr);
		fputs(progb,stderr);
		fputs(":\n\n",stderr);
		top = getprog(progb,bot);
	} else
		top = fgetprog(fp,bot);
	printf("\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;
	fputs("Enter program one line at a time.\n",stderr);
	fputs("Pressing RETURN between arguments.\n",stderr);
	fputs("\tUse END to finish\n\n",stderr);
	header(9);
	while (TRUE) {
		modea = modeb = DIRECT;
		cursor(row,1);
		stout(ct);
		cursor(row,10);
		gets(line,12,stdin);
		for (i = 0; line[i]; ++i) {
			if ((line[i] == '\t') || (line[i] == ' '))
				line[i] = '\0';
			line[i] = toupper(line[i]);
		}
		if ((i = getinst(ct)) == NULL)
			break;
		if (i == ERROR) {
			cursor(row,10);
			fputs("\007???\n",stderr);
			continue;
		}
		cursor(row,50);
		fputs(line,stderr);
		if (code[ct] != 0) {
			cursor(row,20);
			gets(line,12,stdin);
			pt = &line[0];
			cursor(row,60);
			if (*pt == '@') {
				fputs("Indirect",stderr);
				modea = INDEXED;
				++pt;
			} else if (*pt == '#') {
				fputs("Immediate",stderr);
				modea = IMMEDIATE;
				++pt;
			}  else
				fputs("Direct",stderr);
			i = stoi(pt);
			arga[ct] = i;
		}
		if (code[ct] != 4) {
			cursor(row,30);
			gets(line,12,stdin);
			pt = &line[0];
			cursor(row,70);
			if (*pt == '@') {
				fputs("Indirect",stderr);
				modeb = INDEXED;
				++pt;
			} else if (*pt == '#') {
				fputs("Immediate",stderr);
				modeb = IMMEDIATE;
				++pt;
			} else
				fputs("Direct",stderr);
			i = stoi(pt);
			argb[ct] = i;
		}
		modea *= 64;
		modea &= 192;
		modeb *= 16;
		modeb &= 48;
		code[ct] += (modea + modeb);
		++ct;
		if ((++row) > 23) {
			row = 2;
			fputs(CLRSCR,stderr);
			header(1);
		}
	}
	fputs("\n\tCode for ",stderr);
	fputs(prg,stderr);
	fputs(" completed.\n",stderr);
	return(ct);
}

/*
** get a program from a file
*/
fgetprog(prg,ct) int prg,ct;
{
	int i; char *pt;
	fputs("\nNow reasding program file...\n\n",stderr);
	while (TRUE) {
		modea = modeb = DIRECT;
		if (nextword(prg) == FALSE)
			break;
		if ((i = getinst(ct)) == NULL)
			break;
		if (i == ERROR) {
			fputs("\007\nError - file fouled up!!!\n",stderr);
			exit();
		}
		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;
{
	fputs("\nStart execution at location: ",stderr);
	gets(line,12,stdin);
	pc[i] = stoi(line);
	pc[i] = reladr(pc[i],0);
}

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

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



/*
** Show memory
*/
showmem(start,finish) int start, finish;
{
	int ct;
	printf("Addresses %4d through %4d.\n",start,(finish-1));
	show = TRUE;
	putchar('\n');
	for (ct = start; ct < finish; ++ct) {
		readloc(ct);
		putchar('\n');
	}
}
	
/*
****************************
** MASTER BATTLE ROUTINES **
****************************
*/

/*
** Fight it out
*/
fight()
{
	int ct, i;
	show = FALSE;
	fputs(CLRSCR,stderr);
	fputs("\tPRESS ANY KEY TO BEGIN ",stderr);
	while (bdos(06,0xff) == NULL);
	fputs("\n\nBeginning Battle...\n\n",stderr);
	fputs("\t> ^E to Abort <\n\n",stderr);
	printf("\t\t%8s\t\t%8s\n\n",proga,progb);
	for (ct = 0; ct < CUTOFF; ++ct) {
		if ((ct % 5) == 0)
			fputc('\n',stderr);
		stout(ct);
		now = 0;
		if (readloc(pc[0]) == NULL)
			break;
		putchar('\t');
		now = 1;
		if (readloc(pc[1]) == NULL)
			break;
		putchar('\n');
		if ((bdos(6,0xff)&0x7f) == ENQ)
			break;
		for (i = 0; i <2; ++i)
			pc[i] = reladr(pc[i],0);
	}
	fputc(BELL,stderr);
	printf("\nBattle Completed after %d instruction cycles!\n",ct);
	if (ct == CUTOFF)
		fputs("\>Draw<\n",stderr);
}	

/*
** 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 {
		puts("DAT");
		res = NULL;
	}
	printf("\t%c%3d\t%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;
	puts("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
		printf("|#%6d>%3d|",arga[pc[now]],b);
#endif
	} else {
		a = findadr(pc[now], &arga[0], modea);
#ifdef FULLIST
		printf("|%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;
	puts("ADD");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
	if (modea == IMMEDIATE) {
		argb[b] += arga[pc[now]];
#ifdef FULLIST
		printf("|#%6d+%3d|",arga[pc[now]],b);
#endif
	} else {
		a = findadr(pc[now], &arga[0], modea);
#ifdef FULLIST
		printf("|%3d+%3d|",b,a);
#endif
		argb[b] += arga[a];
	}
	++pc[now];
}

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

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

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


/*
** Redcode JMG Instruction
*/
jmg()
{
	int b;
	puts("JMG");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
#ifdef FULLIST
	printf("|%3d|",b);
#endif
	if (argb[b] != 0)
		pc[now] = findadr(pc[now], &arga[0], modea);
	else
		++pc[now];
}
	
/*
** Redcode DJZ Instruction
*/
djz()
{
	int b;
	puts("DJZ");
	if (show)
		return(NULL);
	b = findadr(pc[now], &argb[0], modeb);
#ifdef FULLIST
	printf("|%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;
	puts("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)
		fputc('-',stderr);
	fputc((i / 1000 + '0'),stderr);
	i %= 1000;
	fputc((i / 100 + '0'),stderr);
	i %= 100;
	fputc((i / 10 + '0'),stderr);
	i %= 10;
	fputc((i + '0'),stderr);
	fputs(":\t",stderr);
}

/*
** Address the cursor (Currently set for Heath/Telcon)
*/
cursor(row,col) int row,col;
{
	fputs(ADRCUR,stderr);
	fputc((row+BIAS),stderr);
	fputc((col+BIAS),stderr);
}

/*
** raw console io
*/
keypr()
{
	char c;
        while (!((c=bdos(6, 0xff)&0x7f)))
            ;
	return(c);
}

/* end of CORE WARS 2 */
