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

 Exidy Vertigo hardware

 The Vertigo vector CPU consists of four AMD 2901 bit slice
 processors, logic to control microcode program flow and a digital
 vector generator. The microcode for the bit slice CPUs is stored in 13
 bipolar proms for a total of 512 52bit wide micro instructions. The
 microcode not only crontrols the 2901s but also loading and storing
 of operands and results, program flow control and vector generation.

 +----+----+----+----+----+----+-------+-----+-----+------+-----+----+---+
 |VUC |VUC |VUC |VUC |VUC |VUC |  VUC  | VUC | VUC | VUC  | VUC |VUC |VUC| labels
 | 10 | 13 | 9  | 8  | 7  | 6  |   5   |  12 |  11 |  2   |  1  | 4  | 3 |
 +----+----+----+----+----+----+-------+-----+-----+------+-----+----+---+
 |    |    |    |    |    |    |       |     |     |      |     |PR5/|R5/| schematics
 |J5/4|G5/1|K5/5|L5/6|M5/7|N5/8| P5/9  |H5/2 |HJ5/3|S5/12 |T5/13| 10 |11 |
 +----+----+----+----+----+----+-------+-----+-----+------+-----+----+---+
 55 44|4444|4444|3333|3333|3322|2 2 2 2|2 222|11 11|1 1 11|110 0|0000|0000
 21 98|7654|3210|9876|5432|1098|7 6 5 4|3 210|98 76|5 4 32|109 8|7654|3210

    xx|xxxx|aaaa|bbbb|iiii|iiii|i c m r|r ooo|ii oo|  j jj|jjj m|mmmm|mmmm
    54|3210|3210|3210|8765|4321|0 n r s|w fff|ff aa|  p 43|210 a|aaaa|aaaa
                                    e e|r 210|10 10|  o   |    8|7654|3210
                                    q l i             s
                                        t
                                        e
 x:    address for 64 words of 16 bit wide SRAM
 a:    A register index
 b:    B register index
 i:    2901 instruction
 cn:   carry bit
 mreq, rsel, rwrite: signals for memory access
 of:   vector generator
 if:   vector RAM/ROM data select
 oa:   vector RAM/ROM address select
 jpos: jump condition inverted
 j:    jump condition and type
 m:    jump address

 Variables, enums and defines are named as in the schematics (pp. 6, 7)
 where possible.

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

#include "driver.h"
#include "profiler.h"
#include "vidhrdw/vector.h"
#include "vertigo.h"


/*************************************
 *
 *  Macros and enums
 *
 *************************************/

#define MC_LENGTH 512

#define V_ADDPOINT(h,v,c,i) \
	vector_add_point (((h) & 0x7ff) << 14, (0x6ff - ((v) & 0x7ff)) << 14, VECTOR_COLOR444(c), (i))

#define ADD(r,s,c)	(((r)  + (s) + (c)) & 0xffff)
#define SUBR(r,s,c) ((~(r) + (s) + (c)) & 0xffff)
#define SUBS(r,s,c) (((r) + ~(s) + (c)) & 0xffff)
#define OR(r,s)		((r) | (s))
#define AND(r,s)	((r) & (s))
#define NOTRS(r,s)	(~(r) & (s))
#define EXOR(r,s)	((r) ^ (s))
#define EXNOR(r,s)	(~((r) ^ (s)))

/* values for MC_DST */
enum {
	QREG = 0,
	NOP,
	RAMA,
	RAMF,
	RAMQD,
	RAMD,
	RAMQU,
	RAMU
};

/* values for MC_IF */
enum {
	S_ROMDE = 0,
	S_RAMDE
};

/* values for MC_OA */
enum {
	S_SREG = 0,
	S_ROMA,
	S_RAMD
};

/* values for MC_JMP */
enum {
	S_JBK = 0,
	S_CALL,
	S_OPT,
	S_RETURN
};

/* values for MC_JCON */
enum {
	S_ALWAYS = 0,
	S_MSB,
	S_FEQ0,
	S_Y10,
	S_VFIN,
	S_FPOS,
	S_INTL4
};


