summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author hap <happppp@users.noreply.github.com>2026-04-11 21:25:03 +0200
committer hap <happppp@users.noreply.github.com>2026-04-11 21:25:22 +0200
commit476b92f075f06e99d5c7213848e14b67b16b1774 (patch)
treef1f51a42025cf64743d57446944ca8afcf2aa461
parent8d4d6744c1f81677070b0d08c611f2812248cd2f (diff)
New systems marked not working
------------------------------ Grand Master [hap, Berger]
-rw-r--r--src/devices/machine/7474.h2
-rw-r--r--src/mame/excalibur/mirage.cpp2
-rw-r--r--src/mame/fidelity/phantom.cpp2
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/miltonbradley/grandmaster.cpp551
-rw-r--r--src/mame/miltonbradley/milton.cpp8
-rw-r--r--src/mame/novag/sdiamond.cpp3
7 files changed, 563 insertions, 8 deletions
diff --git a/src/devices/machine/7474.h b/src/devices/machine/7474.h
index 42d6c74b862..09fc7b0fad3 100644
--- a/src/devices/machine/7474.h
+++ b/src/devices/machine/7474.h
@@ -55,7 +55,7 @@ class ttl7474_device : public device_t
{
public:
// construction/destruction
- ttl7474_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ ttl7474_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
// static configuration helpers
auto output_cb() { return m_output_func.bind(); }
diff --git a/src/mame/excalibur/mirage.cpp b/src/mame/excalibur/mirage.cpp
index 23831c1eb75..529a97a837d 100644
--- a/src/mame/excalibur/mirage.cpp
+++ b/src/mame/excalibur/mirage.cpp
@@ -6,7 +6,7 @@ Excalibur Mirage (model 702E)
It's Excalibur's first chess computer, and also Ron Nelson's official return to
chess programming. The x/y motorized magnet is similar to the one used in
-Fidelity Phantom (and Milton Bradley Grand·Master before that).
+Fidelity Phantom (and Milton Bradley Grand Master before that).
Before moving a piece, wait until the computer is done with its own move. After
capturing a piece, select the captured piece from the MAME sensorboard spawn
diff --git a/src/mame/fidelity/phantom.cpp b/src/mame/fidelity/phantom.cpp
index 3f9b700b049..2967d94f3db 100644
--- a/src/mame/fidelity/phantom.cpp
+++ b/src/mame/fidelity/phantom.cpp
@@ -5,7 +5,7 @@
Fidelity Phantom (model 6100)
-Fidelity licensed (or perhaps bought) the design of Milton Bradley's Grand·Master
+Fidelity licensed (or perhaps bought) the design of Milton Bradley's Grand Master
motorized chessboard and released their own version. It has a small LCD panel added,
the rest looks nearly the same from the outside. After Fidelity was taken over by H+G,
it was rereleased in 1990 as the Mephisto Phantom. This is assumed to be identical.
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 491086a0290..38db11c38f6 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -29148,6 +29148,9 @@ wmg
@source:midway/zwackery.cpp
zwackery
+@source:miltonbradley/grandmaster.cpp
+grandmas
+
@source:miltonbradley/microvision.cpp
microvsn
diff --git a/src/mame/miltonbradley/grandmaster.cpp b/src/mame/miltonbradley/grandmaster.cpp
new file mode 100644
index 00000000000..c3941a3b3b3
--- /dev/null
+++ b/src/mame/miltonbradley/grandmaster.cpp
@@ -0,0 +1,551 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+// thanks-to:Berger
+/*******************************************************************************
+
+Milton Bradley (Electronic) Grand Master (stylized as Grand·Master) (model 4243)
+aka Phantom Chess Computer in the UK, and Milton in the rest of Europe
+
+TODO:
+- WIP
+- move chessboard to a device (currently copied from emirage)
+
+*******************************************************************************/
+
+#include "emu.h"
+
+#include "cpu/m6502/m6502.h"
+#include "machine/7474.h"
+#include "machine/clock.h"
+#include "machine/input_merger.h"
+#include "machine/sensorboard.h"
+#include "sound/dac.h"
+#include "video/pwm.h"
+
+#include "speaker.h"
+
+// internal artwork
+//#include "grandmaster.lh"
+
+
+namespace {
+
+class grandmas_state : public driver_device
+{
+public:
+ grandmas_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_irq_ff(*this, "irq_ff%u", 0),
+ m_board(*this, "board"),
+ m_display(*this, "display"),
+ m_dac(*this, "dac"),
+ m_inputs(*this, "IN.0"),
+ m_piece_hand(*this, "cpu_hand"),
+ m_out_motor(*this, "motor%u", 0U),
+ m_out_pos(*this, "pos_%c", unsigned('x'))
+ { }
+
+ void grandmas(machine_config &config);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void machine_reset() override ATTR_COLD;
+
+private:
+ // devices/pointers
+ required_device<cpu_device> m_maincpu;
+ required_device_array<ttl7474_device, 3> m_irq_ff;
+ required_device<sensorboard_device> m_board;
+ required_device<pwm_display_device> m_display;
+ required_device<dac_1bit_device> m_dac;
+ required_ioport m_inputs;
+ output_finder<> m_piece_hand;
+ output_finder<5> m_out_motor;
+ output_finder<2> m_out_pos;
+
+ u8 m_inp_mux = 0;
+
+ u8 m_magnet = 0;
+ u8 m_pieces_map[0x40][0x40] = { };
+
+ u8 m_motor_control[2] = { };
+ u32 m_motor_max[2] = { };
+ u32 m_motor_pos[2] = { };
+ s32 m_motor_drift[2] = { };
+ u8 m_motor_quad[2] = { };
+
+ attotime m_motor_period;
+ attotime m_motor_remain[2];
+ emu_timer *m_motor_timer[2];
+
+ void init_board(u8 data);
+ void clear_board(u8 data);
+ void init_motors();
+
+ void get_scaled_pos(double *x, double *y);
+ void output_magnet_pos();
+ void realign_magnet_pos();
+ void update_piece(u8 magnet);
+
+ TIMER_CALLBACK_MEMBER(motor_count);
+ void motor_control(int m, u8 control);
+
+ void main_map(address_map &map) ATTR_COLD;
+
+ u8 irq_clear_r(offs_t offset);
+ void irq_clear_w(offs_t offset, u8 data = 0);
+
+ u8 status_r();
+ void control_w(u8 data);
+ void leds_w(u8 data);
+ u8 input_r(offs_t offset);
+};
+
+
+
+/*******************************************************************************
+ Initialization
+*******************************************************************************/
+
+void grandmas_state::machine_start()
+{
+ init_motors();
+
+ // resolve outputs
+ m_piece_hand.resolve();
+ m_out_motor.resolve();
+ m_out_pos.resolve();
+
+ // register for savestates
+ save_item(NAME(m_inp_mux));
+
+ save_item(NAME(m_magnet));
+ save_item(NAME(m_pieces_map));
+ save_item(NAME(m_motor_control));
+ save_item(NAME(m_motor_pos));
+ save_item(NAME(m_motor_drift));
+ save_item(NAME(m_motor_quad));
+ save_item(NAME(m_motor_remain));
+}
+
+void grandmas_state::machine_reset()
+{
+ // make sure 7474 is not in an indeterminate state
+ for (int i = 0; i < 3; i++)
+ irq_clear_w(i);
+
+ memset(m_motor_drift, 0, sizeof(m_motor_drift));
+ output_magnet_pos();
+}
+
+void grandmas_state::init_board(u8 data)
+{
+ m_board->preset_chess(data);
+
+ // reposition pieces if board will be rotated
+ if (data & 2)
+ {
+ for (int y = 0; y < 8; y++)
+ for (int x = 7; x >= 0; x--)
+ {
+ m_board->write_piece(x + 4, y, m_board->read_piece(x, y));
+ m_board->write_piece(x, y, 0);
+ }
+ }
+}
+
+void grandmas_state::clear_board(u8 data)
+{
+ memset(m_pieces_map, 0, sizeof(m_pieces_map));
+ m_piece_hand = 0;
+ m_board->clear_board(data);
+}
+
+
+
+/*******************************************************************************
+ Motor Sim
+*******************************************************************************/
+
+void grandmas_state::init_motors()
+{
+ m_motor_period = attotime::from_usec(2500);
+
+ for (int i = 0; i < 2; i++)
+ {
+ m_motor_timer[i] = timer_alloc(FUNC(grandmas_state::motor_count), this);
+ m_motor_remain[i] = m_motor_period / 2;
+ }
+
+ m_motor_max[1] = 575 - 1;
+ m_motor_pos[1] = 522;
+
+ m_motor_max[0] = 750 - 1;
+ m_motor_pos[0] = 139;
+}
+
+void grandmas_state::get_scaled_pos(double *x, double *y)
+{
+ // 64 counts per square
+ *x = std::clamp(double(int(m_motor_pos[0]) - 10) / (64.0 / 4.0) + 2.0, 0.0, 48.0);
+ *y = std::clamp(double(int(m_motor_pos[1]) - 75) / (64.0 / 4.0) + 2.0, 0.0, 32.0);
+}
+
+void grandmas_state::output_magnet_pos()
+{
+ double x, y;
+ get_scaled_pos(&x, &y);
+
+ // put active state on x bit 11
+ const int active = m_magnet ? 0x800 : 0;
+ m_out_pos[0] = int(x * 25.0 + 0.5) | active;
+ m_out_pos[1] = int(y * 25.0 + 0.5);
+
+ popmessage("%d %d",m_motor_pos[0], m_motor_pos[1]);
+}
+
+void grandmas_state::realign_magnet_pos()
+{
+ return;
+
+ // compensate for gradual drift, see BTANB
+ for (int i = 0; i < 2; )
+ {
+ double pos[2];
+ get_scaled_pos(&pos[0], &pos[1]);
+
+ const double limit = 1.0 / 8.0; // 4 counts
+ int inc = 0;
+
+ if ((round(pos[i]) - pos[i]) > limit && m_motor_pos[i] < m_motor_max[i] - 4)
+ inc = 1;
+ else if ((round(pos[i]) - pos[i]) < -limit && m_motor_pos[i] > 4)
+ inc = -1;
+ else
+ i++;
+
+ if (inc != 0)
+ {
+ m_motor_pos[i] += inc * 4;
+ m_motor_drift[i] -= inc;
+
+ logerror("motor %c drift error (%d total)\n", 'X' + i, m_motor_drift[i]);
+ }
+ }
+}
+
+void grandmas_state::update_piece(u8 magnet)
+{
+ if (magnet == m_magnet)
+ return;
+
+ realign_magnet_pos();
+
+ m_magnet = magnet;
+ output_magnet_pos();
+
+ double dx, dy;
+ get_scaled_pos(&dx, &dy);
+
+ int mx = dx + 0.5;
+ int my = dy + 0.5;
+
+ // convert motors position into board coordinates
+ int x = mx / 4 - 2;
+ int y = 7 - (my / 4);
+
+ if (x < 0)
+ x += 12;
+
+ const bool valid_pos = (mx & 3) == 2 && (my & 3) == 2;
+
+ // sensorboard handling is almost the same as fidelity/phantom.cpp
+ if (magnet)
+ {
+ if (valid_pos)
+ {
+ // pick up piece, unless it was picked up by the user
+ const int pos = (y << 4 & 0xf0) | (x & 0x0f);
+ if (pos != m_board->get_handpos())
+ {
+ m_piece_hand = m_board->read_piece(x, y);
+
+ if (m_piece_hand != 0)
+ {
+ m_board->write_piece(x, y, 0);
+ m_board->refresh();
+ }
+ }
+ }
+ else
+ {
+ // pick up piece from internal pieces map
+ m_piece_hand = m_pieces_map[my][mx];
+ m_pieces_map[my][mx] = 0;
+ }
+ }
+ else if (m_piece_hand != 0)
+ {
+ if (valid_pos)
+ {
+ // collision with piece on board (user interference)
+ if (m_board->read_piece(x, y) != 0)
+ popmessage("Collision at %c%d!", x + 'A', y + 1);
+ else
+ {
+ m_board->write_piece(x, y, m_piece_hand);
+ m_board->refresh();
+ }
+ }
+ else
+ {
+ // collision with internal pieces map (shouldn't happen)
+ if (m_pieces_map[my][mx] != 0)
+ popmessage("Internal collision!");
+ else
+ m_pieces_map[my][mx] = m_piece_hand;
+ }
+
+ m_piece_hand = 0;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(grandmas_state::motor_count)
+{
+ const int m = param ? 1 : 0;
+ assert(m_motor_control[m] & 3);
+
+ // 1 quarter rotation per period
+ int inc = 0;
+ if (m_motor_control[m] & 2)
+ {
+ if (m_motor_pos[m] < m_motor_max[m])
+ inc = 1;
+ }
+ else if (m_motor_pos[m] > 0)
+ inc = -1;
+
+ m_motor_remain[m] = m_motor_period;
+
+ if (inc == 0)
+ return;
+
+ m_motor_pos[m] += inc;
+ m_motor_timer[m]->adjust(m_motor_period, m);
+
+ output_magnet_pos();
+
+ // update quadrature encoder
+ static const u8 lut_quad[4] = { 0,1,3,2 };
+ m_motor_quad[m] = lut_quad[m_motor_pos[m] & 3];
+
+ m_irq_ff[m + 1]->clock_w(m_motor_quad[m] >> 1 & 1);
+}
+
+void grandmas_state::motor_control(int m, u8 control)
+{
+ // it's not moving when both directions are set
+ if (control == 3)
+ control = 0;
+
+ if (control == m_motor_control[m])
+ return;
+
+ // remember remaining time
+ if (m_motor_control[m] & 3 && !m_motor_timer[m]->remaining().is_never())
+ {
+ m_motor_remain[m] = m_motor_timer[m]->remaining();
+ m_motor_timer[m]->adjust(attotime::never);
+ }
+
+ // invert remaining time if direction flipped
+ if ((m_motor_control[m] ^ control) & 2)
+ m_motor_remain[m] = m_motor_period - m_motor_remain[m];
+
+ m_motor_control[m] = control;
+
+ // (re)start the timer
+ if (control & 3)
+ m_motor_timer[m]->adjust(m_motor_remain[m], m);
+}
+
+
+
+/*******************************************************************************
+ I/O
+*******************************************************************************/
+
+void grandmas_state::irq_clear_w(offs_t offset, u8 data)
+{
+ // any read or write here clears IRQ F/F
+ m_irq_ff[offset]->clear_w(0);
+ m_irq_ff[offset]->clear_w(1);
+}
+
+u8 grandmas_state::irq_clear_r(offs_t offset)
+{
+ if (!machine().side_effects_disabled())
+ irq_clear_w(offset);
+
+ return 0xff;
+}
+
+u8 grandmas_state::status_r()
+{
+ // d0: magnet sensor
+ u8 data = m_piece_hand ? 1 : 0;
+
+ // d1,d3,d5: IRQ F/F Q
+ for (int i = 0; i < 3; i++)
+ data |= m_irq_ff[2 - i]->output_r() << (i * 2 + 1);
+
+ // d2,d4: motor y/x quadrature state
+ for (int i = 0; i < 2; i++)
+ data |= ((m_motor_quad[i] ^ m_motor_quad[i] >> 1) & 1) << (i ? 2 : 4);
+
+ return data | 0xc0;
+}
+
+void grandmas_state::control_w(u8 data)
+{
+ // d0: vertical motor down
+ // d1: vertical motor up
+ motor_control(1, bitswap<2>(data, 0, 1));
+
+ // d2: horizontal motor left
+ // d3: horizontal motor right
+ motor_control(0, data >> 2 & 3);
+
+ // d4: electromagnet
+ update_piece(BIT(data, 4));
+
+ // d5: speaker out
+ m_dac->write(BIT(data, 5));
+}
+
+void grandmas_state::leds_w(u8 data)
+{
+ // d0-d3: 74145 A-D
+ // 74145 1-8: led data
+ // 74145 0-8: input mux
+ m_inp_mux = data & 0xf;
+
+ // d4,d5: led select
+ m_display->matrix(data >> 4 & 3, (1 << m_inp_mux) >> 1);
+}
+
+u8 grandmas_state::input_r(offs_t offset)
+{
+ u16 data = 0;
+
+ if (m_inp_mux == 8)
+ data = m_inputs->read();
+
+ return ~data >> (offset * 6) | 0xc0;
+}
+
+
+
+/*******************************************************************************
+ Address Maps
+*******************************************************************************/
+
+void grandmas_state::main_map(address_map &map)
+{
+ map.unmap_value_high();
+ map.global_mask(0x7fff);
+
+ map(0x0000, 0x07ff).mirror(0x1800).ram();
+
+ map(0x2000, 0x2002).mirror(0x1ff8).rw(FUNC(grandmas_state::irq_clear_r), FUNC(grandmas_state::irq_clear_w));
+ map(0x2003, 0x2003).mirror(0x1ff8).r(FUNC(grandmas_state::status_r));
+ map(0x2004, 0x2004).mirror(0x1ff8).w(FUNC(grandmas_state::control_w));
+ map(0x2005, 0x2006).mirror(0x1ff8).r(FUNC(grandmas_state::input_r));
+ map(0x2007, 0x2007).mirror(0x1ff8).w(FUNC(grandmas_state::leds_w));
+
+ map(0x4000, 0x7fff).rom().region("maincpu", 0);
+}
+
+
+
+/*******************************************************************************
+ Input Ports
+*******************************************************************************/
+
+static INPUT_PORTS_START( grandmas )
+ PORT_START("IN.0")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_1) // auto/problem
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2)
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3)
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4)
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5)
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6)
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_7)
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_8)
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Q)
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_W)
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E)
+ PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_R)
+INPUT_PORTS_END
+
+
+
+/*******************************************************************************
+ Machine Configs
+*******************************************************************************/
+
+void grandmas_state::grandmas(machine_config &config)
+{
+ // basic machine hardware
+ M6502(config, m_maincpu, 3.58_MHz_XTAL / 2);
+ m_maincpu->set_addrmap(AS_PROGRAM, &grandmas_state::main_map);
+
+ INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, m6502_device::IRQ_LINE);
+
+ TTL7474(config, m_irq_ff[0]).output_cb().set("mainirq", FUNC(input_merger_device::in_w<0>));
+ TTL7474(config, m_irq_ff[1]).output_cb().set("mainirq", FUNC(input_merger_device::in_w<1>));
+ TTL7474(config, m_irq_ff[2]).output_cb().set("mainirq", FUNC(input_merger_device::in_w<2>));
+
+ auto &irq_clock(CLOCK(config, "irq_clock"));
+ irq_clock.set_period(attotime::from_ticks(0x1000, 3.58_MHz_XTAL / 2)); // CD4020 Q12
+ irq_clock.signal_handler().set(m_irq_ff[0], FUNC(ttl7474_device::clock_w));
+
+ SENSORBOARD(config, m_board).set_type(sensorboard_device::BUTTONS);
+ m_board->set_size(8+4, 8);
+ m_board->clear_cb().set(FUNC(grandmas_state::clear_board));
+ m_board->init_cb().set(FUNC(grandmas_state::init_board));
+ m_board->set_delay(attotime::from_msec(150));
+
+ // video hardware
+ PWM_DISPLAY(config, m_display).set_size(2, 8);
+
+ //config.set_default_layout(layout_grandmaster);
+
+ // sound hardware
+ SPEAKER(config, "speaker").front_center();
+ DAC_1BIT(config, m_dac).add_route(ALL_OUTPUTS, "speaker", 0.25);
+}
+
+
+
+/*******************************************************************************
+ ROM Definitions
+*******************************************************************************/
+
+ROM_START( grandmas )
+ ROM_REGION( 0x4000, "maincpu", 0 )
+ ROM_LOAD("c19679_7830043002.u3", 0x0000, 0x2000, CRC(c7a91b34) SHA1(43f9caf8a13ae1a3274851fbcc411bc50c21fe1d) )
+ ROM_LOAD("c19680_7830043001.u2", 0x2000, 0x2000, CRC(b47dda81) SHA1(02c946fa47db1c7edfb59e11ad3c802b92f4d3a1) )
+ROM_END
+
+} // anonymous namespace
+
+
+
+/*******************************************************************************
+ Drivers
+*******************************************************************************/
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS
+SYST( 1983, grandmas, 0, 0, grandmas, grandmas, grandmas_state, empty_init, "Milton Bradley", "Grand Master", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )
diff --git a/src/mame/miltonbradley/milton.cpp b/src/mame/miltonbradley/milton.cpp
index 23072a882ef..007b69dea8b 100644
--- a/src/mame/miltonbradley/milton.cpp
+++ b/src/mame/miltonbradley/milton.cpp
@@ -3,7 +3,7 @@
// thanks-to:Sean Riddle
/*******************************************************************************
-Milton Bradley Milton
+Milton Bradley (Electronic) Milton
This is the talking tabletop game, not the chess computer with the same name.
@@ -12,8 +12,8 @@ Game 2: Same as game 1, but all in one turn.
Game 3: Press phrase end buttons, memorize them, press Go and match them.
Hardware is an odd combination: MC6805P2 MCU, GI SP0250 speech + 2*TMC0430 GROM.
-See patent 4326710 for detailed information, except MC6805 clocked from SP0250 3.12MHz
-and GROM clocked by 3.12MHz/8=390kHz.
+See patent 4326710 for detailed information, except MC6805 clocked from SP0250
+3.12MHz and GROM clocked by 3.12MHz/8=390kHz.
*******************************************************************************/
@@ -308,4 +308,4 @@ ROM_END
*******************************************************************************/
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS
-SYST( 1980, milton, 0, 0, milton, milton, milton_state, empty_init, "Milton Bradley", "Electronic Milton", MACHINE_SUPPORTS_SAVE )
+SYST( 1980, milton, 0, 0, milton, milton, milton_state, empty_init, "Milton Bradley", "Milton", MACHINE_SUPPORTS_SAVE )
diff --git a/src/mame/novag/sdiamond.cpp b/src/mame/novag/sdiamond.cpp
index 8c3a02f4375..fb6ad827912 100644
--- a/src/mame/novag/sdiamond.cpp
+++ b/src/mame/novag/sdiamond.cpp
@@ -7,7 +7,8 @@ Novag (Perfect Technology*) Star Diamond (model 1004)
*: Novag Industries dissolved in 2000. The Novag brand continued for a few years
under Perfect Technology, Ltd., established by the daughter of Novag's founder.
-The main programmer (David Kittinger) also moved to the new company.
+The chief engineer (Wayne Chow) and the chess programmer (David Kittinger) also
+moved to the new company.
Although there may be newer Novag products with (old) software by David Kittinger,
Star Diamond was the last chess computer that he personally worked on.