hmcs400: add i/o ports

This commit is contained in:
hap 2024-09-18 00:48:13 +02:00
parent 2d74a4e866
commit 819379be1e
6 changed files with 195 additions and 57 deletions

View File

@ -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()

View File

@ -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

View File

@ -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);
}
}

View File

@ -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
//-------------------------------------------------

View File

@ -19,6 +19,14 @@ public:
// configuration helpers
// 10 4-bit R ports (port A is 2-bit)
template <std::size_t N> auto read_r() { return m_read_r[N].bind(); }
template <std::size_t N> 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();

View File

@ -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);
}
}