/*************************************
 *
 *  Global variables
 *
 *************************************/

UINT16 *vertigo_vectorram;


/*************************************
 *
 *  Typedefs
 *
 *************************************/

typedef struct _am2901
{
	UINT32 ram[16];	  /* internal ram */
	UINT32 d;		  /* direct data D input */
	UINT32 q;		  /* Q register */
	UINT32 f;		  /* F ALU result */
	UINT32 y;		  /* Y output */
} am2901;

typedef struct _vector_generator
{
	UINT32 sreg;	  /* shift register */
	UINT32 l1;		  /* latch 1 adder operand only */
	UINT32 l2;		  /* latch 2 adder operand only */
	UINT32 c_v;		  /* vertical position counter */
	UINT32 c_h;		  /* horizontal position counter */
	UINT32 c_l;		  /* length counter */
	UINT32 adder_s;	  /* slope generator result and B input */
	UINT32 adder_a;	  /* slope generator A input */
	UINT32 color;	  /* color */
	UINT32 intensity; /* intensity */
	UINT32 brez;	  /* h/v-counters enable */
	UINT32 vfin;	  /* drawing yes/no */
	UINT32 hud1;	  /* h-counter up or down (stored in L1) */
	UINT32 hud2;	  /* h-counter up or down (stored in L2) */
	UINT32 vud1;	  /* v-counter up or down (stored in L1) */
	UINT32 vud2;	  /* v-counter up or down (stored in L2) */
	UINT32 hc1;		  /* use h- or v-counter in L1 mode */
	UINT32 ven;       /* vector intensity enable */
} vector_generator;

typedef struct _microcode
{
	UINT32 x;
	UINT32 a;
	UINT32 b;
	UINT32 inst;
	UINT32 dest;
	UINT32 cn;
	UINT32 mreq;
	UINT32 rsel;
	UINT32 rwrite;
	UINT32 of;
	UINT32 iif;
	UINT32 oa;
	UINT32 jpos;
	UINT32 jmp;
	UINT32 jcon;
	UINT32 ma;
} microcode;

typedef struct _vproc
{
	UINT16 sram[64]; /* external sram */
	UINT16 ramlatch; /* latch between 2901 and sram */
	UINT16 rom_adr;	 /* vector ROM/RAM address latch */
	UINT32 pc;		 /* program counter */
	UINT32 ret;		 /* return address */

} vproc;


/*************************************
 *
 *  Statics
 *
 *************************************/

static vproc vs;
static am2901 bsp;
static vector_generator vgen;
static UINT16 *vertigo_vectorrom;
static microcode mc[MC_LENGTH];


/*************************************
 *
 *  Vector processor initialization
 *
 *************************************/

