e0c6s46: add e0c6s48

New working systems
-------------------
Angel Gotch (Japan) [hap, azya]
Mothra no Tamagotch (Japan) [hap, azya]
This commit is contained in:
hap 2024-12-24 21:40:51 +01:00
parent 7772559b28
commit 20f91f2974
7 changed files with 191 additions and 56 deletions

View File

@ -4,17 +4,17 @@
Seiko Epson E0C6200 CPU core and E0C62 MCU family
References:
- 1998 MF297-06a E0C6200/E0C6200A Core CPU Manual
- 1998 MF1049-01a E0C6S46 Technical Manual
References:
- 1998 MF297-06a E0C6200/E0C6200A Core CPU Manual
- 1998 MF1049-01a E0C6S46 Technical Manual
E0C6200 is a CPU core used as the basis of many chips, it is not standalone.
Seiko Epson often changed prefixes of their device names. Depending on when,
the E0C6200 is known as SMC6200, E0C6200, S1C6200.
E0C6200 is a CPU core used as the basis of many chips, it is not standalone.
Seiko Epson often changed prefixes of their device names. Depending on when,
the E0C6200 is known as SMC6200, E0C6200, S1C6200.
TODO:
- RLC is part of the r,q opcodes and requires that r == q, what happens otherwise?
- documentation is conflicting on whether or not the zero flag is set on RLC/RRC
TODO:
- RLC is part of the r,q opcodes and requires that r == q, what happens otherwise?
- documentation is conflicting on whether or not the zero flag is set on RLC/RRC
*/
@ -839,7 +839,7 @@ void e0c6200_cpu_device::execute_one()
// illegal opcode
default:
logerror("unknown opcode $%03X at $%04X\n", m_op, m_prev_pc);
op_illegal();
break;
} // 0xff0

View File

@ -91,6 +91,7 @@ protected:
void inc_x();
void inc_y();
void do_branch(int condition = 1);
void op_illegal();
// opcode handlers
u8 op_inc(u8 x);

View File

@ -116,6 +116,11 @@ void e0c6200_cpu_device::do_branch(int condition)
m_pc = m_jpc | (m_op & 0xff);
}
void e0c6200_cpu_device::op_illegal()
{
logerror("unknown opcode $%03X at $%04X\n", m_op, m_prev_pc);
}
// common opcodes (simpler ones are handled directly)
// note: it is implied that all opcodes below except RRC take 7 clock cycles (5 already deducted)

View File

@ -2,16 +2,18 @@
// copyright-holders:hap
/*
Seiko Epson E0C6S46 MCU
QFP5-128pin, see manual for pinout
Seiko Epson E0C6S46 family
TODO:
- finish i/o ports
- serial interface
- buzzer envelope addition
- what happens if OSC3 is selected while OSCC (bit 2) is low?
- K input interrupt can trigger if input is active while writing to the mask register
- add mask options for ports (eg. buzzer on output port R4x is optional)
E0C6S46: 6144x12 ROM, 640x4 RAM, 2*80x4 VRAM, LCD has 16 commons and 40 segments
E0C6S48: 8192x12 ROM, 768x4 RAM, 2*102x4 VRAM, LCD has 16 commons and 51 segments
TODO:
- finish i/o ports
- serial interface
- buzzer envelope addition
- what happens if OSC3 is selected while OSCC (bit 2) is low?
- K input interrupt can trigger if input is active while writing to the mask register
- add mask options for ports (eg. buzzer on output port R4x is optional)
*/
@ -28,17 +30,23 @@ enum
IRQREG_INPUT1
};
// device definitions
DEFINE_DEVICE_TYPE(E0C6S46, e0c6s46_device, "e0c6s46", "Seiko Epson E0C6S46")
DEFINE_DEVICE_TYPE(E0C6S48, e0c6s48_device, "e0c6s48", "Seiko Epson E0C6S48")
// internal memory maps
void e0c6s46_device::e0c6s46_program(address_map &map)
void e0c6s46_device::program_map(address_map &map)
{
map(0x0000, 0x17ff).rom();
}
void e0c6s48_device::program_map(address_map &map)
{
map(0x0000, 0x1fff).rom();
}
void e0c6s46_device::e0c6s46_data(address_map &map)
void e0c6s46_device::data_map(address_map &map)
{
map(0x0000, 0x027f).ram();
map(0x0e00, 0x0e4f).ram().share(m_vram[0]);
@ -46,20 +54,35 @@ void e0c6s46_device::e0c6s46_data(address_map &map)
map(0x0f00, 0x0f7f).rw(FUNC(e0c6s46_device::io_r), FUNC(e0c6s46_device::io_w));
}
void e0c6s48_device::data_map(address_map &map)
{
map(0x0000, 0x02ff).ram();
map(0x0e00, 0x0e65).ram().share(m_vram[0]);
map(0x0e80, 0x0ee5).ram().share(m_vram[1]);
map(0x0f00, 0x0f7f).rw(FUNC(e0c6s48_device::io_r), FUNC(e0c6s48_device::io_w));
}
// device definitions
e0c6s46_device::e0c6s46_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
e0c6200_cpu_device(mconfig, E0C6S46, tag, owner, clock, address_map_constructor(FUNC(e0c6s46_device::e0c6s46_program), this), address_map_constructor(FUNC(e0c6s46_device::e0c6s46_data), this)),
// constructor
e0c6s46_device::e0c6s46_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor program, address_map_constructor data) :
e0c6200_cpu_device(mconfig, type, tag, owner, clock, program, data),
m_vram(*this, "vram%u", 1U),
m_osc3(0),
m_write_segs(*this),
m_write_contrast(*this),
m_pixel_cb(*this),
m_write_r(*this),
m_read_p(*this, 0),
m_write_p(*this)
m_write_p(*this),
m_osc3(0)
{ }
e0c6s46_device::e0c6s46_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
e0c6s46_device(mconfig, E0C6S46, tag, owner, clock, address_map_constructor(FUNC(e0c6s46_device::program_map), this), address_map_constructor(FUNC(e0c6s46_device::data_map), this))
{ }
e0c6s48_device::e0c6s48_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
e0c6s46_device(mconfig, E0C6S48, tag, owner, clock, address_map_constructor(FUNC(e0c6s48_device::program_map), this), address_map_constructor(FUNC(e0c6s48_device::data_map), this))
{ }
//-------------------------------------------------
@ -78,9 +101,8 @@ void e0c6s46_device::device_start()
m_core_256_handle = timer_alloc(FUNC(e0c6s46_device::core_256_cb), this);
m_core_256_handle->adjust(attotime::from_ticks(64, m_osc1));
m_prgtimer_handle = timer_alloc(FUNC(e0c6s46_device::prgtimer_cb), this);
m_prgtimer_handle->adjust(attotime::never);
m_buzzer_handle = timer_alloc(FUNC(e0c6s46_device::buzzer_cb), this);
m_buzzer_handle->adjust(attotime::never);
m_osc_change = timer_alloc(FUNC(e0c6s46_device::osc_change), this);
m_lcd_driver = timer_alloc(FUNC(e0c6s46_device::lcd_driver_cb), this);
m_lcd_driver->adjust(attotime::from_ticks(1024, m_osc1));
@ -226,9 +248,9 @@ void e0c6s46_device::execute_one()
{
// E0C6S46 has no support for SLP opcode
if (m_op == 0xff9)
return;
e0c6200_cpu_device::execute_one();
op_illegal();
else
e0c6200_cpu_device::execute_one();
}
@ -394,6 +416,12 @@ TIMER_CALLBACK_MEMBER(e0c6s46_device::core_256_cb)
clock_clktimer();
}
TIMER_CALLBACK_MEMBER(e0c6s46_device::osc_change)
{
// set MCU instruction clock on CLKCHG change
set_clock((m_osc & 8) ? m_osc3 : m_osc1);
}
// clock-timer
@ -790,7 +818,12 @@ void e0c6s46_device::io_w(offs_t offset, u8 data)
// d2: OSC3 on (high freq)
// d3: clock source OSC1 or OSC3
if ((m_osc ^ data) & 8)
set_clock((data & 8) ? m_osc3 : m_osc1);
{
if (m_osc1 == m_osc3)
logerror("io_w unhandled OSC change, PC=$%04X\n", m_prev_pc);
else
m_osc_change->adjust(attotime::zero);
}
m_osc = data;
break;

View File

