From 44feade33ffc1da3bb5a01dabae9dcc938a6f9a6 Mon Sep 17 00:00:00 2001 From: hap Date: Sun, 4 Jun 2023 15:01:26 +0200 Subject: [PATCH] New working systems ------------------- Addometer Calculator [hap, Sean Riddle] --- src/mame/handheld/hh_cop400.cpp | 3 +- src/mame/handheld/hh_pic16.cpp | 3 +- src/mame/handheld/hh_pps41.cpp | 196 +++++++++++++++++++++++++++++++- src/mame/handheld/hh_tms1k.cpp | 3 +- src/mame/itech/iteagle_fpga.cpp | 12 +- src/mame/itech/iteagle_fpga.h | 4 +- src/mame/layout/addocalc.lay | 48 ++++++++ src/mame/mame.lst | 1 + src/mame/tiger/k28.cpp | 14 +-- src/mame/tiger/k28m2.cpp | 4 +- 10 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 src/mame/layout/addocalc.lay diff --git a/src/mame/handheld/hh_cop400.cpp b/src/mame/handheld/hh_cop400.cpp index 484edd1abfe..ebd4ac9ad92 100644 --- a/src/mame/handheld/hh_cop400.cpp +++ b/src/mame/handheld/hh_cop400.cpp @@ -143,7 +143,8 @@ INPUT_CHANGED_MEMBER(hh_cop400_state::reset_button) INPUT_CHANGED_MEMBER(hh_cop400_state::power_button) { - set_power((bool)param); + if (newval != field.defvalue()) + set_power((bool)param); } void hh_cop400_state::set_power(bool state) diff --git a/src/mame/handheld/hh_pic16.cpp b/src/mame/handheld/hh_pic16.cpp index dc65763194f..77b7638ce9d 100644 --- a/src/mame/handheld/hh_pic16.cpp +++ b/src/mame/handheld/hh_pic16.cpp @@ -190,7 +190,8 @@ INPUT_CHANGED_MEMBER(hh_pic16_state::reset_button) INPUT_CHANGED_MEMBER(hh_pic16_state::power_button) { - set_power((bool)param); + if (newval != field.defvalue()) + set_power((bool)param); } void hh_pic16_state::set_power(bool state) diff --git a/src/mame/handheld/hh_pps41.cpp b/src/mame/handheld/hh_pps41.cpp index 016c4a4ff14..56474b4e62e 100644 --- a/src/mame/handheld/hh_pps41.cpp +++ b/src/mame/handheld/hh_pps41.cpp @@ -16,6 +16,7 @@ ROM source notes when dumped from another title, but confident it's the same: #include "cpu/pps41/mm76.h" #include "cpu/pps41/mm78.h" #include "cpu/pps41/mm78la.h" +#include "machine/timer.h" #include "sound/beep.h" #include "sound/spkrdev.h" #include "video/pwm.h" @@ -24,6 +25,7 @@ ROM source notes when dumped from another title, but confident it's the same: #include "speaker.h" // internal artwork +#include "addocalc.lh" #include "brainbaf.lh" #include "dunksunk.lh" #include "ftri1.lh" @@ -53,6 +55,7 @@ public: { } virtual DECLARE_INPUT_CHANGED_MEMBER(reset_button); + virtual DECLARE_INPUT_CHANGED_MEMBER(power_button); protected: virtual void machine_start() override; @@ -72,8 +75,11 @@ protected: // MCU output pin state u16 m_d = 0; u16 m_r = 0; + u8 m_ssc = 0; + u8 m_sdo = 0; u8 read_inputs(int columns); + void set_power(bool state); virtual void update_int() { ; } }; @@ -88,6 +94,8 @@ void hh_pps41_state::machine_start() save_item(NAME(m_plate)); save_item(NAME(m_d)); save_item(NAME(m_r)); + save_item(NAME(m_ssc)); + save_item(NAME(m_sdo)); } @@ -118,6 +126,20 @@ INPUT_CHANGED_MEMBER(hh_pps41_state::reset_button) m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE); } +INPUT_CHANGED_MEMBER(hh_pps41_state::power_button) +{ + if (newval != field.defvalue()) + set_power((bool)param); +} + +void hh_pps41_state::set_power(bool state) +{ + m_maincpu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE); + + if (m_display && !state) + m_display->clear(); +} + /******************************************************************************* @@ -126,6 +148,176 @@ INPUT_CHANGED_MEMBER(hh_pps41_state::reset_button) *******************************************************************************/ +/******************************************************************************* + + Addometer Company Addometer Calculator + * MM78 MCU (label MM78 A7872-11, die label A7872) + * 12-digit 7seg VFD, 1 digit unused (NEC LD8197A/FIP12A4A 0B) + + This is presumably the only pocket calculator by Addometer Company (previously + known as Reliable Typewriter and Adding Machine Corporation). It's a feet + and inches/metric calculator. + +*******************************************************************************/ + +class addocalc_state : public hh_pps41_state +{ +public: + addocalc_state(const machine_config &mconfig, device_type type, const char *tag) : + hh_pps41_state(mconfig, type, tag), + m_power_timer(*this, "power") + { } + + void addocalc(machine_config &config); + + virtual DECLARE_INPUT_CHANGED_MEMBER(power_button) override { hh_pps41_state::power_button(field, param, oldval, newval); update_int(); } + +private: + required_device m_power_timer; + + virtual void update_int() override; + void update_display(); + void write_d(u16 data); + void write_r(u16 data); + u8 read_p(); + void write_ssc(int state); + void write_sdo(int state); + + TIMER_DEVICE_CALLBACK_MEMBER(power_off) { set_power(false); } +}; + +// handlers + +void addocalc_state::update_int() +{ + // ON/OFF button is tied to INT1 + m_maincpu->set_input_line(1, (m_inputs[7]->read() & 1) ? ASSERT_LINE : CLEAR_LINE); +} + +void addocalc_state::update_display() +{ + m_display->matrix(m_d | m_ssc << 10, bitswap<8>(~m_r, 0,1,2,3,4,5,6,7)); +} + +void addocalc_state::write_d(u16 data) +{ + // DIO0-DIO9: digit select + // DIO0-DIO6: input mux + m_d = m_inp_mux = data; + update_display(); +} + +void addocalc_state::write_r(u16 data) +{ + // RIO1-RIO8: digit segment data + m_r = data; + update_display(); +} + +u8 addocalc_state::read_p() +{ + // PI1-PI4: multiplexed inputs + return ~read_inputs(7); +} + +void addocalc_state::write_ssc(int state) +{ + // CLOCK: one more digit + m_ssc = state; + update_display(); +} + +void addocalc_state::write_sdo(int state) +{ + // DATAO: trigger power off after a short delay + if (state != m_sdo) + m_power_timer->adjust(state ? attotime::from_msec(50) : attotime::never); + + m_sdo = state; +} + +// inputs + +static INPUT_PORTS_START( addocalc ) + PORT_START("IN.0") // DIO0 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("CL") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("=") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) + + PORT_START("IN.1") // DIO1 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(u8"×") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(u8"÷") + + PORT_START("IN.2") // DIO2 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("STO") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("REC") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("EX") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME(".") + + PORT_START("IN.3") // DIO3 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("FT") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("IN") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("FRA") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("MET") + + PORT_START("IN.4") // DIO4 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3") + + PORT_START("IN.5") // DIO5 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6") + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7") + + PORT_START("IN.6") // DIO6 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9") + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) + + PORT_START("IN.7") // INT1 + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_CHANGED_MEMBER(DEVICE_SELF, addocalc_state, power_button, true) PORT_NAME("ON/OFF") +INPUT_PORTS_END + +// config + +void addocalc_state::addocalc(machine_config &config) +{ + // basic machine hardware + MM78(config, m_maincpu, 430000); // approximation - VC osc. R=47K + m_maincpu->write_d().set(FUNC(addocalc_state::write_d)); + m_maincpu->write_r().set(FUNC(addocalc_state::write_r)); + m_maincpu->read_p().set(FUNC(addocalc_state::read_p)); + m_maincpu->write_ssc().set(FUNC(addocalc_state::write_ssc)); + m_maincpu->write_sdo().set(FUNC(addocalc_state::write_sdo)); + + TIMER(config, "power").configure_generic(FUNC(addocalc_state::power_off)); + + // video hardware + PWM_DISPLAY(config, m_display).set_size(11, 8); + m_display->set_segmask(0x7ff, 0xff); + config.set_default_layout(layout_addocalc); + + // no sound! +} + +// roms + +ROM_START( addocalc ) + ROM_REGION( 0x0800, "maincpu", 0 ) + ROM_LOAD( "mm78_a7872-11", 0x0000, 0x0800, CRC(1ff26da7) SHA1(b4b9b5886d60dcd634661604f9ef5346a6c8bd1b) ) +ROM_END + + + + + /******************************************************************************* Fonas Tri-1 @@ -197,7 +389,7 @@ static INPUT_PORTS_START( ftri1 ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Swing / S2 V") PORT_START("RESET") - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_pps41_state, reset_button, 0) PORT_CODE(KEYCODE_F1) PORT_NAME("Game Reset") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_CHANGED_MEMBER(DEVICE_SELF, ftri1_state, reset_button, 0) PORT_CODE(KEYCODE_F1) PORT_NAME("Game Reset") INPUT_PORTS_END // config @@ -1616,6 +1808,8 @@ ROM_END *******************************************************************************/ // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS +SYST( 1980, addocalc, 0, 0, addocalc, addocalc, addocalc_state, empty_init, "Addometer Company", "Addometer Calculator", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) + SYST( 1979, ftri1, 0, 0, ftri1, ftri1, ftri1_state, empty_init, "Fonas", "Tri-1 (Fonas)", MACHINE_SUPPORTS_SAVE ) SYST( 1979, mastmind, 0, 0, mastmind, mastmind, mastmind_state, empty_init, "Invicta", "Electronic Master Mind (Invicta)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW ) diff --git a/src/mame/handheld/hh_tms1k.cpp b/src/mame/handheld/hh_tms1k.cpp index 8c15f6013ef..35c57ba59dc 100644 --- a/src/mame/handheld/hh_tms1k.cpp +++ b/src/mame/handheld/hh_tms1k.cpp @@ -480,7 +480,8 @@ INPUT_CHANGED_MEMBER(hh_tms1k_state::reset_button) INPUT_CHANGED_MEMBER(hh_tms1k_state::power_button) { - set_power((bool)param); + if (newval != field.defvalue()) + set_power((bool)param); } void hh_tms1k_state::auto_power_off(int state) diff --git a/src/mame/itech/iteagle_fpga.cpp b/src/mame/itech/iteagle_fpga.cpp index c449b7a9d11..999d93a7dac 100644 --- a/src/mame/itech/iteagle_fpga.cpp +++ b/src/mame/itech/iteagle_fpga.cpp @@ -415,8 +415,7 @@ void iteagle_fpga_device::fpga_w(offs_t offset, uint32_t data, uint32_t mem_mask if (VERBOSE & LOG_SERIAL) { m_serial0_1.write_data((data >> 16) & 0xff, 1); if (m_serial0_1.get_tx_str(1).back() == 0xd) { - logerror("com0: %s", m_serial0_1.get_tx_str(1).c_str()); - osd_printf_info("com0: %s\n", m_serial0_1.get_tx_str(1)); + LOGMASKED(LOG_SERIAL, "com0: %s\n", m_serial0_1.get_tx_str(1)); m_serial0_1.clear_tx_str(1); } } @@ -465,7 +464,7 @@ void iteagle_fpga_device::fpga_w(offs_t offset, uint32_t data, uint32_t mem_mask int chan = 0; m_serial2_3.write_data((data >> 24) & 0xff, chan); if (m_serial2_3.get_tx_str(chan).back() == 0xd) { - LOGMASKED(LOG_SERIAL, "com3: %s\n", m_serial2_3.get_tx_str(chan).c_str()); + LOGMASKED(LOG_SERIAL, "com3: %s\n", m_serial2_3.get_tx_str(chan)); osd_printf_debug("com3: %s\n", m_serial2_3.get_tx_str(chan)); if (m_serial2_3.get_tx_str(chan).find("ATI5") != -1) m_serial2_3.write_rx_str(chan, "OK\r181\r"); @@ -530,7 +529,7 @@ uint8_t iteagle_am85c30::read_control(int channel) void iteagle_am85c30::write_data(uint8_t data, int channel) { - if (VERBOSE & LOG_SERIAL_VERBOSE) printf("chan %i: TX 0x%2X\n", channel, data); + //osd_printf_debug("chan %i: TX 0x%2X\n", channel, data); m_serial_tx[channel] += data; m_rr_regs[channel][0] |= 0x4; // Tx Buffer Empty // Tx Interrupt @@ -541,7 +540,6 @@ void iteagle_am85c30::write_data(uint8_t data, int channel) } // Limit length if (m_serial_tx[channel].size() >= 4000) { - if (VERBOSE & LOG_SERIAL) printf("%s\n", m_serial_tx[channel].c_str()); osd_printf_debug("%s\n", m_serial_tx[channel]); m_serial_tx[channel].clear(); } @@ -551,7 +549,7 @@ uint8_t iteagle_am85c30::read_data(int channel) { uint8_t retVal = 0; if (!m_serial_rx[channel].empty()) { - //logerror("fpga_r: read byte: %c\n", m_serial_rx[channel].at(0)); + //printf("fpga_r: read byte: %c\n", m_serial_rx[channel].at(0)); retVal = m_serial_rx[channel].at(0); m_serial_rx[channel].erase(m_serial_rx[channel].begin()); } @@ -709,7 +707,7 @@ void iteagle_eeprom_device::device_start() uint16_t checkSum = 0; for (int i=0; i<0x3f; i++) { checkSum += m_iteagle_default_eeprom[i]; - //logerror("eeprom init i: %x data: %04x\n", i, iteagle_default_eeprom[i]); + //logerror("eeprom init i: %x data: %04x\n", i, iteagle_default_eeprom[i]); } m_iteagle_default_eeprom[0x3f] = checkSum; diff --git a/src/mame/itech/iteagle_fpga.h b/src/mame/itech/iteagle_fpga.h index b060bd7fe56..2ee938b9469 100644 --- a/src/mame/itech/iteagle_fpga.h +++ b/src/mame/itech/iteagle_fpga.h @@ -27,6 +27,7 @@ public: std::string get_tx_str(int channel) { return m_serial_tx[channel]; } void clear_tx_str(int channel) { m_serial_tx[channel].clear(); } bool check_interrupt() { return (m_rr_regs[0][3] != 0); } + private: uint8_t m_rr_regs[2][16]; uint8_t m_wr_regs[2][16]; @@ -80,7 +81,7 @@ private: devcb_read16 m_gunx_cb; devcb_read16 m_guny_cb; - emu_timer * m_timer; + emu_timer *m_timer; int m_irq_num; int m_serial_irq_num; @@ -169,7 +170,6 @@ private: uint32_t ctrl_r(offs_t offset, uint32_t mem_mask = ~0); void ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); - }; DECLARE_DEVICE_TYPE(ITEAGLE_FPGA, iteagle_fpga_device) diff --git a/src/mame/layout/addocalc.lay b/src/mame/layout/addocalc.lay new file mode 100644 index 00000000000..f140c33097c --- /dev/null +++ b/src/mame/layout/addocalc.lay @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index d0a0afac015..9367d228977 100755 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -18740,6 +18740,7 @@ us2pfball // US Games uspbball // US Games @source:handheld/hh_pps41.cpp +addocalc // Addometer Company brainbaf // Mattel dunksunk // Kmart ftri1 // Fonas diff --git a/src/mame/tiger/k28.cpp b/src/mame/tiger/k28.cpp index c97a25d826e..492897ff03e 100644 --- a/src/mame/tiger/k28.cpp +++ b/src/mame/tiger/k28.cpp @@ -30,7 +30,6 @@ TODO: #include "emu.h" #include "cpu/mcs48/mcs48.h" -#include "machine/timer.h" #include "machine/tms6100.h" #include "video/mm5445.h" #include "video/pwm.h" @@ -54,7 +53,6 @@ public: m_display(*this, "display"), m_tms6100(*this, "tms6100"), m_speech(*this, "speech"), - m_onbutton_timer(*this, "on_button"), m_inputs(*this, "IN.%u", 0) { } @@ -73,10 +71,10 @@ private: required_device m_display; required_device m_tms6100; required_device m_speech; - required_device m_onbutton_timer; required_ioport_array<7> m_inputs; bool m_power_on = false; + attotime m_onbutton_time; u8 m_inp_mux = 0; u8 m_phoneme = 0x3f; int m_speech_strobe = 0; @@ -95,6 +93,7 @@ void k28_state::machine_start() { // register for savestates save_item(NAME(m_power_on)); + save_item(NAME(m_onbutton_time)); save_item(NAME(m_inp_mux)); save_item(NAME(m_phoneme)); save_item(NAME(m_speech_strobe)); @@ -112,8 +111,8 @@ void k28_state::machine_reset() m_power_on = true; m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); - // the game relies on reading the on-button as pressed when it's turned on - m_onbutton_timer->adjust(attotime::from_msec(250)); + // it relies on reading the on-button as pressed when it's turned on + m_onbutton_time = machine().time() + attotime::from_msec(250); } INPUT_CHANGED_MEMBER(k28_state::power_on) @@ -126,6 +125,7 @@ void k28_state::power_off() { m_power_on = false; m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + m_display->clear(); } @@ -189,7 +189,7 @@ u8 k28_state::mcu_p1_r() data |= m_inputs[i]->read(); // force press on-button at boot - if (i == 5 && m_onbutton_timer->enabled()) + if (i == 5 && machine().time() < m_onbutton_time) data |= 1; } @@ -310,8 +310,6 @@ void k28_state::k28(machine_config &config) TMS6100(config, m_tms6100, 3.579545_MHz_XTAL / 15); // CLK tied to 8021 ALE pin - TIMER(config, "on_button").configure_generic(nullptr); - // video hardware MM5445(config, m_vfd).output_cb().set(FUNC(k28_state::vfd_output_w)); PWM_DISPLAY(config, m_display).set_size(9, 16); diff --git a/src/mame/tiger/k28m2.cpp b/src/mame/tiger/k28m2.cpp index 0e871125329..2ad8525f92f 100644 --- a/src/mame/tiger/k28m2.cpp +++ b/src/mame/tiger/k28m2.cpp @@ -7,8 +7,8 @@ Tiger Electronics K-2-8 (model 7-232) Sold in Hong Kong, distributed in US as: - Coleco: Talking Teacher (model 8100) - Sears: Talkatron: Learning Computer -1981 K-2-8 models 7-230 and 7-231 are on different hardware, showing a different -keyboard, VFD, and use the SC-01 speech chip. --> driver k28.cpp +1981 K-2-8 models 7-230 and 7-231 are on different hardware, having a different +keyboard, VFD, and the SC-01-A speech chip. --> driver k28.cpp Hardware notes: - PCB label: 201223A (main), REV0 ET828D (LCD)