void vertigo_vproc_init(void)
{
	int i;
	UINT64 *mcode;

	vertigo_vectorrom = (UINT16 *)memory_region(REGION_USER1);
	mcode = (UINT64 *)memory_region(REGION_PROMS);

	/* Decode microcode */
	for (i = 0; i < MC_LENGTH; i++)
	{
		mc[i].x = (mcode[i] >> 44) & 0x3f;
		mc[i].a = (mcode[i] >> 40) & 0xf;
		mc[i].b = (mcode[i] >> 36) & 0xf;
		mc[i].inst = (mcode[i] >> 27) & 077;
		mc[i].dest = (mcode[i] >> 33) & 07;
		mc[i].cn = (mcode[i] >> 26) & 0x1;
		mc[i].mreq = (mcode[i] >> 25) & 0x1;
		mc[i].rwrite = (mcode[i] >> 23) & 0x1;
		mc[i].rsel = mc[i].rwrite & ((mcode[i] >> 24) & 0x1);
		mc[i].of =  (mcode[i] >> 20) & 0x7;
		mc[i].iif = (mcode[i] >> 18) & 0x3;
		mc[i].oa = (mcode[i] >> 16) & 0x3;
		mc[i].jpos = (mcode[i] >> 14) & 0x1;
		mc[i].jmp = (mcode[i] >> 12) & 0x3;
		mc[i].jcon = (mcode[i] >> 9) & 0x7;
		mc[i].ma = mcode[i] & 0x1ff;
	}

	memset(&vs, 0, sizeof(vs));
	memset(&bsp, 0, sizeof(bsp));
	memset(&vgen, 0, sizeof(vgen));

	state_save_register_item_array("vector_proc", 0, vs.sram);
	state_save_register_item("vector_proc", 0, vs.ramlatch);
	state_save_register_item("vector_proc", 0, vs.rom_adr);
	state_save_register_item("vector_proc", 0, vs.pc);
	state_save_register_item("vector_proc", 0, vs.ret);

	state_save_register_item_array("vector_proc", 0, bsp.ram);
	state_save_register_item("vector_proc", 0, bsp.d);
	state_save_register_item("vector_proc", 0, bsp.q);
	state_save_register_item("vector_proc", 0, bsp.f);
	state_save_register_item("vector_proc", 0, bsp.y);

	state_save_register_item("vector_proc", 0, vgen.sreg);
	state_save_register_item("vector_proc", 0, vgen.l1);
	state_save_register_item("vector_proc", 0, vgen.l2);
	state_save_register_item("vector_proc", 0, vgen.c_v);
	state_save_register_item("vector_proc", 0, vgen.c_h);
	state_save_register_item("vector_proc", 0, vgen.c_l);
	state_save_register_item("vector_proc", 0, vgen.adder_s);
	state_save_register_item("vector_proc", 0, vgen.adder_a);
	state_save_register_item("vector_proc", 0, vgen.color);
	state_save_register_item("vector_proc", 0, vgen.intensity);
	state_save_register_item("vector_proc", 0, vgen.brez);
	state_save_register_item("vector_proc", 0, vgen.vfin);
	state_save_register_item("vector_proc", 0, vgen.hud1);
	state_save_register_item("vector_proc", 0, vgen.hud2);
	state_save_register_item("vector_proc", 0, vgen.vud1);
	state_save_register_item("vector_proc", 0, vgen.vud2);
	state_save_register_item("vector_proc", 0, vgen.hc1);
	state_save_register_item("vector_proc", 0, vgen.ven);
}


/********************************************
 *
 *  4 x AM2901 bit slice processors
 *  Q3 and IN3 are hardwired
 *
 ********************************************/

