From 20f91f29740f4ba74117716daf6e92e9542c43df Mon Sep 17 00:00:00 2001 From: hap Date: Tue, 24 Dec 2024 21:40:51 +0100 Subject: e0c6s46: add e0c6s48 New working systems ------------------- Angel Gotch (Japan) [hap, azya] Mothra no Tamagotch (Japan) [hap, azya] --- src/devices/cpu/e0c6200/e0c6200.cpp | 20 ++++---- src/devices/cpu/e0c6200/e0c6200.h | 1 + src/devices/cpu/e0c6200/e0c6200op.cpp | 5 ++ src/devices/cpu/e0c6200/e0c6s46.cpp | 77 ++++++++++++++++++++++--------- src/devices/cpu/e0c6200/e0c6s46.h | 56 +++++++++++++++-------- src/mame/handheld/hh_e0c6x.cpp | 86 +++++++++++++++++++++++++++++++++-- src/mame/mame.lst | 2 + 7 files changed, 191 insertions(+), 56 deletions(-) diff --git a/src/devices/cpu/e0c6200/e0c6200.cpp b/src/devices/cpu/e0c6200/e0c6200.cpp index e8c8852af9c..83c3eec704b 100644 --- a/src/devices/cpu/e0c6200/e0c6200.cpp +++ b/src/devices/cpu/e0c6200/e0c6200.cpp @@ -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 diff --git a/src/devices/cpu/e0c6200/e0c6200.h b/src/devices/cpu/e0c6200/e0c6200.h index 0024092e02a..58c4769d55c 100644 --- a/src/devices/cpu/e0c6200/e0c6200.h +++ b/src/devices/cpu/e0c6200/e0c6200.h @@ -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); diff --git a/src/devices/cpu/e0c6200/e0c6200op.cpp b/src/devices/cpu/e0c6200/e0c6200op.cpp index 93ab8011f7d..d637c3cb312 100644 --- a/src/devices/cpu/e0c6200/e0c6200op.cpp +++ b/src/devices/cpu/e0c6200/e0c6200op.cpp @@ -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) diff --git a/src/devices/cpu/e0c6200/e0c6s46.cpp b/src/devices/cpu/e0c6200/e0c6s46.cpp index 8b92eab28ea..02b8d25faca 100644 --- a/src/devices/cpu/e0c6200/e0c6s46.cpp +++ b/src/devices/cpu/e0c6200/e0c6s46.cpp @@ -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; diff --git a/src/devices/cpu/e0c6200/e0c6s46.h b/src/devices/cpu/e0c6200/e0c6s46.h index d836109c6ef..e0b7148423e 100644 --- a/src/devices/cpu/e0c6200/e0c6s46.h +++ b/src/devices/cpu/e0c6200/e0c6s46.h @@ -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 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 diff --git a/src/mame/handheld/hh_e0c6x.cpp b/src/mame/handheld/hh_e0c6x.cpp index ef73da5e8c9..1e776dac2de 100644 --- a/src/mame/handheld/hh_e0c6x.cpp +++ b/src/mame/handheld/hh_e0c6x.cpp @@ -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 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 ) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index e91c29ca4e9..6e096f5fd76 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -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 -- cgit v1.2.3