// 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(); }