static void am2901x4 (am2901 *bsp, microcode *mc)
{
	switch (mc->inst)
	{
	case 000: bsp->f = ADD(bsp->ram[mc->a], bsp->q, mc->cn); break;
	case 001: bsp->f = ADD(bsp->ram[mc->a], bsp->ram[mc->b], mc->cn); break;
	case 002: bsp->f = ADD(0, bsp->q, mc->cn); break;
	case 003: bsp->f = ADD(0, bsp->ram[mc->b], mc->cn); break;
	case 004: bsp->f = ADD(0, bsp->ram[mc->a], mc->cn); break;
	case 005: bsp->f = ADD(bsp->d, bsp->ram[mc->a], mc->cn); break;
	case 006: bsp->f = ADD(bsp->d, bsp->q, mc->cn); break;
	case 007: bsp->f = ADD(bsp->d, 0, mc->cn); break;

	case 010: bsp->f = SUBR(bsp->ram[mc->a], bsp->q, mc->cn); break;
	case 011: bsp->f = SUBR(bsp->ram[mc->a], bsp->ram[mc->b], mc->cn); break;
	case 012: bsp->f = SUBR(0, bsp->q, mc->cn); break;
	case 013: bsp->f = SUBR(0, bsp->ram[mc->b], mc->cn); break;
	case 014: bsp->f = SUBR(0, bsp->ram[mc->a], mc->cn); break;
	case 015: bsp->f = SUBR(bsp->d, bsp->ram[mc->a], mc->cn); break;
	case 016: bsp->f = SUBR(bsp->d, bsp->q, mc->cn); break;
	case 017: bsp->f = SUBR(bsp->d, 0, mc->cn); break;

	case 020: bsp->f = SUBS(bsp->ram[mc->a], bsp->q, mc->cn); break;
	case 021: bsp->f = SUBS(bsp->ram[mc->a], bsp->ram[mc->b], mc->cn); break;
	case 022: bsp->f = SUBS(0, bsp->q, mc->cn); break;
	case 023: bsp->f = SUBS(0, bsp->ram[mc->b], mc->cn); break;
	case 024: bsp->f = SUBS(0, bsp->ram[mc->a], mc->cn); break;
	case 025: bsp->f = SUBS(bsp->d, bsp->ram[mc->a], mc->cn); break;
	case 026: bsp->f = SUBS(bsp->d, bsp->q, mc->cn); break;
	case 027: bsp->f = SUBS(bsp->d, 0, mc->cn); break;

	case 030: bsp->f = OR(bsp->ram[mc->a], bsp->q); break;
	case 031: bsp->f = OR(bsp->ram[mc->a], bsp->ram[mc->b]); break;
	case 032: bsp->f = OR(0, bsp->q); break;
	case 033: bsp->f = OR(0, bsp->ram[mc->b]); break;
	case 034: bsp->f = OR(0, bsp->ram[mc->a]); break;
	case 035: bsp->f = OR(bsp->d, bsp->ram[mc->a]); break;
	case 036: bsp->f = OR(bsp->d, bsp->q); break;
	case 037: bsp->f = OR(bsp->d, 0); break;

	case 040: bsp->f = AND(bsp->ram[mc->a], bsp->q); break;
	case 041: bsp->f = AND(bsp->ram[mc->a], bsp->ram[mc->b]); break;
	case 042: bsp->f = AND(0, bsp->q); break;
	case 043: bsp->f = AND(0, bsp->ram[mc->b]); break;
	case 044: bsp->f = AND(0, bsp->ram[mc->a]); break;
	case 045: bsp->f = AND(bsp->d, bsp->ram[mc->a]); break;
	case 046: bsp->f = AND(bsp->d, bsp->q); break;
	case 047: bsp->f = AND(bsp->d, 0); break;

	case 050: bsp->f = NOTRS(bsp->ram[mc->a], bsp->q); break;
	case 051: bsp->f = NOTRS(bsp->ram[mc->a], bsp->ram[mc->b]); break;
	case 052: bsp->f = NOTRS(0, bsp->q); break;
	case 053: bsp->f = NOTRS(0, bsp->ram[mc->b]); break;
	case 054: bsp->f = NOTRS(0, bsp->ram[mc->a]); break;
	case 055: bsp->f = NOTRS(bsp->d, bsp->ram[mc->a]); break;
	case 056: bsp->f = NOTRS(bsp->d, bsp->q); break;
	case 057: bsp->f = NOTRS(bsp->d, 0); break;

	case 060: bsp->f = EXOR(bsp->ram[mc->a], bsp->q); break;
	case 061: bsp->f = EXOR(bsp->ram[mc->a], bsp->ram[mc->b]); break;
	case 062: bsp->f = EXOR(0, bsp->q); break;
	case 063: bsp->f = EXOR(0, bsp->ram[mc->b]); break;
	case 064: bsp->f = EXOR(0, bsp->ram[mc->a]); break;
	case 065: bsp->f = EXOR(bsp->d, bsp->ram[mc->a]); break;
	case 066: bsp->f = EXOR(bsp->d, bsp->q); break;
	case 067: bsp->f = EXOR(bsp->d, 0); break;

	case 070: bsp->f = EXNOR(bsp->ram[mc->a], bsp->q); break;
	case 071: bsp->f = EXNOR(bsp->ram[mc->a], bsp->ram[mc->b]); break;
	case 072: bsp->f = EXNOR(0, bsp->q); break;
	case 073: bsp->f = EXNOR(0, bsp->ram[mc->b]); break;
	case 074: bsp->f = EXNOR(0, bsp->ram[mc->a]); break;
	case 075: bsp->f = EXNOR(bsp->d, bsp->ram[mc->a]); break;
	case 076: bsp->f = EXNOR(bsp->d, bsp->q); break;
	case 077: bsp->f = EXNOR(bsp->d, 0); break;
	}

	switch (mc->dest)
	{
	case QREG:
		bsp->q = bsp->f;
		bsp->y = bsp->f;
		break;
	case NOP:
		bsp->y = bsp->f;
		break;
	case RAMA:
		bsp->y = bsp->ram[mc->a];
		bsp->ram[mc->b] = bsp->f;
		break;
	case RAMF:
		bsp->y = bsp->f;
		bsp->ram[mc->b] = bsp->f;
		break;
	case RAMQD:
		bsp->y = bsp->f;
		bsp->q = (bsp->q >> 1) & 0x7fff;		  /* Q3 is low */
		bsp->ram[mc->b] = (bsp->f >> 1) | 0x8000; /* IN3 is high! */
		break;
	case RAMD:
		bsp->y = bsp->f;
		bsp->ram[mc->b] = (bsp->f >> 1) | 0x8000; /* IN3 is high! */
		break;
	case RAMQU:
		bsp->y = bsp->f;
		bsp->ram[mc->b] = (bsp->f << 1) & 0xffff;
		bsp->q = (bsp->q << 1) & 0xffff;
		break;
	case RAMU:
		bsp->y = bsp->f;
		bsp->ram[mc->b] = (bsp->f << 1) & 0xffff;
		break;
	}
}


