summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/hmcs400/hmcs400.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/hmcs400/hmcs400.cpp')
-rw-r--r--src/devices/cpu/hmcs400/hmcs400.cpp1024
1 files changed, 1024 insertions, 0 deletions
diff --git a/src/devices/cpu/hmcs400/hmcs400.cpp b/src/devices/cpu/hmcs400/hmcs400.cpp
new file mode 100644
index 00000000000..3d41bd1f00e
--- /dev/null
+++ b/src/devices/cpu/hmcs400/hmcs400.cpp
@@ -0,0 +1,1024 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/*
+
+Hitachi HMCS400 MCU family cores
+
+It's the successor to HMCS40, it was mainly used in consumer electronics, not
+much in games.
+
+Compared to HMCS40, it accepts a higher clock speed, and it has more versatile
+peripherals, like a serial interface. The opcodes were mostly kept the same.
+They added an extra RAM addressing mode, and interrupt-related opcodes were
+removed (interrupt flags are via memory-mapped I/O).
+
+TODO:
+- add serial interface
+- do the LAW/LWA opcodes not work on early revisions of HMCS400? the 1988 user
+ manual warns that the W register is write-only, and that there is no efficient
+ way to save this register when using interrupts
+- what happens when accessing ROM/RAM out of address range? Hitachi documentation
+ says 'unused', but maybe it's mirrored?
+
+*/
+
+#include "emu.h"
+#include "hmcs400.h"
+#include "hmcs400d.h"
+
+
+//-------------------------------------------------
+// device types
+//-------------------------------------------------
+
+// C = standard
+// CL = low-power
+// AC = high-speed
+
+// HMCS408, HMCS41x, HMCS42x have a mask option for the system clock divider
+// HMCS41x don't have timer A or serial interface, HMCS41x and HMCS42x have less I/O pins
+// rev 2 apparently added LAW/LWA opcodes?
+
+// HMCS402C/CL/AC, DP-64S or FP-64, 2Kx10 ROM, 160x4 RAM
+DEFINE_DEVICE_TYPE(HD614022, hd614022_device, "hd614022", "Hitachi HD614022") // C, rev 2
+DEFINE_DEVICE_TYPE(HD614023, hd614023_device, "hd614023", "Hitachi HD614023") // C, rev 1
+DEFINE_DEVICE_TYPE(HD614025, hd614025_device, "hd614025", "Hitachi HD614025") // CL, rev 2
+DEFINE_DEVICE_TYPE(HD614026, hd614026_device, "hd614026", "Hitachi HD614026") // CL, rev 1
+DEFINE_DEVICE_TYPE(HD614028, hd614028_device, "hd614028", "Hitachi HD614028") // AC, rev 2
+DEFINE_DEVICE_TYPE(HD614029, hd614029_device, "hd614029", "Hitachi HD614029") // AC, rev 1
+
+// HMCS404C/CL/AC, DP-64S or FP-64, 4Kx10 ROM, 256x4 RAM
+DEFINE_DEVICE_TYPE(HD614042, hd614042_device, "hd614042", "Hitachi HD614042") // C, rev 2
+DEFINE_DEVICE_TYPE(HD614043, hd614043_device, "hd614043", "Hitachi HD614043") // C, rev 1
+DEFINE_DEVICE_TYPE(HD614045, hd614045_device, "hd614045", "Hitachi HD614045") // CL, rev 2
+DEFINE_DEVICE_TYPE(HD614046, hd614046_device, "hd614046", "Hitachi HD614046") // CL, rev 1
+DEFINE_DEVICE_TYPE(HD614048, hd614048_device, "hd614048", "Hitachi HD614048") // AC, rev 2
+DEFINE_DEVICE_TYPE(HD614049, hd614049_device, "hd614049", "Hitachi HD614049") // AC, rev 1
+
+// HMCS408C/CL/AC, DP-64S or FP-64, 8Kx10 ROM, 512x4 RAM
+DEFINE_DEVICE_TYPE(HD614080, hd614080_device, "hd614080", "Hitachi HD614080") // C, rev 2
+DEFINE_DEVICE_TYPE(HD614081, hd614081_device, "hd614081", "Hitachi HD614081") // C, rev 1
+DEFINE_DEVICE_TYPE(HD614085, hd614085_device, "hd614085", "Hitachi HD614085") // CL, rev 2
+DEFINE_DEVICE_TYPE(HD614086, hd614086_device, "hd614086", "Hitachi HD614086") // CL, rev 1
+DEFINE_DEVICE_TYPE(HD614088, hd614088_device, "hd614088", "Hitachi HD614088") // AC, rev 2
+DEFINE_DEVICE_TYPE(HD614089, hd614089_device, "hd614089", "Hitachi HD614089") // AC, rev 1
+
+// HMCS412C/CL/AC, DP-42/DP-42S or FP-44A, 2Kx10 ROM, 160x4 RAM
+DEFINE_DEVICE_TYPE(HD614120, hd614120_device, "hd614120", "Hitachi HD614120") // C
+DEFINE_DEVICE_TYPE(HD614125, hd614125_device, "hd614125", "Hitachi HD614125") // CL
+DEFINE_DEVICE_TYPE(HD614128, hd614128_device, "hd614128", "Hitachi HD614128") // AC
+
+// HMCS414C/CL/AC, DP-42/DP-42S or FP-44A, 4Kx10 ROM, 160x4 RAM
+DEFINE_DEVICE_TYPE(HD614140, hd614140_device, "hd614140", "Hitachi HD614140") // C
+DEFINE_DEVICE_TYPE(HD614145, hd614145_device, "hd614145", "Hitachi HD614145") // CL
+DEFINE_DEVICE_TYPE(HD614148, hd614148_device, "hd614148", "Hitachi HD614148") // AC
+
+// HMCS424C/CL/AC, DP-42/DP-42S or FP-44A, 4Kx10 ROM, 256x4 RAM
+DEFINE_DEVICE_TYPE(HD404240, hd404240_device, "hd404240", "Hitachi HD404240") // C
+DEFINE_DEVICE_TYPE(HD40L4240, hd40l4240_device, "hd40l4240", "Hitachi HD40L4240") // CL
+DEFINE_DEVICE_TYPE(HD40A4240, hd40a4240_device, "hd40a4240", "Hitachi HD40A4240") // AC
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+hmcs400_cpu_device::hmcs400_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 rom_size, u32 ram_size) :
+ cpu_device(mconfig, type, tag, owner, clock),
+ device_nvram_interface(mconfig, *this),
+ m_program_config("program", ENDIANNESS_LITTLE, 16, 14, -1, address_map_constructor(FUNC(hmcs400_cpu_device::program_map), this)),
+ m_data_config("data", ENDIANNESS_LITTLE, 8, 10, 0, address_map_constructor(FUNC(hmcs400_cpu_device::data_map), this)),
+ m_ram(*this, "ram%u", 0U),
+ m_nvram_defval(0),
+ m_nvram_battery(true),
+ m_rom_size(rom_size),
+ m_ram_size(ram_size),
+ m_has_div(false),
+ m_divider(8),
+ m_read_r(*this, 0),
+ m_write_r(*this),
+ m_read_d(*this, 0),
+ m_write_d(*this),
+ m_stop_cb(*this)
+{
+ // disable nvram by default (set to true if MCU is battery-backed when in stop mode)
+ nvram_enable_backup(false);
+}
+
+hmcs400_cpu_device::~hmcs400_cpu_device() { }
+
+
+hmcs402_cpu_device::hmcs402_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs400_cpu_device(mconfig, type, tag, owner, clock, 0x800, 96)
+{ }
+
+hd614022_device::hd614022_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614022, tag, owner, clock)
+{ }
+hd614023_device::hd614023_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614023, tag, owner, clock)
+{ }
+hd614025_device::hd614025_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614025, tag, owner, clock)
+{ }
+hd614026_device::hd614026_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614026, tag, owner, clock)
+{ }
+hd614028_device::hd614028_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614028, tag, owner, clock)
+{ }
+hd614029_device::hd614029_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs402_cpu_device(mconfig, HD614029, tag, owner, clock)
+{ }
+
+
+hmcs404_cpu_device::hmcs404_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs400_cpu_device(mconfig, type, tag, owner, clock, 0x1000, 192)
+{ }
+
+hd614042_device::hd614042_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614042, tag, owner, clock)
+{ }
+hd614043_device::hd614043_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614043, tag, owner, clock)
+{ }
+hd614045_device::hd614045_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614045, tag, owner, clock)
+{ }
+hd614046_device::hd614046_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614046, tag, owner, clock)
+{ }
+hd614048_device::hd614048_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614048, tag, owner, clock)
+{ }
+hd614049_device::hd614049_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs404_cpu_device(mconfig, HD614049, tag, owner, clock)
+{ }
+
+
+hmcs408_cpu_device::hmcs408_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs400_cpu_device(mconfig, type, tag, owner, clock, 0x2000, 448)
+{
+ m_has_div = true;
+}
+
+hd614080_device::hd614080_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614080, tag, owner, clock)
+{ }
+hd614081_device::hd614081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614081, tag, owner, clock)
+{ }
+hd614085_device::hd614085_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614085, tag, owner, clock)
+{ }
+hd614086_device::hd614086_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614086, tag, owner, clock)
+{ }
+hd614088_device::hd614088_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614088, tag, owner, clock)
+{ }
+hd614089_device::hd614089_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs408_cpu_device(mconfig, HD614089, tag, owner, clock)
+{ }
+
+
+hmcs41x_cpu_device::hmcs41x_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 rom_size, u32 ram_size) :
+ hmcs400_cpu_device(mconfig, type, tag, owner, clock, rom_size, ram_size)
+{
+ m_has_div = true;
+}
+
+hmcs412_cpu_device::hmcs412_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs41x_cpu_device(mconfig, type, tag, owner, clock, 0x800, 96)
+{ }
+
+hd614120_device::hd614120_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs412_cpu_device(mconfig, HD614120, tag, owner, clock)
+{ }
+hd614125_device::hd614125_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs412_cpu_device(mconfig, HD614125, tag, owner, clock)
+{ }
+hd614128_device::hd614128_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs412_cpu_device(mconfig, HD614128, tag, owner, clock)
+{ }
+
+
+hmcs414_cpu_device::hmcs414_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs41x_cpu_device(mconfig, type, tag, owner, clock, 0x1000, 96)
+{ }
+
+hd614140_device::hd614140_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs414_cpu_device(mconfig, HD614140, tag, owner, clock)
+{ }
+hd614145_device::hd614145_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs414_cpu_device(mconfig, HD614145, tag, owner, clock)
+{ }
+hd614148_device::hd614148_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs414_cpu_device(mconfig, HD614148, tag, owner, clock)
+{ }
+
+
+hmcs424_cpu_device::hmcs424_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
+ hmcs41x_cpu_device(mconfig, type, tag, owner, clock, 0x1000, 192)
+{ }
+
+hd404240_device::hd404240_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs424_cpu_device(mconfig, HD404240, tag, owner, clock)
+{ }
+hd40l4240_device::hd40l4240_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs424_cpu_device(mconfig, HD40L4240, tag, owner, clock)
+{ }
+hd40a4240_device::hd40a4240_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ hmcs424_cpu_device(mconfig, HD40A4240, tag, owner, clock)
+{ }
+
+
+//-------------------------------------------------
+// initialization
+//-------------------------------------------------
+
+void hmcs400_cpu_device::device_start()
+{
+ m_program = &space(AS_PROGRAM);
+ m_data = &space(AS_DATA);
+
+ // zerofill
+ m_pc = 0;
+ m_prev_pc = 0;
+ m_sp = 0;
+ m_op = 0;
+ m_param = 0;
+ m_i = 0;
+
+ m_a = 0;
+ m_b = 0;
+ m_w = 0;
+ m_x = 0;
+ m_spx = 0;
+ m_y = 0;
+ m_spy = 0;
+ m_st = 0;
+ m_ca = 0;
+ m_standby = false;
+ m_stop = false;
+
+ memset(m_r, 0, sizeof(m_r));
+ memset(m_r_mask, 0, sizeof(m_r_mask));
+ m_r_dir = 0;
+ m_d = 0;
+ m_d_mask = 0;
+
+ m_int_line[0] = m_int_line[1] = 1;
+ m_irq_flags = 0;
+ m_irq_mask = 0x33ff;
+ m_pmr = 0;
+ m_prescaler = 0;
+ m_timer_mode[0] = m_timer_mode[1] = 0;
+ m_timer_div[0] = m_timer_div[1] = 0;
+ m_timer_count[0] = m_timer_count[1] = 0;
+ m_timer_load = 0;
+ m_timer_b_low = 0;
+
+ // register for savestates
+ save_item(NAME(m_nvram_battery));
+ save_item(NAME(m_pc));
+ save_item(NAME(m_prev_pc));
+ save_item(NAME(m_sp));
+ save_item(NAME(m_op));
+
+ save_item(NAME(m_a));
+ save_item(NAME(m_b));
+ save_item(NAME(m_w));
+ save_item(NAME(m_x));
+ save_item(NAME(m_spx));
+ save_item(NAME(m_y));
+ save_item(NAME(m_spy));
+ save_item(NAME(m_st));
+ save_item(NAME(m_ca));
+ save_item(NAME(m_standby));
+ save_item(NAME(m_stop));
+
+ save_item(NAME(m_r));
+ save_item(NAME(m_r_mask));
+ save_item(NAME(m_r_dir));
+ save_item(NAME(m_d));
+ save_item(NAME(m_d_mask));
+
+ save_item(NAME(m_int_line));
+ save_item(NAME(m_irq_flags));
+ save_item(NAME(m_pmr));
+ save_item(NAME(m_prescaler));
+ save_item(NAME(m_timer_mode));
+ save_item(NAME(m_timer_div));
+ save_item(NAME(m_timer_count));
+ save_item(NAME(m_timer_load));
+ save_item(NAME(m_timer_b_low));
+
+ // register state for debugger
+ state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%04X").noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).formatstr("%04X").noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_st).formatstr("%2s").noshow();
+
+ m_state_count = 0;
+ state_add(++m_state_count, "PC", m_pc).formatstr("%04X"); // 1
+ state_add(++m_state_count, "SP", m_sp).formatstr("%03X"); // 2
+ state_add(++m_state_count, "A", m_a).formatstr("%01X"); // 3
+ state_add(++m_state_count, "B", m_b).formatstr("%01X"); // 4
+ state_add(++m_state_count, "W", m_w).formatstr("%01X"); // 5
+ state_add(++m_state_count, "X", m_x).formatstr("%01X"); // 6
+ state_add(++m_state_count, "SPX", m_spx).formatstr("%01X"); // 7
+ state_add(++m_state_count, "Y", m_y).formatstr("%01X"); // 8
+ state_add(++m_state_count, "SPY", m_spy).formatstr("%01X"); // 9
+
+ state_add(++m_state_count, "ST", m_st).formatstr("%01X").noshow(); // 10
+ state_add(++m_state_count, "CA", m_ca).formatstr("%01X").noshow(); // 11
+
+ set_icountptr(m_icount);
+}
+
+void hmcs41x_cpu_device::device_start()
+{
+ hmcs400_cpu_device::device_start();
+
+ // no timer A or serial interface
+ m_irq_mask = 0x033f;
+ m_data->unmap_readwrite(0x005, 0x008);
+}
+
+void hmcs400_cpu_device::device_reset()
+{
+ m_pc = 0;
+ m_sp = 0x3ff;
+ m_st = 1;
+ m_standby = false;
+ m_stop = false;
+ m_stop_cb(0);
+
+ // clear peripherals
+ m_irq_flags = 0xaaa8; // IM=1, IF=0, IE=0
+ m_pmr = 0;
+
+ m_prescaler = 0;
+ tm_w(0, 0, 0xf);
+ tm_w(1, 0, 0xf);
+ m_timer_count[0] = m_timer_count[1] = 0;
+ m_timer_load = 0;
+ m_timer_b_low = 0;
+
+ // all I/O ports set to input
+ reset_io();
+}
+
+
+//-------------------------------------------------
+// disasm
+//-------------------------------------------------
+
+void hmcs400_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("%c%c",
+ m_ca ? 'C':'c',
+ m_st ? 'S':'s'
+ );
+ break;
+
+ default: break;
+ }
+}
+
+std::unique_ptr<util::disasm_interface> hmcs400_cpu_device::create_disassembler()
+{
+ return std::make_unique<hmcs400_disassembler>();
+}
+
+
+//-------------------------------------------------
+// internal memory maps
+//-------------------------------------------------
+
+void hmcs400_cpu_device::program_map(address_map &map)
+{
+ map.unmap_value_high();
+ map(0, m_rom_size - 1).rom();
+}
+
+void hmcs400_cpu_device::data_map(address_map &map)
+{
+ map.unmap_value_high();
+ map(0x000, 0x003).rw(FUNC(hmcs400_cpu_device::irq_control_r), FUNC(hmcs400_cpu_device::irq_control_w));
+ map(0x004, 0x004).w(FUNC(hmcs400_cpu_device::pmr_w));
+ map(0x008, 0x009).w(FUNC(hmcs400_cpu_device::tm_w));
+ map(0x00a, 0x00a).rw(FUNC(hmcs400_cpu_device::tcbl_r), FUNC(hmcs400_cpu_device::tlrl_w));
+ map(0x00b, 0x00b).rw(FUNC(hmcs400_cpu_device::tcbu_r), FUNC(hmcs400_cpu_device::tlru_w));
+
+ map(0x020, 0x020 + m_ram_size - 1).ram().share(m_ram[0]);
+ map(0x3c0, 0x3ff).ram().share(m_ram[1]); // stack
+}
+
+device_memory_interface::space_config_vector hmcs400_cpu_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_DATA, &m_data_config)
+ };
+}
+
+
+//-------------------------------------------------
+// nvram
+//-------------------------------------------------
+
+bool hmcs400_cpu_device::nvram_write(util::write_stream &file)
+{
+ // if it's currently not battery-backed, don't save at all
+ if (!m_nvram_battery)
+ return true;
+
+ std::error_condition err;
+ size_t actual;
+
+ // main RAM and stack area
+ for (auto & ram : m_ram)
+ {
+ std::tie(err, actual) = write(file, &ram[0], ram.bytes());
+ if (err)
+ return false;
+ }
+
+ return true;
+}
+
+bool hmcs400_cpu_device::nvram_read(util::read_stream &file)
+{
+ std::error_condition err;
+ size_t actual;
+
+ // main RAM and stack area
+ for (auto & ram : m_ram)
+ {
+ std::tie(err, actual) = read(file, &ram[0], ram.bytes());
+ if (err || (ram.bytes() != actual))
+ return false;
+ }
+
+ return true;
+}
+
+void hmcs400_cpu_device::nvram_default()
+{
+ if (!nvram_backup_enabled())
+ return;
+
+ // default nvram from mytag:nvram region if it exists
+ memory_region *region = memregion("nvram");
+ if (region != nullptr)
+ {
+ const u32 total = m_ram[0].bytes() + m_ram[1].bytes();
+ if (region->bytes() != total)
+ fatalerror("%s: Wrong region size (expected 0x%x, found 0x%x)", region->name(), total, region->bytes());
+
+ u32 offset = 0;
+ for (auto & ram : m_ram)
+ {
+ std::copy_n(&region->as_u8(offset), ram.bytes(), &ram[0]);
+ offset += ram.bytes();
+ }
+ }
+ else
+ {
+ for (auto & ram : m_ram)
+ std::fill_n(&ram[0], ram.bytes(), m_nvram_defval);
+ }
+}
+
+
+//-------------------------------------------------
+// i/o ports
+//-------------------------------------------------
+
+void hmcs400_cpu_device::reset_io()
+{
+ // D4-D15 are high-voltage
+ m_d_mask = m_d = 0x000f;
+ m_write_d(m_d_mask);
+
+ // R0 and R6-R8 are write-only, R9 and RA are read-only
+ m_r_dir = 0x16affe;
+
+ for (int i = 0; i < 11; i++)
+ {
+ // R0-R2 and RA are high-voltage
+ u8 mask = (i >= 3 && i <= 9) ? 0xf : 0;
+
+ m_r_mask[i] = m_r[i] = mask;
+ m_write_r[i](i, mask, 0xf);
+ }
+}
+
+void hmcs41x_cpu_device::reset_io()
+{
+ // does not have R5-R9
+ hmcs400_cpu_device::reset_io();
+ m_r_dir &= ~0xffc00;
+}
+
+u8 hmcs400_cpu_device::read_r(u8 index)
+{
+ // reads from write-only or non-existent ports are invalid
+ const u8 dir = m_r_dir >> (index * 2) & 3;
+ if (~dir & 1)
+ {
+ logerror("read from %s port R%X @ $%04X\n", (dir & 2) ? "output" : "unknown", index, m_prev_pc);
+ return 0xf;
+ }
+
+ u8 mask = (index == 10) ? 3 : 0xf; // port A is 2-bit
+ u8 inp = m_read_r[index](index, mask);
+
+ if (m_read_r[index].isunset())
+ {
+ inp = m_r_mask[index];
+ logerror("read from unmapped port R%X @ $%04X\n", index, m_prev_pc);
+ }
+
+ u8 out = m_r[index];
+
+ // R32/R33 are multiplexed with ext interrupts
+ if (index == 3)
+ {
+ u8 pmr_mask = m_pmr & 0xc;
+ u8 ext_int = m_int_line[1] << 3 | m_int_line[0] << 2;
+ inp = (inp & ~pmr_mask) | (ext_int & pmr_mask);
+ out = (out & ~pmr_mask) | (m_r_mask[index] & pmr_mask);
+ }
+
+ if (m_r_mask[index])
+ return (inp & out) & mask;
+ else
+ return (inp | out) & mask;
+}
+
+void hmcs400_cpu_device::write_r(u8 index, u8 data)
+{
+ data &= 0xf;
+
+ // ignore writes to read-only or non-existent ports
+ if (~m_r_dir >> (index * 2) & 2)
+ return;
+
+ if (m_write_r[index].isunset())
+ logerror("write $%X to unmapped port R%d @ $%04X\n", data, index, m_prev_pc);
+
+ m_r[index] = data;
+ u8 out = data;
+
+ // R32/R33 are multiplexed with ext interrupts
+ if (index == 3)
+ {
+ u8 pmr_mask = m_pmr & 0xc;
+ out = (out & ~pmr_mask) | (m_r_mask[index] & pmr_mask);
+ }
+
+ m_write_r[index](index, out, 0xf);
+}
+
+int hmcs400_cpu_device::read_d(u8 index)
+{
+ index &= 0xf;
+ u16 mask = 1 << index;
+ u16 inp = m_read_d(0, mask);
+
+ if (m_read_d.isunset())
+ {
+ inp = m_d_mask;
+ logerror("read from unmapped port D%d @ $%04X\n", index, m_prev_pc);
+ }
+
+ if (m_d_mask & mask)
+ return BIT(inp & m_d, index);
+ else
+ return BIT(inp | m_d, index);
+}
+
+void hmcs400_cpu_device::write_d(u8 index, int state)
+{
+ index &= 0xf;
+ u16 mask = 1 << index;
+
+ if (m_write_d.isunset())
+ logerror("write %d to unmapped port D%d @ $%04X\n", state, index, m_prev_pc);
+
+ m_d = (m_d & ~mask) | (state ? mask : 0);
+ m_write_d(0, m_d, mask);
+}
+
+
+//-------------------------------------------------
+// interrupts
+//-------------------------------------------------
+
+bool hmcs400_cpu_device::access_mode(u8 mem_mask, bool bit_mode)
+{
+ mem_mask &= 0xf;
+ bool err = true;
+
+ if (bit_mode)
+ {
+ if (population_count_32(mem_mask) == 1)
+ return true;
+ err = mem_mask == 0xf;
+ }
+ else
+ {
+ if (mem_mask == 0xf)
+ return true;
+ }
+
+ if (err || mem_mask == 0)
+ logerror("invalid access to I/O register @ $%04X\n", m_prev_pc);
+
+ return false;
+}
+
+u8 hmcs400_cpu_device::irq_control_r(offs_t offset, u8 mem_mask)
+{
+ // mask out unused bits (RSP is write-only)
+ const u16 unused = ~m_irq_mask | 2;
+ u16 data = m_irq_flags | unused;
+
+ if (!machine().side_effects_disabled())
+ {
+ // can only read one bit at a time
+ if (!access_mode(mem_mask, true))
+ return 0xf;
+
+ if (mem_mask << (offset * 4) & unused)
+ logerror("read from unused IRQ control bit @ $%04X\n", m_prev_pc);
+ }
+
+ return data >> (offset * 4) & 0xf;
+}
+
+void hmcs400_cpu_device::irq_control_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ // can only write one bit at a time
+ if (!access_mode(mem_mask, true))
+ return;
+
+ data &= mem_mask;
+ u16 mask = mem_mask << (offset * 4);
+
+ // ignore writes to unused bits
+ if (mask & ~m_irq_mask)
+ return;
+
+ // ignore writing 1 to flags that can only be cleared
+ if (mask & 0x5556 && data)
+ return;
+
+ // bit 1: RSP (reset SP)
+ if (mask & 0x0002)
+ m_sp = 0x3ff;
+
+ m_irq_flags = (m_irq_flags & ~mask) | (data ? mask : 0);
+}
+
+void hmcs400_cpu_device::pmr_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return;
+
+ u8 prev = m_pmr;
+ m_pmr = data;
+
+ // trigger irq on rising edge if ext int line was low
+ for (int i = 0; i < 2; i++)
+ if (BIT(data & ~prev, i + 2))
+ ext_int_edge(i);
+
+ // refresh R32/R33
+ if ((data ^ prev) & 0xc)
+ write_r(3, m_r[3]);
+}
+
+void hmcs400_cpu_device::take_interrupt(int irq)
+{
+ cycle();
+ cycle();
+ push_stack();
+ m_irq_flags &= ~1;
+
+ standard_irq_callback(irq, m_pc);
+
+ u8 vector = irq * 2 + 2;
+ m_prev_pc = m_pc = vector;
+}
+
+void hmcs400_cpu_device::check_interrupts()
+{
+ // irq priority is in the same order as the irq control flags
+ u16 irq = (m_irq_flags & m_irq_mask) >> 2;
+
+ for (int i = 0; i < 7; i++)
+ {
+ // pending irq when IF=1 and IM=0
+ if ((irq & 3) == 1)
+ {
+ if (m_irq_flags & 1)
+ take_interrupt(i);
+
+ m_standby = false;
+ return;
+ }
+
+ irq >>= 2;
+ }
+}
+
+void hmcs400_cpu_device::ext_int_edge(int line)
+{
+ // ext interrupts are masked with PMR2/3
+ if (!m_int_line[line] && BIT(m_pmr, line + 2))
+ {
+ m_irq_flags |= 1 << (line * 2 + 2);
+
+ // timer B event counter on INT1
+ if (line == 1 && !m_timer_div[1])
+ clock_timer(1);
+ }
+}
+
+void hmcs400_cpu_device::execute_set_input(int line, int state)
+{
+ state = state ? 1 : 0;
+
+ if (line != 0 && line != 1)
+ return;
+
+ // active-low, irq on falling edge
+ state ^= 1;
+ bool irq = (m_int_line[line] && !state);
+ m_int_line[line] = state;
+
+ if (irq && !m_stop)
+ ext_int_edge(line);
+}
+
+
+//-------------------------------------------------
+// timers
+//-------------------------------------------------
+
+void hmcs400_cpu_device::tm_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return;
+
+ // TMA/TMB prescaler divide ratio masks
+ static const u16 div[2][8] =
+ {
+ { 0x400, 0x200, 0x100, 0x40, 0x10, 4, 2, 1 },
+ { 0x400, 0x100, 0x40, 0x10, 4, 2, 1, 0 }
+ };
+
+ m_timer_mode[offset] = data & 0xf;
+ m_timer_div[offset] = div[offset][data & 7];
+}
+
+void hmcs400_cpu_device::tlrl_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return;
+
+ // TLRL: timer load register lower
+ m_timer_load = (m_timer_load & 0xf0) | (data & 0xf);
+}
+
+void hmcs400_cpu_device::tlru_w(offs_t offset, u8 data, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return;
+
+ // TLRU: timer load register upper
+ m_timer_load = (m_timer_load & 0x0f) | data << 4;
+ m_timer_count[1] = m_timer_load;
+}
+
+u8 hmcs400_cpu_device::tcbl_r(offs_t offset, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return 0xf;
+
+ // TCBL: timer counter B lower
+ return m_timer_b_low;
+}
+
+u8 hmcs400_cpu_device::tcbu_r(offs_t offset, u8 mem_mask)
+{
+ if (!access_mode(mem_mask))
+ return 0xf;
+
+ // TCBU: timer counter B upper (latches TCBL)
+ if (!machine().side_effects_disabled())
+ m_timer_b_low = m_timer_count[1] & 0xf;
+
+ return m_timer_count[1] >> 4;
+}
+
+void hmcs400_cpu_device::clock_timer(int timer)
+{
+ if (++m_timer_count[timer] == 0)
+ {
+ // set timer overflow irq flag
+ m_irq_flags |= 1 << (timer * 2 + 6);
+
+ // timer B reload function
+ if (timer == 1 && m_timer_mode[1] & 8)
+ m_timer_count[1] = m_timer_load;
+ }
+}
+
+void hmcs400_cpu_device::clock_prescaler()
+{
+ u16 prev = m_prescaler;
+ m_prescaler = (m_prescaler + 1) & 0x7ff;
+
+ // increment timers based on prescaler divide ratio
+ for (int i = 0; i < 2; i++)
+ if (m_prescaler & ~prev & m_timer_div[i])
+ clock_timer(i);
+}
+
+
+//-------------------------------------------------
+// execute
+//-------------------------------------------------
+
+void hmcs400_cpu_device::cycle()
+{
+ m_icount--;
+ clock_prescaler();
+}
+
+u16 hmcs400_cpu_device::fetch()
+{
+ u16 data = m_program->read_word(m_pc);
+ m_pc = (m_pc + 1) & 0x3fff;
+ cycle();
+
+ return data & 0x3ff;
+}
+
+void hmcs400_cpu_device::execute_run()
+{
+ // in stop mode, the internal clock is not running
+ if (m_stop)
+ {
+ debugger_wait_hook();
+ m_icount = 0;
+ return;
+ }
+
+ while (m_icount > 0)
+ {
+ m_prev_pc = m_pc;
+ check_interrupts();
+
+ // in standby mode, opcode execution is halted
+ if (m_standby)
+ {
+ cycle();
+ continue;
+ }
+
+ // fetch next opcode
+ debugger_instruction_hook(m_pc);
+ m_op = fetch();
+ m_i = m_op & 0xf;
+
+ // 2-byte opcodes / RAM address
+ if ((m_op >= 0x100 && m_op < 0x140) || (m_op >= 0x150 && m_op < 0x1b0))
+ m_param = fetch();
+ else
+ m_param = (m_w << 8 | m_x << 4 | m_y) & 0x3ff;
+
+ // handle opcode
+ switch (m_op & 0x3f0)
+ {
+ case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0: op_cal(); break;
+
+ case 0x020: case 0x120: op_inem(); break;
+ case 0x030: case 0x130: op_ilem(); break;
+ case 0x070: op_ynei(); break;
+ case 0x0b0: op_tbr(); break;
+ case 0x150: op_jmpl(); break;
+ case 0x160: op_call(); break;
+ case 0x170: op_brl(); break;
+ case 0x1a0: op_lmi(); break;
+ case 0x1b0: op_p(); break;
+
+ case 0x200: op_lbi(); break;
+ case 0x210: op_lyi(); break;
+ case 0x220: op_lxi(); break;
+ case 0x230: op_lai(); break;
+ case 0x240: op_lbr(); break;
+ case 0x250: op_lar(); break;
+ case 0x260: op_redd(); break;
+ case 0x270: op_lamr(); break;
+ case 0x280: op_ai(); break;
+ case 0x290: op_lmiiy(); break;
+ case 0x2a0: op_tdd(); break;
+ case 0x2b0: op_alei(); break;
+ case 0x2c0: op_lrb(); break;
+ case 0x2d0: op_lra(); break;
+ case 0x2e0: op_sedd(); break;
+ case 0x2f0: op_xmra(); break;
+
+ default:
+ if ((m_op & 0x300) == 0x300)
+ {
+ op_br(); break;
+ }
+
+ switch (m_op & 0x3fc)
+ {
+ case 0x084: case 0x184: op_sem(); break;
+ case 0x088: case 0x188: op_rem(); break;
+ case 0x08c: case 0x18c: op_tm(); break;
+
+ case 0x000: op_xsp(); break;
+ case 0x040: op_lbm(); break;
+ case 0x080: op_xma(); break;
+ case 0x090: op_lam(); break;
+ case 0x094: op_lma(); break;
+ case 0x0c0: op_xmb(); break;
+ case 0x0f0: op_lwi(); break;
+
+ default:
+ switch (m_op)
+ {
+ case 0x004: case 0x104: op_anem(); break;
+ case 0x008: case 0x108: op_am(); break;
+ case 0x00c: case 0x10c: op_orm(); break;
+ case 0x014: case 0x114: op_alem(); break;
+ case 0x018: case 0x118: op_amc(); break;
+ case 0x01c: case 0x11c: op_eorm(); break;
+ case 0x098: case 0x198: op_smc(); break;
+ case 0x09c: case 0x19c: op_anm(); break;
+
+ case 0x010: op_rtn(); break;
+ case 0x011: op_rtni(); break;
+ case 0x044: op_bnem(); break;
+ case 0x048: op_lab(); break;
+ case 0x04c: op_ib(); break;
+ case 0x050: case 0x051: op_lmaiy(); break;
+ case 0x054: op_ayy(); break;
+ case 0x058: op_laspy(); break;
+ case 0x05c: op_iy(); break;
+ case 0x060: op_nega(); break;
+ case 0x064: op_red(); break;
+ case 0x068: op_laspx(); break;
+ case 0x06f: op_tc(); break;
+
+ case 0x0a0: op_rotr(); break;
+ case 0x0a1: op_rotl(); break;
+ case 0x0a6: op_daa(); break;
+ case 0x0aa: op_das(); break;
+ case 0x0af: op_lay(); break;
+ case 0x0c4: op_blem(); break;
+ case 0x0c8: op_lba(); break;
+ case 0x0cf: op_db(); break;
+ case 0x0d0: case 0x0d1: op_lmady(); break;
+ case 0x0d4: op_syy(); break;
+ case 0x0d8: op_lya(); break;
+ case 0x0df: op_dy(); break;
+ case 0x0e0: op_td(); break;
+ case 0x0e4: op_sed(); break;
+ case 0x0e8: op_lxa(); break;
+ case 0x0ec: op_rec(); break;
+ case 0x0ef: op_sec(); break;
+
+ case 0x100: op_law(); break;
+ case 0x110: op_lwa(); break;
+ case 0x140: op_comb(); break;
+ case 0x144: op_or(); break;
+ case 0x148: op_sts(); break;
+ case 0x14c: op_sby(); break;
+ case 0x14d: op_stop(); break;
+ case 0x180: op_xma(); break;
+ case 0x190: op_lam(); break;
+ case 0x194: op_lma(); break;
+
+ default: op_illegal(); break;
+ }
+ break; // 0x3ff
+
+ }
+ break; // 0x3fc
+
+ } // 0x3f0
+ }
+}