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