/********************************************
 *
 *  Vector Generator
 *
 *  This part of the hardware draws vectors
 *  under control of the bit slice processors.
 *  It is just a bunch of counters, latches
 *  and DACs.
 *
 ********************************************/

static void vertigo_vgen (vector_generator *vg)
{
	if (vg->c_l & 0x800)
	{
		vg->vfin = 1;
		vg->c_l = (vg->c_l+1) & 0xfff;

		if ((vg->c_l & 0x800) == 0)
		{
			vg->brez = 0;
			vg->vfin = 0;
		}

		if (vg->brez) /* H/V counter enabled */
		{
			/* Depending on MSB of adder only one or both
               counters are de-/incremented. This is all
               defined by the shift register which is
               latched in bits 12-15 of L1/L2.
            */
			if (vg->adder_s & 0x800)
			{
				if (vg->hc1)
					vg->c_h += vg->hud1? -1: 1;
				else
					vg->c_v += vg->vud1? -1: 1;
				vg->adder_a = vg->l1;
			}
			else
			{
				vg->c_h += vg->hud2? -1: 1;
				vg->c_v += vg->vud2? -1: 1;
				vg->adder_a = vg->l2;
			}

			/* H/V counters are 12 bit */
			vg->c_v &= 0xfff;
			vg->c_h &= 0xfff;
		}

		vg->adder_s = (vg->adder_s + vg->adder_a) & 0xfff;
	}

	if (vg->brez ^ vg->ven)
	{
		if (vg->brez)
		V_ADDPOINT (vg->c_h, vg->c_v, 0, 0);
		else
			V_ADDPOINT (vg->c_h, vg->c_v, vg->color, vg->intensity);
		vg->ven = vg->brez;
	}
}

/*************************************
 *
 *  Vector processor
 *
 *************************************/

