// license:BSD-3-Clause // copyright-holders:hap /****************************************************************************** Novag generic 6502 based chess computer driver TODO: - move other Novag sets here when applicable ------------------------------------------------------------------------------- Super Constellation Chess Computer (model 844): - UMC UM6502C @ 4 MHz (8MHz XTAL), 600Hz IRQ(source unknown?) - 2*2KB RAM TC5516APL-2 battery-backed, 2*32KB ROM custom label - TTL, buzzer, 24 LEDs, 8*8 chessboard buttons - external ports for clock and printer, not emulated here ******************************************************************************/ #include "emu.h" #include "cpu/m6502/m6502.h" #include "machine/nvram.h" #include "sound/beep.h" // internal artwork #include "supercon.lh" // clickable class novag6502_state : public driver_device { public: novag6502_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_beeper(*this, "beeper"), m_inp_matrix(*this, "IN"), m_display_wait(33), m_display_maxy(1), m_display_maxx(0) { } // devices/pointers required_device m_maincpu; optional_device m_beeper; optional_ioport_array<8> m_inp_matrix; DECLARE_WRITE8_MEMBER(supercon_1e_w); DECLARE_WRITE8_MEMBER(supercon_1f_w); DECLARE_READ8_MEMBER(supercon_1e_r); DECLARE_READ8_MEMBER(supercon_1f_r); // misc common UINT16 m_inp_mux; // multiplexed keypad mask UINT16 m_led_select; UINT16 m_led_data; UINT16 read_inputs(int columns); // display common int m_display_wait; // led/lamp off-delay in microseconds (default 33ms) int m_display_maxy; // display matrix number of rows int m_display_maxx; // display matrix number of columns (max 31 for now) UINT32 m_display_state[0x20]; // display matrix rows data (last bit is used for always-on) UINT16 m_display_segmask[0x20]; // if not 0, display matrix row is a digit, mask indicates connected segments UINT32 m_display_cache[0x20]; // (internal use) UINT8 m_display_decay[0x20][0x20]; // (internal use) TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); void display_update(); void set_display_size(int maxx, int maxy); void set_display_segmask(UINT32 digits, UINT32 mask); void display_matrix(int maxx, int maxy, UINT32 setx, UINT32 sety, bool update = true); protected: virtual void machine_start() override; virtual void machine_reset() override; }; // machine start/reset void novag6502_state::machine_start() { // zerofill memset(m_display_state, 0, sizeof(m_display_state)); memset(m_display_cache, ~0, sizeof(m_display_cache)); memset(m_display_decay, 0, sizeof(m_display_decay)); memset(m_display_segmask, 0, sizeof(m_display_segmask)); m_inp_mux = 0; m_led_select = 0; m_led_data = 0; // register for savestates save_item(NAME(m_display_maxy)); save_item(NAME(m_display_maxx)); save_item(NAME(m_display_wait)); save_item(NAME(m_display_state)); /* save_item(NAME(m_display_cache)); */ // don't save! save_item(NAME(m_display_decay)); save_item(NAME(m_display_segmask)); save_item(NAME(m_inp_mux)); save_item(NAME(m_led_select)); save_item(NAME(m_led_data)); } void novag6502_state::machine_reset() { } /*************************************************************************** Helper Functions ***************************************************************************/ // The device may strobe the outputs very fast, it is unnoticeable to the user. // To prevent flickering here, we need to simulate a decay. void novag6502_state::display_update() { UINT32 active_state[0x20]; for (int y = 0; y < m_display_maxy; y++) { active_state[y] = 0; for (int x = 0; x <= m_display_maxx; x++) { // turn on powered segments if (m_display_state[y] >> x & 1) m_display_decay[y][x] = m_display_wait; // determine active state UINT32 ds = (m_display_decay[y][x] != 0) ? 1 : 0; active_state[y] |= (ds << x); } } // on difference, send to output for (int y = 0; y < m_display_maxy; y++) if (m_display_cache[y] != active_state[y]) { if (m_display_segmask[y] != 0) output().set_digit_value(y, active_state[y] & m_display_segmask[y]); const int mul = (m_display_maxx <= 10) ? 10 : 100; for (int x = 0; x <= m_display_maxx; x++) { int state = active_state[y] >> x & 1; char buf1[0x10]; // lampyx char buf2[0x10]; // y.x if (x == m_display_maxx) { // always-on if selected sprintf(buf1, "lamp%da", y); sprintf(buf2, "%d.a", y); } else { sprintf(buf1, "lamp%d", y * mul + x); sprintf(buf2, "%d.%d", y, x); } output().set_value(buf1, state); output().set_value(buf2, state); } } memcpy(m_display_cache, active_state, sizeof(m_display_cache)); } TIMER_DEVICE_CALLBACK_MEMBER(novag6502_state::display_decay_tick) { // slowly turn off unpowered segments for (int y = 0; y < m_display_maxy; y++) for (int x = 0; x <= m_display_maxx; x++) if (m_display_decay[y][x] != 0) m_display_decay[y][x]--; display_update(); } void novag6502_state::set_display_size(int maxx, int maxy) { m_display_maxx = maxx; m_display_maxy = maxy; } void novag6502_state::set_display_segmask(UINT32 digits, UINT32 mask) { // set a segment mask per selected digit, but leave unselected ones alone for (int i = 0; i < 0x20; i++) { if (digits & 1) m_display_segmask[i] = mask; digits >>= 1; } } void novag6502_state::display_matrix(int maxx, int maxy, UINT32 setx, UINT32 sety, bool update) { set_display_size(maxx, maxy); // update current state UINT32 mask = (1 << maxx) - 1; for (int y = 0; y < maxy; y++) m_display_state[y] = (sety >> y & 1) ? ((setx & mask) | (1 << maxx)) : 0; if (update) display_update(); } // generic input handlers UINT16 novag6502_state::read_inputs(int columns) { UINT16 ret = 0; // read selected input rows for (int i = 0; i < columns; i++) if (m_inp_mux >> i & 1) ret |= m_inp_matrix[i]->read(); return ret; } // Devices, I/O /****************************************************************************** Super Constellation ******************************************************************************/ // TTL WRITE8_MEMBER(novag6502_state::supercon_1e_w) { // d0-d7: input mux, led data m_inp_mux = m_led_data = data; display_matrix(8, 3, m_led_data, m_led_select); } WRITE8_MEMBER(novag6502_state::supercon_1f_w) { // d0-d3: ? // d4-d6: select led row m_led_select = data >> 4 & 7; display_matrix(8, 3, m_led_data, m_led_select); // d7: enable beeper m_beeper->set_state(data >> 7 & 1); } READ8_MEMBER(novag6502_state::supercon_1e_r) { // d0-d5: ? // d6,d7: multiplexed inputs (side panel) return (read_inputs(8) >> 2 & 0xc0) ^ 0xff; } READ8_MEMBER(novag6502_state::supercon_1f_r) { // d0-d7: multiplexed inputs (chessboard squares) return ~read_inputs(8) & 0xff; } /****************************************************************************** Address Maps ******************************************************************************/ static ADDRESS_MAP_START( supercon_mem, AS_PROGRAM, 8, novag6502_state ) AM_RANGE(0x0000, 0x0fff) AM_RAM AM_SHARE("nvram") AM_RANGE(0x1c00, 0x1c00) AM_WRITENOP // printer/clock? AM_RANGE(0x1d00, 0x1d00) AM_WRITENOP // printer/clock? AM_RANGE(0x1e00, 0x1e00) AM_READWRITE(supercon_1e_r, supercon_1e_w) AM_RANGE(0x1f00, 0x1f00) AM_READWRITE(supercon_1f_r, supercon_1f_w) AM_RANGE(0x2000, 0xffff) AM_ROM ADDRESS_MAP_END /****************************************************************************** Input Ports ******************************************************************************/ static INPUT_PORTS_START( supercon ) PORT_START("IN.0") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h8") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g8") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f8") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e8") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d8") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c8") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b8") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a8") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_I) PORT_NAME("New Game") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_NAME("King / Multi Move / Player/Player") PORT_START("IN.1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h7") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g7") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f7") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e7") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d7") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c7") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b7") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a7") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_U) PORT_NAME("Verify / Set Up") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_NAME("Queen / Best Move/Random / Training Level") PORT_START("IN.2") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h6") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g6") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f6") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e6") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d6") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c6") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b6") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a6") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Change Color") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_NAME("Bishop / Sound / Depth Search") PORT_START("IN.3") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h5") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g5") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f5") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e5") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d5") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c5") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b5") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a5") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Clear Board") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_NAME("Knight / Solve Mate") PORT_START("IN.4") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h4") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g4") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f4") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e4") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d4") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c4") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b4") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a4") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R) PORT_NAME("Print Moves") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_NAME("Rook / Print Board") PORT_START("IN.5") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h3") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g3") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f3") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e3") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d3") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c3") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b3") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a3") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Form Size") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_NAME("Pion / Print List / Acc. Time") PORT_START("IN.6") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h2") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g2") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f2") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e2") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d2") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c2") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b2") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a2") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W) PORT_NAME("Hint") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_NAME("Set Level") PORT_START("IN.7") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square h1") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square g1") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square f1") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square e1") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square d1") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square c1") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square b1") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Square a1") PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q) PORT_NAME("Go") PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_NAME("Take Back") INPUT_PORTS_END /****************************************************************************** Machine Drivers ******************************************************************************/ static MACHINE_CONFIG_START( supercon, novag6502_state ) /* basic machine hardware */ MCFG_CPU_ADD("maincpu", M6502, XTAL_8MHz/2) MCFG_CPU_PERIODIC_INT_DRIVER(novag6502_state, irq0_line_hold, 600) // guessed MCFG_CPU_PROGRAM_MAP(supercon_mem) MCFG_NVRAM_ADD_1FILL("nvram") MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", novag6502_state, display_decay_tick, attotime::from_msec(1)) MCFG_DEFAULT_LAYOUT(layout_supercon) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_ADD("beeper", BEEP, 1200) // guessed MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) MACHINE_CONFIG_END /****************************************************************************** ROM Definitions ******************************************************************************/ ROM_START( supercon ) ROM_REGION( 0x10000, "maincpu", 0 ) ROM_LOAD("novag_8441", 0x0000, 0x8000, CRC(b853cf6e) SHA1(1a759072a5023b92c07f1fac01b7a21f7b5b45d0) ) // label obscured by Q.C. sticker ROM_LOAD("novag_8442", 0x8000, 0x8000, CRC(c8f82331) SHA1(f7fd039f9a3344db9749931490ded9e9e309cfbe) ) ROM_END /****************************************************************************** Drivers ******************************************************************************/ /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY, FULLNAME, FLAGS */ CONS( 1984, supercon, 0, 0, supercon, supercon, driver_device, 0, "Novag", "Super Constellation", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )