From 819379be1e044c5f9cd8c9f238f16179d45dc310 Mon Sep 17 00:00:00 2001 From: hap Date: Wed, 18 Sep 2024 00:48:13 +0200 Subject: [PATCH] hmcs400: add i/o ports --- src/devices/cpu/hmcs40/hmcs40.cpp | 63 ++++++++++--------- src/devices/cpu/hmcs40/hmcs40.h | 25 ++++---- src/devices/cpu/hmcs40/hmcs40op.cpp | 18 +++--- src/devices/cpu/hmcs400/hmcs400.cpp | 88 ++++++++++++++++++++++++++- src/devices/cpu/hmcs400/hmcs400.h | 26 +++++++- src/devices/cpu/hmcs400/hmcs400op.cpp | 32 +++++++++- 6 files changed, 195 insertions(+), 57 deletions(-) diff --git a/src/devices/cpu/hmcs40/hmcs40.cpp b/src/devices/cpu/hmcs40/hmcs40.cpp index 723934604ac..a10d31fca36 100644 --- a/src/devices/cpu/hmcs40/hmcs40.cpp +++ b/src/devices/cpu/hmcs40/hmcs40.cpp @@ -247,13 +247,8 @@ void hmcs40_cpu_device::device_reset() m_iri = m_irt = 0; m_if[0] = m_if[1] = m_tf = 1; - // clear i/o - m_d = m_polarity; - for (int i = 0; i < 16; i++) - hmcs40_cpu_device::write_d(i, m_polarity); - - for (int i = 0; i < 8; i++) - hmcs40_cpu_device::write_r(i, m_polarity & 0xf); + // all I/O ports set to input + reset_io(); } @@ -325,13 +320,22 @@ device_memory_interface::space_config_vector hmcs40_cpu_device::memory_space_con //------------------------------------------------- -// i/o handling +// i/o ports //------------------------------------------------- -u8 hmcs40_cpu_device::read_r(int index) +void hmcs40_cpu_device::reset_io() +{ + m_d = m_polarity; + m_write_d(m_polarity); + + for (int i = 0; i < 8; i++) + hmcs40_cpu_device::write_r(i, m_polarity); +} + +u8 hmcs40_cpu_device::read_r(u8 index) { index &= 7; - u8 inp = m_read_r[index](index, 0xff); + u8 inp = m_read_r[index](index); if (m_polarity) return (inp & m_r[index]) & 0xf; @@ -339,38 +343,39 @@ u8 hmcs40_cpu_device::read_r(int index) return (inp | m_r[index]) & 0xf; } -void hmcs40_cpu_device::write_r(int index, u8 data) +void hmcs40_cpu_device::write_r(u8 index, u8 data) { index &= 7; data &= 0xf; m_r[index] = data; - m_write_r[index](index, data, 0xff); + m_write_r[index](index, data); } -int hmcs40_cpu_device::read_d(int index) +int hmcs40_cpu_device::read_d(u8 index) { - index &= 15; + index &= 0xf; + u16 inp = m_read_d(0, 1 << index); if (m_polarity) - return (m_read_d(index, 0xffff) & m_d) >> index & 1; + return BIT(inp & m_d, index); else - return (m_read_d(index, 0xffff) | m_d) >> index & 1; + return BIT(inp | m_d, index); } -void hmcs40_cpu_device::write_d(int index, int state) +void hmcs40_cpu_device::write_d(u8 index, int state) { - index &= 15; - state = (state) ? 1 : 0; + index &= 0xf; + u16 mask = 1 << index; - m_d = (m_d & ~(1 << index)) | state << index; - m_write_d(index, m_d, 0xffff); + m_d = (m_d & ~mask) | (state ? mask : 0); + m_write_d(0, m_d, mask); } // HMCS43: // R0 is input-only, R1 is i/o, R2,R3 are output-only, no R4-R7 // D0-D3 are i/o, D4-D15 are output-only -u8 hmcs43_cpu_device::read_r(int index) +u8 hmcs43_cpu_device::read_r(u8 index) { index &= 7; @@ -380,7 +385,7 @@ u8 hmcs43_cpu_device::read_r(int index) return hmcs40_cpu_device::read_r(index); } -void hmcs43_cpu_device::write_r(int index, u8 data) +void hmcs43_cpu_device::write_r(u8 index, u8 data) { index &= 7; @@ -390,7 +395,7 @@ void hmcs43_cpu_device::write_r(int index, u8 data) logerror("ineffective write to port R%d = $%X at $%04X\n", index, data & 0xf, m_prev_pc); } -int hmcs43_cpu_device::read_d(int index) +int hmcs43_cpu_device::read_d(u8 index) { index &= 15; @@ -404,7 +409,7 @@ int hmcs43_cpu_device::read_d(int index) // R0-R3 are i/o, R4,R5 are extra registers, no R6,R7 // D0-D15 are i/o -u8 hmcs44_cpu_device::read_r(int index) +u8 hmcs44_cpu_device::read_r(u8 index) { index &= 7; @@ -414,7 +419,7 @@ u8 hmcs44_cpu_device::read_r(int index) return hmcs40_cpu_device::read_r(index); } -void hmcs44_cpu_device::write_r(int index, u8 data) +void hmcs44_cpu_device::write_r(u8 index, u8 data) { index &= 7; @@ -428,7 +433,7 @@ void hmcs44_cpu_device::write_r(int index, u8 data) // R0-R5 are i/o, R6 is output-only, no R7 // D0-D15 are i/o -u8 hmcs45_cpu_device::read_r(int index) +u8 hmcs45_cpu_device::read_r(u8 index) { index &= 7; @@ -438,7 +443,7 @@ u8 hmcs45_cpu_device::read_r(int index) return hmcs40_cpu_device::read_r(index); } -void hmcs45_cpu_device::write_r(int index, u8 data) +void hmcs45_cpu_device::write_r(u8 index, u8 data) { index &= 7; @@ -450,7 +455,7 @@ void hmcs45_cpu_device::write_r(int index, u8 data) //------------------------------------------------- -// interrupt/timer handling +// interrupt/timer //------------------------------------------------- void hmcs40_cpu_device::do_interrupt() diff --git a/src/devices/cpu/hmcs40/hmcs40.h b/src/devices/cpu/hmcs40/hmcs40.h index c9316bbe69a..833b5891cd2 100644 --- a/src/devices/cpu/hmcs40/hmcs40.h +++ b/src/devices/cpu/hmcs40/hmcs40.h @@ -12,7 +12,7 @@ #pragma once -// I/O ports setup +// input lines enum { @@ -195,10 +195,11 @@ protected: void pop_stack(); void push_stack(); - virtual u8 read_r(int index); - virtual void write_r(int index, u8 data); - virtual int read_d(int index); - virtual void write_d(int index, int state); + virtual void reset_io(); + virtual u8 read_r(u8 index); + virtual void write_r(u8 index, u8 data); + virtual int read_d(u8 index); + virtual void write_d(u8 index, int state); void cycle(); void increment_tc(); @@ -309,9 +310,9 @@ protected: hmcs43_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 polarity); // overrides - virtual u8 read_r(int index) override; - virtual void write_r(int index, u8 data) override; - virtual int read_d(int index) override; + virtual u8 read_r(u8 index) override; + virtual void write_r(u8 index, u8 data) override; + virtual int read_d(u8 index) override; }; class hd38750_device : public hmcs43_cpu_device @@ -345,8 +346,8 @@ protected: hmcs44_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 polarity); // overrides - virtual u8 read_r(int index) override; - virtual void write_r(int index, u8 data) override; + virtual u8 read_r(u8 index) override; + virtual void write_r(u8 index, u8 data) override; }; class hd38800_device : public hmcs44_cpu_device @@ -380,8 +381,8 @@ protected: hmcs45_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 polarity); // overrides - virtual u8 read_r(int index) override; - virtual void write_r(int index, u8 data) override; + virtual u8 read_r(u8 index) override; + virtual void write_r(u8 index, u8 data) override; }; class hd38820_device : public hmcs45_cpu_device diff --git a/src/devices/cpu/hmcs40/hmcs40op.cpp b/src/devices/cpu/hmcs40/hmcs40op.cpp index a2c201946cb..262e82f1d48 100644 --- a/src/devices/cpu/hmcs40/hmcs40op.cpp +++ b/src/devices/cpu/hmcs40/hmcs40op.cpp @@ -663,24 +663,24 @@ void hmcs40_cpu_device::op_lrb() void hmcs40_cpu_device::op_p() { + // P p: Pattern Generation cycle(); - // P p: Pattern Generation u16 address = m_a | m_b << 4 | m_c << 8 | (m_op & 7) << 9 | (m_pc & ~0x3f); - u16 o = m_program->read_word(address & m_prgmask); + u16 data = m_program->read_word(address & m_prgmask); // destination is determined by the 2 highest bits - if (o & 0x100) + if (data & 0x100) { // B3 B2 B1 B0 A0 A1 A2 A3 - m_a = bitswap<4>(o,0,1,2,3); - m_b = o >> 4 & 0xf; + m_a = bitswap<4>(data,0,1,2,3); + m_b = data >> 4 & 0xf; } - if (o & 0x200) + if (data & 0x200) { // R20 R21 R22 R23 R30 R31 R32 R33 - o = bitswap<8>(o,0,1,2,3,4,5,6,7); - write_r(2, o & 0xf); - write_r(3, o >> 4 & 0xf); + data = bitswap<8>(data,0,1,2,3,4,5,6,7); + write_r(2, data & 0xf); + write_r(3, data >> 4 & 0xf); } } diff --git a/src/devices/cpu/hmcs400/hmcs400.cpp b/src/devices/cpu/hmcs400/hmcs400.cpp index 261e482ac8c..0c8cc8d4a9c 100644 --- a/src/devices/cpu/hmcs400/hmcs400.cpp +++ b/src/devices/cpu/hmcs400/hmcs400.cpp @@ -18,6 +18,8 @@ TODO: 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? +- current I/O ports are hardcoded for HMS402/4/8, which will need to be changed + when other MCU types are added */ @@ -73,7 +75,11 @@ hmcs400_cpu_device::hmcs400_cpu_device(const machine_config &mconfig, device_typ m_rom_size(rom_size), m_ram_size(ram_size), m_has_div(false), - m_divider(8) + m_divider(8), + m_read_r(*this, 0), + m_write_r(*this), + m_read_d(*this, 0), + m_write_d(*this) { } hmcs400_cpu_device::~hmcs400_cpu_device() { } @@ -185,8 +191,6 @@ void hmcs400_cpu_device::device_start() save_item(NAME(m_prev_pc)); save_item(NAME(m_sp)); save_item(NAME(m_op)); - save_item(NAME(m_param)); - save_item(NAME(m_i)); save_item(NAME(m_a)); save_item(NAME(m_b)); @@ -198,6 +202,9 @@ void hmcs400_cpu_device::device_start() save_item(NAME(m_st)); save_item(NAME(m_ca)); + save_item(NAME(m_r)); + save_item(NAME(m_d)); + // register state for debugger state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%04X").noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).formatstr("%04X").noshow(); @@ -225,6 +232,9 @@ void hmcs400_cpu_device::device_reset() m_pc = 0; m_sp = 0x3ff; m_st = 1; + + // all I/O ports set to input + reset_io(); } @@ -278,6 +288,78 @@ device_memory_interface::space_config_vector hmcs400_cpu_device::memory_space_co } +//------------------------------------------------- +// 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); + + for (int i = 0; i < 10; 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); + } +} + +u8 hmcs400_cpu_device::read_r(u8 index) +{ + // reads from write-only or non-existent ports are invalid + bool write_only = index == 0 || (index >= 6 && index <= 8); + if (write_only || index > 10) + { + logerror("read from %s port R%X at $%04X\n", write_only ? "output" : "unknown", index, m_prev_pc); + return 0xf; + } + + u8 inp = m_read_r[index].isunset() ? m_r_mask[index] : m_read_r[index](index); + u8 mask = (index == 10) ? 3 : 0xf; // port A is 2-bit + + if (m_r_mask[index]) + return (inp & m_r[index]) & mask; + else + return (inp | m_r[index]) & mask; +} + +void hmcs400_cpu_device::write_r(u8 index, u8 data) +{ + // ignore writes to read-only or non-existent ports + if (index > 8) + return; + + data &= 0xf; + m_r[index] = data; + m_write_r[index](index, data); +} + +int hmcs400_cpu_device::read_d(u8 index) +{ + index &= 0xf; + u16 mask = 1 << index; + u16 inp = m_read_d.isunset() ? m_d_mask : m_read_d(0, mask); + + 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; + + m_d = (m_d & ~mask) | (state ? mask : 0); + m_write_d(0, m_d, mask); +} + + //------------------------------------------------- // execute //------------------------------------------------- diff --git a/src/devices/cpu/hmcs400/hmcs400.h b/src/devices/cpu/hmcs400/hmcs400.h index 965131df530..c8572fe0192 100644 --- a/src/devices/cpu/hmcs400/hmcs400.h +++ b/src/devices/cpu/hmcs400/hmcs400.h @@ -19,6 +19,14 @@ public: // configuration helpers + // 10 4-bit R ports (port A is 2-bit) + template auto read_r() { return m_read_r[N].bind(); } + template auto write_r() { return m_write_r[N].bind(); } + + // 16-bit discrete + auto read_d() { return m_read_d.bind(); } + auto write_d() { return m_write_d.bind(); } + // system clock divider mask option (only for HMCS408, HMCS414, HMCS424) // valid options: 4, 8, 16, default to 8 auto &set_divider(u8 div) { assert(m_has_div); m_divider = div; return *this; } @@ -81,7 +89,16 @@ protected: u8 m_st; // status flag u8 m_ca; // carry flag - u16 fetch(); + u8 m_r[10]; // R outputs state + u8 m_r_mask[10]; + u16 m_d; // D pins state + u16 m_d_mask; + + // I/O handlers + devcb_read8::array<8> m_read_r; + devcb_write8::array<8> m_write_r; + devcb_read16 m_read_d; + devcb_write16 m_write_d; // misc internal helpers u8 ram_r(u8 mem_mask = 0xf); @@ -89,7 +106,14 @@ protected: void pop_stack(); void push_stack(); + void reset_io(); + u8 read_r(u8 index); + void write_r(u8 index, u8 data); + int read_d(u8 index); + void write_d(u8 index, int state); + void cycle(); + u16 fetch(); // opcode handlers void op_illegal(); diff --git a/src/devices/cpu/hmcs400/hmcs400op.cpp b/src/devices/cpu/hmcs400/hmcs400op.cpp index b9656db83b5..0fb1ea015c5 100644 --- a/src/devices/cpu/hmcs400/hmcs400op.cpp +++ b/src/devices/cpu/hmcs400/hmcs400op.cpp @@ -534,9 +534,9 @@ void hmcs400_cpu_device::op_cal() // CAL a: Subroutine Jump on Status 1 if (m_st) { + cycle(); push_stack(); m_pc = m_op & 0x3f; - cycle(); } else m_st = 1; @@ -563,9 +563,9 @@ void hmcs400_cpu_device::op_tbr() void hmcs400_cpu_device::op_rtn() { // RTN: Return from Subroutine + cycle(); + cycle(); pop_stack(); - cycle(); - cycle(); } void hmcs400_cpu_device::op_rtni() @@ -580,56 +580,82 @@ void hmcs400_cpu_device::op_rtni() void hmcs400_cpu_device::op_sed() { // SED: Set Discrete I/O Latch + write_d(m_y, 1); } void hmcs400_cpu_device::op_sedd() { // SEDD m: Set Discrete I/O Latch Direct + write_d(m_i, 1); } void hmcs400_cpu_device::op_red() { // RED: Reset Discrete I/O Latch + write_d(m_y, 0); } void hmcs400_cpu_device::op_redd() { // REDD m: Reset Discrete I/O Latch Direct + write_d(m_i, 0); } void hmcs400_cpu_device::op_td() { // TD: Test Discrete I/O Latch + m_st = read_d(m_y); } void hmcs400_cpu_device::op_tdd() { // TDD m: Test Discrete I/O Latch Direct + m_st = read_d(m_i); } void hmcs400_cpu_device::op_lar() { // LAR m: Load A from R Port Register + m_a = read_r(m_i); } void hmcs400_cpu_device::op_lbr() { // LBR m: Load B from R Port Register + m_b = read_r(m_i); } void hmcs400_cpu_device::op_lra() { // LRA m: Load R Port Register from A + write_r(m_i, m_a); } void hmcs400_cpu_device::op_lrb() { // LRB m: Load R Port Register from B + write_r(m_i, m_b); } void hmcs400_cpu_device::op_p() { // P p: Pattern Generation + cycle(); + u16 data = m_program->read_word(m_i << 8 | m_b << 4 | m_a); + + // destination is determined by the 2 highest bits + if (data & 0x100) + { + // to A/B registers + m_a = data & 0xf; + m_b = data >> 4 & 0xf; + } + if (data & 0x200) + { + // to R1/R2 ports + write_r(1, data & 0xf); + write_r(2, data >> 4 & 0xf); + } }