new driver: TecToy Pense Bem (2017)

Equivalent to VTech Smart Start from the 80s.
This commit is contained in:
Felipe Corrêa da Silva Sanches 2017-10-07 15:59:35 -03:00 committed by Vas Crabb
parent daf9e638a0
commit c82bdb636a
5 changed files with 398 additions and 0 deletions

View File

@ -3785,6 +3785,11 @@ files {
MAME_DIR .. "src/mame/video/comquest.cpp", MAME_DIR .. "src/mame/video/comquest.cpp",
} }
createMESSProjects(_target, _subtarget, "tectoy")
files {
MAME_DIR .. "src/mame/drivers/pensebem.cpp",
}
createMESSProjects(_target, _subtarget, "tektroni") createMESSProjects(_target, _subtarget, "tektroni")
files { files {
MAME_DIR .. "src/mame/drivers/tek405x.cpp", MAME_DIR .. "src/mame/drivers/tek405x.cpp",

View File

@ -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(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(ATMEGA328, atmega328_device, "atmega328", "Atmel ATmega328")
DEFINE_DEVICE_TYPE(ATMEGA644, atmega644_device, "atmega644", "Atmel ATmega644") DEFINE_DEVICE_TYPE(ATMEGA644, atmega644_device, "atmega644", "Atmel ATmega644")
DEFINE_DEVICE_TYPE(ATMEGA1280, atmega1280_device, "atmega1280", "Atmel ATmega1280") 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)); 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) void atmega328_device::atmega328_internal_map(address_map &map)
{ {
map(0x0000, 0x00ff).rw(FUNC(atmega328_device::regs_r), FUNC(atmega328_device::regs_w)); 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 // 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) void atmega328_device::update_interrupt(int source)
{ {
const interrupt_condition &condition = s_int_conditions[source]; const interrupt_condition &condition = s_int_conditions[source];
@ -1401,6 +1433,31 @@ inline void avr8_device::timer1_tick()
m_timer1_count = 0; m_timer1_count = 0;
increment = 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]; m_r[AVR8_REGIDX_TIFR1] |= s_ocf1[reg];
update_interrupt(s_int1[reg]); update_interrupt(s_int1[reg]);
} }

View File

@ -298,6 +298,7 @@ protected:
// device type definition // device type definition
DECLARE_DEVICE_TYPE(ATMEGA88, atmega88_device) DECLARE_DEVICE_TYPE(ATMEGA88, atmega88_device)
DECLARE_DEVICE_TYPE(ATMEGA168, atmega168_device)
DECLARE_DEVICE_TYPE(ATMEGA328, atmega328_device) DECLARE_DEVICE_TYPE(ATMEGA328, atmega328_device)
DECLARE_DEVICE_TYPE(ATMEGA644, atmega644_device) DECLARE_DEVICE_TYPE(ATMEGA644, atmega644_device)
DECLARE_DEVICE_TYPE(ATMEGA1280, atmega1280_device) DECLARE_DEVICE_TYPE(ATMEGA1280, atmega1280_device)
@ -314,6 +315,18 @@ public:
void atmega88_internal_map(address_map &map); 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 // ======================> atmega328_device
class atmega328_device : public avr8_device class atmega328_device : public avr8_device

View File

@ -0,0 +1,320 @@
// license:GPL-2.0+
// copyright-holders:Felipe Sanches
/*
Pense Bem (TecToy 2017)
driver by Felipe Correa da Silva Sanches <juca@members.fsf.org>
---------------------------------------------------------------------
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<avr8_device> m_maincpu;
required_device<dac_bit_interface> m_dac;
required_device<pwm_display_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)

View File

@ -33378,6 +33378,9 @@ pengo5 // 834-0386 (c) 1982 Sega
pengob // bootleg pengob // bootleg
penta // bootleg penta // bootleg
@source:pensebem.cpp
pbem2017 // 2017 TecToy Pense Bem
@source:pentagon.cpp @source:pentagon.cpp
pent1024 // pent1024 //
pentagon // pentagon //