/*************************************************************************
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 "emu.h"
#include "profiler.h"
#include "video/vector.h"
#include "includes/vertigo.h"
/*************************************
*
* Macros and enums
*
*************************************/
#define V_ADDPOINT(m,h,v,c,i) \
vector_add_point (m, ((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
};
/*************************************
*
* Vector processor initialization
*
*************************************/
void vertigo_vproc_init(running_machine &machine)
{
vertigo_state *state = machine.driver_data<vertigo_state>();
state_save_register_item_array(machine, "vector_proc", NULL, 0, state->m_vs.sram);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vs.ramlatch);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vs.rom_adr);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vs.pc);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vs.ret);
state_save_register_item_array(machine, "vector_proc", NULL, 0, state->m_bsp.ram);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_bsp.d);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_bsp.q);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_bsp.f);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_bsp.y);
state->m_vgen.set_machine(machine);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.sreg);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.l1);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.l2);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.c_v);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.c_h);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.c_l);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.adder_s);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.adder_a);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.color);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.intensity);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.brez);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.vfin);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.hud1);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.hud2);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.vud1);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.vud2);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.hc1);
state_save_register_item(machine, "vector_proc", NULL, 0, state->m_vgen.ven);
}
void vertigo_vproc_reset(running_machine &machine)
{
vertigo_state *state = machine.driver_data<vertigo_state>();
int i;
UINT64 *mcode;
state->m_vectorrom = (UINT16 *)machine.region("user1")->base();
mcode = (UINT64 *)machine.region("proms")->base();
/* Decode microcode */
for (i = 0; i < MC_LENGTH; i++)
{
state->m_mc[i].x = (mcode[i] >> 44) & 0x3f;
state->m_mc[i].a = (mcode[i] >> 40) & 0xf;
state->m_mc[i].b = (mcode[i] >> 36) & 0xf;
state->m_mc[i].inst = (mcode[i] >> 27) & 077;
state->m_mc[i].dest = (mcode[i] >> 33) & 07;
state->m_mc[i].cn = (mcode[i] >> 26) & 0x1;
state->m_mc[i].mreq = (mcode[i] >> 25) & 0x1;
state->m_mc[i].rwrite = (mcode[i] >> 23) & 0x1;
state->m_mc[i].rsel = state->m_mc[i].rwrite & ((mcode[i] >> 24) & 0x1);
state->m_mc[i].of = (mcode[i] >> 20) & 0x7;
state->m_mc[i].iif = (mcode[i] >> 18) & 0x3;
state->m_mc[i].oa = (mcode[i] >> 16) & 0x3;
state->m_mc[i].jpos = (mcode[i] >> 14) & 0x1;
state->m_mc[i].jmp = (mcode[i] >> 12) & 0x3;
state->m_mc[i].jcon = (mcode[i] >> 9) & 0x7;
state->m_mc[i].ma = mcode[i] & 0x1ff;
}
memset(&state->m_vs, 0, sizeof(state->m_vs));
memset(&state->m_bsp, 0, sizeof(state->m_bsp));
memset(&state->m_vgen, 0, sizeof(state->m_vgen));
}
/********************************************
*
* 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->machine(), vg->c_h, vg->c_v, 0, 0);
else
V_ADDPOINT (vg->machine(), vg->c_h, vg->c_v, vg->color, vg->intensity);
vg->ven = vg->brez;
}
}
/*************************************
*
* Vector processor
*
*************************************/
void vertigo_vproc(running_machine &machine, int cycles, int irq4)
{
vertigo_state *state = machine.driver_data<vertigo_state>();
int jcond;
microcode *cmc;
if (irq4) vector_clear_list();
g_profiler.start(PROFILER_USER1);
while (cycles--)
{
/* Microcode at current PC */
cmc = &state->m_mc[state->m_vs.pc];
/* Load data */
if (cmc->iif == S_RAMDE)
{
state->m_bsp.d = state->m_vs.ramlatch;
}
else if (cmc->iif == S_ROMDE)
{
if (state->m_vs.rom_adr < 0x2000)
{
state->m_bsp.d = state->m_vectorram[state->m_vs.rom_adr & 0xfff];
}
else
{
state->m_bsp.d = state->m_vectorrom[state->m_vs.rom_adr & 0x7fff];
}
}
/* SRAM selected ? */
if (cmc->rsel == 0)
{
if (cmc->rwrite)
{
state->m_bsp.d = state->m_vs.sram[cmc->x];
}
else
{
/* Data can be transferred between vector ROM/RAM
and SRAM without going through the 2901 */
state->m_vs.sram[cmc->x] = state->m_bsp.d;
}
}
am2901x4 (&state->m_bsp, cmc);
/* Store data */
switch (cmc->oa)
{
case S_RAMD:
state->m_vs.ramlatch = state->m_bsp.y;
if (cmc->iif==S_RAMDE && (cmc->rsel == 0) && (cmc->rwrite == 0))
state->m_vs.sram[cmc->x] = state->m_vs.ramlatch;
break;
case S_ROMA:
state->m_vs.rom_adr = state->m_bsp.y;
break;
case S_SREG:
/* FPOS is shifted into sreg */
state->m_vgen.sreg = (state->m_vgen.sreg >> 1) | ((state->m_bsp.f >> 9) & 4);
break;
default:
break;
}
/* Vector generator setup */
switch (cmc->of)
{
case 0:
state->m_vgen.color = state->m_bsp.y & 0xfff;
break;
case 1:
state->m_vgen.intensity = state->m_bsp.y & 0xff;
break;
case 2:
state->m_vgen.l1 = state->m_bsp.y & 0xfff;
state->m_vgen.adder_s = 0;
state->m_vgen.adder_a = state->m_vgen.l2;
state->m_vgen.hud1 = state->m_vgen.sreg & 1;
state->m_vgen.vud1 = state->m_vgen.sreg & 2;
state->m_vgen.hc1 = state->m_vgen.sreg & 4;
state->m_vgen.brez = 1;
break;
case 3:
state->m_vgen.l2 = state->m_bsp.y & 0xfff;
state->m_vgen.adder_s = (state->m_vgen.adder_s + state->m_vgen.adder_a) & 0xfff;
if (state->m_vgen.adder_s & 0x800)
state->m_vgen.adder_a = state->m_vgen.l1;
else
state->m_vgen.adder_a = state->m_vgen.l2;
state->m_vgen.hud2 = state->m_vgen.sreg & 1;
state->m_vgen.vud2 = state->m_vgen.sreg & 2;
break;
case 4:
state->m_vgen.c_v = state->m_bsp.y & 0xfff;
break;
case 5:
state->m_vgen.c_h = state->m_bsp.y & 0xfff;
break;
case 6:
/* Loading the c_l counter starts
* the vector counters if MSB is set
*/
state->m_vgen.c_l = state->m_bsp.y & 0xfff;
break;
}
vertigo_vgen (&state->m_vgen);
/* Microcode program flow */
switch (cmc->jcon)
{
case S_MSB:
/* ALU most significant bit */
jcond = (state->m_bsp.f >> 15) & 1;
break;
case S_FEQ0:
/* ALU is 0 */
jcond = (state->m_bsp.f == 0)? 1 : 0;
break;
case S_Y10:
jcond = (state->m_bsp.y >> 10) & 1;
break;
case S_VFIN:
jcond = state->m_vgen.vfin;
break;
case S_FPOS:
/* FPOS is bit 11 */
jcond = (state->m_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 */
state->m_vs.pc = cmc->ma;
break;
case S_CALL:
/* call and store return address */
state->m_vs.ret = (state->m_vs.pc + 1) & 0xff;
state->m_vs.pc = (state->m_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) */
state->m_vs.pc = (state->m_vs.pc & 0x100) | (cmc->ma & 0xf0) | ((state->m_bsp.d >> 12) & 0xf);
break;
case S_RETURN:
/* return from call */
state->m_vs.pc = (state->m_vs.pc & 0x100) | state->m_vs.ret;
break;
}
}
else
{
state->m_vs.pc = (state->m_vs.pc & 0x100) | ((state->m_vs.pc + 1) & 0xff);
}
}
g_profiler.stop();
}