summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/cpu.lua19
-rw-r--r--scripts/target/mame/mess.lua4
-rw-r--r--src/devices/cpu/pic17/pic17.cpp1096
-rw-r--r--src/devices/cpu/pic17/pic17.h180
-rw-r--r--src/devices/cpu/pic17/pic17c4x.cpp537
-rw-r--r--src/devices/cpu/pic17/pic17c4x.h181
-rw-r--r--src/devices/cpu/pic17/pic17d.cpp239
-rw-r--r--src/devices/cpu/pic17/pic17d.h36
-rw-r--r--src/mame/drivers/teamjocs.cpp4
-rw-r--r--src/mame/drivers/xbase09.cpp197
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/mess.flt1
-rw-r--r--src/tools/unidasm.cpp2
13 files changed, 2497 insertions, 2 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index ac904576e65..2f6ed7aabff 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -1309,6 +1309,25 @@ if (CPUS["PIC16"]~=null or _OPTIONS["with-tools"]) then
end
--------------------------------------------------
+-- Microchip PIC17
+--@src/devices/cpu/pic17/pic17.h,CPUS["PIC17"] = true
+--------------------------------------------------
+
+if (CPUS["PIC17"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/cpu/pic17/pic17.cpp",
+ MAME_DIR .. "src/devices/cpu/pic17/pic17.h",
+ MAME_DIR .. "src/devices/cpu/pic17/pic17c4x.cpp",
+ MAME_DIR .. "src/devices/cpu/pic17/pic17c4x.h",
+ }
+end
+
+if (CPUS["PIC17"]~=null or _OPTIONS["with-tools"]) then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/pic17/pic17d.cpp")
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/pic17/pic17d.h")
+end
+
+--------------------------------------------------
-- MIPS R3000 (MIPS I/II) series
--@src/devices/cpu/mips/mips1.h,CPUS["MIPS1"] = true
--------------------------------------------------
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 529f96c5354..27c76e7629a 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -65,6 +65,7 @@ CPUS["DSP16"] = true
CPUS["DSP32C"] = true
CPUS["PIC16C5X"] = true
CPUS["PIC16C62X"] = true
+CPUS["PIC17"] = true
CPUS["G65816"] = true
CPUS["SPC700"] = true
CPUS["E1"] = true
@@ -2819,6 +2820,8 @@ files {
MAME_DIR .. "src/mame/drivers/ergo201.cpp",
MAME_DIR .. "src/mame/drivers/microterm.cpp",
MAME_DIR .. "src/mame/drivers/microterm_f8.cpp",
+ MAME_DIR .. "src/mame/machine/ergo201_kbd.cpp",
+ MAME_DIR .. "src/mame/machine/ergo201_kbd.h",
}
createMESSProjects(_target, _subtarget, "mips")
@@ -4466,6 +4469,7 @@ files {
MAME_DIR .. "src/mame/drivers/vp415.cpp",
MAME_DIR .. "src/mame/drivers/vsmilepro.cpp",
MAME_DIR .. "src/mame/drivers/wicat.cpp",
+ MAME_DIR .. "src/mame/drivers/xbase09.cpp",
MAME_DIR .. "src/mame/drivers/xor100.cpp",
MAME_DIR .. "src/mame/includes/xor100.h",
MAME_DIR .. "src/mame/drivers/zms8085.cpp",
diff --git a/src/devices/cpu/pic17/pic17.cpp b/src/devices/cpu/pic17/pic17.cpp
new file mode 100644
index 00000000000..47aa63553f3
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17.cpp
@@ -0,0 +1,1096 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Microchip PIC17CXXX High-Performance 8-bit MCU family
+
+ Common features of this family include:
+ — Four oscillator options (maximum rate of 33 MHz except on
+ PIC17C42)
+ — 2048 to 16,384 16-bit words of internal EPROM program memory
+ in microcontroller modes (may be protected; certain models use
+ mask ROM instead); special “long” table writes are used to
+ program EPROM when Vpp is applied
+ — 64K 16-bit words of external address space in microprocessor
+ mode, readable and writable through TABPTR and TABLAT
+ — 232 or more bytes of internal data RAM
+ — Two registers for indirect access to entire register file, with
+ optional post-increment or post-decrement for each
+ — Independent bank selection for peripheral SFRs and general-
+ purpose RAM (up to 16 banks are selectable for each, though no
+ more than 8 of the former and 4 of the latter were implemented)
+ — All instructions execute in a single cycle except for program
+ transfers and table reads/writes
+ – Signed overflow flag and decimal adjustment for addition
+ - 8 x 8 unsigned multiplication in hardware (except PIC17C42)
+ — Single-bit instructions can manipulate any addressable register
+ — MOVFP and MOVPF can transfer data between registers without
+ touching the working register
+ — CALL and GOTO allow efficient program transfers within 8K page
+ — 16-level on-chip program stack (non-addressable)
+ — Multiple 8-bit and 16-bit on-chip timer/counters, with 16-bit
+ input capture and 10-bit PWM output
+ — USART serial I/O with dedicated 8-bit baud rate generator
+ — Watchdog timer with internal RC oscillator
+ — SLEEP mode with wake-up upon interrupt
+
+ Notable architectural quirks:
+ — PCH is copied to PCLATH whenever PCL is read unless the
+ operation on that register is of the read-modify-write type.
+ — MOVWF, CLRF, SETF, NEGW and DAW read the previous value of
+ the destination register before discarding and replacing it.
+ — A quirk of instruction decoding causes RETURN, SLEEP, CLRWDT
+ and RETFIE to perform dummy reads from PCL, PCLATH, ALUSTA
+ and T0STA respectively. This means that RETURN also copies PCH
+ to PCLATH before it pops the stack.
+ — The internal workings of the stack are fully specified, even
+ though no instruction can push or pop values from the stack,
+ adjust the stack pointer or even sense its current level
+ (aside from the crude heuristic of the STKAV flag).
+ — TABLWT and TABLRD access different implementations of TABLAT.
+ Neither half of a data word latched from external memory by
+ TABLRD will be written back by a following TABLWT. To ensure
+ correct data is present on both halves of the bus, TLWT must
+ precede TABLWT to refresh the alternate half of TABLAT.
+ — “Long” writes to internal EPROM (not emulated) can be aborted
+ by the setting of any interrupt flag, whether enabled or not.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "pic17.h"
+#include "pic17d.h"
+
+ALLOW_SAVE_TYPE(pic17_cpu_device::exec_phase);
+
+pic17_cpu_device::pic17_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size, address_map_constructor data_map)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_program_config("program", ENDIANNESS_LITTLE, 16, 16, -1, address_map_constructor(FUNC(pic17_cpu_device::program_map), this))
+ , m_data_config("data", ENDIANNESS_LITTLE, 8, 12, 0, data_map)
+ , m_rom_size(rom_size)
+ , m_mode(mode::EXTENDED_MICROCONTROLLER)
+ , m_pc(0)
+ , m_ppc(0)
+ , m_paddr(0)
+ , m_raddr(0)
+ , m_ir(0)
+ , m_tmp(0)
+ , m_execphase(exec_phase::Q1)
+ , m_execflags(0)
+ , m_icount(0)
+ , m_pclath(0)
+ , m_wreg(0)
+ , m_alusta(0)
+ , m_cpusta(0x3c)
+ , m_intsta(0)
+ , m_tblptr(0)
+ , m_tablat(0)
+ , m_tlwt(0)
+ , m_prod(0)
+ , m_fsr{0, 0}
+ , m_bsr(0)
+ , m_stkptr(0)
+ , m_t0sta(0)
+ , m_tmr0(0)
+ , m_ps(0)
+{
+ std::fill_n(&m_stack[0], 16, 0);
+}
+
+void pic17_cpu_device::program_map(address_map &map)
+{
+ if (m_mode != mode::MICROPROCESSOR)
+ {
+ map(0x0000, m_rom_size - 1).rom().region(DEVICE_SELF, 0);
+ //map(0xfe00, 0xffff).rom().region("config", 0);
+ }
+}
+
+void pic17_cpu_device::core_data_map(address_map &map)
+{
+ // Unbanked registers (INDF0 and INDF1 are not physical registers)
+ map(0x000, 0x000).lr8([]() { return 0; }, "0").nopw();
+ map(0x001, 0x001).rw(FUNC(pic17_cpu_device::fsr0_r), FUNC(pic17_cpu_device::fsr0_w));
+ map(0x002, 0x002).rw(FUNC(pic17_cpu_device::pcl_r), FUNC(pic17_cpu_device::pcl_w));
+ map(0x003, 0x003).rw(FUNC(pic17_cpu_device::pclath_r), FUNC(pic17_cpu_device::pclath_w));
+ map(0x004, 0x004).rw(FUNC(pic17_cpu_device::alusta_r), FUNC(pic17_cpu_device::alusta_w));
+ map(0x005, 0x005).rw(FUNC(pic17_cpu_device::t0sta_r), FUNC(pic17_cpu_device::t0sta_w));
+ map(0x006, 0x006).rw(FUNC(pic17_cpu_device::cpusta_r), FUNC(pic17_cpu_device::cpusta_w));
+ map(0x007, 0x007).rw(FUNC(pic17_cpu_device::intsta_r), FUNC(pic17_cpu_device::intsta_w));
+ map(0x008, 0x008).lr8([]() { return 0; }, "0").nopw();
+ map(0x009, 0x009).rw(FUNC(pic17_cpu_device::fsr1_r), FUNC(pic17_cpu_device::fsr1_w));
+ map(0x00a, 0x00a).rw(FUNC(pic17_cpu_device::wreg_r), FUNC(pic17_cpu_device::wreg_w));
+ map(0x00b, 0x00b).rw(FUNC(pic17_cpu_device::tmr0l_r), FUNC(pic17_cpu_device::tmr0l_w));
+ map(0x00c, 0x00c).rw(FUNC(pic17_cpu_device::tmr0h_r), FUNC(pic17_cpu_device::tmr0h_w));
+ map(0x00d, 0x00d).rw(FUNC(pic17_cpu_device::tblptrl_r), FUNC(pic17_cpu_device::tblptrl_w));
+ map(0x00e, 0x00e).rw(FUNC(pic17_cpu_device::tblptrh_r), FUNC(pic17_cpu_device::tblptrh_w));
+ map(0x00f, 0x00f).rw(FUNC(pic17_cpu_device::bsr_r), FUNC(pic17_cpu_device::bsr_w));
+ map(0x018, 0x018).rw(FUNC(pic17_cpu_device::prodl_r), FUNC(pic17_cpu_device::prodl_w)); // not on PIC17C42
+ map(0x019, 0x019).rw(FUNC(pic17_cpu_device::prodh_r), FUNC(pic17_cpu_device::prodh_w)); // not on PIC17C42
+}
+
+std::unique_ptr<util::disasm_interface> pic17_cpu_device::create_disassembler()
+{
+ return std::make_unique<pic17_disassembler>();
+}
+
+device_memory_interface::space_config_vector pic17_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)
+ };
+}
+
+u8 pic17_cpu_device::pcl_r()
+{
+ // Note that this side effect is implicitly performed by the RETURN instruction
+ if (!machine().side_effects_disabled() && (m_execflags & REGWT) == 0)
+ m_pclath = m_pc >> 8;
+ return m_pc & 0x00ff;
+}
+
+void pic17_cpu_device::pcl_w(u8 data)
+{
+ m_pc = u8(m_pclath) << 8 | data;
+ m_execflags |= FORCENOP;
+}
+
+void pic17_cpu_device::debug_set_pc(u16 data)
+{
+ m_pc = m_ppc = data;
+ m_ir = m_cache.read_word(m_pc);
+}
+
+u8 pic17_cpu_device::pclath_r()
+{
+ return m_pclath;
+}
+
+void pic17_cpu_device::pclath_w(u8 data)
+{
+ m_pclath = data;
+}
+
+u8 pic17_cpu_device::wreg_r()
+{
+ return m_wreg;
+}
+
+void pic17_cpu_device::wreg_w(u8 data)
+{
+ m_wreg = data;
+}
+
+u8 pic17_cpu_device::alusta_r()
+{
+ return m_alusta;
+}
+
+void pic17_cpu_device::alusta_w(u8 data)
+{
+ m_alusta = data;
+}
+
+u8 pic17_cpu_device::cpusta_r()
+{
+ // STKAV is read as 0 when stack is full
+ if (m_stkptr == 0xf)
+ return m_cpusta & 0xdf;
+ else
+ return m_cpusta;
+}
+
+void pic17_cpu_device::cpusta_w(u8 data)
+{
+ // Only GLINTD is writable
+ if (BIT(data, 4))
+ {
+ m_cpusta |= 0x10;
+
+ // PIC17C42 lacks this failsafe, allowing an interrupt to be taken right after an instruction that sets GLINTD
+ m_execflags &= ~INTRPT;
+ }
+ else
+ m_cpusta &= 0xef;
+}
+
+u8 pic17_cpu_device::intsta_r()
+{
+ return m_intsta;
+}
+
+void pic17_cpu_device::intsta_w(u8 data)
+{
+ // PEIF is read-only
+ m_intsta = (m_intsta & 0x80) | (data & 0x7f);
+}
+
+void pic17_cpu_device::int_edge(bool rising)
+{
+ if (BIT(m_t0sta, 7) == rising)
+ m_intsta |= 0x10;
+}
+
+void pic17_cpu_device::t0cki_edge(bool rising)
+{
+ if (BIT(m_t0sta, 6) == rising)
+ {
+ m_intsta |= 0x40;
+ if (!BIT(m_t0sta, 5))
+ increment_tmr0();
+ }
+}
+
+void pic17_cpu_device::set_peif(bool state)
+{
+ if (state)
+ m_intsta |= 0x80;
+ else
+ {
+ m_intsta &= 0x7f;
+ if (((m_intsta >> 4) & m_intsta) == 0)
+ m_execflags &= ~INTRPT;
+ }
+}
+
+u8 pic17_cpu_device::t0sta_r()
+{
+ return m_t0sta;
+}
+
+void pic17_cpu_device::t0sta_w(u8 data)
+{
+ m_t0sta = data & 0xfe;
+}
+
+u8 pic17_cpu_device::tmr0l_r()
+{
+ return m_tmr0 & 0x00ff;
+}
+
+void pic17_cpu_device::tmr0l_w(u8 data)
+{
+ m_tmr0 = u8(data) | (m_tmr0 & 0xff00);
+ m_ps = 0;
+}
+
+u8 pic17_cpu_device::tmr0h_r()
+{
+ return m_tmr0 >> 8;
+}
+
+void pic17_cpu_device::tmr0h_w(u8 data)
+{
+ m_tmr0 = u8(data) << 8 | (m_tmr0 & 0x00ff);
+ m_ps = 0;
+}
+
+void pic17_cpu_device::increment_tmr0()
+{
+ // Prescaler is an 8-bit ripple counter
+ ++m_ps;
+
+ // Tap the prescaler output (PSOUT)
+ if ((m_ps & make_bitmask<u8>((m_t0sta & 0x1e) >> 1)) == 0)
+ {
+ // Set T0IF on overflow
+ if (m_tmr0 == 0xffff)
+ m_intsta |= 0x20;
+ ++m_tmr0;
+ }
+}
+
+u8 pic17_cpu_device::fsr0_r()
+{
+ return m_fsr[0];
+}
+
+void pic17_cpu_device::fsr0_w(u8 data)
+{
+ m_fsr[0] = data;
+}
+
+u8 pic17_cpu_device::fsr1_r()
+{
+ return m_fsr[1];
+}
+
+void pic17_cpu_device::fsr1_w(u8 data)
+{
+ m_fsr[1] = data;
+}
+
+u8 pic17_cpu_device::bsr_r()
+{
+ return m_bsr;
+}
+
+void pic17_cpu_device::bsr_w(u8 data)
+{
+ m_bsr = data;
+}
+
+u8 pic17_cpu_device::tblptrl_r()
+{
+ return m_tblptr & 0x00ff;
+}
+
+void pic17_cpu_device::tblptrl_w(u8 data)
+{
+ m_tblptr = (m_tblptr & 0xff00) | data;
+}
+
+u8 pic17_cpu_device::tblptrh_r()
+{
+ return m_tblptr >> 8;
+}
+
+void pic17_cpu_device::tblptrh_w(u8 data)
+{
+ m_tblptr = (m_tblptr & 0x00ff) | u16(data) << 8;
+}
+
+u8 pic17_cpu_device::prodl_r()
+{
+ return m_prod & 0x00ff;
+}
+
+void pic17_cpu_device::prodl_w(u8 data)
+{
+ m_prod = (m_prod & 0xff00) | data;
+}
+
+u8 pic17_cpu_device::prodh_r()
+{
+ return m_prod >> 8;
+}
+
+void pic17_cpu_device::prodh_w(u8 data)
+{
+ m_prod = (m_prod & 0x00ff) | u16(data) << 8;
+}
+
+void pic17_cpu_device::set_skip()
+{
+ m_execflags |= SKIP;
+}
+
+void pic17_cpu_device::clear_watchdog_timer()
+{
+ //logerror("%04X: Watchdog timer cleared\n", m_ppc);
+}
+
+u16 pic17_cpu_device::banked_register(u8 r)
+{
+ // INDF0 and INDF1 substitute contents of FSR0/FSR1
+ if ((r & 0xf7) == 0)
+ {
+ // Post auto-decrement, post auto-increment or no change
+ u8 fsmode = m_alusta >> (BIT(r, 3) ? 6 : 4);
+ if (BIT(fsmode, 1))
+ r = m_fsr[BIT(r, 3)];
+ else if (BIT(fsmode, 0))
+ r = m_fsr[BIT(r, 3)]++;
+ else
+ r = m_fsr[BIT(r, 3)]--;
+ }
+
+ if (r >= 0x20)
+ {
+ // Addresses from 20h to FFh use GPR banks
+ return u16(m_bsr & 0xf0) << 4 | r;
+ }
+ else if ((r & 0x18) == 0x10)
+ {
+ // Addresses from 10h to 17h use SFR banks
+ return u16(m_bsr & 0x0f) << 8 | r;
+ }
+ else
+ {
+ // Other addresses are unbanked
+ return r;
+ }
+}
+
+void pic17_cpu_device::stack_push(u16 addr)
+{
+ m_stack[m_stkptr] = addr;
+
+ // Check for stack overflow
+ if (m_stkptr == 0xf)
+ {
+ logerror("%04X: Stack overflow\n", m_pc);
+
+ // STKAV remains cleared until reset
+ m_cpusta &= 0xdf;
+ }
+
+ m_stkptr = (m_stkptr + 1) & 0xf;
+}
+
+u16 pic17_cpu_device::stack_pop()
+{
+ m_stkptr = (m_stkptr - 1) & 0xf;
+ return m_stack[m_stkptr];
+}
+
+u16 pic17_cpu_device::interrupt_vector()
+{
+ u8 active_ints = (m_intsta >> 4) & m_intsta;
+ if (BIT(active_ints, 0))
+ {
+ // Interrupt on RA0/INT pin edge
+ standard_irq_callback(0);
+ m_intsta &= 0xef; // INTF is cleared
+ return 0x0008;
+ }
+ else if (BIT(active_ints, 1))
+ {
+ // Interrupt on TMR0 overflow
+ standard_irq_callback(1);
+ m_intsta &= 0xdf; // T0IF is cleared
+ return 0x0010;
+ }
+ else if (BIT(active_ints, 2))
+ {
+ // Interrupt on RA1/T0CKI pin edge
+ standard_irq_callback(2);
+ m_intsta &= 0xbf; // T0CKIF is cleared
+ return 0x0018;
+ }
+ else if (BIT(active_ints, 3))
+ {
+ // Peripheral interrupts (not further distinguished here, and PEIF is *not* cleared)
+ standard_irq_callback(3);
+ return 0x0020;
+ }
+ else
+ {
+ // This may happen when interrupt enable flags are cleared at the wrong moment
+ logerror("%04X: Spurious interrupt taken\n");
+ return 0x0000;
+ }
+}
+
+void pic17_cpu_device::set_zero(bool z)
+{
+ if (z)
+ m_alusta |= 0x04;
+ else
+ m_alusta &= 0xfb;
+}
+
+void pic17_cpu_device::set_carry(bool c)
+{
+ if (c)
+ m_alusta |= 0x01;
+ else
+ m_alusta &= 0xfe;
+}
+
+void pic17_cpu_device::add_with_carry(u8 augend, bool cin)
+{
+ // Carry out is inverted borrow for subtraction
+ bool cout = m_tmp + augend + cin >= 0x100;
+ bool dc = (m_tmp & 0x0f) + (augend & 0x0f) + cin >= 0x10;
+ s16 sum = s8(m_tmp) + s8(augend) + cin;
+ m_tmp = sum & 0x00ff;
+
+ // Set OV, Z, DC and C flags
+ m_alusta &= 0xf0;
+ if (sum < -128 || sum >= 128)
+ m_alusta |= 0x08;
+ if (m_tmp == 0)
+ m_alusta |= 0x04;
+ if (dc)
+ m_alusta |= 0x02;
+ if (cout)
+ m_alusta |= 0x01;
+}
+
+void pic17_cpu_device::decimal_adjust()
+{
+ u16 result = m_wreg;
+
+ if (BIT(m_alusta, 1) || (result & 0x0f) > 0x09)
+ {
+ result += 0x06;
+ if (result >= 0x100)
+ m_alusta |= 0x01;
+ }
+ if (BIT(m_alusta, 0) || (result & 0xf0) > 0x90)
+ {
+ result += 0x60;
+ if (result >= 0x100)
+ m_alusta |= 0x01;
+ }
+}
+
+void pic17_cpu_device::q1_decode()
+{
+ m_raddr = 0x000;
+ if (m_ir >= 0xc000)
+ {
+ // GOTO and CALL write to PCL (after setting PCLATH)
+ m_raddr = 0x002;
+ m_execflags |= REGWT;
+ }
+ else if (m_ir >= 0xb000)
+ {
+ if ((m_ir & 0x0f00) == 0x700)
+ {
+ // LCALL is a literal write to PCL
+ m_raddr = 0x002;
+ m_execflags |= REGWT;
+ }
+ else if ((m_ir & 0x0f00) == 0x800 || (m_ir & 0x0e00) == 0xa00)
+ {
+ // MOVLB and MOVLR operate on BSR
+ m_raddr = 0x00f;
+ m_execflags |= REGWT;
+ }
+ }
+ else if ((m_ir & 0xe000) == 0x4000)
+ {
+ // MOVPF reads register in 'p' field first
+ m_raddr = banked_register((m_ir & 0x1f00) >> 8);
+ }
+ else
+ {
+ if (m_ir != 0x0000)
+ m_raddr = banked_register(m_ir & 0x00ff);
+ if (m_ir < 0x3000)
+ {
+ // Byte-oriented register operations with d = 1
+ // CLRF, SETF, NEGW, DAW always copy their result to the register, even when s = 0
+ if (m_ir >= 0x2800 || BIT(m_ir, 8))
+ m_execflags |= REGWT;
+ }
+ else if ((m_ir & 0xf800) == 0x3800 || (m_ir & 0xf000) == 0x8000)
+ {
+ // Bitwise read-modify-write instructions
+ m_execflags |= REGWT;
+ }
+ else if ((m_ir & 0xf400) == 0xa000)
+ {
+ // TABLRD/TBRD
+ m_execflags |= REGWT;
+ }
+ }
+}
+
+void pic17_cpu_device::q2_read()
+{
+ if (m_ir >= 0xb000)
+ {
+ // Read literal data only
+ m_tmp = m_ir & 0x00ff;
+
+ // Set PCLATH for GOTO/CALL
+ if (m_ir >= 0xc000)
+ m_pclath = (m_pc & 0xe000) >> 8 | (m_ir & 0x1f00) >> 8;
+ }
+ else if ((m_ir & 0xa400) == 0xa000)
+ {
+ // Read TABLATH or TABLATL
+ if (BIT(m_ir, 9))
+ m_tmp = m_tablat >> 8;
+ else
+ m_tmp = m_tablat & 0x00ff;
+ }
+ else if (m_ir != 0x0000)
+ {
+ // Read register (dummy read for some instructions)
+ m_tmp = m_data.read_byte(m_raddr);
+
+ if ((m_ir & 0xc000) == 0x4000)
+ {
+ // Swap out address for MOVFP/MOVPF
+ u8 f = m_ir & 0x00ff;
+ u8 p = (m_ir & 0x1f00) >> 8;
+ if (p != f)
+ m_raddr = banked_register(BIT(m_ir, 13) ? p : f);
+
+ // Change from read to write mode
+ m_execflags |= REGWT;
+ }
+ }
+}
+
+void pic17_cpu_device::q3_execute()
+{
+ if (m_ir >= 0xe000)
+ stack_push(m_pc);
+ else switch (m_ir & 0xff00)
+ {
+ case 0x0000:
+ switch (m_ir & 0x00ff)
+ {
+ case 0x00: // NOP
+ break;
+
+ case 0x02: // RETURN
+ m_pc = stack_pop();
+ m_execflags |= FORCENOP;
+ break;
+
+ case 0x03: // SLEEP
+ clear_watchdog_timer();
+ m_cpusta |= 0x08; // set TO
+ m_cpusta &= 0xfb; // clear PD
+ m_execflags |= SLEEP;
+ break;
+
+ case 0x04: // CLRWDT
+ clear_watchdog_timer();
+ break;
+
+ case 0x05: // RETFIE
+ m_pc = stack_pop();
+ m_execflags |= FORCENOP;
+
+ // Clear global interrupt disable bit
+ m_cpusta &= 0xef;
+ break;
+ }
+ break;
+
+ case 0x0100: // MOVWF
+ m_tmp = m_wreg;
+ break;
+
+ case 0x0200: case 0x0300: // SUBWFB
+ add_with_carry(~m_wreg, BIT(m_alusta, 0));
+ break;
+
+ case 0x0400: case 0x0500: // SUBWF
+ case 0xb200: // SUBLW
+ add_with_carry(~m_wreg, true);
+ break;
+
+ case 0x0600: case 0x0700: // DECF
+ add_with_carry(0xff, false);
+ break;
+
+ case 0x0800: case 0x0900: // IORWF
+ case 0xb300: // IORLW
+ m_tmp |= m_wreg;
+ set_zero(m_tmp);
+ break;
+
+ case 0x0a00: case 0x0b00: // ANDWF
+ case 0xb500: // ANDLW
+ m_tmp &= m_wreg;
+ set_zero(m_tmp);
+ break;
+
+ case 0x0c00: case 0x0d00: // XORWF
+ case 0xb400: // XORLW
+ m_tmp ^= m_wreg;
+ set_zero(m_tmp);
+ break;
+
+ case 0x0e00: case 0x0f00: // ADDWF
+ case 0xb100: // ADDLW
+ add_with_carry(m_wreg, false);
+ break;
+
+ case 0x1000: case 0x1100: // ADDWFC
+ add_with_carry(m_wreg, BIT(m_alusta, 0));
+ break;
+
+ case 0x1200: case 0x1300: // COMF
+ m_tmp ^= 0xff;
+ set_zero(m_tmp);
+ break;
+
+ case 0x1400: case 0x1500: // INCF
+ add_with_carry(1, false);
+ break;
+
+ case 0x1600: case 0x1700: // DECFSZ
+ --m_tmp;
+ if (m_tmp == 0)
+ set_skip();
+ break;
+
+ case 0x1800: case 0x1900: // RRCF
+ set_carry(BIT(std::exchange(m_tmp, (m_tmp >> 1) | (m_alusta << 7)), 0));
+ break;
+
+ case 0x1a00: case 0x1b00: // RLCF
+ set_carry(BIT(std::exchange(m_tmp, (m_tmp << 1) | (m_alusta & 0x01)), 7));
+ break;
+
+ case 0x1c00: case 0x1d00: // SWAPF
+ m_tmp = (m_tmp << 4) | (m_tmp >> 4);
+ break;
+
+ case 0x1e00: case 0x1f00: // INCFSZ
+ ++m_tmp;
+ if (m_tmp == 0)
+ set_skip();
+ break;
+
+ case 0x2000: case 0x2100: // RRNCF
+ m_tmp = (m_tmp >> 1) | (m_tmp << 7);
+ break;
+
+ case 0x2200: case 0x2300: // RLNCF
+ m_tmp = (m_tmp << 1) | (m_tmp >> 7);
+ break;
+
+ case 0x2400: case 0x2500: // INFSNZ
+ ++m_tmp;
+ if (m_tmp != 0)
+ set_skip();
+ break;
+
+ case 0x2600: case 0x2700: // DCFSNZ
+ --m_tmp;
+ if (m_tmp != 0)
+ set_skip();
+ break;
+
+ case 0x2800: case 0x2900: // CLRF
+ m_tmp = 0;
+ break;
+
+ case 0x2a00: case 0x2b00: // SETF
+ m_tmp = 0xff;
+ break;
+
+ case 0x2c00: case 0x2d00: // NEGW
+ m_tmp = 0;
+ add_with_carry(~m_wreg, true);
+ break;
+
+ case 0x2e00: case 0x2f00: // DAW
+ decimal_adjust();
+ break;
+
+ case 0x3000: // CPFSLT (unsigned comparison)
+ if (m_tmp < m_wreg)
+ set_skip();
+ break;
+
+ case 0x3100: // CPFSEQ
+ if (m_tmp == m_wreg)
+ set_skip();
+ break;
+
+ case 0x3200: // CPFSGT (unsigned comparison)
+ if (m_tmp > m_wreg)
+ set_skip();
+ break;
+
+ case 0x3300: // TSTFSZ
+ if (m_tmp == 0)
+ set_skip();
+ break;
+
+ case 0x3400: // MULWF
+ case 0xbc00: // MULLW
+ m_prod = u16(m_wreg) * u16(m_tmp);
+ break;
+
+ case 0x3800: case 0x3900: case 0x3a00: case 0x3b00: case 0x3c00: case 0x3d00: case 0x3e00: case 0x3f00: // BTG
+ m_tmp ^= 1 << ((m_ir & 0x0700) >> 8);
+ break;
+
+ case 0x8000: case 0x8100: case 0x8200: case 0x8300: case 0x8400: case 0x8500: case 0x8600: case 0x8700: // BSF
+ m_tmp |= 1 << ((m_ir & 0x0700) >> 8);
+ break;
+
+ case 0x8800: case 0x8900: case 0x8a00: case 0x8b00: case 0x8c00: case 0x8d00: case 0x8e00: case 0x8f00: // BCF
+ m_tmp &= ~(1 << ((m_ir & 0x0700) >> 8));
+ break;
+
+ case 0x9000: case 0x9100: case 0x9200: case 0x9300: case 0x9400: case 0x9500: case 0x9600: case 0x9700: // BTFSS
+ if ((m_tmp & (1 << ((m_ir & 0x0700) >> 8))) != 0)
+ set_skip();
+ break;
+
+ case 0x9800: case 0x9900: case 0x9a00: case 0x9b00: case 0x9c00: case 0x9d00: case 0x9e00: case 0x9f00: // BTFSC
+ if ((m_tmp & (1 << ((m_ir & 0x0700) >> 8))) == 0)
+ set_skip();
+ break;
+
+ case 0xb600: // RETLW
+ m_pc = stack_pop();
+ m_execflags |= FORCENOP;
+ break;
+
+ case 0xb700: // LCALL
+ stack_push(m_pc);
+ break;
+
+ case 0xb800: // MOVLB
+ m_tmp = (m_bsr & 0xf0) | (m_tmp & 0x0f);
+ break;
+
+ case 0xba00: case 0xbb00: // MOVLR
+ m_tmp = (m_tmp & 0xf0) | (m_bsr & 0x0f);
+ break;
+ }
+}
+
+void pic17_cpu_device::q4_write()
+{
+ if ((m_execflags & REGWT) != 0)
+ {
+ m_data.write_byte(m_raddr, m_tmp);
+ m_execflags &= ~REGWT;
+ if ((m_ir & 0xfc00) == 0xa800)
+ {
+ m_execflags |= TABLRD;
+ if (BIT(m_ir, 8))
+ m_execflags |= TBLPTRI;
+ }
+ }
+ else if ((m_ir & 0xf400) == 0xa400)
+ {
+ // Set TABLATH or TABLATL for TABLWT/TLWT
+ if (BIT(m_ir, 9))
+ {
+ m_tablat = (m_tablat & 0x00ff) | u16(m_tmp) << 8;
+ m_tlwt = (m_tlwt & 0x00ff) | u16(m_tmp) << 8;
+ }
+ else
+ {
+ m_tablat = (m_tablat & 0xff00) | m_tmp;
+ m_tlwt = (m_tlwt & 0xff00) | m_tmp;
+ }
+ if (BIT(m_ir, 11))
+ {
+ m_execflags |= TABLWT;
+ if (BIT(m_ir, 8))
+ m_execflags |= TBLPTRI;
+ }
+ }
+
+ // MOVLW, ADDLW, SUBLW, IORLW, XORLW, ANDLW, RETLW always store result in W; other ALU instructions do so when d = 0
+ if ((m_ir >= 0x0200 && m_ir < 0x3000 && !BIT(m_ir, 8)) || (m_ir >= 0xb000 && m_ir < 0xb700))
+ m_wreg = m_tmp;
+}
+
+void pic17_cpu_device::increment_timers()
+{
+ // TMR0 increment is inhibited by count writes to TMR0L
+ if (BIT(m_t0sta, 5) && ((m_execflags & REGWT) == 0 || m_raddr != 0x00b))
+ increment_tmr0();
+}
+
+void pic17_cpu_device::execute_run()
+{
+ switch (m_execphase)
+ {
+ while (true)
+ {
+ case exec_phase::Q1:
+ if ((m_execflags & SLEEP) != 0)
+ {
+ if (((m_intsta >> 4) & m_intsta) != 0)
+ m_execflags &= ~SLEEP;
+ else
+ {
+ // Do nothing until time to wake up
+ m_icount = 0;
+ m_execphase = exec_phase::Q1;
+ return;
+ }
+ }
+ if (m_execflags == 0)
+ {
+ m_ppc = m_pc;
+ debugger_instruction_hook(m_pc);
+ ++m_pc;
+ q1_decode();
+ }
+ else if ((m_execflags & (TABLRD | TABLWT)) == 0)
+ {
+ if ((m_execflags & SKIP) != 0)
+ ++m_pc;
+ m_ir = 0;
+ m_execflags &= ~(SKIP | FORCENOP);
+ }
+ if (--m_icount <= 0)
+ {
+ m_execphase = exec_phase::Q2;
+ return;
+ }
+
+ case exec_phase::Q2:
+ if ((m_execflags & (TABLRD | TABLWT)) != 0)
+ {
+ m_paddr = m_tblptr;
+ if ((m_execflags & TBLPTRI) != 0)
+ ++m_tblptr;
+ }
+ else
+ {
+ m_paddr = m_pc;
+ q2_read();
+ }
+ if (--m_icount <= 0)
+ {
+ m_execphase = exec_phase::Q3;
+ return;
+ }
+
+ case exec_phase::Q3:
+ if ((m_execflags & (TABLRD | TABLWT)) == 0)
+ {
+ if ((m_execflags & INTRPT) != 0)
+ {
+ m_execflags &= ~INTRPT;
+
+ // Set global interrupt disable flag
+ m_cpusta |= 0x10;
+
+ // Call to interrupt vector
+ stack_push(m_pc);
+ m_pc = interrupt_vector();
+ m_execflags |= FORCENOP;
+ }
+ else
+ q3_execute();
+ }
+ if (--m_icount <= 0)
+ {
+ m_execphase = exec_phase::Q4;
+ return;
+ }
+
+ case exec_phase::Q4:
+ if (!BIT(m_cpusta, 4) && ((m_intsta >> 4) & m_intsta) != 0)
+ m_execflags = (m_execflags | INTRPT) & ~SLEEP;
+ increment_timers();
+ if ((m_execflags & TABLWT) != 0)
+ {
+ // TABLAT value used here may not be from TABLRD, but must be written with TABLAT/TLWT.
+ // See PIC17C44 Rev. A Silicon Errata Sheet, DS30412C/44/E1A2.
+ m_program.write_word(m_paddr, m_tlwt);
+ m_execflags &= ~(TABLWT | TBLPTRI);
+ }
+ else if ((m_execflags & TABLRD) != 0)
+ {
+ m_tablat = m_program.read_word(m_paddr);
+ m_execflags &= ~(TABLRD | TBLPTRI);
+ }
+ else
+ {
+ u16 inst = m_cache.read_word(m_paddr);
+ q4_write();
+ m_ir = inst;
+ }
+ if (--m_icount <= 0)
+ {
+ m_execphase = exec_phase::Q1;
+ return;
+ }
+ }
+ }
+}
+
+void pic17_cpu_device::device_start()
+{
+ // Hook address spaces
+ space(AS_PROGRAM).cache(m_cache);
+ space(AS_PROGRAM).specific(m_program);
+ space(AS_DATA).specific(m_data);
+ set_icountptr(m_icount);
+
+ // Register debug state
+ state_add<u16>(PIC17_PC, "PC",
+ [this]() { return m_pc; },
+ [this](u16 data) { debug_set_pc(data); }
+ );
+ state_add<u16>(STATE_GENPC, "GENPC",
+ [this]() { return m_pc; },
+ [this](u16 data) { debug_set_pc(data); }
+ ).noshow();
+ state_add<u16>(STATE_GENPCBASE, "CURPC",
+ [this]() { return m_ppc; },
+ [this](u16 data) { debug_set_pc(data); }
+ ).noshow();
+ state_add(PIC17_PCLATH, "PCLATH", m_pclath);
+ state_add(PIC17_STKPTR, "STKPTR", m_stkptr).mask(0xf);
+ state_add<u16>(PIC17_TOS, "TOS",
+ [this]() { return m_stack[(m_stkptr - 1) & 0xf]; },
+ [this](u16 data) { m_stack[(m_stkptr - 1) & 0xf] = data; }
+ );
+ state_add(PIC17_WREG, "W", m_wreg);
+ state_add(PIC17_PROD, "PROD", m_prod); // not in PIC17C42
+ state_add(PIC17_ALUSTA, "ALUSTA", m_alusta);
+ state_add(STATE_GENFLAGS, "FLAGS", m_alusta).formatstr("%10s").noshow();
+ state_add(PIC17_CPUSTA, "CPUSTA", m_cpusta).mask(0x3c); // PIC17C75X also implements bits 1 and 0
+ state_add(PIC17_INTSTA, "INTSTA", m_intsta);
+ state_add(PIC17_FSR0, "FSR0", m_fsr[0]);
+ state_add(PIC17_FSR1, "FSR1", m_fsr[1]);
+ state_add(PIC17_BSR, "BSR", m_bsr);
+ state_add(PIC17_TBLPTR, "TBLPTR", m_tblptr);
+ state_add(PIC17_TABLAT, "TABLAT", m_tablat);
+ state_add(PIC17_TLWT, "TLWT", m_tlwt);
+ state_add(PIC17_T0STA, "T0STA", m_t0sta).mask(0xfe);
+ state_add(PIC17_TMR0, "TMR0", m_tmr0);
+ state_add(PIC17_PS, "PS", m_ps);
+
+ // Save state
+ save_item(NAME(m_pc));
+ save_item(NAME(m_ppc));
+ save_item(NAME(m_paddr));
+ save_item(NAME(m_raddr));
+ save_item(NAME(m_ir));
+ save_item(NAME(m_tmp));
+ save_item(NAME(m_execphase));
+ save_item(NAME(m_execflags));
+ save_item(NAME(m_pclath));
+ save_item(NAME(m_wreg));
+ save_item(NAME(m_alusta));
+ save_item(NAME(m_cpusta));
+ save_item(NAME(m_intsta));
+ save_item(NAME(m_tblptr));
+ save_item(NAME(m_tablat));
+ save_item(NAME(m_tlwt));
+ save_item(NAME(m_prod));
+ save_item(NAME(m_fsr));
+ save_item(NAME(m_bsr));
+ save_item(NAME(m_stkptr));
+ save_item(NAME(m_stack));
+ save_item(NAME(m_t0sta));
+ save_item(NAME(m_tmr0));
+ save_item(NAME(m_ps));
+}
+
+void pic17_cpu_device::device_reset()
+{
+ // Reset CPU
+ m_pc = 0;
+ m_execphase = exec_phase::Q1;
+ m_execflags = FORCENOP;
+ m_alusta |= 0xf0;
+ m_t0sta = 0;
+ m_cpusta |= 0x3c;
+ m_intsta = 0;
+ m_tblptr = 0; // not cleared on PIC17C42
+ m_bsr = 0;
+ m_stkptr = 0;
+}
+
+void pic17_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("1%c 0%c %c%c%c%c",
+ BIT(m_alusta, 7) ? '=' : BIT(m_alusta, 6) ? '+' : '-',
+ BIT(m_alusta, 5) ? '=' : BIT(m_alusta, 4) ? '+' : '-',
+ BIT(m_alusta, 3) ? 'V' : '.', // OV
+ BIT(m_alusta, 2) ? 'Z' : '.',
+ BIT(m_alusta, 1) ? 'D' : '.', // DC
+ BIT(m_alusta, 0) ? 'C' : '.');
+ break;
+ }
+}
diff --git a/src/devices/cpu/pic17/pic17.h b/src/devices/cpu/pic17/pic17.h
new file mode 100644
index 00000000000..d29a25fc958
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17.h
@@ -0,0 +1,180 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_PIC17_PIC17_H
+#define MAME_CPU_PIC17_PIC17_H
+
+#pragma once
+
+class pic17_cpu_device : public cpu_device
+{
+public:
+ enum {
+ PIC17_PC,
+ PIC17_PCLATH,
+ PIC17_STKPTR,
+ PIC17_TOS,
+ PIC17_WREG,
+ PIC17_PROD,
+ PIC17_ALUSTA,
+ PIC17_CPUSTA,
+ PIC17_INTSTA,
+ PIC17_FSR0,
+ PIC17_FSR1,
+ PIC17_BSR,
+ PIC17_TBLPTR,
+ PIC17_TABLAT,
+ PIC17_TLWT,
+ PIC17_T0STA,
+ PIC17_TMR0,
+ PIC17_PS
+ };
+
+ enum class mode {
+ MICROPROCESSOR,
+ MICROCONTROLLER,
+ EXTENDED_MICROCONTROLLER
+ };
+
+ // misc. configuration
+ void set_mode(mode m) { m_mode = m; }
+
+protected:
+ pic17_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size, address_map_constructor data_map);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual void execute_run() override;
+
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+
+ // device_state_interface overrides
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+ // internal data map
+ void core_data_map(address_map &map);
+
+ // helpers for derived classes
+ void int_edge(bool rising);
+ void t0cki_edge(bool rising);
+ void set_peif(bool state);
+ virtual void increment_timers();
+
+private:
+ enum class exec_phase : u8 { Q1, Q2, Q3, Q4 };
+
+ enum exec_flags : u8 {
+ REGWT = 1 << 0U,
+ FORCENOP = 1 << 1U,
+ SKIP = 1 << 2U,
+ INTRPT = 1 << 3U,
+ TBLPTRI = 1 << 4U,
+ TABLRD = 1 << 5U,
+ TABLWT = 1 << 6U,
+ SLEEP = 1 << 7U
+ };
+
+ // internal register accessors
+ u8 pcl_r();
+ void pcl_w(u8 data);
+ void debug_set_pc(u16 data);
+ u8 pclath_r();
+ void pclath_w(u8 data);
+ u8 wreg_r();
+ void wreg_w(u8 data);
+ u8 alusta_r();
+ void alusta_w(u8 data);
+ u8 cpusta_r();
+ void cpusta_w(u8 data);
+ u8 intsta_r();
+ void intsta_w(u8 data);
+ u8 t0sta_r();
+ void t0sta_w(u8 data);
+ u8 tmr0l_r();
+ void tmr0l_w(u8 data);
+ u8 tmr0h_r();
+ void tmr0h_w(u8 data);
+ void increment_tmr0();
+ u8 fsr0_r();
+ void fsr0_w(u8 data);
+ u8 fsr1_r();
+ void fsr1_w(u8 data);
+ u8 bsr_r();
+ void bsr_w(u8 data);
+ u8 tblptrl_r();
+ void tblptrl_w(u8 data);
+ u8 tblptrh_r();
+ void tblptrh_w(u8 data);
+ u8 prodl_r();
+ void prodl_w(u8 data);
+ u8 prodh_r();
+ void prodh_w(u8 data);
+
+ // execution helpers
+ void set_skip();
+ void clear_watchdog_timer();
+ u16 banked_register(u8 r);
+ void stack_push(u16 addr);
+ u16 stack_pop();
+ u16 interrupt_vector();
+ void set_zero(bool z);
+ void set_carry(bool c);
+ void add_with_carry(u8 augend, bool cin);
+ void decimal_adjust();
+ void q1_decode();
+ void q2_read();
+ void q3_execute();
+ void q4_write();
+
+ // address map constructor
+ void program_map(address_map &map);
+
+ // address spaces
+ const address_space_config m_program_config;
+ const address_space_config m_data_config;
+ const u16 m_rom_size;
+ memory_access<16, 1, -1, ENDIANNESS_LITTLE>::cache m_cache;
+ memory_access<16, 1, -1, ENDIANNESS_LITTLE>::specific m_program;
+ memory_access<12, 0, 0, ENDIANNESS_LITTLE>::specific m_data;
+
+ // mode configuration
+ mode m_mode;
+
+ // execution state
+ u16 m_pc;
+ u16 m_ppc;
+ u16 m_paddr;
+ u16 m_raddr;
+ u16 m_ir;
+ u8 m_tmp;
+ exec_phase m_execphase;
+ u8 m_execflags;
+ s32 m_icount;
+
+ // internal status and control registers
+ u8 m_pclath;
+ u8 m_wreg;
+ u8 m_alusta;
+ u8 m_cpusta;
+ u8 m_intsta;
+ u16 m_tblptr;
+ u16 m_tablat;
+ u16 m_tlwt;
+ u16 m_prod;
+ u8 m_fsr[2];
+ u8 m_bsr;
+ u8 m_stkptr;
+ u16 m_stack[16];
+ u8 m_t0sta;
+ u16 m_tmr0;
+ u8 m_ps;
+};
+
+#endif // MAME_CPU_PIC17_PIC17_H
diff --git a/src/devices/cpu/pic17/pic17c4x.cpp b/src/devices/cpu/pic17/pic17c4x.cpp
new file mode 100644
index 00000000000..07976c32b79
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17c4x.cpp
@@ -0,0 +1,537 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Microchip PIC17C4X microcontrollers
+
+ Comparative list of features:
+
+ Clock frequency 33 MHz maximum (PIC17C42: 25 MHz maximum)
+ EPROM (PIC17C42(A)) 2K (mask ROM on PIC17CR42)
+ EPROM (PIC17C43) 4K (mask ROM on PIC17CR43)
+ EPROM (PIC17C44) 8K
+ RAM (PIC17C42(A)) 232 bytes
+ RAM (PIC17C43/44) 454 bytes
+ Pin count 40 (PDIP, CERDIP) or 44 (PLCC, MQFP, TQFP)
+ Port pins 33 (19 shared with external bus)
+ Interrupt sources 11
+ 8-bit timers 2 (cascadable)
+ 16-bit timers 16 (including TMR0)
+ Capture inputs 2
+ PWM outputs 2
+ USART 1
+
+ The original PIC17C42 lacks a hardware multiplier and has a few
+ CPU core bugs that were corrected in PIC17C42A and the rest of
+ the family.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "pic17c4x.h"
+
+// device type definitions
+DEFINE_DEVICE_TYPE(PIC17C43, pic17c43_device, "pic17c43", "Microchip PIC17C43")
+DEFINE_DEVICE_TYPE(PIC17C44, pic17c44_device, "pic17c44", "Microchip PIC17C44")
+
+pic17c4x_device::pic17c4x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size, address_map_constructor data_map)
+ : pic17_cpu_device(mconfig, type, tag, owner, clock, rom_size, data_map)
+ , m_port_out_cb(*this)
+ , m_rain(0x3f)
+ , m_lata(0)
+ , m_rbpu(false)
+ , m_ddrb(0)
+ , m_latb(0)
+ , m_tmr8bit{0, 0}
+ , m_pr8bit{0, 0}
+ , m_tmr3(0)
+ , m_ca16bit{0, 0}
+ , m_tcon1(0)
+ , m_tcon2(0)
+ , m_rcsta(0)
+ , m_txsta(0)
+ , m_spbrg(0)
+ , m_pir(0)
+ , m_pie(0)
+{
+}
+
+pic17c43_device::pic17c43_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size)
+ : pic17c4x_device(mconfig, type, tag, owner, clock, rom_size, address_map_constructor(FUNC(pic17c43_device::data_map), this))
+{
+}
+
+pic17c43_device::pic17c43_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic17c43_device(mconfig, PIC17C43, tag, owner, clock, 0x1000)
+{
+}
+
+pic17c44_device::pic17c44_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic17c43_device(mconfig, PIC17C44, tag, owner, clock, 0x2000)
+{
+}
+
+void pic17c4x_device::data_map(address_map &map)
+{
+ core_data_map(map);
+
+ // TODO: add handlers for more of these SFRs
+ map(0x010, 0x010).rw(FUNC(pic17c4x_device::porta_r), FUNC(pic17c4x_device::porta_w));
+ map(0x011, 0x011).rw(FUNC(pic17c4x_device::ddrb_r), FUNC(pic17c4x_device::ddrb_w));
+ map(0x012, 0x012).rw(FUNC(pic17c4x_device::portb_r), FUNC(pic17c4x_device::portb_w));
+ map(0x013, 0x013).rw(FUNC(pic17c4x_device::rcsta_r), FUNC(pic17c4x_device::rcsta_w));
+ //map(0x014, 0x014).r(FUNC(pic17c4x_device::rcreg_r));
+ map(0x015, 0x015).rw(FUNC(pic17c4x_device::txsta_r), FUNC(pic17c4x_device::txsta_w));
+ map(0x016, 0x016).w(FUNC(pic17c4x_device::txreg_w));
+ map(0x017, 0x017).rw(FUNC(pic17c4x_device::spbrg_r), FUNC(pic17c4x_device::spbrg_w));
+ //map(0x110, 0x110).rw(FUNC(pic17c4x_device::ddrc_r), FUNC(pic17c4x_device::ddrc_w));
+ //map(0x111, 0x111).rw(FUNC(pic17c4x_device::portc_r), FUNC(pic17c4x_device::portc_w));
+ //map(0x112, 0x112).rw(FUNC(pic17c4x_device::ddrd_r), FUNC(pic17c4x_device::ddrd_w));
+ //map(0x113, 0x113).rw(FUNC(pic17c4x_device::portd_r), FUNC(pic17c4x_device::portd_w));
+ //map(0x114, 0x114).rw(FUNC(pic17c4x_device::ddre_r), FUNC(pic17c4x_device::ddre_w));
+ //map(0x115, 0x115).rw(FUNC(pic17c4x_device::porte_r), FUNC(pic17c4x_device::porte_w));
+ map(0x116, 0x116).rw(FUNC(pic17c4x_device::pir_r), FUNC(pic17c4x_device::pir_w));
+ map(0x117, 0x117).rw(FUNC(pic17c4x_device::pie_r), FUNC(pic17c4x_device::pie_w));
+ map(0x210, 0x210).rw(FUNC(pic17c4x_device::tmr1_r), FUNC(pic17c4x_device::tmr1_w));
+ map(0x211, 0x211).rw(FUNC(pic17c4x_device::tmr2_r), FUNC(pic17c4x_device::tmr2_w));
+ map(0x212, 0x212).rw(FUNC(pic17c4x_device::tmr3l_r), FUNC(pic17c4x_device::tmr3l_w));
+ map(0x213, 0x213).rw(FUNC(pic17c4x_device::tmr3h_r), FUNC(pic17c4x_device::tmr3h_w));
+ map(0x214, 0x214).rw(FUNC(pic17c4x_device::pr1_r), FUNC(pic17c4x_device::pr1_w));
+ map(0x215, 0x215).rw(FUNC(pic17c4x_device::pr2_r), FUNC(pic17c4x_device::pr2_w));
+ map(0x216, 0x216).rw(FUNC(pic17c4x_device::pr3l_ca1l_r), FUNC(pic17c4x_device::pr3l_ca1l_w));
+ map(0x217, 0x217).rw(FUNC(pic17c4x_device::pr3h_ca1h_r), FUNC(pic17c4x_device::pr3h_ca1h_w));
+ //map(0x310, 0x310).rw(FUNC(pic17c4x_device::pw1dcl_r), FUNC(pic17c4x_device::pw1dcl_w));
+ //map(0x311, 0x311).rw(FUNC(pic17c4x_device::pw2dcl_r), FUNC(pic17c4x_device::pw2dcl_w));
+ //map(0x312, 0x312).rw(FUNC(pic17c4x_device::pw1dch_r), FUNC(pic17c4x_device::pw1dch_w));
+ //map(0x313, 0x313).rw(FUNC(pic17c4x_device::pw2dch_r), FUNC(pic17c4x_device::pw2dch_w));
+ map(0x314, 0x314).r(FUNC(pic17c4x_device::ca2l_r));
+ map(0x315, 0x315).r(FUNC(pic17c4x_device::ca2h_r));
+ map(0x316, 0x316).rw(FUNC(pic17c4x_device::tcon1_r), FUNC(pic17c4x_device::tcon1_w));
+ map(0x317, 0x317).rw(FUNC(pic17c4x_device::tcon2_r), FUNC(pic17c4x_device::tcon2_w));
+}
+
+void pic17c43_device::data_map(address_map &map)
+{
+ pic17c4x_device::data_map(map);
+
+ // RAM: 454 bytes
+ map(0x01a, 0x0ff).ram();
+ map(0x120, 0x1ff).ram();
+}
+
+u8 pic17c4x_device::porta_r()
+{
+ // RA2 and RA3 are open-drain I/O
+ // TODO: RA4 and RA5 carry USART signals
+ u8 porta = (m_rain & 0x33) | (m_rain & m_lata & 0x0c);
+ if (m_rbpu)
+ porta |= 0x80;
+ return porta;
+}
+
+void pic17c4x_device::porta_w(u8 data)
+{
+ // RA0 and RA1 have no output drivers
+ // RA4 and RA5 can only be output from USART
+ m_lata = (m_lata & 0x30) | (data & 0x0c);
+ m_port_out_cb[0](m_lata);
+
+ if (m_rbpu != BIT(data, 7))
+ {
+ m_rbpu = BIT(data, 7);
+ if (m_ddrb != 0)
+ m_port_out_cb[1](m_rbpu ? (m_latb | m_ddrb) : (m_latb & m_ddrb));
+ }
+}
+
+u8 pic17c4x_device::ddrb_r()
+{
+ return m_ddrb;
+}
+
+void pic17c4x_device::ddrb_w(u8 data)
+{
+ if (m_ddrb != data)
+ {
+ m_ddrb = data;
+ m_port_out_cb[1](m_rbpu ? (m_latb | m_ddrb) : (m_latb & m_ddrb));
+ }
+}
+
+u8 pic17c4x_device::portb_r()
+{
+ return m_latb | (m_rbpu ? m_ddrb : 0);
+}
+
+void pic17c4x_device::portb_w(u8 data)
+{
+ m_latb = data;
+ m_port_out_cb[1](m_rbpu ? (m_latb | m_ddrb) : (m_latb & m_ddrb));
+}
+
+u8 pic17c4x_device::rcsta_r()
+{
+ return m_rcsta;
+}
+
+void pic17c4x_device::rcsta_w(u8 data)
+{
+ // FERR, OERR and RX9D are read-only; bit 3 is unimplemented
+ m_rcsta = (data & 0xf0) | (m_rcsta & 0x07);
+}
+
+u8 pic17c4x_device::txsta_r()
+{
+ return m_txsta;
+}
+
+void pic17c4x_device::txsta_w(u8 data)
+{
+ // TRMT is read-only; bits 2 & 3 are unimplemented
+ m_txsta = (data & 0xf1) | (m_txsta & 0x02);
+}
+
+void pic17c4x_device::txreg_w(u8 data)
+{
+ logerror("%s: Writing %02X to TXREG\n", machine().describe_context(), data);
+
+ // Clear TXIF
+ m_pir &= 0xfd;
+ if ((m_pir & m_pie) == 0)
+ set_peif(false);
+}
+
+u8 pic17c4x_device::spbrg_r()
+{
+ return m_spbrg;
+}
+
+void pic17c4x_device::spbrg_w(u8 data)
+{
+ m_spbrg = data;
+}
+
+u8 pic17c4x_device::tmr1_r()
+{
+ return m_tmr8bit[0];
+}
+
+void pic17c4x_device::tmr1_w(u8 data)
+{
+ m_tmr8bit[0] = data;
+}
+
+u8 pic17c4x_device::tmr2_r()
+{
+ return m_tmr8bit[1];
+}
+
+void pic17c4x_device::tmr2_w(u8 data)
+{
+ m_tmr8bit[1] = data;
+}
+
+u8 pic17c4x_device::tmr3l_r()
+{
+ return m_tmr3 & 0x00ff;
+}
+
+void pic17c4x_device::tmr3l_w(u8 data)
+{
+ m_tmr3 = (m_tmr3 & 0xff00) | data;
+}
+
+u8 pic17c4x_device::tmr3h_r()
+{
+ return m_tmr3 >> 8;
+}
+
+void pic17c4x_device::tmr3h_w(u8 data)
+{
+ m_tmr3 = u16(data) << 8 | (m_tmr3 & 0x00ff);
+}
+
+u8 pic17c4x_device::pr1_r()
+{
+ return m_pr8bit[0];
+}
+
+void pic17c4x_device::pr1_w(u8 data)
+{
+ m_pr8bit[0] = data;
+}
+
+u8 pic17c4x_device::pr2_r()
+{
+ return m_pr8bit[1];
+}
+
+void pic17c4x_device::pr2_w(u8 data)
+{
+ m_pr8bit[1] = data;
+}
+
+u8 pic17c4x_device::pr3l_ca1l_r()
+{
+ return m_ca16bit[0] & 0x00ff;
+}
+
+void pic17c4x_device::pr3l_ca1l_w(u8 data)
+{
+ m_ca16bit[0] = (m_ca16bit[0] & 0xff00) | data;
+}
+
+u8 pic17c4x_device::pr3h_ca1h_r()
+{
+ return m_ca16bit[0] >> 8;
+}
+
+void pic17c4x_device::pr3h_ca1h_w(u8 data)
+{
+ m_ca16bit[0] = u16(data) << 8 | (m_ca16bit[0] & 0x00ff);
+}
+
+u8 pic17c4x_device::ca2l_r()
+{
+ return m_ca16bit[1] & 0x00ff;
+}
+
+u8 pic17c4x_device::ca2h_r()
+{
+ return m_ca16bit[1] >> 8;
+}
+
+u8 pic17c4x_device::tcon1_r()
+{
+ return m_tcon1;
+}
+
+void pic17c4x_device::tcon1_w(u8 data)
+{
+ m_tcon1 = data;
+}
+
+u8 pic17c4x_device::tcon2_r()
+{
+ return m_tcon2;
+}
+
+void pic17c4x_device::tcon2_w(u8 data)
+{
+ // CA1OVF and CA2OVF are read-only
+ m_tcon2 = (m_tcon2 & 0xc0) | (data & 0x3f);
+}
+
+u8 pic17c4x_device::pir_r()
+{
+ return m_pir;
+}
+
+void pic17c4x_device::pir_w(u8 data)
+{
+ // TXIF and RCIF are read-only
+ m_pir = (data & 0xfc) | (m_pir & 0x03);
+ set_peif((m_pie & m_pir) != 0);
+}
+
+u8 pic17c4x_device::pie_r()
+{
+ return m_pie;
+}
+
+void pic17c4x_device::pie_w(u8 data)
+{
+ m_pie = data;
+ set_peif((m_pie & m_pir) != 0);
+}
+
+void pic17c4x_device::increment_timers()
+{
+ pic17_cpu_device::increment_timers();
+
+ // Advance TMR1
+ if (BIT(m_tcon2, 0) && !BIT(m_tcon1, 0))
+ {
+ if (BIT(m_tcon1, 3))
+ {
+ // T16 mode: TMR1 and TMR2 cascaded
+ if (m_tmr8bit[0] == m_pr8bit[0] && m_tmr8bit[1] == m_pr8bit[1])
+ {
+ // Roll over and set TMR1IF
+ m_tmr8bit[0] = 0;
+ m_tmr8bit[1] = 0;
+ m_pir |= 0x10;
+ if (BIT(m_pie, 4))
+ set_peif(true);
+ }
+ else
+ {
+ ++m_tmr8bit[0];
+ if (m_tmr8bit[0] == 0 && BIT(m_tcon2, 1))
+ ++m_tmr8bit[1];
+ }
+ }
+ else
+ {
+ if (m_tmr8bit[0] == m_pr8bit[0])
+ {
+ // Roll over and set TMR1IF
+ m_tmr8bit[0] = 0;
+ m_pir |= 0x10;
+ if (BIT(m_pie, 4))
+ set_peif(true);
+ }
+ else
+ ++m_tmr8bit[0];
+ }
+ }
+
+ // Advance TMR2 (as 8-bit counter only)
+ if (BIT(m_tcon2, 1) && (m_tcon1 & 0x0a) == 0)
+ {
+ if (m_tmr8bit[1] == m_pr8bit[1])
+ {
+ // Roll over and set TMR2IF
+ m_tmr8bit[1] = 0;
+ m_pir |= 0x20;
+ if (BIT(m_pie, 5))
+ set_peif(true);
+ }
+ else
+ ++m_tmr8bit[1];
+ }
+
+ // Advance TMR3
+ if (BIT(m_tcon2, 2) && !BIT(m_tcon1, 2))
+ {
+ // Period register is not used to determine overflow when CA1 mode is selected
+ if (m_tmr3 == (BIT(m_tcon2, 3) ? 0xffff : m_ca16bit[0]))
+ {
+ // Roll over and set TMR3IF
+ m_tmr3 = 0;
+ m_pir |= 0x40;
+ if (BIT(m_pie, 6))
+ set_peif(true);
+ }
+ else
+ ++m_tmr3;
+ }
+}
+
+void pic17c4x_device::device_resolve_objects()
+{
+ // Resolve callback objects
+ m_port_out_cb.resolve_all_safe();
+}
+
+void pic17c4x_device::device_start()
+{
+ pic17_cpu_device::device_start();
+
+ // Register debug state
+ state_add(PIC17_LATA, "LATA", m_lata).mask(0x3c);
+ state_add(PIC17_DDRB, "DDRB", m_ddrb);
+ state_add(PIC17_LATB, "LATB", m_latb);
+ state_add(PIC17_TMR1, "TMR1", m_tmr8bit[0]);
+ state_add(PIC17_TMR2, "TMR2", m_tmr8bit[1]);
+ state_add(PIC17_TMR3, "TMR3", m_tmr3);
+ state_add(PIC17_PR1, "PR1", m_pr8bit[0]);
+ state_add(PIC17_PR2, "PR2", m_pr8bit[1]);
+ state_add(PIC17_CA1, "CA1", m_ca16bit[0]);
+ state_add(PIC17_CA2, "CA2", m_ca16bit[1]);
+ state_add(PIC17_TCON1, "TCON1", m_tcon1);
+ state_add(PIC17_TCON2, "TCON2", m_tcon2);
+ state_add(PIC17_RCSTA, "RCSTA", m_rcsta).mask(0xf7);
+ state_add(PIC17_TXSTA, "TXSTA", m_txsta).mask(0xf3);
+ state_add(PIC17_SPBRG, "SPBRG", m_spbrg);
+ state_add(PIC17_PIE, "PIE", m_pie);
+ state_add(PIC17_PIR, "PIR", m_pir);
+
+ // Save state
+ save_item(NAME(m_lata));
+ save_item(NAME(m_rbpu));
+ save_item(NAME(m_ddrb));
+ save_item(NAME(m_latb));
+ save_item(NAME(m_tmr8bit));
+ save_item(NAME(m_pr8bit));
+ save_item(NAME(m_tmr3));
+ save_item(NAME(m_ca16bit));
+ save_item(NAME(m_tcon1));
+ save_item(NAME(m_tcon2));
+ save_item(NAME(m_rcsta));
+ save_item(NAME(m_txsta));
+ save_item(NAME(m_spbrg));
+ save_item(NAME(m_pie));
+ save_item(NAME(m_pir));
+}
+
+void pic17c4x_device::device_reset()
+{
+ pic17_cpu_device::device_reset();
+
+ // Reset ports
+ m_lata = 0x3c;
+ m_rbpu = false;
+ m_ddrb = 0xff;
+ m_port_out_cb[0](0x3c);
+ m_port_out_cb[1](0xff);
+
+ // Reset timers
+ //m_pw2dcl &= 0xc0;
+ m_tcon1 = 0;
+ m_tcon2 = 0;
+
+ // Reset USART
+ m_rcsta &= 0x01;
+ m_txsta = (m_txsta & 0x01) | 0x02;
+
+ // Reset peripheral interrupts
+ m_pir = 0x02;
+ m_pie = 0;
+}
+
+void pic17c4x_device::execute_set_input(int linenum, int state)
+{
+ switch (linenum)
+ {
+ case RA0_LINE:
+ if (state != CLEAR_LINE && BIT(m_rain, 0))
+ {
+ // Falling edge
+ m_rain &= 0x3e;
+ int_edge(false);
+ }
+ else if (state == CLEAR_LINE && !BIT(m_rain, 0))
+ {
+ // Rising edge
+ m_rain |= 0x01;
+ int_edge(true);
+ }
+ break;
+
+ case RA1_LINE:
+ if (state != CLEAR_LINE && BIT(m_rain, 1))
+ {
+ // Falling edge
+ m_rain &= 0x3d;
+ t0cki_edge(false);
+ }
+ else if (state == CLEAR_LINE && !BIT(m_rain, 1))
+ {
+ // Rising edge
+ m_rain |= 0x02;
+ t0cki_edge(true);
+ }
+ break;
+
+ case RA2_LINE:
+ case RA3_LINE:
+ case RA4_LINE:
+ case RA5_LINE:
+ if (state == ASSERT_LINE)
+ m_rain &= ~(1 << (linenum - RA0_LINE));
+ else
+ m_rain |= 1 << (linenum - RA0_LINE);
+ break;
+ }
+}
diff --git a/src/devices/cpu/pic17/pic17c4x.h b/src/devices/cpu/pic17/pic17c4x.h
new file mode 100644
index 00000000000..e1ae9290230
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17c4x.h
@@ -0,0 +1,181 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+ _____ _____
+ Vdd 1 |* \__/ | 40 RD0/AD8
+ RC0/AD0 2 | | 39 RD1/AD9
+ RC1/AD1 3 | | 38 RD2/AD10
+ RC2/AD2 4 | | 37 RD3/AD11
+ RC3/AD3 5 | | 36 RD4/AD12
+ RC4/AD4 6 | | 35 RD5/AD13
+ RC5/AD5 7 | | 34 RD6/AD14
+ RC6/AD6 8 | | 33 RD7/AD15
+ RC7/AD7 9 | | 32 _MCLR/Vpp
+ Vss 10 | PIC17C4X | 31 Vss
+ RB0/CAP1 11 | | 30 RE0/ALE
+ RB1/CAP2 12 | | 29 RE1/_OE
+ RB2/PWM1 13 | | 28 RE2/_WR
+ RB3/PWM2 14 | | 27 TEST
+ RB4/TCLK12 15 | | 26 RA0/INT
+ RB5/TCLK3 16 | | 25 RA1/T0CKI
+ RB6 17 | | 24 RA2
+ RB7 18 | | 23 RA3
+ OSC1/CLKIN 19 | | 22 RA4/RX/DT
+ OSC2/CLKOUT 20 |______________| 21 RA5/TX/CK
+
+**********************************************************************/
+
+#ifndef MAME_CPU_PIC17_PIC17C4X_H
+#define MAME_CPU_PIC17_PIC17C4X_H
+
+#pragma once
+
+#include "pic17.h"
+
+class pic17c4x_device : public pic17_cpu_device
+{
+public:
+ enum {
+ RA0_LINE = 0,
+ INT_LINE = RA0_LINE,
+ RA1_LINE = 1,
+ T0CKI_LINE = RA1_LINE,
+ RA2_LINE = 2,
+ RA3_LINE = 3,
+ RA4_LINE = 4,
+ RX_LINE = RA4_LINE,
+ RA5_LINE = 5
+ };
+
+ enum {
+ PIC17_LATA = PIC17_PS + 1,
+ PIC17_DDRB,
+ PIC17_LATB,
+ PIC17_TMR1,
+ PIC17_TMR2,
+ PIC17_TMR3,
+ PIC17_PR1,
+ PIC17_PR2,
+ PIC17_CA1,
+ PIC17_CA2,
+ PIC17_TCON1,
+ PIC17_TCON2,
+ PIC17_RCSTA,
+ PIC17_TXSTA,
+ PIC17_SPBRG,
+ PIC17_PIE,
+ PIC17_PIR
+ };
+
+ // callback configuration
+ auto ra_out_cb() { return m_port_out_cb[0].bind(); }
+ auto rb_out_cb() { return m_port_out_cb[1].bind(); }
+ auto rc_out_cb() { return m_port_out_cb[2].bind(); }
+ auto rd_out_cb() { return m_port_out_cb[3].bind(); }
+ auto re_out_cb() { return m_port_out_cb[4].bind(); }
+
+protected:
+ pic17c4x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size, address_map_constructor data_map);
+
+ // device-level overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual u32 execute_input_lines() const noexcept override { return 6; } // for now
+ virtual bool execute_input_edge_triggered(int linenum) const noexcept override { return linenum == INT_LINE || linenum == T0CKI_LINE; }
+ virtual void execute_set_input(int linenum, int state) override;
+
+ void data_map(address_map &map);
+
+ virtual void increment_timers() override;
+
+private:
+ u8 porta_r();
+ void porta_w(u8 data);
+ u8 ddrb_r();
+ void ddrb_w(u8 data);
+ u8 portb_r();
+ void portb_w(u8 data);
+ u8 tmr1_r();
+ void tmr1_w(u8 data);
+ u8 tmr2_r();
+ void tmr2_w(u8 data);
+ u8 tmr3l_r();
+ void tmr3l_w(u8 data);
+ u8 tmr3h_r();
+ void tmr3h_w(u8 data);
+ u8 pr1_r();
+ void pr1_w(u8 data);
+ u8 pr2_r();
+ void pr2_w(u8 data);
+ u8 pr3l_ca1l_r();
+ void pr3l_ca1l_w(u8 data);
+ u8 pr3h_ca1h_r();
+ void pr3h_ca1h_w(u8 data);
+ u8 ca2l_r();
+ u8 ca2h_r();
+ u8 tcon1_r();
+ void tcon1_w(u8 data);
+ u8 tcon2_r();
+ void tcon2_w(u8 data);
+ u8 rcsta_r();
+ void rcsta_w(u8 data);
+ u8 txsta_r();
+ void txsta_w(u8 data);
+ void txreg_w(u8 data);
+ u8 spbrg_r();
+ void spbrg_w(u8 data);
+ u8 pir_r();
+ void pir_w(u8 data);
+ u8 pie_r();
+ void pie_w(u8 data);
+
+ // callback objects
+ devcb_write8::array<5> m_port_out_cb;
+
+ // internal state
+ u8 m_rain;
+ u8 m_lata;
+ bool m_rbpu;
+ u8 m_ddrb;
+ u8 m_latb;
+ u8 m_tmr8bit[2];
+ u8 m_pr8bit[2];
+ u16 m_tmr3;
+ u16 m_ca16bit[2];
+ u8 m_tcon1;
+ u8 m_tcon2;
+ u8 m_rcsta;
+ u8 m_txsta;
+ u8 m_spbrg;
+ u8 m_pir;
+ u8 m_pie;
+};
+
+class pic17c43_device : public pic17c4x_device
+{
+public:
+ // device type constructor
+ pic17c43_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+protected:
+ pic17c43_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u16 rom_size);
+
+private:
+ void data_map(address_map &map);
+};
+
+class pic17c44_device : public pic17c43_device
+{
+public:
+ // device type constructor
+ pic17c44_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+// device type declarations
+DECLARE_DEVICE_TYPE(PIC17C43, pic17c43_device)
+DECLARE_DEVICE_TYPE(PIC17C44, pic17c44_device)
+
+#endif // MAME_CPU_PIC17_PIC17C4X_H
diff --git a/src/devices/cpu/pic17/pic17d.cpp b/src/devices/cpu/pic17/pic17d.cpp
new file mode 100644
index 00000000000..016a3cfe809
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17d.cpp
@@ -0,0 +1,239 @@
+ // license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Microchip PIC17 disassembler
+
+***************************************************************************/
+
+#include "emu.h"
+#include "pic17d.h"
+
+const char *const pic17_disassembler::s_peripheral_regs[0x20] =
+{
+ "INDF0", "FSR0", "PCL", "PCLATH", "ALUSTA", "T0STA", "CPUSTA", "INTSTA",
+ "INDF1", "FSR1", "WREG", "TMR0L", "TMR0H", "TBLPTRL", "TBLPTRH", "BSR",
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // banked
+ "PRODL", "PRODH", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
+};
+
+const char *const pic17_disassembler::s_tb_ops[4] =
+{
+ "TLRD", "TLWT", "TABLRD", "TABLWT"
+};
+
+const char *const pic17_disassembler::s_bit_ops[4] =
+{
+ "BSF", "BCF", "BTFSS", "BTFSC"
+};
+
+const char *const pic17_disassembler::s_cp_ops[4] =
+{
+ "CPFSLT", "CPFSEQ", "CPFSGT", "TSTFSZ"
+};
+
+const char *const pic17_disassembler::s_alu_ops[0x30 / 2] =
+{
+ "MOVWF", "SUBWFB", "SUBWF", "DECF", "IORWF", "ANDWF", "XORWF", "ADDWF",
+ "ADDWFC", "COMF", "INCF", "DECFSZ", "RRCF", "RLCF", "SWAPF", "INCFSZ",
+ "RRNCF", "RLNCF", "INFSNZ", "DCFSNZ", "CLRF", "SETF", "NEGW", "DAW"
+};
+
+pic17_disassembler::pic17_disassembler()
+ : util::disasm_interface()
+{
+}
+
+u32 pic17_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+void pic17_disassembler::format_register(std::ostream &stream, u8 reg) const
+{
+ if (reg < 0x20 && s_peripheral_regs[reg] != nullptr)
+ stream << s_peripheral_regs[reg];
+ else
+ util::stream_format(stream, "R%02X", reg);
+}
+
+void pic17_disassembler::format_literal(std::ostream &stream, u8 data) const
+{
+ util::stream_format(stream, "%02Xh", data);
+}
+
+void pic17_disassembler::format_address(std::ostream &stream, u16 dst) const
+{
+ util::stream_format(stream, "%04Xh", dst);
+}
+
+offs_t pic17_disassembler::disassemble(std::ostream &stream, offs_t pc, const pic17_disassembler::data_buffer &opcodes, const pic17_disassembler::data_buffer &params)
+{
+ u16 opcode = opcodes.r16(pc);
+ offs_t words = 1;
+
+ if (opcode >= 0xc000)
+ {
+ if (BIT(opcode, 13))
+ {
+ util::stream_format(stream, "%-8s", "CALL");
+ words |= STEP_OVER;
+ }
+ else
+ util::stream_format(stream, "%-8s", "GOTO");
+ format_address(stream, ((pc + 1) & 0xe000) | (opcode & 0x1fff));
+ }
+ else if (opcode >= 0xb000)
+ {
+ // Literal and control operations
+ switch (opcode & 0x0f00)
+ {
+ case 0x000:
+ util::stream_format(stream, "%-8s", "MOVLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x100:
+ util::stream_format(stream, "%-8s", "ADDLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x200:
+ util::stream_format(stream, "%-8s", "SUBLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x300:
+ util::stream_format(stream, "%-8s", "IORLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x400:
+ util::stream_format(stream, "%-8s", "XORLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x500:
+ util::stream_format(stream, "%-8s", "ANDLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ case 0x600:
+ util::stream_format(stream, "%-8s", "RETLW");
+ format_literal(stream, opcode & 0x00ff);
+ words |= STEP_OUT;
+ break;
+
+ case 0x700:
+ util::stream_format(stream, "%-8s", "LCALL");
+ format_literal(stream, opcode & 0x00ff);
+ words |= STEP_OVER;
+ break;
+
+ case 0x800:
+ util::stream_format(stream, "%-8s%d", "MOVLB", opcode & 0x000f);
+ break;
+
+ case 0xa00: case 0xb00:
+ util::stream_format(stream, "%-8s%d", "MOVLR", (opcode & 0x00f0) >> 4);
+ break;
+
+ case 0xc00:
+ util::stream_format(stream, "%-8s", "MULLW");
+ format_literal(stream, opcode & 0x00ff);
+ break;
+
+ default:
+ util::stream_format(stream, "%-8s%04Xh", "DW", opcode);
+ break;
+ }
+ }
+ else if (opcode >= 0xa000)
+ {
+ util::stream_format(stream, "%-8s%d,", s_tb_ops[BIT(opcode, 10, 2)], BIT(opcode, 9));
+ if (BIT(opcode, 11))
+ util::stream_format(stream, "%d,", BIT(opcode, 8));
+ format_register(stream, opcode & 0x00ff);
+ }
+ else if (opcode >= 0x8000)
+ {
+ // Bit-oriented file register operations
+ util::stream_format(stream, "%-8s", s_bit_ops[BIT(opcode, 11, 2)]);
+ format_register(stream, opcode & 0x00ff);
+ util::stream_format(stream, ",%d", BIT(opcode, 8, 3));
+ }
+ else if (opcode >= 0x6000)
+ {
+ // Byte-to-byte move operations
+ util::stream_format(stream, "%-8s", "MOVFP");
+ format_register(stream, opcode & 0x00ff);
+ stream << ",";
+ format_register(stream, (opcode & 0x1f00) >> 8);
+ }
+ else if (opcode >= 0x4000)
+ {
+ util::stream_format(stream, "%-8s", "MOVPF");
+ format_register(stream, (opcode & 0x1f00) >> 8);
+ stream << ",";
+ format_register(stream, opcode & 0x00ff);
+ }
+ else if (opcode >= 0x3800)
+ {
+ util::stream_format(stream, "%-8s", "BTG");
+ format_register(stream, opcode & 0x00ff);
+ util::stream_format(stream, ",%d", BIT(opcode, 8, 3));
+ }
+ else if (opcode >= 0x3000)
+ {
+ if (opcode < 0x3400)
+ {
+ util::stream_format(stream, "%-8s", s_cp_ops[BIT(opcode, 8, 2)]);
+ format_register(stream, opcode & 0x00ff);
+ }
+ else if (opcode < 0x3500)
+ {
+ util::stream_format(stream, "%-8s", "MULWF");
+ format_register(stream, opcode & 0x00ff);
+ }
+ else
+ util::stream_format(stream, "%-8s%04Xh", "DW", opcode);
+ }
+ else if (opcode >= 0x0100)
+ {
+ // Byte-oriented file register operations
+ util::stream_format(stream, "%-8s", s_alu_ops[BIT(opcode, 9, 5)]);
+ format_register(stream, opcode & 0x00ff);
+ if (opcode >= 0x0200)
+ util::stream_format(stream, ",%c", BIT(opcode, 8) ? 'F' : 'W');
+ }
+ else switch (opcode)
+ {
+ case 0x0000:
+ stream << "NOP";
+ break;
+
+ case 0x0002:
+ stream << "RETURN";
+ words |= STEP_OUT;
+ break;
+
+ case 0x0003:
+ stream << "SLEEP";
+ break;
+
+ case 0x0004:
+ stream << "CLRWDT";
+ break;
+
+ case 0x0005:
+ stream << "RETFIE";
+ words |= STEP_OUT;
+ break;
+
+ default:
+ util::stream_format(stream, "%-8s%04Xh", "DW", opcode);
+ break;
+ }
+
+ return words;
+}
diff --git a/src/devices/cpu/pic17/pic17d.h b/src/devices/cpu/pic17/pic17d.h
new file mode 100644
index 00000000000..43c074031ff
--- /dev/null
+++ b/src/devices/cpu/pic17/pic17d.h
@@ -0,0 +1,36 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_CPU_PIC17_PIC17D_H
+#define MAME_CPU_PIC17_PIC17D_H
+
+#pragma once
+
+class pic17_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ pic17_disassembler();
+
+protected:
+ // disassembler overrides
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+private:
+ // register names
+ static const char *const s_peripheral_regs[0x20];
+
+ // instruction mnemonics
+ static const char *const s_tb_ops[4];
+ static const char *const s_bit_ops[4];
+ static const char *const s_cp_ops[4];
+ static const char *const s_alu_ops[0x30 / 2];
+
+ // internal helpers
+ void format_register(std::ostream &stream, u8 reg) const;
+ void format_literal(std::ostream &stream, u8 data) const;
+ void format_address(std::ostream &stream, u16 dst) const;
+};
+
+#endif // MAME_CPU_PIC17_PIC17D_H
diff --git a/src/mame/drivers/teamjocs.cpp b/src/mame/drivers/teamjocs.cpp
index b9710da9dd1..cdbd7512aea 100644
--- a/src/mame/drivers/teamjocs.cpp
+++ b/src/mame/drivers/teamjocs.cpp
@@ -32,7 +32,7 @@
*/
#include "emu.h"
-#include "cpu/pic16c5x/pic16c5x.h"
+#include "cpu/pic17/pic17c4x.h"
#include "machine/nvram.h"
#include "sound/okim6376.h"
#include "speaker.h"
@@ -59,7 +59,7 @@ INPUT_PORTS_END
void teamjocs_state::teamjocs(machine_config &config)
{
- PIC16C55(config, m_maincpu, 4_MHz_XTAL); // actually PIC17C44-16
+ PIC17C44(config, m_maincpu, 4_MHz_XTAL);
SPEAKER(config, "mono").front_center();
diff --git a/src/mame/drivers/xbase09.cpp b/src/mame/drivers/xbase09.cpp
new file mode 100644
index 00000000000..f7401397e41
--- /dev/null
+++ b/src/mame/drivers/xbase09.cpp
@@ -0,0 +1,197 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/****************************************************************************
+
+ Skeleton driver for XBase 09 drum machine by JoMoX GmbH.
+
+****************************************************************************/
+
+#include "emu.h"
+#include "cpu/pic17/pic17c4x.h"
+#include "machine/nvram.h"
+
+class xbase09_state : public driver_device
+{
+public:
+ xbase09_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_analog_ports(*this, "ANALOG%02X", 0U)
+ , m_port_select(0)
+ {
+ }
+
+ void xbase09(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ void portb_w(u8 data);
+ u16 ram_r(offs_t offset);
+ void ram_w(offs_t offset, u16 data);
+ void mem_map(address_map &map);
+
+ required_device<pic17c4x_device> m_maincpu;
+ required_ioport_array<0x16> m_analog_ports;
+
+ std::unique_ptr<u8[]> m_nvram_data;
+ u8 m_port_select;
+};
+
+void xbase09_state::machine_start()
+{
+ m_nvram_data = std::make_unique<u8[]>(0x8000);
+ subdevice<nvram_device>("nvram")->set_base(m_nvram_data.get(), 0x8000);
+
+ save_pointer(NAME(m_nvram_data), 0x8000);
+ save_item(NAME(m_port_select));
+}
+
+
+void xbase09_state::portb_w(u8 data)
+{
+ m_port_select = data >> 3;
+}
+
+u16 xbase09_state::ram_r(offs_t offset)
+{
+ return m_nvram_data[offset] | 0xff00;
+}
+
+void xbase09_state::ram_w(offs_t offset, u16 data)
+{
+ switch (offset)
+ {
+ case 0x0007:
+ // Successive-approximation ADC implemented using software algorithm and LM393 comparator
+ if (m_port_select < 0x16 && m_analog_ports[m_port_select]->read() < (data >> 8))
+ m_maincpu->set_input_line(pic17c4x_device::RA0_LINE, ASSERT_LINE);
+ else
+ m_maincpu->set_input_line(pic17c4x_device::RA0_LINE, CLEAR_LINE);
+ break;
+
+ default:
+ if ((data & 0xff00) != 0)
+ logerror("%s: Writing %04X to %04X\n", machine().describe_context(), data, offset + 0x8000);
+ break;
+ }
+
+ m_nvram_data[offset] = data & 0x00ff;
+}
+
+
+void xbase09_state::mem_map(address_map &map)
+{
+ map(0x0000, 0x7fff).rom().region("firmware", 0);
+ map(0x8000, 0xffff).rw(FUNC(xbase09_state::ram_r), FUNC(xbase09_state::ram_w));
+}
+
+
+static INPUT_PORTS_START(xbase09)
+ PORT_START("ANALOG00")
+ PORT_BIT(0xff, 0x00, IPT_UNKNOWN)
+
+ PORT_START("ANALOG01")
+ PORT_BIT(0xff, 0x01, IPT_UNKNOWN)
+
+ PORT_START("ANALOG02")
+ PORT_BIT(0xff, 0x02, IPT_UNKNOWN)
+
+ PORT_START("ANALOG03")
+ PORT_BIT(0xff, 0x03, IPT_UNKNOWN)
+
+ PORT_START("ANALOG04")
+ PORT_BIT(0xff, 0x04, IPT_UNKNOWN)
+
+ PORT_START("ANALOG05")
+ PORT_BIT(0xff, 0x05, IPT_UNKNOWN)
+
+ PORT_START("ANALOG06")
+ PORT_BIT(0xff, 0x06, IPT_UNKNOWN)
+
+ PORT_START("ANALOG07")
+ PORT_BIT(0xff, 0x07, IPT_UNKNOWN)
+
+ PORT_START("ANALOG08")
+ PORT_BIT(0xff, 0x08, IPT_UNKNOWN)
+
+ PORT_START("ANALOG09")
+ PORT_BIT(0xff, 0x09, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0A")
+ PORT_BIT(0xff, 0x0a, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0B")
+ PORT_BIT(0xff, 0x14, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0C")
+ PORT_BIT(0xff, 0x1e, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0D")
+ PORT_BIT(0xff, 0x28, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0E")
+ PORT_BIT(0xff, 0x32, IPT_UNKNOWN)
+
+ PORT_START("ANALOG0F")
+ PORT_BIT(0xff, 0x3c, IPT_UNKNOWN)
+
+ PORT_START("ANALOG10")
+ PORT_BIT(0xff, 0x46, IPT_UNKNOWN)
+
+ PORT_START("ANALOG11")
+ PORT_BIT(0xff, 0x50, IPT_UNKNOWN)
+
+ PORT_START("ANALOG12")
+ PORT_BIT(0xff, 0x5a, IPT_UNKNOWN)
+
+ PORT_START("ANALOG13")
+ PORT_BIT(0xff, 0x64, IPT_UNKNOWN)
+
+ PORT_START("ANALOG14")
+ PORT_BIT(0xff, 0xc8, IPT_UNKNOWN)
+
+ PORT_START("ANALOG15")
+ PORT_BIT(0xff, 0xff, IPT_UNKNOWN)
+INPUT_PORTS_END
+
+void xbase09_state::xbase09(machine_config &config)
+{
+ PIC17C43(config, m_maincpu, 16_MHz_XTAL); // PIC17C43-16/P
+ m_maincpu->set_mode(pic17c43_device::mode::MICROPROCESSOR);
+ m_maincpu->set_addrmap(AS_PROGRAM, &xbase09_state::mem_map);
+ m_maincpu->rb_out_cb().set(FUNC(xbase09_state::portb_w));
+
+ NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // SRM2B256SLMX10, NEC D43256BGU-70LL or equivalent + battery
+
+ //MAX509(config, "dac"); // MAX509BWCP (near top of PCB)
+}
+
+// IC positions are not labeled on PCB except for stickered PLDs
+ROM_START(xbase09)
+ ROM_REGION16_LE(0x10000, "firmware", 0)
+ ROM_DEFAULT_BIOS("v209")
+ ROM_SYSTEM_BIOS(0, "v132", "Version 1.32")
+ ROMX_LOAD("xbase_09__1.32l.bin", 0x0000, 0x8000, CRC(30dc47c6) SHA1(ee79f9e98c06edd8a5963fcd325da3490e8e5b76), ROM_BIOS(0) | ROM_SKIP(1))
+ ROMX_LOAD("xbase_09__1.32h.bin", 0x0001, 0x8000, CRC(ce2df930) SHA1(56922d069b53013f2a3646dcd63fbbc9b609cf5b), ROM_BIOS(0) | ROM_SKIP(1))
+ ROM_SYSTEM_BIOS(1, "v208", "Version 2.08 (without Tempo lock)")
+ ROMX_LOAD("xbase_09__2.08l.bin", 0x0000, 0x8000, CRC(c0f06ef6) SHA1(912f0b01cf5cfdc37ce61b3eacf933174f80171d), ROM_BIOS(1) | ROM_SKIP(1))
+ ROMX_LOAD("xbase_09__2.08h.bin", 0x0001, 0x8000, CRC(f1287888) SHA1(ee162887af1cfe968004b8c2e1c5417be42b2e9f), ROM_BIOS(1) | ROM_SKIP(1))
+ ROM_SYSTEM_BIOS(2, "v209", "Version 2.09 (with Tempo lock)")
+ ROMX_LOAD("xbase_09__2.09l.bin", 0x0000, 0x8000, CRC(60667060) SHA1(6a004934dd3f5f729f59bcbcb79e86e0e5f97fe9), ROM_BIOS(2) | ROM_SKIP(1))
+ ROMX_LOAD("xbase_09__2.09h.bin", 0x0001, 0x8000, CRC(64645c77) SHA1(35cc18774d9e4cc61af0469144c49dddfabc92b7), ROM_BIOS(2) | ROM_SKIP(1))
+ ROM_SYSTEM_BIOS(3, "sx209", "SX Version 2.09") // "requires hardware update," so may need to be a separate system
+ ROMX_LOAD("xbase09sx_2.09l.bin", 0x0000, 0x8000, CRC(7e87c30d) SHA1(2237f235749ae583327f1f8d893ceb3bb6502ae9), ROM_BIOS(3) | ROM_SKIP(1))
+ ROMX_LOAD("xbase09sx_2.09h.bin", 0x0001, 0x8000, CRC(ac9ad2e3) SHA1(8977e4520072b4ef6b603bcb0574663f10ba2629), ROM_BIOS(3) | ROM_SKIP(1))
+
+ ROM_REGION(0x10000, "hhrom", 0)
+ ROM_LOAD("xbase_09__rom_0.bin", 0x00000, 0x10000, NO_DUMP) // size unknown
+
+ ROM_REGION(0x3000, "plds", 0)
+ ROM_LOAD("xbase.ic17", 0x0000, 0x0a92, NO_DUMP) // next to CPU
+ ROM_LOAD("xbase.ic59", 0x1000, 0x0a92, NO_DUMP) // right side of HH ROM
+ ROM_LOAD("xbase.ic60", 0x2000, 0x0a92, NO_DUMP) // left side of HH ROM; confirmed to be a PALCE20V8H-15PC/4
+ROM_END
+
+SYST(1997, xbase09, 0, 0, xbase09, xbase09, xbase09_state, empty_init, "JoMoX", "XBase 09 Midi Controlled Analogue Drum Module", MACHINE_IS_SKELETON)
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 681cdae543f..2b07e1f6ca0 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -41756,6 +41756,9 @@ ltv_naru //
domfitad //
dombikec //
+@source:xbase09.cpp
+xbase09 //
+
@source:xbox.cpp
xbox //
diff --git a/src/mame/mess.flt b/src/mame/mess.flt
index 377208bcedd..5f145e2fa93 100644
--- a/src/mame/mess.flt
+++ b/src/mame/mess.flt
@@ -1081,6 +1081,7 @@ x1twin.cpp
x68k.cpp
xavix.cpp
xavix2.cpp
+xbase09.cpp
xbox.cpp
xerox820.cpp
xor100.cpp
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 4b420b90853..06d7747b1ac 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -119,6 +119,7 @@ using util::BIT;
#include "cpu/pic16/pic16d.h"
#include "cpu/pic16c5x/16c5xdsm.h"
#include "cpu/pic16c62x/16c62xdsm.h"
+#include "cpu/pic17/pic17d.h"
#include "cpu/powerpc/ppc_dasm.h"
#include "cpu/pps4/pps4dasm.h"
#include "cpu/psx/psxdasm.h"
@@ -480,6 +481,7 @@ static const dasm_table_entry dasm_table[] =
{ "pic16", le, -1, []() -> util::disasm_interface * { return new pic16_disassembler; } },
{ "pic16c5x", le, -1, []() -> util::disasm_interface * { return new pic16c5x_disassembler; } },
{ "pic16c62x", le, -1, []() -> util::disasm_interface * { return new pic16c62x_disassembler; } },
+ { "pic17", le, -1, []() -> util::disasm_interface * { return new pic17_disassembler; } },
{ "powerpc", be, 0, []() -> util::disasm_interface * { return new powerpc_disassembler; } },
{ "pps4", le, 0, []() -> util::disasm_interface * { return new pps4_disassembler; } },
{ "psxcpu", le, 0, []() -> util::disasm_interface * { return new psxcpu_disassembler; } },