summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/compuchess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/compuchess.cpp')
-rw-r--r--src/mame/drivers/compuchess.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/src/mame/drivers/compuchess.cpp b/src/mame/drivers/compuchess.cpp
new file mode 100644
index 00000000000..f7a4759aa89
--- /dev/null
+++ b/src/mame/drivers/compuchess.cpp
@@ -0,0 +1,427 @@
+// license:GPL-2.0+
+// copyright-holders:Peter Trauner, Wilbert Pol, hap
+// thanks-to:Sean Riddle
+/******************************************************************************
+
+DCS CompuChess / Novag Chess Champion MK I
+Initial version by PeT mess@utanet.at 2000,2001.
+Driver largely rewritten over the years.
+
+TODO:
+- cncchess sound is wrong, it should be a long dual-tone alarm sound
+- verify CPU speed
+- dump/add cmpchess2
+
+BTANB:
+- digits may flash briefly after entering a command, eg. the "b" or "P" digit
+ after setting board preset, this happens on the real device
+
+*******************************************************************************
+
+Data Cash Systems's CompuChess (1977, one of the first chess computers).
+The game underneath is better known as Novag's MK I, it was an unlicensed clone.
+The ROM is identical. DCS sued JS&A / Novag Industries for copyright infringement
+and somehow didn't manage to win the case.
+
+Unlike CompuChess, MK I was a large success, we can assume that it kickstarted
+Novag's chess computer generation. It was also distributed as "Computer Chess"
+by JS&A, in the same casing as MK I.
+
+To start playing, enter difficulty level (1-6), then when it says "bP", press A
+for new game, B for empty board, or C for continue.
+
+MK I hardware description:
+- An F8 3850 CPU accompanied by a 3853 memory interface, approx 2MHz
+ Variations seen:
+ - MOSTEK MK 3853N 7915 Philippines (static memory interface for F8)
+ - MOSTEK MK 3850N-3 7917 Philipines (Fairchild F8 CPU)
+ - 3850PK F7901 SINGAPORE (Fairchild F8 CPU)
+ - 3853PK F7851 SINGAPORE (static memory interface for F8)
+- 2KB 2316 compatible ROM
+ Variations seen:
+ - Signetics 7916E C48091 82S210-1 COPYRIGHT
+ - RO-3-8316A 8316A-4480 7904 TAIWAN
+- 2 x 2111 256x4 SRAM to provide 256 bytes of RAM
+ Variations seen:
+ - AM9111 BPC / P2111A-4 7851
+- 16 keys placed in a 4 x 4 matrix
+- Power on switch
+- L/S switch. This switch is directly tied to the RESET pin of the F8 CPU.
+ This allows the user to reset the CPU without destroying the RAM contents.
+- A 4 character 11 segment digit display using a 15 pin interface. Of the 15
+ pins 3 pins are not connected, so three segments are never used and this
+ leaves a standard 7 segments display with a dot in the lower right.
+- The digit display is driven by two other components:
+ - SN75492N MALAYSIA 7840B
+ - ULN2033A 7847
+- Hardware addressing is controlled by a HBF4001AE.
+- No speaker.
+
+*******************************************************************************
+
+Conic Computer Chess (aka Master I in Germany) is also based on DCS CompuChess,
+probably the 2nd edition. Conic have done a few changes, not enough to hide that
+they 'borrowed' code from CompuChess. They also added a piezo circuit.
+
+Fairchild 3850PK CPU @ 2MHz (LC circuit), 3853PK
+2KB ROM (3216), 256 bytes RAM (2*2111A)
+555, 4001, 4011 for display blinking and beeper
+
+"bP" buttons are F, G, H (instead of A, B, C)
+
+******************************************************************************/
+
+#include "emu.h"
+#include "cpu/f8/f8.h"
+#include "machine/f3853.h"
+#include "machine/timer.h"
+#include "sound/beep.h"
+#include "video/pwm.h"
+#include "speaker.h"
+
+// internal artwork
+#include "cmpchess.lh" // clickable
+#include "novag_mk1.lh" // clickable
+#include "cncchess.lh" // clickable
+
+
+namespace {
+
+class compuchess_state : public driver_device
+{
+public:
+ compuchess_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_display(*this, "display"),
+ m_beeper_off(*this, "beeper_off"),
+ m_beeper(*this, "beeper"),
+ m_inputs(*this, "IN.%u", 0)
+ { }
+
+ DECLARE_INPUT_CHANGED_MEMBER(reset_switch) { update_reset(newval); }
+
+ // machine drivers
+ void cmpchess(machine_config &config);
+ void mk1(machine_config &config);
+ void cnc(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ // devices/pointers
+ required_device<cpu_device> m_maincpu;
+ required_device<pwm_display_device> m_display;
+ optional_device<timer_device> m_beeper_off;
+ optional_device<beep_device> m_beeper;
+ required_ioport_array<4> m_inputs;
+
+ // address maps
+ void main_map(address_map &map);
+ void main_io(address_map &map);
+ void cnc_io(address_map &map);
+
+ TIMER_DEVICE_CALLBACK_MEMBER(beeper_off) { m_beeper->set_state(0); }
+ TIMER_DEVICE_CALLBACK_MEMBER(blink) { m_blink = !m_blink; update_display(); }
+ void update_display();
+ void update_reset(ioport_value state);
+
+ // I/O handlers
+ DECLARE_READ8_MEMBER(beeper_r);
+ DECLARE_WRITE8_MEMBER(input_w);
+ DECLARE_READ8_MEMBER(input_r);
+ DECLARE_WRITE8_MEMBER(digit_select_w);
+ DECLARE_WRITE8_MEMBER(digit_data_w);
+ DECLARE_READ8_MEMBER(digit_data_r);
+
+ DECLARE_WRITE8_MEMBER(input_digit_select_w) { input_w(space, offset, data); digit_select_w(space, offset, data); }
+ DECLARE_WRITE8_MEMBER(input_digit_data_w) { input_w(space, offset, data); digit_data_w(space, offset, data); }
+
+ u8 m_inp_mux;
+ u8 m_digit_select;
+ u8 m_digit_data;
+ bool m_blink;
+};
+
+void compuchess_state::machine_start()
+{
+ // zerofill
+ m_inp_mux = 0;
+ m_digit_select = 0;
+ m_digit_data = 0;
+ m_blink = false;
+
+ // register for savestates
+ save_item(NAME(m_inp_mux));
+ save_item(NAME(m_digit_select));
+ save_item(NAME(m_digit_data));
+ save_item(NAME(m_blink));
+}
+
+void compuchess_state::machine_reset()
+{
+ update_reset(ioport("RESET")->read());
+}
+
+void compuchess_state::update_reset(ioport_value state)
+{
+ // reset switch is tied to F3850 RESET pin
+ m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
+
+ // clear display
+ if (state)
+ m_display->clear();
+}
+
+
+
+/******************************************************************************
+ Devices, I/O
+******************************************************************************/
+
+READ8_MEMBER(compuchess_state::beeper_r)
+{
+ // cncchess: trigger beeper
+ if (!machine().side_effects_disabled() && m_beeper != nullptr)
+ {
+ m_beeper->set_state(1);
+ m_beeper_off->adjust(attotime::from_msec(50)); // wrong, see TODO
+ }
+
+ return m_maincpu->space(AS_PROGRAM).read_byte(offset);
+}
+
+void compuchess_state::update_display()
+{
+ // display panel goes into automated blink mode if DP segment is held high,
+ // and DP segment itself by default only appears to be active if no other segments are
+ u8 dmask = (m_digit_data == 1) ? 0x80 : 0x7f;
+ u8 bmask = (m_blink && m_digit_data & 1) ? 0 : 0xff;
+ u8 bstate = m_digit_data & bmask & 1; // DP state for ignoring dmask
+
+ u8 digit_data = bitswap<8>(m_digit_data,0,2,1,3,4,5,6,7) & dmask & bmask;
+ m_display->matrix(m_digit_select, bstate << 8 | digit_data);
+}
+
+WRITE8_MEMBER(compuchess_state::digit_data_w)
+{
+ // digit segment data
+ m_digit_data = data;
+ update_display();
+}
+
+READ8_MEMBER(compuchess_state::digit_data_r)
+{
+ return m_digit_data;
+}
+
+WRITE8_MEMBER(compuchess_state::digit_select_w)
+{
+ // d0-d3: digit select (active low)
+ m_digit_select = ~data & 0xf;
+ update_display();
+}
+
+WRITE8_MEMBER(compuchess_state::input_w)
+{
+ // input matrix is shared with either digit_data_w, or digit_select_w
+ m_inp_mux = data;
+}
+
+READ8_MEMBER(compuchess_state::input_r)
+{
+ u8 data = m_inp_mux;
+
+ // d0-d3: multiplexed inputs from d4-d7
+ for (int i = 0; i < 4; i++)
+ if (m_inp_mux & m_inputs[i]->read() << 4)
+ data |= 1 << i;
+
+ // d4-d7: multiplexed inputs from d0-d3
+ for (int i = 0; i < 4; i++)
+ if (BIT(m_inp_mux, i))
+ data |= m_inputs[i]->read() << 4;
+
+ return data;
+}
+
+
+
+/******************************************************************************
+ Address Maps
+******************************************************************************/
+
+void compuchess_state::main_map(address_map &map)
+{
+ map(0x0000, 0x07ff).rom();
+ map(0x1800, 0x18ff).ram();
+ map(0x8000, 0xffff).r(FUNC(compuchess_state::beeper_r));
+}
+
+void compuchess_state::main_io(address_map &map)
+{
+ map(0x00, 0x00).rw(FUNC(compuchess_state::input_r), FUNC(compuchess_state::input_digit_data_w));
+ map(0x01, 0x01).w(FUNC(compuchess_state::digit_select_w));
+ map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
+}
+
+void compuchess_state::cnc_io(address_map &map)
+{
+ map(0x00, 0x00).rw(FUNC(compuchess_state::digit_data_r), FUNC(compuchess_state::digit_data_w));
+ map(0x01, 0x01).rw(FUNC(compuchess_state::input_r), FUNC(compuchess_state::input_digit_select_w));
+ map(0x0c, 0x0f).rw("smi", FUNC(f3853_device::read), FUNC(f3853_device::write));
+}
+
+
+
+/******************************************************************************
+ Input Ports
+******************************************************************************/
+
+static INPUT_PORTS_START( cmpchess )
+ PORT_START("IN.0")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("D / Play")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / White Bishop")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / White Queen")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / White King")
+
+ PORT_START("IN.1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_M) PORT_NAME("H / md") // more data
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_NAME("G / White Pawn")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / White Rook")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / White Knight")
+
+ PORT_START("IN.2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / fp") // find piece(position)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Black Bishop")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Black Queen")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Black King")
+
+ PORT_START("IN.3")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8 / ep") // enter piece(position)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7 / Black Pawn")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Black Rook")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Black Knight")
+
+ PORT_START("RESET")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, compuchess_state, reset_switch, nullptr) PORT_NAME("Reset Switch") // L.S. switch on the MK I
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( cncchess )
+ PORT_START("IN.0")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4 / Black Bishop")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3 / Black Knight")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2 / Black Rook")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1 / Black Pawn")
+
+ PORT_START("IN.1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_I) PORT_NAME("8 / IP") // insert piece (same as ep)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_S) PORT_NAME("7 / SP") // search piece (same as fp)
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6 / Black King")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5 / Black Queen")
+
+ PORT_START("IN.2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_D) PORT_NAME("D / White Bishop")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_C) PORT_NAME("C / White Knight")
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("B / White Rook")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_A) PORT_NAME("A / White Pawn")
+
+ PORT_START("IN.3")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_H) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("H / GO")
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_M) PORT_NAME("G / MD") // more data
+ PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F) PORT_NAME("F / White King")
+ PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("E / White Queen")
+
+ PORT_START("RESET")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, compuchess_state, reset_switch, nullptr) PORT_NAME("Reset")
+INPUT_PORTS_END
+
+
+
+/******************************************************************************
+ Machine Configs
+******************************************************************************/
+
+void compuchess_state::cmpchess(machine_config &config)
+{
+ /* basic machine hardware */
+ F8(config, m_maincpu, 3.579545_MHz_XTAL/2); // Fairchild 3850PK
+ m_maincpu->set_addrmap(AS_PROGRAM, &compuchess_state::main_map);
+ m_maincpu->set_addrmap(AS_IO, &compuchess_state::main_io);
+ m_maincpu->set_irq_acknowledge_callback("smi", FUNC(f3853_device::int_acknowledge));
+
+ f3853_device &smi(F3853(config, "smi", 3.579545_MHz_XTAL/2));
+ smi.int_req_callback().set_inputline("maincpu", F8_INPUT_LINE_INT_REQ);
+
+ /* video hardware */
+ PWM_DISPLAY(config, m_display).set_size(4, 8+1);
+ m_display->set_segmask(0xf, 0xff);
+ config.set_default_layout(layout_cmpchess);
+
+ TIMER(config, "blink_display").configure_periodic(FUNC(compuchess_state::blink), attotime::from_msec(250)); // approximation
+}
+
+void compuchess_state::mk1(machine_config &config)
+{
+ cmpchess(config);
+
+ /* basic machine hardware */
+ m_maincpu->set_clock(2000000); // it's a bit faster than cmpchess
+ subdevice<f3853_device>("smi")->set_clock(2000000);
+
+ config.set_default_layout(layout_novag_mk1);
+}
+
+void compuchess_state::cnc(machine_config &config)
+{
+ mk1(config);
+
+ /* basic machine hardware */
+ m_maincpu->set_addrmap(AS_IO, &compuchess_state::cnc_io);
+
+ config.set_default_layout(layout_cncchess);
+
+ /* sound hardware */
+ SPEAKER(config, "speaker").front_center();
+ BEEP(config, m_beeper, 2000); // wrong, see TODO
+ m_beeper->add_route(ALL_OUTPUTS, "speaker", 0.25);
+ TIMER(config, "beeper_off").configure_generic(FUNC(compuchess_state::beeper_off));
+}
+
+
+
+/******************************************************************************
+ ROM Definitions
+******************************************************************************/
+
+ROM_START( cmpchess )
+ ROM_REGION( 0x0800, "maincpu", 0 )
+ ROM_LOAD("32014-4950", 0x0000, 0x0800, CRC(278f7bf3) SHA1(b384c95ba691d52dfdddd35987a71e9746a46170) )
+ROM_END
+
+ROM_START( ccmk1 )
+ ROM_REGION( 0x0800, "maincpu", 0 )
+ ROM_LOAD("82c210-1", 0x0000, 0x0800, CRC(278f7bf3) SHA1(b384c95ba691d52dfdddd35987a71e9746a46170) )
+ROM_END
+
+
+ROM_START( cncchess )
+ ROM_REGION( 0x0800, "maincpu", 0 )
+ ROM_LOAD("c48170.u1", 0x0000, 0x0800, CRC(5beace32) SHA1(9df924037614831a86b73eb3a16bbc80c63257a2) ) // 2316B
+ROM_END
+
+} // anonymous namespace
+
+
+
+/******************************************************************************
+ Drivers
+******************************************************************************/
+
+// YEAR NAME PARENT CMP MACHINE INPUT STATE INIT COMPANY, FULLNAME, FLAGS
+CONS( 1977, cmpchess, 0, 0, cmpchess, cmpchess, compuchess_state, empty_init, "Data Cash Systems", "CompuChess", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
+CONS( 1978, ccmk1, cmpchess, 0, mk1, cmpchess, compuchess_state, empty_init, "Novag", "Chess Champion: MK I", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
+
+CONS( 1979, cncchess, 0, 0, cnc, cncchess, compuchess_state, empty_init, "Conic", "Computer Chess (Conic)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )