1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
// 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 "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);
uint8_t port_b_r();
void port_b_w(uint8_t data);
void port_c_w(uint8_t data);
void port_d_w(uint8_t data);
void port_e_w(uint8_t data);
void update_display();
uint8_t m_port_b = 0;
uint8_t m_port_c = 0;
uint8_t m_port_d = 0;
uint8_t m_port_e = 0;
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()
{
save_item(NAME(m_port_b));
save_item(NAME(m_port_c));
save_item(NAME(m_port_d));
save_item(NAME(m_port_e));
}
void pensebem2017_state::machine_reset()
{
m_port_b = 0;
m_port_c = 0;
m_port_d = 0;
m_port_e = 0;
}
uint8_t pensebem2017_state::port_b_r()
{
uint8_t value = m_port_b & 0xc3;
int bit;
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;
}
void pensebem2017_state::port_b_w(uint8_t data) // buzzer + keyboard select rows
{
m_port_b = data;
m_dac->write(BIT(data, 1));
}
void pensebem2017_state::port_c_w(uint8_t data) // display
{
m_port_c = data;
update_display();
}
void pensebem2017_state::port_d_w(uint8_t data) // display
{
m_port_d = data;
}
void pensebem2017_state::port_e_w(uint8_t data) // display
{
m_port_e = data;
update_display();
}
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 ! */
}
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::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_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);
m_maincpu->gpio_in<AVR8_IO_PORTB>().set(FUNC(pensebem2017_state::port_b_r));
m_maincpu->gpio_out<AVR8_IO_PORTB>().set(FUNC(pensebem2017_state::port_b_w));
m_maincpu->gpio_out<AVR8_IO_PORTC>().set(FUNC(pensebem2017_state::port_c_w));
m_maincpu->gpio_out<AVR8_IO_PORTD>().set(FUNC(pensebem2017_state::port_d_w));
m_maincpu->gpio_out<AVR8_IO_PORTE>().set(FUNC(pensebem2017_state::port_e_w));
/* 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)
|