void vertigo_vproc(int cycles, int irq4)
{
	int jcond;
	microcode *cmc;

	if (irq4) vector_clear_list();

	profiler_mark(PROFILER_USER1);

	while (cycles--)
	{
		/* Microcode at current PC */
		cmc = &mc[vs.pc];

		/* Load data */
		if (cmc->iif == S_RAMDE)
		{
			bsp.d = vs.ramlatch;
		}
		else if (cmc->iif == S_ROMDE)
		{
			if (vs.rom_adr < 0x2000)
			{
				bsp.d = vertigo_vectorram[vs.rom_adr & 0xfff];
			}
			else
			{
				bsp.d = vertigo_vectorrom[vs.rom_adr & 0x7fff];
			}
		}

		/* SRAM selected ? */
		if (cmc->rsel == 0)
		{
			if (cmc->rwrite)
			{
				bsp.d = vs.sram[cmc->x];
			}
			else
			{
				/* Data can be transferred between vector ROM/RAM
                   and SRAM without going through the 2901 */
				vs.sram[cmc->x] = bsp.d;
			}
		}

		am2901x4 (&bsp, cmc);

		/* Store data */
		switch (cmc->oa)
		{
		case S_RAMD:
			vs.ramlatch = bsp.y;
			if (cmc->iif==S_RAMDE && (cmc->rsel == 0) && (cmc->rwrite == 0))
				vs.sram[cmc->x] = vs.ramlatch;
			break;
		case S_ROMA:
			vs.rom_adr = bsp.y;
			break;
		case S_SREG:
			/* FPOS is shifted into sreg */
			vgen.sreg = (vgen.sreg >> 1) | ((bsp.f >> 9) & 4);
			break;
		default:
			break;
		}

		/* Vector generator setup */
		switch (cmc->of)
		{
		case 0:
			vgen.color = bsp.y & 0xfff;
			break;
		case 1:
			vgen.intensity = bsp.y & 0xff;
			break;
		case 2:
			vgen.l1 = bsp.y & 0xfff;
			vgen.adder_s = 0;
			vgen.adder_a = vgen.l2;
			vgen.hud1 = vgen.sreg & 1;
			vgen.vud1 = vgen.sreg & 2;
			vgen.hc1  = vgen.sreg & 4;
			vgen.brez = 1;
			break;
		case 3:
			vgen.l2 = bsp.y & 0xfff;
			vgen.adder_s = (vgen.adder_s + vgen.adder_a) & 0xfff;
			if (vgen.adder_s & 0x800)
				vgen.adder_a = vgen.l1;
			else
				vgen.adder_a = vgen.l2;
			vgen.hud2 = vgen.sreg & 1;
			vgen.vud2 = vgen.sreg & 2;
			break;
		case 4:
			vgen.c_v = bsp.y & 0xfff;
			break;
		case 5:
			vgen.c_h = bsp.y & 0xfff;
			break;
		case 6:
			/* Loading the c_l counter starts
             * the vector counters if MSB is set
             */
			vgen.c_l = bsp.y & 0xfff;
			break;
		}

		vertigo_vgen (&vgen);

		/* Microcode program flow */
		switch (cmc->jcon)
		{
		case S_MSB:
			/* ALU most significant bit */
			jcond = (bsp.f >> 15) & 1;
			break;
		case S_FEQ0:
			/* ALU is 0 */
			jcond = (bsp.f == 0)? 1 : 0;
			break;
		case S_Y10:
			jcond = (bsp.y >> 10) & 1;
			break;
		case S_VFIN:
			jcond = vgen.vfin;
			break;
		case S_FPOS:
			/* FPOS is bit 11 */
			jcond = (bsp.f >> 11) & 1;
			break;
		case S_INTL4:
			jcond = irq4;
			/* Detect idle loop. If the code takes a jump
             on irq4 or !irq4 the destination is a idle loop
             waiting for irq4 state change. We then take a short
             cut and run for just 100 cycles to make sure the
             loop is actually entered.
            */
			if ((cmc->jpos != irq4) && cycles > 100)
			{
				cycles=100;
			}
			break;
		default:
			jcond = 1;
			break;
		}

		if (jcond ^ cmc->jpos)
		{
			/* Except for JBK, address bit 8 isn't changed
               in program flow. */
			switch (cmc->jmp)
			{
			case S_JBK:
				/* JBK is the only jump where MA8 is used */
				vs.pc = cmc->ma;
				break;
			case S_CALL:
				/* call and store return address */
				vs.ret = (vs.pc + 1) & 0xff;
				vs.pc = (vs.pc & 0x100) | (cmc->ma & 0xff);
				break;
			case S_OPT:
				/* OPT is used for microcode jump tables. The first
                   four address bits are defined by bits 12-15
                   of 2901 input (D) */
				vs.pc = (vs.pc & 0x100) | (cmc->ma & 0xf0) | ((bsp.d >> 12) & 0xf);
				break;
			case S_RETURN:
				/* return from call */
				vs.pc = (vs.pc & 0x100) | vs.ret;
				break;
			}
		}
		else
		{
			vs.pc = (vs.pc & 0x100) | ((vs.pc + 1) & 0xff);
		}
	}

	profiler_mark(PROFILER_END);
}