@ -2,7 +2,7 @@
// copyright-holders:hap
/*
Seiko Epson E0C6S46 MCU
Seiko Epson E0C6S46 family
*/
@ -11,6 +11,19 @@
#include "e0c6200.h"
// for the 2 K input ports, use set_input_line(line, state)
enum
{
E0C6S46_LINE_K00 = 0,
E0C6S46_LINE_K01,
E0C6S46_LINE_K02,
E0C6S46_LINE_K03,
E0C6S46_LINE_K10,
E0C6S46_LINE_K11,
E0C6S46_LINE_K12,
E0C6S46_LINE_K13
};
enum
{
E0C6S46_PORT_R0X = 0,
@ -28,18 +41,7 @@ enum
E0C6S46_PORT_P3X
};
// for the 2 K input ports, use set_input_line(line, state)
enum
{
E0C6S46_LINE_K00 = 0,
E0C6S46_LINE_K01,
E0C6S46_LINE_K02,
E0C6S46_LINE_K03,
E0C6S46_LINE_K10,
E0C6S46_LINE_K11,
E0C6S46_LINE_K12,
E0C6S46_LINE_K13
};
// no pinout diagram here, refer to the manual
class e0c6s46_device : public e0c6200_cpu_device
@ -72,6 +74,8 @@ public:
void set_osc3(u32 osc) { m_osc3 = osc; }
protected:
e0c6s46_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor program, address_map_constructor data);
// device-level overrides
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
@ -81,20 +85,18 @@ protected:
virtual void execute_one() override;
virtual bool check_interrupt() override;
void e0c6s46_data(address_map &map) ATTR_COLD;
void e0c6s46_program(address_map &map) ATTR_COLD;
private:
u8 io_r(offs_t offset);
void io_w(offs_t offset, u8 data);
required_shared_ptr_array<u8, 2> m_vram;
private:
void program_map(address_map &map) ATTR_COLD;
void data_map(address_map &map) ATTR_COLD;
u8 m_irqflag[6];
u8 m_irqmask[6];
u8 m_osc;
u32 m_osc1;
u32 m_osc3;
u8 m_svd;
// lcd driver
@ -167,9 +169,25 @@ private:
void schedule_buzzer();
void reset_buzzer();
void clock_bz_1shot();
u32 m_osc1;
u32 m_osc3;
emu_timer *m_osc_change;
TIMER_CALLBACK_MEMBER(osc_change);
};
class e0c6s48_device : public e0c6s46_device
{
public:
e0c6s48_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
private:
void program_map(address_map &map) ATTR_COLD;
void data_map(address_map &map) ATTR_COLD;
};
DECLARE_DEVICE_TYPE(E0C6S46, e0c6s46_device)
DECLARE_DEVICE_TYPE(E0C6S48, e0c6s48_device)
#endif // MAME_CPU_E0C6200_E0C6S46_H

View File

