/*
 *	LIFE: lifetab.c:
 *
 *	Deal with the dreaded lookup tables
 *
 *	copyscr() is also here, at the end.
 */
#include <stdio.h>
#include <osbind.h>

char *lifetab;		/* The big one, exported to other files */
long spread[1024];	/* spread bits of a 10-bit word out to every 3 bits */

inittables()
{
	initlifetab();
	initspread();
	showlifetab();
}

static char nntab[512];	/* how many neighbours from 9-bit pattern */

/*
 *	Allocate and fill in the table
 */
initlifetab()
{
	lifetab = (char *) Malloc(256L*1024L);	/* 2^18 elements */
	if (lifetab == (char *) 0) {
		fputs("Out of memory\n", stderr);
		exit(1);
	}

#define dbra dbf
	asm {
		/* precomputed 512-byte table to give number of neighbours
		 * from bit pattern */
		; A3	nntab
		; A2	roving pointer into nntab[]
		; D7	loop variable
		; D6	copy of D7 to destroy
		; D5	nneigh
		; D0	scratch

		; nntab contains entries which include the centre bit in the
		; count, but the index always &= 0757 so these are not used.
		lea	nntab(A4),A3	; point at start of nntab

		move.l	#511,D7		; for (i=511,
for1:			moveq	#0,D5	; nneigh = 0
			move.w	D7,D6	; bits = i
			/* there's always at least one bit set */
do1:						; do {
				addq.b	#1,D5		; nneigh++
				move.w	D6,D0		; clear one bit of D6
				subq.w	#1,D0
				and.w	D0,D6
			bne	do1		; } while (bits != 0)

			move.b	D5,0(A3,D7.w)	; nntab[i] = nneigh;
		dbls	D7,for1

		/* do 0 specially to speed above bitcount loop */
		move.b	#0,(A3)		; nntab[0] = 0

		/* generate the life table proper */
; A3	nntab
; A2	pointer into lifetab[]
; D7	l - the 30-bit pattern whose successor byte we want
; D6	l2 (copy of l to destroy)
; D5	the result (byte)
; D4	j - which of the four bits we are working out
; D3	jj - 1<<j (well not, actually, cos j counts down)
; D2	nneigh
; D1	bit array: bit N = whether we live with N neighbours if we were alive
; D0	bit array: bit N = whether we live with N neighbours if we were dead
; Standard Conway life is with D1=12 (000001100) and D0=8 (000001000)
		moveq	#0,D2		; clear top word for safety
		move.w	#1023-32,D1		; 111011111 - survive with 2 or 3
		move.w	#1023-32-64,D0		; 110011111 - birth with 3
		move.l	#(1L<<18-1),D7
		movea.l	lifetab(A4),A2	; Point A2 just past end of lifetab
		adda.l	D7,A2
		adda.l	#1,A2
for2:			moveq	#0,D5	; only need .b
			move.l	D7,D6	; l2 = l
			moveq	#3,D4	; for (j=3,2,1,0
			moveq	#1,D3	;	jj = 1,2,4,8
for3:				move.w	D6,D2	; nneigh = nntab[l2 & 0757];
				andi.w	#0757,D2; use nneigh (ie D2) as a temp
				move.b	0(A3,D2.w),D2
				/* work out new life value */
				andi.w	#0x00FF,D2; remove garbage
				btst	#4,D6	; if (cell was alive) {
				beq	wasdead
					cmp.b	#8,D2
					
					btst.l	D2,D1	; does it live?
					bne	islive
					/* duplicate isdead endloop code */
					lsr.l	#3,D6
					asl.b	#1,D3
					dbra	D4,for3
					bra	rof3
wasdead:
					btst.l	D2,D0
					beq	isdead
islive:				or.b	D3,D5	; result = jj
isdead:
				lsr.l	#3,D6	; l2 >>= 3
				asl.b	#1,D3	; jj <<= 1
			dbra	D4,for3
rof3:			move.b	D5,-(A2)	; lifetab[l] = result
		subq.l	#1,D7
		bcc	for2		; loop if >= 0
	}
}

/*
 *	Debugging routines for ensuring that the table is right
 */
checklifetab()
{
	char buf[256];
	long orig;
	register int i;
	char result;

	while (gets(buf) != NULL) {
		orig = 0L;
		for (i=0; buf[i] != NULL; i++) {
			if (buf[i] == '1') orig |= 1L<<i;
		}
		result = lifetab[orig];
		for (i=0; i<8; i++) {
			putchar(result & 1<<i ? '1' : '0');
		}
		putchar('\n');
	}
}

showlifetab()
{

	int i;			/* loop variable for all screen words */
	int *scrbase;		/* start of screen in memory */
	register char *cp;	/* traces lifetab[] */

	scrbase = (int *) Logbase();

	for (i=0, cp = lifetab; i<16000; i++) {
		scrbase[i] = 	  (*cp++ & 017) << 12
				| (*cp++ & 017) << 8
				| (*cp++ & 017) << 4
				| (*cp++ & 017);
	}
	/* pause for keypress */
	Bconin(2);

	for (i=0, cp +=384*4; i<16000; i++) {
		scrbase[i] = 	  (*cp++ & 017) << 12
				| (*cp++ & 017) << 8
				| (*cp++ & 017) << 4
				| (*cp++ & 017);
	}
	/* pause for keypress */
	Bconin(2);

	for (i=0, cp +=384*4; i<16000; i++) {
		scrbase[i] = 	  (*cp++ & 017) << 12
				| (*cp++ & 017) << 8
				| (*cp++ & 017) << 4
				| (*cp++ & 017);
	}
	/* pause for keypress */
	Bconin(2);

	for (i=0, cp +=384*4; i<16000; i++) {
		scrbase[i] = 	  (*cp++ & 017) << 12
				| (*cp++ & 017) << 8
				| (*cp++ & 017) << 4
				| (*cp++ & 017);
	}
	/* pause for keypress */
	Bconin(2);
}

shownntab()
{

	int i;			/* loop variable for all screen words */
	int *scrbase;		/* start of screen in memory */
	register char *cp;	/* traces nntab[] */
	register char *sp;	/* traces screen */

	scrbase = (int *) Logbase();

	for (i=0, cp = nntab, sp = (char *)scrbase; i<512; i++) {
		*sp++ = *cp++;
	}

	/* pause for keypress */
	Bconin(2);
}

initspread()
{
	register int i, j;
	long int l;

	for (i=0; i<1024; i++) {
		l = 0L;
		for (j=0; j<10; j++) {
			if (i & (1<<j)) l |= 1L << (j*3);
		}
		spread[i] = l;
	}
}

/*
 *	Asm routine do bcopy a screen-sized area (32000 bytes of
 *	contiguous memory)
 */

copyscreen(oldscr, newscr)
int *oldscr, *newscr;
{
#define dbra dbf	/* sigh */
	asm {
		move.l	oldscr(A6),A0
		move.l	newscr(A6),A1
		; We copy 8 longwords at a time
		; (32 is an exact divisor of 32000, surprisingly enough)

		move.w	#999,D0			; dbra starts with niter-1
loop1:			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
			move.l (A0)+,(A1)+
		dbra D0,loop1
	}
}
