diff options
author | R. Belmont <rb6502@users.noreply.github.com> | 2013-03-25 02:33:41 +0000 |
---|---|---|
committer | R. Belmont <rb6502@users.noreply.github.com> | 2013-03-25 02:33:41 +0000 |
commit | 3f8554905387dfc4f5178dd837cb44852e033ca1 (patch) | |
tree | 3eb6659485c7ce98f3c0b58f3b3fc46fa840990e /src/emu/cpu/m6502 | |
parent | 6d6e25adafc2cb64e0b2c3cb91e7c4c5d3ffb227 (diff) |
Add support for Mitsubishi M50740/50741 MCUs. [R. Belmont]
Diffstat (limited to 'src/emu/cpu/m6502')
-rw-r--r-- | src/emu/cpu/m6502/m5074x.c | 513 | ||||
-rw-r--r-- | src/emu/cpu/m6502/m5074x.h | 141 |
2 files changed, 654 insertions, 0 deletions
diff --git a/src/emu/cpu/m6502/m5074x.c b/src/emu/cpu/m6502/m5074x.c new file mode 100644 index 00000000000..e05fcd2eba0 --- /dev/null +++ b/src/emu/cpu/m6502/m5074x.c @@ -0,0 +1,513 @@ +/* + Mitsubishi M5074x 8-bit microcontroller family +*/ + +#include "emu.h" +#include "m5074x.h" + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define IRQ_CNTRREQ (0x80) +#define IRQ_CNTRENA (0x40) +#define IRQ_TMR1REQ (0x20) +#define IRQ_TMR1ENA (0x10) +#define IRQ_TMR2REQ (0x08) +#define IRQ_TMR2ENA (0x04) +#define IRQ_INTREQ (0x02) +#define IRQ_INTENA (0x01) + +#define TMRC_TMRXREQ (0x80) +#define TMRC_TMRXENA (0x40) +#define TMRC_TMRXHLT (0x20) +#define TMRC_TMRXMDE (0x0c) + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type M50740 = &device_creator<m50740_device>; +const device_type M50741 = &device_creator<m50741_device>; + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// m5074x_device - constructor +//------------------------------------------------- +m5074x_device::m5074x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, address_map_constructor internal_map) : + m740_device(mconfig, type, name, tag, owner, clock), + m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0, internal_map), + read_p0(*this), + read_p1(*this), + read_p2(*this), + read_p3(*this), + write_p0(*this), + write_p1(*this), + write_p2(*this), + write_p3(*this) +{ +} + +void m5074x_device::device_config_complete() +{ + m_shortname = "m5074x"; +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void m5074x_device::device_start() +{ + read_p0.resolve_safe(0); + read_p1.resolve_safe(0); + read_p2.resolve_safe(0); + read_p3.resolve_safe(0); + write_p0.resolve_safe(); + write_p1.resolve_safe(); + write_p2.resolve_safe(); + write_p3.resolve_safe(); + + for (int i = 0; i < NUM_TIMERS; i++) + { + m_timers[i] = timer_alloc(i, NULL); + } + + m740_device::device_start(); + + save_item(NAME(m_ports)); + save_item(NAME(m_ddrs)); + save_item(NAME(m_intctrl)); + save_item(NAME(m_tmrctrl)); + save_item(NAME(m_tmr12pre)); + save_item(NAME(m_tmr1)); + save_item(NAME(m_tmr2)); + save_item(NAME(m_tmrxpre)); + save_item(NAME(m_tmrx)); + save_item(NAME(m_tmr1latch)); + save_item(NAME(m_tmr2latch)); + save_item(NAME(m_tmrxlatch)); + save_item(NAME(m_last_all_ints)); + + memset(m_ports, 0, sizeof(m_ports)); + memset(m_ddrs, 0, sizeof(m_ddrs)); + m_intctrl = m_tmrctrl = 0; + m_tmr12pre = m_tmrxpre = 0; + m_tmr1 = m_tmr2 = m_tmrx = 0; + m_last_all_ints = 0; +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void m5074x_device::device_reset() +{ + m740_device::device_reset(); + + // all ports reset to input on startup + memset(m_ports, 0, sizeof(m_ports)); + memset(m_ddrs, 0, sizeof(m_ddrs)); + m_intctrl = m_tmrctrl = 0; + m_tmr12pre = m_tmrxpre = 0; + m_tmr1 = m_tmr2 = m_tmrx = 0; +} + +void m5074x_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_1: + m_tmr1--; + + if (m_tmr1 <= 0) + { + m_intctrl |= IRQ_TMR1REQ; + m_tmr1 = m_tmr1latch; + recalc_irqs(); + } + break; + + case TIMER_2: + m_tmr2--; + + if (m_tmr2 <= 0) + { + m_intctrl |= IRQ_TMR2REQ; + m_tmr2 = m_tmr2latch; + recalc_irqs(); + } + break; + + case TIMER_X: + m_tmrx--; + + if (m_tmrx <= 0) + { + m_tmrctrl |= TMRC_TMRXREQ; + m_tmrx = m_tmrxlatch; + recalc_irqs(); + } + break; + } +} + +void m5074x_device::execute_set_input(int inputnum, int state) +{ + switch (inputnum) + { + case M5074X_INT1_LINE: + if (state == ASSERT_LINE) + { + m_intctrl |= IRQ_INTREQ; + } + else + { + m_intctrl &= ~IRQ_INTREQ; + } + break; + + case M5074X_SET_OVERFLOW: // the base 740 class can handle this + m740_device::execute_set_input(M740_SET_OVERFLOW, state); + break; + } + + recalc_irqs(); +} + +void m5074x_device::recalc_irqs() +{ + UINT8 all_ints = 0; + + if ((m_intctrl & (IRQ_CNTRREQ|IRQ_CNTRENA)) == (IRQ_CNTRREQ|IRQ_CNTRENA)) + { + all_ints |= 0x01; + } + if ((m_tmrctrl & (TMRC_TMRXREQ|TMRC_TMRXENA)) == (TMRC_TMRXREQ|TMRC_TMRXENA)) + { + all_ints |= 0x02; + } + if ((m_intctrl & (IRQ_TMR1REQ|IRQ_TMR1ENA)) == (IRQ_TMR1REQ|IRQ_TMR1ENA)) + { + all_ints |= 0x04; + } + if ((m_intctrl & (IRQ_TMR2REQ|IRQ_TMR2ENA)) == (IRQ_TMR2REQ|IRQ_TMR2ENA)) + { + all_ints |= 0x08; + } + if ((m_intctrl & (IRQ_INTREQ|IRQ_INTENA)) == (IRQ_INTREQ|IRQ_INTENA)) + { + all_ints |= 0x10; + } + + // check all 6 IRQ bits for changes + for (int i = 0; i < 6; i++) + { + // if bit is set now + if (all_ints & (1 << i)) + { + // and wasn't last time + if (!(m_last_all_ints & (1 << i))) + { + m740_device::execute_set_input(M740_INT0_LINE + i, ASSERT_LINE); + } + } + else // bit is clear now + { + // ...and wasn't clear last time + if (m_last_all_ints & (1 << i)) + { + m740_device::execute_set_input(M740_INT0_LINE + i, CLEAR_LINE); + } + } + } + + m_last_all_ints = all_ints; +} + +void m5074x_device::recalc_timer(int timer) +{ + int hz; + + switch (timer) + { + case 0: + hz = clock() / 16; + hz /= (m_tmr12pre + 2); + m_timers[TIMER_1]->adjust(attotime::from_hz(hz), 0, attotime::from_hz(hz)); + break; + + case 1: + hz = clock() / 16; + hz /= (m_tmr12pre + 2); + m_timers[TIMER_2]->adjust(attotime::from_hz(hz), 0, attotime::from_hz(hz)); + break; + + case 2: + // Timer X modes: 00 = free run countdown, 01 = invert CNTR pin each time expires, + // 10 = count each time CNTR pin inverts, 11 = count when CNTR pin low + if ((m_tmrctrl & TMRC_TMRXMDE) == 0) + { + // stop bit? + if (m_tmrctrl & TMRC_TMRXHLT) + { + m_timers[TIMER_X]->adjust(attotime::never, 0, attotime::never); + } + else + { + hz = clock() / 16; + hz /= (m_tmrxpre + 2); + m_timers[TIMER_X]->adjust(attotime::from_hz(hz), 0, attotime::from_hz(hz)); + } + } + else + { + fatalerror("M5074x: Unhandled timer X mode %d\n", (m_tmrctrl&TMRC_TMRXMDE)>>2); + } + break; + } +} + +void m5074x_device::send_port(address_space &space, UINT8 offset, UINT8 data) +{ + switch (offset) + { + case 0: + write_p0(data); + break; + + case 1: + write_p1(data); + break; + + case 2: + write_p2(data); + break; + + case 3: + write_p3(data); + break; + } +} + +UINT8 m5074x_device::read_port(UINT8 offset) +{ + UINT8 incoming = 0; + + switch (offset) + { + case 0: + incoming = read_p0(); + break; + + case 1: + incoming = read_p1(); + break; + + case 2: + incoming = read_p2(); + break; + + case 3: + incoming = read_p3(); + break; + } + + // apply data direction registers + incoming &= (m_ddrs[offset] ^ 0xff); + // OR in ddr-masked version of port writes + incoming |= (m_ports[offset] & m_ddrs[offset]); + + return incoming; +} + +READ8_MEMBER(m5074x_device::ports_r) +{ + switch (offset) + { + case 0: + return read_port(0); + + case 1: + return m_ddrs[0]; + + case 2: + return read_port(1); + + case 3: + return m_ddrs[1]; + + case 4: + return read_port(2); + + case 5: + return m_ddrs[2]; + + case 8: + return read_port(3); + + case 9: + return m_ddrs[3]; + } + + return 0xff; +} + +WRITE8_MEMBER(m5074x_device::ports_w) +{ + switch (offset) + { + case 0: // p0 + send_port(space, 0, data & m_ddrs[0]); + m_ports[0] = data; + break; + + case 1: // p0 ddr + send_port(space, 0, m_ports[0] & data); + m_ddrs[0] = data; + break; + + case 2: // p1 + send_port(space, 1, data & m_ddrs[1]); + m_ports[1] = data; + break; + + case 3: // p1 ddr + send_port(space, 1, m_ports[1] & data); + m_ddrs[1] = data; + break; + + case 4: // p2 + send_port(space, 2, data & m_ddrs[2]); + m_ports[2] = data; + break; + + case 5: // p2 ddr + send_port(space, 2, m_ports[2] & data); + m_ddrs[2] = data; + break; + + case 8: // p3 + send_port(space, 3, data & m_ddrs[3]); + m_ports[3] = data; + break; + + case 9: // p3 ddr + send_port(space, 3, m_ports[3] & data); + m_ddrs[3] = data; + break; + } +} + +READ8_MEMBER(m5074x_device::tmrirq_r) +{ + switch (offset) + { + case 0: + return m_tmr12pre; + + case 1: + return m_tmr1; + + case 2: + return m_tmr2; + + case 3: + return m_tmrxpre; + + case 4: + return m_tmrx; + + case 5: + return m_intctrl; + + case 6: + return m_tmrctrl; + } + + return 0xff; +} + +WRITE8_MEMBER(m5074x_device::tmrirq_w) +{ +// printf("%02x to tmrirq @ %d\n", data, offset); + + switch (offset) + { + case 0: + m_tmr12pre = data; + recalc_timer(0); + recalc_timer(1); + break; + + case 1: + m_tmr1 = m_tmr1latch = data; + break; + + case 2: + m_tmr2 = m_tmr2latch = data; + break; + + case 3: + m_tmrxpre = m_tmrxlatch = data; + recalc_timer(2); + break; + + case 4: + m_tmrx = data; + break; + + case 5: + m_intctrl = data; + recalc_irqs(); + break; + + case 6: + m_tmrctrl = data; + recalc_irqs(); + break; + } +} + +/* M50740 - baseline for this familiy */ +static ADDRESS_MAP_START( m50740_map, AS_PROGRAM, 8, m50740_device ) + ADDRESS_MAP_GLOBAL_MASK(0x1fff) + AM_RANGE(0x0000, 0x005f) AM_RAM + AM_RANGE(0x00e0, 0x00e9) AM_READWRITE(ports_r, ports_w) + AM_RANGE(0x00f9, 0x00ff) AM_READWRITE(tmrirq_r, tmrirq_w) + AM_RANGE(0x1400, 0x1fff) AM_ROM AM_REGION(":m50740", 0) +ADDRESS_MAP_END + +m50740_device::m50740_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + m5074x_device(mconfig, M50740, "Mitsubishi M50740", tag, owner, clock, ADDRESS_MAP_NAME(m50740_map)) +{ +} + +m50740_device::m50740_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) : + m5074x_device(mconfig, type, name, tag, owner, clock, ADDRESS_MAP_NAME(m50740_map)) +{ +} + +/* M50741 - 50740 with a larger internal ROM */ +static ADDRESS_MAP_START( m50741_map, AS_PROGRAM, 8, m50741_device ) + ADDRESS_MAP_GLOBAL_MASK(0x1fff) + AM_RANGE(0x0000, 0x005f) AM_RAM + AM_RANGE(0x00e0, 0x00e9) AM_READWRITE(ports_r, ports_w) + AM_RANGE(0x00f9, 0x00ff) AM_READWRITE(tmrirq_r, tmrirq_w) + AM_RANGE(0x1000, 0x1fff) AM_ROM AM_REGION(":m50741", 0) +ADDRESS_MAP_END + +m50741_device::m50741_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + m5074x_device(mconfig, M50740, "Mitsubishi M50741", tag, owner, clock, ADDRESS_MAP_NAME(m50741_map)) +{ +} + +m50741_device::m50741_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) : + m5074x_device(mconfig, type, name, tag, owner, clock, ADDRESS_MAP_NAME(m50741_map)) +{ +} + diff --git a/src/emu/cpu/m6502/m5074x.h b/src/emu/cpu/m6502/m5074x.h new file mode 100644 index 00000000000..726eaf59262 --- /dev/null +++ b/src/emu/cpu/m6502/m5074x.h @@ -0,0 +1,141 @@ +#pragma once + +#ifndef __M5074X_H__ +#define __M5074X_H__ + +#include "m740.h" + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_M5074X_PORT0_CALLBACKS(_read, _write) \ + downcast<m5074x_device *>(device)->set_p0_callbacks(DEVCB2_##_read, DEVCB2_##_write); + +#define MCFG_M5074X_PORT1_CALLBACKS(_read, _write) \ + downcast<m5074x_device *>(device)->set_p1_callbacks(DEVCB2_##_read, DEVCB2_##_write); + +#define MCFG_M5074X_PORT2_CALLBACKS(_read, _write) \ + downcast<m5074x_device *>(device)->set_p2_callbacks(DEVCB2_##_read, DEVCB2_##_write); + +#define MCFG_M5074X_PORT3_CALLBACKS(_read, _write) \ + downcast<m5074x_device *>(device)->set_p3_callbacks(DEVCB2_##_read, DEVCB2_##_write); + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> m5074x_device + +class m5074x_device : public m740_device +{ + friend class m50740_device; + friend class m50741_device; + + enum + { + M5074X_INT1_LINE = INPUT_LINE_IRQ0, + + M5074X_SET_OVERFLOW = M740_SET_OVERFLOW + }; + + enum + { + TIMER_1 = 0, + TIMER_2, + TIMER_X, + + NUM_TIMERS + }; + +public: + // construction/destruction + m5074x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, address_map_constructor internal_map); + + const address_space_config m_program_config; + + template<class _read, class _write> void set_p0_callbacks(_read rd, _write wr) + { + read_p0.set_callback(rd); + write_p0.set_callback(wr); + } + + template<class _read, class _write> void set_p1_callbacks(_read rd, _write wr) + { + read_p1.set_callback(rd); + write_p1.set_callback(wr); + } + + template<class _read, class _write> void set_p2_callbacks(_read rd, _write wr) + { + read_p2.set_callback(rd); + write_p2.set_callback(wr); + } + + template<class _read, class _write> void set_p3_callbacks(_read rd, _write wr) + { + read_p3.set_callback(rd); + write_p3.set_callback(wr); + } + + devcb2_read8 read_p0, read_p1, read_p2, read_p3; + devcb2_write8 write_p0, write_p1, write_p2, write_p3; + + DECLARE_READ8_MEMBER(ports_r); + DECLARE_WRITE8_MEMBER(ports_w); + DECLARE_READ8_MEMBER(tmrirq_r); + DECLARE_WRITE8_MEMBER(tmrirq_w); + + bool are_port_bits_output(UINT8 port, UINT8 mask) { return ((m_ddrs[port] & mask) == mask) ? true : false; } + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual void device_config_complete(); + virtual void execute_set_input(int inputnum, int state); + virtual const address_space_config *memory_space_config(address_spacenum spacenum) const { return (spacenum == AS_PROGRAM) ? &m_program_config : NULL; } + + void send_port(address_space &space, UINT8 offset, UINT8 data); + UINT8 read_port(UINT8 offset); + + void recalc_irqs(); + void recalc_timer(int timer); + + UINT8 m_ports[6], m_ddrs[6]; + UINT8 m_intctrl, m_tmrctrl; + UINT8 m_tmr12pre, m_tmr1, m_tmr2, m_tmrxpre, m_tmrx; + UINT8 m_tmr1latch, m_tmr2latch, m_tmrxlatch; + UINT8 m_last_all_ints; + +private: + emu_timer *m_timers[NUM_TIMERS]; +}; + +class m50740_device : public m5074x_device +{ +public: + m50740_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + m50740_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock); + +protected: + +private: +}; + +class m50741_device : public m5074x_device +{ +public: + m50741_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + m50741_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock); + +protected: + +private: +}; + +extern const device_type M50740; +extern const device_type M50741; + +#endif |