@ -11,8 +11,8 @@ to play the games for a longer time. For the drivers that don't have an SVG
screen, use -prescale or -nofilter to disable bilinear filtering.
TODO:
- add the Mothra Tamagotchi version that was recently dumped (has a E0C6S48)
- SVGs could be more accurate? it seems they're handmade instead of a 1:1 scan
like for eg. the Game & Watch LCDs
- alienfev unmapped reads/writes, or are they harmless?
- add LCD deflicker like hh_sm510? see venusdm for example
- hook up LCD contrast, does any game use it? (eg. for fade-out)
@ -51,7 +51,7 @@ protected:
void lcd_segment_w(offs_t offset, u8 data) { m_out_x[offset & 0xf][offset >> 4] = data; }
required_device<e0c6s46_device> m_maincpu;
output_finder<16, 40> m_out_x;
output_finder<16, 51> m_out_x; // max 16 * 51
};
void hh_e0c6x_state::machine_start()
@ -93,7 +93,7 @@ INPUT_CHANGED_MEMBER(hh_e0c6x_state::input_changed)
* Seiko Epson E0C6S46 MCU under epoxy
* 32*16 LCD screen + 8 custom segments, 1-bit sound
Generation 2 is on the exact same hardware
Generation 2 is on the exact same hardware.
*******************************************************************************/
@ -164,13 +164,87 @@ ROM_END
/*******************************************************************************
Bandai Tamagotchi Angel (aka Angel Gotch in Japan)
* Seiko Epson E0C6S48
* 32*16 LCD screen + 8 custom segments, 1-bit sound
Mothra no Tamagotch is on similar hardware.
*******************************************************************************/
class tamaang_state : public hh_e0c6x_state
{
public:
tamaang_state(const machine_config &mconfig, device_type type, const char *tag) :
hh_e0c6x_state(mconfig, type, tag)
{ }
void tamaang(machine_config &config);
};
// inputs
static INPUT_PORTS_START( tamaang )
PORT_INCLUDE( tama )
PORT_MODIFY("K0")
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_CHANGED_CB(3) PORT_NAME("Vibration Sensor")
INPUT_PORTS_END
// config
void tamaang_state::tamaang(machine_config &config)
{
// basic machine hardware
E0C6S48(config, m_maincpu, 32.768_kHz_XTAL);
m_maincpu->set_osc3(1'000'000);
m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
m_maincpu->write_segs().set(FUNC(tamaang_state::lcd_segment_w));
// video hardware
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
screen.set_refresh_hz(32);
screen.set_size(1119, 1080);
screen.set_visarea_full();
config.set_default_layout(layout_hh_e0c6x_lcd);
// sound hardware
SPEAKER(config, "mono").front_center();
SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.25);
}
// roms
ROM_START( tamaang )
ROM_REGION( 0x4000, "maincpu", 0 )
ROM_LOAD( "tamaang.bin", 0x0000, 0x4000, CRC(87bcb59f) SHA1(f5899bb7717756ac581451cf16cf97d909961c5c) )
ROM_REGION( 139978, "screen", 0)
ROM_LOAD( "tamaang.svg", 0, 139978, CRC(76f27f06) SHA1(b416275a12173316e053fa994c5fd68a4d5c1a5c) )
ROM_END
ROM_START( tamamot )
ROM_REGION( 0x4000, "maincpu", 0 )
ROM_LOAD( "tamamot.bin", 0x0000, 0x4000, CRC(85e4bee9) SHA1(74c1f6761724b7cbda8bca3113db78586b786d2d) )
ROM_REGION( 138289, "screen", 0)
ROM_LOAD( "tamamot.svg", 0, 138289, CRC(4e8210c2) SHA1(522536ae5bf744889c0d028c3a292bdf649f81e3) )
ROM_END
/*******************************************************************************
Epoch Chibi Pachi: Alien Fever
* Seiko Epson E0C6S46 MCU
* 39*16 LCD screen, 1-bit sound
It's a Pachislot keychain game, the MCU runs on the higher-speed OSC3.
It's a Pachislot keychain game, the MCU constantly runs on the higher-speed OSC3.
*******************************************************************************/
@ -200,8 +274,8 @@ void alienfev_state::alienfev(machine_config &config)
{
// basic machine hardware
E0C6S46(config, m_maincpu, 32.768_kHz_XTAL);
m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
m_maincpu->set_osc3(1'000'000);
m_maincpu->write_r<4>().set("speaker", FUNC(speaker_sound_device::level_w)).bit(3);
// video hardware
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
@ -310,6 +384,8 @@ ROM_END
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS
SYST( 1997, tama, 0, 0, tama, tama, tama_state, empty_init, "Bandai", "Tamagotchi (Gen. 1, World)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamag2, 0, 0, tama, tama, tama_state, empty_init, "Bandai", "Tamagotchi (Gen. 2, Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamaang, 0, 0, tamaang, tamaang, tamaang_state, empty_init, "Bandai", "Angel Gotch (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, tamamot, 0, 0, tamaang, tama, tamaang_state, empty_init, "Bandai", "Mothra no Tamagotch (Japan)", MACHINE_SUPPORTS_SAVE | MACHINE_REQUIRES_ARTWORK )
SYST( 1997, alienfev, 0, 0, alienfev, alienfev, alienfev_state, empty_init, "Epoch", "Chibi Pachi: Alien Fever", MACHINE_SUPPORTS_SAVE )

View File

@ -19148,7 +19148,9 @@ qkspeller // National Semiconductor
@source:handheld/hh_e0c6x.cpp
alienfev // Epoch
tama // Bandai
tamaang // Bandai
tamag2 // Bandai
tamamot // Bandai
venusdm // Nikko
@source:handheld/hh_hmcs40.cpp