diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 960fac3996d..d3ddb169b2a 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -3785,6 +3785,11 @@ files { MAME_DIR .. "src/mame/video/comquest.cpp", } +createMESSProjects(_target, _subtarget, "tectoy") +files { + MAME_DIR .. "src/mame/drivers/pensebem.cpp", +} + createMESSProjects(_target, _subtarget, "tektroni") files { MAME_DIR .. "src/mame/drivers/tek405x.cpp", diff --git a/src/devices/cpu/avr8/avr8.cpp b/src/devices/cpu/avr8/avr8.cpp index cbd56d599e8..de75492ee7d 100644 --- a/src/devices/cpu/avr8/avr8.cpp +++ b/src/devices/cpu/avr8/avr8.cpp @@ -593,6 +593,7 @@ static const char avr8_reg_name[4] = { 'A', 'B', 'C', 'D' }; //************************************************************************** DEFINE_DEVICE_TYPE(ATMEGA88, atmega88_device, "atmega88", "Atmel ATmega88") +DEFINE_DEVICE_TYPE(ATMEGA168, atmega168_device, "atmega168", "Atmel ATmega168") DEFINE_DEVICE_TYPE(ATMEGA328, atmega328_device, "atmega328", "Atmel ATmega328") DEFINE_DEVICE_TYPE(ATMEGA644, atmega644_device, "atmega644", "Atmel ATmega644") DEFINE_DEVICE_TYPE(ATMEGA1280, atmega1280_device, "atmega1280", "Atmel ATmega1280") @@ -608,6 +609,11 @@ void atmega88_device::atmega88_internal_map(address_map &map) map(0x0000, 0x00ff).rw(FUNC(atmega88_device::regs_r), FUNC(atmega88_device::regs_w)); } +void atmega168_device::atmega168_internal_map(address_map &map) +{ + map(0x0000, 0x00ff).rw(FUNC(atmega168_device::regs_r), FUNC(atmega168_device::regs_w)); +} + void atmega328_device::atmega328_internal_map(address_map &map) { map(0x0000, 0x00ff).rw(FUNC(atmega328_device::regs_r), FUNC(atmega328_device::regs_w)); @@ -642,6 +648,15 @@ atmega88_device::atmega88_device(const machine_config &mconfig, const char *tag, { } +//------------------------------------------------- +// atmega168_device - constructor +//------------------------------------------------- + +atmega168_device::atmega168_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : avr8_device(mconfig, tag, owner, clock, ATMEGA168, 0x1fff, address_map_constructor(FUNC(atmega168_device::atmega168_internal_map), this), 3) +{ +} + //------------------------------------------------- // atmega328_device - constructor //------------------------------------------------- @@ -1098,6 +1113,23 @@ void avr8_device::update_interrupt(int source) } } +//TODO: review this! +void atmega168_device::update_interrupt(int source) +{ + const interrupt_condition &condition = s_int_conditions[source]; + + int intstate = 0; + if (m_r[condition.m_intreg] & condition.m_intmask) + intstate = (m_r[condition.m_regindex] & condition.m_regmask) ? 1 : 0; + + set_irq_line(condition.m_intindex << 1, intstate); + + if (intstate) + { + m_r[condition.m_regindex] &= ~condition.m_regmask; + } +} + void atmega328_device::update_interrupt(int source) { const interrupt_condition &condition = s_int_conditions[source]; @@ -1401,6 +1433,31 @@ inline void avr8_device::timer1_tick() m_timer1_count = 0; increment = 0; } + + switch (m_timer1_compare_mode[reg] & 3) + { + case 0: /* Normal Operation; OC1A/B disconnected */ + break; + + case 1: /* Toggle OC1A on compare match */ + if (reg == 0) + { + LOGMASKED(LOG_TIMER1, "%s: timer1: Toggle OC1%c on match\n", machine().describe_context()); + m_io->write_byte(AVR8_IO_PORTB, m_io->read_byte(AVR8_IO_PORTB) ^ (2 << reg)); + } + break; + + case 2: /* Clear OC1A/B on compare match */ + LOGMASKED(LOG_TIMER1, "%s: timer1: Clear OC1%c on match\n", machine().describe_context(), reg ? 'B' : 'A'); + m_io->write_byte(AVR8_IO_PORTB, m_io->read_byte(AVR8_IO_PORTB) & ~(2 << reg)); + break; + + case 3: /* Set OC1A/B on compare match */ + LOGMASKED(LOG_TIMER1, "%s: timer1: Set OC1%c on match\n", machine().describe_context(), reg ? 'B' : 'A'); + m_io->write_byte(AVR8_IO_PORTB, m_io->read_byte(AVR8_IO_PORTB) | (2 << reg)); + break; + } + m_r[AVR8_REGIDX_TIFR1] |= s_ocf1[reg]; update_interrupt(s_int1[reg]); } diff --git a/src/devices/cpu/avr8/avr8.h b/src/devices/cpu/avr8/avr8.h index 8fde28f55d8..fc3b6be5a94 100644 --- a/src/devices/cpu/avr8/avr8.h +++ b/src/devices/cpu/avr8/avr8.h @@ -298,6 +298,7 @@ protected: // device type definition DECLARE_DEVICE_TYPE(ATMEGA88, atmega88_device) +DECLARE_DEVICE_TYPE(ATMEGA168, atmega168_device) DECLARE_DEVICE_TYPE(ATMEGA328, atmega328_device) DECLARE_DEVICE_TYPE(ATMEGA644, atmega644_device) DECLARE_DEVICE_TYPE(ATMEGA1280, atmega1280_device) @@ -314,6 +315,18 @@ public: void atmega88_internal_map(address_map &map); }; +// ======================> atmega168_device + +class atmega168_device : public avr8_device +{ +public: + // construction/destruction + atmega168_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + virtual void update_interrupt(int source) override; + void atmega168_internal_map(address_map &map); +}; + // ======================> atmega328_device class atmega328_device : public avr8_device diff --git a/src/mame/drivers/pensebem.cpp b/src/mame/drivers/pensebem.cpp new file mode 100644 index 00000000000..fbdc9fcd803 --- /dev/null +++ b/src/mame/drivers/pensebem.cpp @@ -0,0 +1,320 @@ +// license:GPL-2.0+ +// copyright-holders:Felipe Sanches +/* + Pense Bem (TecToy 2017) + driver by Felipe Correa da Silva Sanches + +--------------------------------------------------------------------- + + In the 80s Tec Toy released Pense Bem in Brazil. It is most + likely identical to VTech Smart Start since I think they run the + exact same Z8 code. The only difference must be branding in the + handheld case. + + In 2017 Tec Toy re-released Pense Bem in Brazil but this time + using an Atmel ATMEGA168PB chip. It is not clear if the ATMEGA + code contains a copy of the Z8 ROM and emulates the Z8, or if + Tec Toy ported the original Z8 code retargetting it to the + Atmel chip. Or even if they simply reimplemented the full + functionality from scratch. + + Inspecting the ATMEGA disasm, it does not look like an emulation + of the original Z8 code, but further research would be needed + to be sure. + + As of October 2020, there's still no successfull ROM dump of the + original Pense Bem's Z8 ROM code, so this driver only emulates + the 2017 re-release. + +--------------------------------------------------------------------- + + The 2017 edition of TecToy's Pense Bem has a 2x4 programming + pin-header at position CN4: + + CN4 - ATMEGA + 1 - 4 VCC + 2 - 15 MOSI + 3 - 31 TXD + 4 - 16 MISO + 5 - 30 RXD + 6 - 17 SCK + 7 - 5 GND + 8 - 29 RESET + + R34 is the pull-up resistor for the RESET signal + +--------------------------------------------------------------------- + + Changelog: + + 2020 OCT 27 [Felipe Sanches]: + * Fixed keyboard inputs, display & buzzer. + * Implementation of AVR8 Timer 1 Output Compare Match A is + sub-optimal resulting in bad sound quality when emulating + the buzzer. + + 2017 OCT 07 [Felipe Sanches]: + * Initial driver skeleton + +--------------------------------------------------------------------- + +== Notes about the hardware: == +=== Select keypad rows: === +keypad pin 1 - PORT_B5 +keypad pin 2 - PORT_B3 +keypad pin 12 - PORT_B2 +keypad pin 13 - PORT_B4 + +=== Read keypad Columns: === +keypad pin 3 - PORT_E0 +keypad pin 4 - PORT_D2 +keypad pin 5 - PORT_E1 +keypad pin 6 - PORT_D7 +keypad pin 7 - PORT_D6 +keypad pin 8 - PORT_D5 +keypad pin 9 - PORT_D4 +keypad pin 10 - PORT_D3 + +=== Display digits: === +digit_7 - PORT_E2 +digit_6 - PORT_E3 +digit_5 - PORT_C0 +digit_4 - PORT_C1 +digit_3 - PORT_C2 +digit_2 - PORT_C3 +digit_1 - PORT_C4 +digit_0 - PORT_C5 + +=== Display segments: === +In parentheses are the resistor reference numbers on +the PCB for each of these signals. + +seg_7 (R11) - PORT_D7 +seg_6 (R12) - PORT_D6 +seg_5 (R17) - PORT_D5 +seg_4 (R13) - PORT_D4 +seg_3 (R14) - PORT_D3 +seg_2 (R15) - PORT_D2 +seg_1 (R18) - PORT_E1 +seg_0 (R16) - PORT_E0 + +=== Piezo buzzer: === +Port B, bit 1 + +--------------------------------------------------------------------- */ + +#include "emu.h" +#include "cpu/avr8/avr8.h" +#include "video/pwm.h" +#include "sound/dac.h" +#include "rendlay.h" +#include "screen.h" +#include "speaker.h" + + +class pensebem2017_state : public driver_device +{ +public: + pensebem2017_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_dac(*this, "dac"), + m_display(*this, "display"), + m_keyb_rows(*this, "ROW%u", 0U) + { + } + + void pensebem2017(machine_config &config); + +private: + void prg_map(address_map &map); + void data_map(address_map &map); + void io_map(address_map &map); + void update_display(); + uint8_t port_r(offs_t offset); + void port_w(offs_t offset, uint8_t value); + + uint8_t m_port_b; + uint8_t m_port_c; + uint8_t m_port_d; + uint8_t m_port_e; + + required_device m_maincpu; + required_device m_dac; + required_device m_display; + required_ioport_array<4> m_keyb_rows; + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; +}; + +void pensebem2017_state::machine_start() +{ +} + +uint8_t pensebem2017_state::port_r(offs_t offset) +{ + uint8_t value; + int bit; + + switch(offset) + { + case AVR8_IO_PORTB: + value = m_port_b & 0xc3; + for (bit=0; bit<8; bit++) + { + if (bit < 2 && !BIT(m_port_e, bit)) break; + if (bit >= 2 && !BIT(m_port_d, bit)) break; + } + if (BIT(m_keyb_rows[0]->read(), bit)) value |= (1 << 5); + if (BIT(m_keyb_rows[1]->read(), bit)) value |= (1 << 3); + if (BIT(m_keyb_rows[2]->read(), bit)) value |= (1 << 2); + if (BIT(m_keyb_rows[3]->read(), bit)) value |= (1 << 4); + return value; + default: + return 0xff; + } +} + +void pensebem2017_state::port_w(offs_t offset, uint8_t data) +{ + switch(offset) + { + case AVR8_IO_PORTB: // buzzer + keyboard_select_rows + m_port_b = data; + m_dac->write(BIT(data, 1)); + break; + case AVR8_IO_PORTC: // display + m_port_c = data; + update_display(); + break; + case AVR8_IO_PORTD: // display + m_port_d = data; + break; + case AVR8_IO_PORTE: // display + m_port_e = data; + update_display(); + break; + default: + break; + } +} + +void pensebem2017_state::update_display() +{ + m_display->matrix( + ~bitswap<8>((m_port_c << 2 & 0xfc) | (m_port_e >> 2 & 0x03), 7,6,5,4,3,2,1,0), + ~((m_port_d & 0xfc) | (m_port_e & 0x03)) + ); +} + +void pensebem2017_state::prg_map(address_map &map) +{ + map(0x0000, 0x3fff).rom().region("maincpu", 0); /* 16 kbytes of Flash ROM */ +} + +void pensebem2017_state::data_map(address_map &map) +{ + map(0x0000, 0x03ff).ram(); /* ATMEGA168PB Internal 1024 bytes of SRAM */ + map(0x0400, 0xffff).ram(); /* Some additional SRAM ? This is likely an exagerated amount ! */ +} + +void pensebem2017_state::io_map(address_map &map) +{ + map(AVR8_IO_PORTA, AVR8_IO_PORTE).rw(FUNC(pensebem2017_state::port_r), FUNC(pensebem2017_state::port_w)); +} + +static INPUT_PORTS_START( pensebem2017 ) + PORT_START("ROW0") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("A") PORT_CODE(KEYCODE_A) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("C") PORT_CODE(KEYCODE_C) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("B") PORT_CODE(KEYCODE_B) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Desliga") PORT_CODE(KEYCODE_MINUS) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Livro") PORT_CODE(KEYCODE_L) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("D") PORT_CODE(KEYCODE_D) + + PORT_START("ROW1") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("1") PORT_CODE(KEYCODE_1) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Adicao") PORT_CODE(KEYCODE_Q) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Subtracao") PORT_CODE(KEYCODE_W) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH_PAD) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("*") PORT_CODE(KEYCODE_ASTERISK) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS_PAD) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("+") PORT_CODE(KEYCODE_PLUS_PAD) + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("0") PORT_CODE(KEYCODE_0) + + PORT_START("ROW2") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Multiplicacao") PORT_CODE(KEYCODE_E) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Aritmetica") PORT_CODE(KEYCODE_T) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Divisao") PORT_CODE(KEYCODE_R) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Adivinhe o Número") PORT_CODE(KEYCODE_P) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Número do Meio") PORT_CODE(KEYCODE_O) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Memória Tons") PORT_CODE(KEYCODE_I) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Siga-me") PORT_CODE(KEYCODE_U) + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Operacao") PORT_CODE(KEYCODE_Y) + + PORT_START("ROW3") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("2") PORT_CODE(KEYCODE_2) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("4") PORT_CODE(KEYCODE_4) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("3") PORT_CODE(KEYCODE_3) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("9") PORT_CODE(KEYCODE_9) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("8") PORT_CODE(KEYCODE_8) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("7") PORT_CODE(KEYCODE_7) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("6") PORT_CODE(KEYCODE_6) + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("5") PORT_CODE(KEYCODE_5) +INPUT_PORTS_END + +void pensebem2017_state::machine_reset() +{ + m_port_b = 0; + m_port_c = 0; + m_port_d = 0; + m_port_e = 0; +} + +void pensebem2017_state::pensebem2017(machine_config &config) +{ + /* CPU */ + ATMEGA168(config, m_maincpu, 16_MHz_XTAL); /* Actual chip is an Atmel ATMEGA168PB */ + m_maincpu->set_addrmap(AS_PROGRAM, &pensebem2017_state::prg_map); + m_maincpu->set_addrmap(AS_DATA, &pensebem2017_state::data_map); + m_maincpu->set_addrmap(AS_IO, &pensebem2017_state::io_map); + m_maincpu->set_eeprom_tag("eeprom"); + m_maincpu->set_low_fuses(0xf7); + m_maincpu->set_high_fuses(0xdd); + m_maincpu->set_extended_fuses(0xf9); + m_maincpu->set_lock_bits(0x0f); + + /* video hardware */ + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG)); + screen.set_refresh_hz(50); + screen.set_size(1490, 1080); + screen.set_visarea_full(); + PWM_DISPLAY(config, m_display).set_size(8, 8); + m_display->set_segmask(0xff, 0xff); + + /* sound hardware */ + SPEAKER(config, "speaker").front_center(); + DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); +} + +ROM_START( pbem2017 ) + ROM_REGION(0x20000, "maincpu", 0) + ROM_DEFAULT_BIOS("sept2017") + + /* September 2017 release */ + ROM_SYSTEM_BIOS(0, "sept2017", "SEPT/2017") + ROMX_LOAD("pensebem-2017.bin", 0x0000, 0x35b6, CRC(d394279e) SHA1(5576599394231c1f83817dd55992e3b5838ab003), ROM_BIOS(0)) + + /* on-die 4kbyte eeprom */ + ROM_REGION(0x1000, "eeprom", ROMREGION_ERASEFF) + + ROM_REGION(0x42e1a, "screen", 0) + ROM_LOAD("pensebem.svg", 0, 0x42e1a, CRC(7146c0db) SHA1(966e95742acdda05028ee7b0c1352c88abb35041)) +ROM_END + +/* YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME */ +COMP(2017, pbem2017, 0, 0, pensebem2017, pensebem2017, pensebem2017_state, empty_init, "TecToy", "Pense Bem (2017)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 30bf5078924..8cb770a0333 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -33378,6 +33378,9 @@ pengo5 // 834-0386 (c) 1982 Sega pengob // bootleg penta // bootleg +@source:pensebem.cpp +pbem2017 // 2017 TecToy Pense Bem + @source:pentagon.cpp pent1024 // pentagon //