summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/capricorn
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2017-07-08 11:31:42 +0200
committer Vas Crabb <cuavas@users.noreply.github.com>2017-07-08 19:31:42 +1000
commit5201a7f6bf1baf7e697b46b1292faaed5d850992 (patch)
tree0748d90f27b6e77f2479416dd9f843241258d841 /src/devices/cpu/capricorn
parent8b542ca5bd4127784ccce7f6f85b69c72b39f959 (diff)
Begin of HP80 emulation (#2448)
What works: * HP85A machine with 16K of RAM * Capricorn CPU works * Keyboard works (with minor issues) * CRT text / graphics modes work (correct speed is not emulated yet so service ROM complaints) * BASIC is usable What is missing (and I'll have hopefully working soon): * HW timers * Beeper * Integral printer * DC100 cassette drive * Extension ROMs * I/O modules (especially the HPIB interface so that we can hook up floppy drives) * Other models in the family (e.g. HP86)
Diffstat (limited to 'src/devices/cpu/capricorn')
-rw-r--r--src/devices/cpu/capricorn/capricorn.cpp1519
-rw-r--r--src/devices/cpu/capricorn/capricorn.h133
-rw-r--r--src/devices/cpu/capricorn/capricorn_dasm.cpp213
3 files changed, 1865 insertions, 0 deletions
diff --git a/src/devices/cpu/capricorn/capricorn.cpp b/src/devices/cpu/capricorn/capricorn.cpp
new file mode 100644
index 00000000000..cb897bd1a59
--- /dev/null
+++ b/src/devices/cpu/capricorn/capricorn.cpp
@@ -0,0 +1,1519 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+//
+// *****************************
+// Emulator for HP Capricorn CPU
+// *****************************
+//
+#include "emu.h"
+#include "capricorn.h"
+#include "debugger.h"
+
+// Register indexes
+// GP registers are named "R" & the octal representation of the index (00-77)
+enum {
+ CAPRICORN_R00,
+ CAPRICORN_R01,
+ CAPRICORN_R02,
+ CAPRICORN_R03,
+ CAPRICORN_R04,
+ CAPRICORN_R05,
+ CAPRICORN_R06,
+ CAPRICORN_R07,
+ CAPRICORN_R10,
+ CAPRICORN_R11,
+ CAPRICORN_R12,
+ CAPRICORN_R13,
+ CAPRICORN_R14,
+ CAPRICORN_R15,
+ CAPRICORN_R16,
+ CAPRICORN_R17,
+ CAPRICORN_R20,
+ CAPRICORN_R21,
+ CAPRICORN_R22,
+ CAPRICORN_R23,
+ CAPRICORN_R24,
+ CAPRICORN_R25,
+ CAPRICORN_R26,
+ CAPRICORN_R27,
+ CAPRICORN_R30,
+ CAPRICORN_R31,
+ CAPRICORN_R32,
+ CAPRICORN_R33,
+ CAPRICORN_R34,
+ CAPRICORN_R35,
+ CAPRICORN_R36,
+ CAPRICORN_R37,
+ CAPRICORN_R40,
+ CAPRICORN_R41,
+ CAPRICORN_R42,
+ CAPRICORN_R43,
+ CAPRICORN_R44,
+ CAPRICORN_R45,
+ CAPRICORN_R46,
+ CAPRICORN_R47,
+ CAPRICORN_R50,
+ CAPRICORN_R51,
+ CAPRICORN_R52,
+ CAPRICORN_R53,
+ CAPRICORN_R54,
+ CAPRICORN_R55,
+ CAPRICORN_R56,
+ CAPRICORN_R57,
+ CAPRICORN_R60,
+ CAPRICORN_R61,
+ CAPRICORN_R62,
+ CAPRICORN_R63,
+ CAPRICORN_R64,
+ CAPRICORN_R65,
+ CAPRICORN_R66,
+ CAPRICORN_R67,
+ CAPRICORN_R70,
+ CAPRICORN_R71,
+ CAPRICORN_R72,
+ CAPRICORN_R73,
+ CAPRICORN_R74,
+ CAPRICORN_R75,
+ CAPRICORN_R76,
+ CAPRICORN_R77,
+ CAPRICORN_ARP,
+ CAPRICORN_DRP,
+ CAPRICORN_E
+};
+
+// Bit manipulation
+namespace {
+ static constexpr unsigned BIT_MASK(unsigned n)
+ {
+ return 1U << n;
+ }
+
+ template<typename T> void BIT_CLR(T& w , unsigned n)
+ {
+ w &= ~(T)BIT_MASK(n);
+ }
+
+ template<typename T> void BIT_SET(T& w , unsigned n)
+ {
+ w |= (T)BIT_MASK(n);
+ }
+
+ template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
+ {
+ if (bit) {
+ BIT_SET(w , n);
+ } else {
+ BIT_CLR(w , n);
+ }
+ }
+}
+
+// Bits in m_flags
+static constexpr unsigned FLAGS_DCM_BIT = 0; // Decimal/binary mode
+static constexpr unsigned FLAGS_CY_BIT = 1; // Carry
+static constexpr unsigned FLAGS_OVF_BIT = 2; // Overflow
+static constexpr unsigned FLAGS_LSB_BIT = 3; // LSB
+static constexpr unsigned FLAGS_MSB_BIT = 4; // MSB
+static constexpr unsigned FLAGS_Z_BIT = 5; // Zero
+static constexpr unsigned FLAGS_LDZ_BIT = 6; // Left digit zero
+static constexpr unsigned FLAGS_RDZ_BIT = 7; // Right digit zero
+static constexpr unsigned FLAGS_IRL_BIT = 8; // Interrupt request
+
+// Special registers
+static constexpr unsigned REG_BANK_PTR = 0; // R0: register bank pointer
+static constexpr unsigned REG_INDEX_SCRATCH = 2; // R2 & R3: index scratch registers
+static constexpr unsigned REG_PC = 4; // R4 & R5: PC
+static constexpr unsigned REG_SP = 6; // R6 & R7: return stack pointer
+
+// Bit in address values that specifies external address (0) or internal register (1)
+static constexpr unsigned GP_REG_BIT = 17;
+static constexpr unsigned GP_REG_MASK = BIT_MASK(GP_REG_BIT);
+static constexpr unsigned ADDR_MASK = 0xffff;
+
+// Mask of bits in ARP & DRP
+static constexpr uint8_t ARP_DRP_MASK = 0x3f;
+
+// Mask of bits in E
+static constexpr uint8_t E_MASK = 0xf;
+
+DEFINE_DEVICE_TYPE(HP_CAPRICORN , capricorn_cpu_device , "capricorn" , "HP-Capricorn")
+
+capricorn_cpu_device::capricorn_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cpu_device(mconfig, HP_CAPRICORN, tag, owner, clock),
+ m_program_config("program" , ENDIANNESS_LITTLE , 8 , 16)
+{
+}
+
+uint8_t capricorn_cpu_device::flatten_burst()
+{
+ m_flatten = true;
+ return (uint8_t)(m_curr_addr - m_start_addr);
+}
+
+void capricorn_cpu_device::device_start()
+{
+ // ARP
+ state_add(CAPRICORN_ARP , "ARP" , m_arp).formatstr("%2s");
+ // DRP
+ state_add(CAPRICORN_DRP , "DRP" , m_drp).formatstr("%2s");
+ // E
+ state_add(CAPRICORN_E , "E" , m_reg_E).formatstr("%1X");
+ // R00 .. R77
+ for (unsigned i = 0; i < 64; i++) {
+ state_add(CAPRICORN_R00 + i , util::string_format("R%02o" , i).c_str() , m_reg[ i ]);
+ }
+ // PC
+ state_add(STATE_GENPC, "GENPC", m_genpc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_genpc).noshow();
+ // Flags
+ state_add(STATE_GENFLAGS , "GENFLAGS" , m_flags).noshow().formatstr("%9s");
+
+ m_program = &space(AS_PROGRAM);
+ m_direct = &m_program->direct();
+
+ save_item(NAME(m_reg));
+ save_item(NAME(m_arp));
+ save_item(NAME(m_drp));
+ save_item(NAME(m_reg_E));
+ save_item(NAME(m_flags));
+
+ m_icountptr = &m_icount;
+}
+
+void capricorn_cpu_device::device_reset()
+{
+ for (auto& reg : m_reg) {
+ reg = 0;
+ }
+ m_arp = 0;
+ m_drp = 0;
+ m_reg_E = 0;
+ m_flags = 0;
+ // Reset vector @0
+ vector_to_pc(0);
+}
+
+void capricorn_cpu_device::execute_run()
+{
+ do {
+ if (BIT(m_flags , FLAGS_IRL_BIT)) {
+ // Handle interrupt
+ take_interrupt();
+ } else {
+ debugger_instruction_hook(this, m_genpc);
+
+ uint8_t opcode = fetch();
+ execute_one(opcode);
+ offset_pc(1);
+ }
+ } while (m_icount > 0);
+}
+
+void capricorn_cpu_device::execute_set_input(int linenum, int state)
+{
+ if (linenum == 0) {
+ COPY_BIT(state != 0 , m_flags, FLAGS_IRL_BIT);
+ }
+}
+
+std::vector<std::pair<int, const address_space_config *>> capricorn_cpu_device::memory_space_config() const
+{
+ return std::vector<std::pair<int, const address_space_config *>> {
+ std::make_pair(AS_PROGRAM, &m_program_config)
+ };
+}
+
+void capricorn_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ if (entry.index() == CAPRICORN_ARP) {
+ str = string_format("%02o" , m_arp);
+ } else if (entry.index() == CAPRICORN_DRP) {
+ str = string_format("%02o" , m_drp);
+ } else if (entry.index() == STATE_GENFLAGS) {
+ str = string_format("%s %c %c %c" , BIT(m_flags , FLAGS_DCM_BIT) ? "BCD" : "BIN",
+ BIT(m_flags , FLAGS_CY_BIT) ? 'C' : ' ',
+ BIT(m_flags , FLAGS_OVF_BIT) ? 'O' : ' ',
+ BIT(m_flags , FLAGS_Z_BIT) ? 'Z' : ' ');
+ }
+}
+
+offs_t capricorn_cpu_device::disasm_disassemble(std::ostream &stream, offs_t pc, const u8 *oprom, const u8 *opram, u32 options)
+{
+ extern CPU_DISASSEMBLE(capricorn);
+ return CPU_DISASSEMBLE_NAME(capricorn)(this, stream, pc, oprom, opram, options);
+}
+
+void capricorn_cpu_device::start_mem_burst(ea_addr_t addr)
+{
+ m_flatten = false;
+ // Only relevant for memory access (not for internal registers)
+ m_start_addr = (uint16_t)(addr & ADDR_MASK);
+}
+
+uint16_t capricorn_cpu_device::read_u16(ea_addr_t addr)
+{
+ PAIR16 tmp;
+
+ start_mem_burst(addr);
+ tmp.b.l = RM(addr);
+ tmp.b.h = RM(addr);
+ return tmp.w;
+}
+
+void capricorn_cpu_device::write_u16(ea_addr_t addr , uint16_t v)
+{
+ PAIR16 tmp;
+
+ tmp.w = v;
+
+ start_mem_burst(addr);
+ WM(addr , tmp.b.l);
+ WM(addr , tmp.b.h);
+}
+
+uint8_t capricorn_cpu_device::RM(ea_addr_t& addr)
+{
+ uint8_t res;
+
+ if (BIT(addr , GP_REG_BIT)) {
+ res = m_reg[ addr & ARP_DRP_MASK ];
+ } else {
+ m_curr_addr = (uint16_t)(addr & ADDR_MASK);
+ res = m_program->read_byte(m_flatten ? m_start_addr : m_curr_addr);
+ }
+ addr++;
+ return res;
+}
+
+void capricorn_cpu_device::WM(ea_addr_t& addr , uint8_t v)
+{
+ if (BIT(addr , GP_REG_BIT)) {
+ m_reg[ addr & ARP_DRP_MASK ] = v;
+ } else {
+ m_curr_addr = (uint16_t)(addr & ADDR_MASK);
+ m_program->write_byte(m_flatten ? m_start_addr : m_curr_addr , v);
+ }
+ addr++;
+}
+
+uint8_t capricorn_cpu_device::fetch()
+{
+ m_genpc = read_u16(REG_PC | GP_REG_MASK);
+ return m_direct->read_byte(m_genpc);
+}
+
+void capricorn_cpu_device::offset_pc(uint16_t offset)
+{
+ m_genpc = read_u16(REG_PC | GP_REG_MASK);
+ m_genpc += offset;
+ write_u16(REG_PC | GP_REG_MASK, m_genpc);
+}
+
+void capricorn_cpu_device::vector_to_pc(uint8_t vector)
+{
+ m_genpc = read_u16(vector);
+ write_u16(REG_PC | GP_REG_MASK, m_genpc);
+}
+
+void capricorn_cpu_device::do_jump(bool condition)
+{
+ m_icount -= 4;
+ offset_pc(1);
+ uint16_t disp = fetch();
+ if (condition) {
+ m_icount--;
+ if (BIT(disp , 7)) {
+ disp -= 0x100;
+ }
+ offset_pc(disp);
+ }
+}
+
+uint8_t capricorn_cpu_device::get_lower_boundary() const
+{
+ if (BIT(m_drp , 5)) {
+ return m_drp & ~7U;
+ } else {
+ return m_drp & ~1U;
+ }
+}
+
+uint8_t capricorn_cpu_device::get_upper_boundary() const
+{
+ if (BIT(m_drp , 5)) {
+ return m_drp | 7;
+ } else {
+ return m_drp | 1;
+ }
+}
+
+void capricorn_cpu_device::update_flags_right(uint8_t res)
+{
+ // Update RDZ & LSB flags on least significant byte of single/multi byte operations
+ COPY_BIT((res & 0xf) == 0 , m_flags, FLAGS_RDZ_BIT);
+ COPY_BIT(BIT(res , 0) , m_flags, FLAGS_LSB_BIT);
+}
+
+void capricorn_cpu_device::update_flags_left(uint8_t res)
+{
+ // Update LDZ, MSB flags on most significant byte of single/multi byte operations
+ COPY_BIT((res & 0xf0) == 0 , m_flags, FLAGS_LDZ_BIT);
+ COPY_BIT(BIT(res , 7) , m_flags, FLAGS_MSB_BIT);
+}
+
+void capricorn_cpu_device::update_flags_every(uint8_t res)
+{
+ // Update Z flag on every byte of single/multi byte operations
+ // It's assumed that Z=1 at start of each operation
+ if (res) {
+ BIT_CLR(m_flags, FLAGS_Z_BIT);
+ }
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_reg_imm()
+{
+ // Register immediate addressing mode
+ return (ea_addr_t)m_arp | GP_REG_MASK;
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_lit_imm(bool multibyte)
+{
+ // Literal immediate addressing mode
+ offset_pc(1);
+ ea_addr_t res = m_genpc;
+ if (multibyte) {
+ offset_pc(get_upper_boundary() - m_drp);
+ }
+ return res;
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_reg_dir()
+{
+ // Register direct addressing mode
+ m_icount--;
+ return read_u16(m_arp | GP_REG_MASK);
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_lit_dir()
+{
+ // Literal direct addressing mode
+ m_icount--;
+ offset_pc(1);
+ ea_addr_t res = read_u16(m_genpc);
+ offset_pc(1);
+ return res;
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_reg_indir()
+{
+ // Register indirect addressing mode
+ m_icount -= 3;
+ return read_u16(read_u16(m_arp | GP_REG_MASK));
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_idx_dir()
+{
+ // Indexed direct addressing mode
+ m_icount -= 3;
+ offset_pc(1);
+ uint16_t res = read_u16(m_genpc) + read_u16(m_arp | GP_REG_MASK);
+ offset_pc(1);
+ write_u16(REG_INDEX_SCRATCH | GP_REG_MASK, res);
+ return res;
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_lit_indir()
+{
+ // Literal indirect addressing mode
+ m_icount -= 3;
+ offset_pc(1);
+ ea_addr_t res = read_u16(read_u16(m_genpc));
+ offset_pc(1);
+ return res;
+}
+
+capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_idx_indir()
+{
+ // Indexed indirect addressing mode
+ m_icount -= 5;
+ offset_pc(1);
+ uint16_t res = read_u16(m_genpc) + read_u16(m_arp | GP_REG_MASK);
+ offset_pc(1);
+ write_u16(REG_INDEX_SCRATCH | GP_REG_MASK, res);
+ return read_u16(res);
+}
+
+uint8_t capricorn_cpu_device::add_bcd_digits(uint8_t first , uint8_t second , bool& carry)
+{
+ if (first > 9) {
+ first -= 8;
+ }
+ uint8_t res = first + second + carry;
+ carry = res > 9;
+ if (carry) {
+ res -= 10;
+ }
+ return res;
+}
+
+uint8_t capricorn_cpu_device::add_bcd_bytes(uint8_t first , uint8_t second , bool& carry)
+{
+ uint8_t rd = add_bcd_digits(first & 0xf, second & 0xf, carry);
+ uint8_t ld = add_bcd_digits(first >> 4, second >> 4, carry);
+ return (ld << 4) | rd;
+}
+
+uint8_t capricorn_cpu_device::sub_bcd_digits(uint8_t first , uint8_t second , bool& carry)
+{
+ int res = first + (9 - second) + carry;
+ carry = false;
+ if (res < 0) {
+ res += 8;
+ } else if (res >= 10) {
+ res -= 10;
+ carry = true;
+ }
+ return (uint8_t)res;
+}
+
+uint8_t capricorn_cpu_device::sub_bcd_bytes(uint8_t first , uint8_t second , bool& carry)
+{
+ uint8_t rd = sub_bcd_digits(first & 0xf, second & 0xf, carry);
+ uint8_t ld = sub_bcd_digits(first >> 4, second >> 4, carry);
+ return (ld << 4) | rd;
+}
+
+#define OP_ITERATION_START_FWD(idx , multi) \
+ unsigned boundary = multi ? get_upper_boundary() : m_drp; \
+ BIT_SET(m_flags , FLAGS_Z_BIT); \
+ bool first = true; \
+ for (unsigned idx = m_drp; idx <= boundary; idx++)
+
+#define OP_ITERATION_START_REV(idx , multi) \
+ int boundary = multi ? get_lower_boundary() : m_drp; \
+ BIT_SET(m_flags , FLAGS_Z_BIT); \
+ bool first = true; \
+ for (int idx = m_drp; idx >= boundary; idx--)
+
+#define OP1_GET(idx , op1) \
+ m_icount--; \
+ uint8_t op1 = m_reg[ idx ];
+
+#define OP2_GET(idx , ea , op1 , op2) \
+ m_icount--; \
+ uint8_t op1 = m_reg[ idx ]; \
+ uint8_t op2 = RM(ea);
+
+#define RES_SET(idx , res) \
+ m_reg[ idx ] = res;
+
+#define OP_ITERATION_END_FWD(res) \
+ if (first) { \
+ update_flags_right(res); \
+ first = false; \
+ } \
+ update_flags_left(res); \
+ update_flags_every(res);
+
+#define OP_ITERATION_END_REV(res) \
+ if (first) { \
+ update_flags_left(res); \
+ first = false; \
+ } \
+ update_flags_right(res); \
+ update_flags_every(res);
+
+void capricorn_cpu_device::do_AN_op(ea_addr_t ea)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags , FLAGS_OVF_BIT);
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , true) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res = op1 & op2;
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::do_LD_op(ea_addr_t ea , bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags , FLAGS_OVF_BIT);
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ // op1 is unused (it's overwritten by op2)
+ (void)op1;
+ uint8_t res = op2;
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::do_ST_op(ea_addr_t ea , bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags , FLAGS_OVF_BIT);
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i , op1);
+
+ WM(ea , op1);
+ OP_ITERATION_END_FWD(op1);
+ }
+}
+
+void capricorn_cpu_device::do_AD_op(ea_addr_t ea , bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = false;
+ bool ovf = false;
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = add_bcd_bytes(op1 , op2 , carry);
+ } else {
+ uint16_t tmp = (uint16_t)op1 + (uint16_t)op2 + carry;
+ carry = BIT(tmp , 8);
+ ovf = BIT((tmp ^ op1) & (tmp ^ op2) , 7);
+ res = (uint8_t)tmp;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_SB_op(ea_addr_t ea , bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = true;
+ bool ovf = false;
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = sub_bcd_bytes(op1 , op2 , carry);
+ } else {
+ op2 = ~op2;
+ uint16_t tmp = (uint16_t)op1 + (uint16_t)op2 + carry;
+ carry = BIT(tmp , 8);
+ ovf = BIT((tmp ^ op1) & (tmp ^ op2) , 7);
+ res = (uint8_t)tmp;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_CM_op(ea_addr_t ea , bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = true;
+ bool ovf = false;
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = sub_bcd_bytes(op1 , op2 , carry);
+ } else {
+ op2 = ~op2;
+ uint16_t tmp = (uint16_t)op1 + (uint16_t)op2 + carry;
+ carry = BIT(tmp , 8);
+ ovf = BIT((tmp ^ op1) & (tmp ^ op2) , 7);
+ res = (uint8_t)tmp;
+ }
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_OR_op(bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags, FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ ea_addr_t ea = get_ea_reg_imm();
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res = op1 | op2;
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::do_XR_op(bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags, FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ ea_addr_t ea = get_ea_reg_imm();
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP2_GET(i , ea , op1 , op2);
+
+ uint8_t res = op1 ^ op2;
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::do_IC_op(bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = true;
+ bool ovf = false;
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = add_bcd_bytes(op1 , 0 , carry);
+ } else {
+ uint16_t tmp = (uint16_t)op1 + carry;
+ carry = BIT(tmp , 8);
+ // Overflow = 1 when a positive number is incremented into a negative one
+ ovf = BIT(tmp & ~op1 , 7);
+ res = (uint8_t)tmp;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_DC_op(bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = false;
+ bool ovf = false;
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = sub_bcd_bytes(op1 , 0 , carry);
+ } else {
+ uint16_t tmp = (uint16_t)op1 + 0xff + carry;
+ carry = BIT(tmp , 8);
+ // Overflow = 1 when a negative number is decremented into a positive one
+ ovf = BIT(~tmp & op1 , 7);
+ res = (uint8_t)tmp;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_TC_op(bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = true;
+ bool ovf = false;
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = sub_bcd_bytes(0 , op1 , carry);
+ } else {
+ op1 = ~op1;
+ uint16_t tmp = (uint16_t)op1 + carry;
+ carry = BIT(tmp , 8);
+ ovf = BIT(tmp & ~op1 , 7);
+ res = (uint8_t)tmp;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_NC_op(bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags, FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ bool carry = false;
+ res = sub_bcd_bytes(0 , op1 , carry);
+ } else {
+ res = ~op1;
+ }
+ RES_SET(i , res);
+
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::do_TS_op(bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags, FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ OP_ITERATION_END_FWD(op1);
+ }
+}
+
+void capricorn_cpu_device::do_CL_op(bool multibyte)
+{
+ m_icount -= 4;
+ BIT_CLR(m_flags, FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ (void)op1;
+ RES_SET(i , 0);
+ OP_ITERATION_END_FWD(0);
+ }
+}
+
+void capricorn_cpu_device::do_EL_op(bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = BIT(m_flags , FLAGS_CY_BIT);
+ bool ovf = false;
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = (op1 << 4) | m_reg_E;
+ m_reg_E = (op1 >> 4) & E_MASK;
+ carry = false;
+ } else {
+ uint16_t tmp = (uint16_t)op1 << 1;
+ res = (uint8_t)tmp + carry;
+ ovf = BIT(res ^ op1 , 7);
+ carry = BIT(tmp , 8);
+ }
+ RES_SET(i , res);
+ OP_ITERATION_END_FWD(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+ COPY_BIT(ovf , m_flags , FLAGS_OVF_BIT);
+}
+
+void capricorn_cpu_device::do_LL_op(bool multibyte)
+{
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ m_reg_E = 0;
+ } else {
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ }
+ do_EL_op(multibyte);
+}
+
+void capricorn_cpu_device::do_ER_op(bool multibyte)
+{
+ m_icount -= 4;
+ bool carry = BIT(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags, FLAGS_OVF_BIT);
+
+ OP_ITERATION_START_REV(i , multibyte) {
+ OP1_GET(i, op1);
+
+ uint8_t res;
+
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ res = (op1 >> 4) | (m_reg_E << 4);
+ m_reg_E = op1 & E_MASK;
+ carry = false;
+ } else {
+ res = op1 >> 1;
+ if (carry) {
+ BIT_SET(res, 7);
+ }
+ carry = BIT(op1 , 0);
+ }
+ RES_SET(i , res);
+ OP_ITERATION_END_REV(res);
+ }
+ COPY_BIT(carry , m_flags , FLAGS_CY_BIT);
+}
+
+void capricorn_cpu_device::do_LR_op(bool multibyte)
+{
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ m_reg_E = 0;
+ } else {
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ }
+ do_ER_op(multibyte);
+}
+
+void capricorn_cpu_device::do_SAD_op()
+{
+ m_icount -= 8;
+ uint16_t tmp = read_u16(REG_SP | GP_REG_MASK);
+ ea_addr_t ea = tmp;
+ write_u16(REG_SP | GP_REG_MASK, tmp + 3);
+
+ start_mem_burst(ea);
+
+ uint8_t byte = m_arp;
+ if (BIT(m_flags , FLAGS_CY_BIT)) {
+ BIT_SET(byte, 6);
+ }
+ if (BIT(m_flags , FLAGS_OVF_BIT)) {
+ BIT_SET(byte, 7);
+ }
+ WM(ea , byte);
+
+ byte = m_drp;
+ if (BIT(m_flags , FLAGS_DCM_BIT)) {
+ BIT_SET(byte, 6);
+ }
+ if (BIT(m_flags , FLAGS_OVF_BIT)) {
+ BIT_SET(byte, 7);
+ }
+ WM(ea , byte);
+
+ byte = 0;
+ if (BIT(m_flags , FLAGS_LSB_BIT)) {
+ BIT_SET(byte, 0);
+ }
+ if (!BIT(m_flags , FLAGS_RDZ_BIT)) {
+ BIT_SET(byte, 1);
+ }
+ if (!BIT(m_flags , FLAGS_Z_BIT)) {
+ BIT_SET(byte, 2);
+ }
+ if (!BIT(m_flags , FLAGS_LDZ_BIT)) {
+ BIT_SET(byte, 6);
+ }
+ if (BIT(m_flags , FLAGS_MSB_BIT)) {
+ BIT_SET(byte, 7);
+ }
+ WM(ea , byte);
+}
+
+void capricorn_cpu_device::do_PAD_op()
+{
+ m_icount -= 8;
+ uint16_t tmp = read_u16(REG_SP | GP_REG_MASK) - 3;
+ ea_addr_t ea = tmp;
+ write_u16(REG_SP | GP_REG_MASK, tmp);
+
+ uint8_t byte;
+ start_mem_burst(ea);
+
+ byte = RM(ea);
+ m_arp = byte & ARP_DRP_MASK;
+ COPY_BIT(BIT(byte , 6), m_flags, FLAGS_CY_BIT);
+ COPY_BIT(BIT(byte , 7), m_flags, FLAGS_OVF_BIT);
+
+ byte = RM(ea);
+ m_drp = byte & ARP_DRP_MASK;
+ COPY_BIT(BIT(byte , 6), m_flags, FLAGS_DCM_BIT);
+
+ byte = RM(ea);
+ COPY_BIT(BIT(byte , 0), m_flags, FLAGS_LSB_BIT);
+ COPY_BIT(!BIT(byte , 1), m_flags, FLAGS_RDZ_BIT);
+ COPY_BIT(!BIT(byte , 2), m_flags, FLAGS_Z_BIT);
+ COPY_BIT(!BIT(byte , 6), m_flags, FLAGS_LDZ_BIT);
+ COPY_BIT(BIT(byte , 7), m_flags, FLAGS_MSB_BIT);
+}
+
+void capricorn_cpu_device::do_RTN_op()
+{
+ m_icount -= 5;
+ uint16_t tmp = read_u16(REG_SP | GP_REG_MASK) - 2;
+ write_u16(REG_PC | GP_REG_MASK, read_u16(tmp) - 1);
+ write_u16(REG_SP | GP_REG_MASK, tmp);
+}
+
+void capricorn_cpu_device::push_pc()
+{
+ uint16_t tmp = read_u16(REG_SP | GP_REG_MASK);
+ write_u16(tmp, m_genpc);
+ write_u16(REG_SP | GP_REG_MASK, tmp + 2);
+}
+
+void capricorn_cpu_device::do_JSB_op(ea_addr_t ea)
+{
+ m_icount -= 8;
+ offset_pc(1);
+ push_pc();
+ write_u16(REG_PC | GP_REG_MASK, (uint16_t)(ea - 1));
+}
+
+void capricorn_cpu_device::do_PU_op(bool multibyte , bool direct , bool increment)
+{
+ m_icount -= 5;
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags , FLAGS_OVF_BIT);
+
+ uint16_t ar = read_u16(m_arp | GP_REG_MASK);
+ ea_addr_t ea;
+ if (direct) {
+ unsigned n_regs = multibyte ? (get_upper_boundary() - m_drp + 1) : 1;
+ if (increment) {
+ ea = ar;
+ ar += n_regs;
+ } else {
+ ar -= n_regs;
+ ea = ar;
+ }
+ } else {
+ m_icount -= 2;
+ if (increment) {
+ ea = read_u16(ar);
+ ar += 2;
+ } else {
+ ar -= 2;
+ ea = read_u16(ar);
+ }
+ }
+ write_u16(m_arp | GP_REG_MASK, ar);
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ WM(ea , op1);
+ OP_ITERATION_END_FWD(op1);
+ }
+}
+
+void capricorn_cpu_device::do_PO_op(bool multibyte , bool direct , bool increment)
+{
+ m_icount -= 5;
+ BIT_CLR(m_flags , FLAGS_CY_BIT);
+ BIT_CLR(m_flags , FLAGS_OVF_BIT);
+
+ uint16_t ar = read_u16(m_arp | GP_REG_MASK);
+ ea_addr_t ea;
+ if (direct) {
+ unsigned n_regs = multibyte ? (get_upper_boundary() - m_drp + 1) : 1;
+ if (increment) {
+ ea = ar;
+ ar += n_regs;
+ } else {
+ ar -= n_regs;
+ ea = ar;
+ }
+ } else {
+ m_icount -= 2;
+ if (increment) {
+ ea = read_u16(ar);
+ ar += 2;
+ } else {
+ ar -= 2;
+ ea = read_u16(ar);
+ }
+ }
+ write_u16(m_arp | GP_REG_MASK, ar);
+
+ start_mem_burst(ea);
+
+ OP_ITERATION_START_FWD(i , multibyte) {
+ OP1_GET(i, op1);
+
+ (void)op1;
+ uint8_t res = RM(ea);
+ RES_SET(i, res);
+ OP_ITERATION_END_FWD(res);
+ }
+}
+
+void capricorn_cpu_device::execute_one(uint8_t opcode)
+{
+ // DRP & ARP instructions
+ if ((opcode & 0xc0) == 0x00) {
+ // ARP
+ if (opcode == 0x01) {
+ m_icount -= 3;
+ m_arp = m_reg[ REG_BANK_PTR ] & ARP_DRP_MASK;
+ } else {
+ m_icount -= 2;
+ m_arp = opcode & ARP_DRP_MASK;
+ }
+ } else if ((opcode & 0xc0) == 0x40) {
+ // DRP
+ if (opcode == 0x41) {
+ m_icount -= 3;
+ m_drp = m_reg[ REG_BANK_PTR ] & ARP_DRP_MASK;
+ } else {
+ m_icount -= 2;
+ m_drp = opcode & ARP_DRP_MASK;
+ }
+ } else {
+ // Opcodes without mask
+ switch (opcode) {
+ case 0x98:
+ // BIN
+ m_icount -= 4;
+ BIT_CLR(m_flags , FLAGS_DCM_BIT);
+ break;
+
+ case 0x99:
+ // BCD
+ m_icount -= 4;
+ BIT_SET(m_flags , FLAGS_DCM_BIT);
+ break;
+
+ case 0x9a:
+ // SAD
+ do_SAD_op();
+ break;
+
+ case 0x9b:
+ // DCE
+ m_icount -= 2;
+ m_reg_E = (m_reg_E - 1) & E_MASK;
+ break;
+
+ case 0x9c:
+ // ICE
+ m_icount -= 2;
+ m_reg_E = (m_reg_E + 1) & E_MASK;
+ break;
+
+ case 0x9d:
+ // CLE
+ m_icount -= 2;
+ m_reg_E = 0;
+ break;
+
+ case 0x9e:
+ // RTN
+ do_RTN_op();
+ break;
+
+ case 0x9f:
+ // PAD
+ do_PAD_op();
+ break;
+
+ case 0xc6:
+ // JSB
+ do_JSB_op(get_ea_idx_dir());
+ break;
+
+ case 0xc7:
+ // ANM
+ do_AN_op(get_ea_reg_imm());
+ break;
+
+ case 0xce:
+ // JSB
+ do_JSB_op(get_ea_lit_dir());
+ break;
+
+ case 0xcf:
+ // ANM
+ do_AN_op(get_ea_lit_imm(true));
+ break;
+
+ case 0xd7:
+ // ANM
+ do_AN_op(get_ea_lit_dir());
+ break;
+
+ case 0xdf:
+ // ANM
+ do_AN_op(get_ea_reg_dir());
+ break;
+
+ case 0xf0:
+ // JMP
+ do_jump(true);
+ break;
+
+ case 0xf1:
+ // JNO
+ do_jump(!BIT(m_flags , FLAGS_OVF_BIT));
+ break;
+
+ case 0xf2:
+ // JOD
+ do_jump(BIT(m_flags , FLAGS_LSB_BIT));
+ break;
+
+ case 0xf3:
+ // JEV
+ do_jump(!BIT(m_flags , FLAGS_LSB_BIT));
+ break;
+
+ case 0xf4:
+ // JNG
+ do_jump(BIT(m_flags , FLAGS_OVF_BIT) != BIT(m_flags , FLAGS_MSB_BIT));
+ break;
+
+ case 0xf5:
+ // JPS
+ do_jump(BIT(m_flags , FLAGS_OVF_BIT) == BIT(m_flags , FLAGS_MSB_BIT));
+ break;
+
+ case 0xf6:
+ // JNZ
+ do_jump(!BIT(m_flags , FLAGS_Z_BIT));
+ break;
+
+ case 0xf7:
+ // JZR
+ do_jump(BIT(m_flags , FLAGS_Z_BIT));
+ break;
+
+ case 0xf8:
+ // JEN
+ do_jump(m_reg_E != 0);
+ break;
+
+ case 0xf9:
+ // JEZ
+ do_jump(m_reg_E == 0);
+ break;
+
+ case 0xfa:
+ // JNC
+ do_jump(!BIT(m_flags , FLAGS_CY_BIT));
+ break;
+
+ case 0xfb:
+ // JCY
+ do_jump(BIT(m_flags , FLAGS_CY_BIT));
+ break;
+
+ case 0xfc:
+ // JLZ
+ do_jump(BIT(m_flags , FLAGS_LDZ_BIT));
+ break;
+
+ case 0xfd:
+ // JLN
+ do_jump(!BIT(m_flags , FLAGS_LDZ_BIT));
+ break;
+
+ case 0xfe:
+ // JRZ
+ do_jump(BIT(m_flags , FLAGS_RDZ_BIT));
+ break;
+
+ case 0xff:
+ // JRN
+ do_jump(!BIT(m_flags , FLAGS_RDZ_BIT));
+ break;
+
+ default:
+ // Opcodes with 0xfe mask (M/B bit is in b0)
+ bool multibyte = BIT(opcode , 0);
+ switch (opcode & 0xfe) {
+ case 0x80:
+ // EL
+ do_EL_op(multibyte);
+ break;
+
+ case 0x82:
+ // ER
+ do_ER_op(multibyte);
+ break;
+
+ case 0x84:
+ // LL
+ do_LL_op(multibyte);
+ break;
+
+ case 0x86:
+ // LR
+ do_LR_op(multibyte);
+ break;
+
+ case 0x88:
+ // IC
+ do_IC_op(multibyte);
+ break;
+
+ case 0x8a:
+ // DC
+ do_DC_op(multibyte);
+ break;
+
+ case 0x8c:
+ // TC
+ do_TC_op(multibyte);
+ break;
+
+ case 0x8e:
+ // NC
+ do_NC_op(multibyte);
+ break;
+
+ case 0x90:
+ // TS
+ do_TS_op(multibyte);
+ break;
+
+ case 0x92:
+ // CL
+ do_CL_op(multibyte);
+ break;
+
+ case 0x94:
+ // OR
+ do_OR_op(multibyte);
+ break;
+
+ case 0x96:
+ // XR
+ do_XR_op(multibyte);
+ break;
+
+ case 0xa0:
+ // LD
+ do_LD_op(get_ea_reg_imm() , multibyte);
+ break;
+
+ case 0xa2:
+ // ST
+ do_ST_op(get_ea_reg_imm() , multibyte);
+ break;
+
+ case 0xa4:
+ // LD
+ do_LD_op(get_ea_reg_dir() , multibyte);
+ break;
+
+ case 0xa6:
+ // ST
+ do_ST_op(get_ea_reg_dir() , multibyte);
+ break;
+
+ case 0xa8:
+ // LD
+ do_LD_op(get_ea_lit_imm(multibyte) , multibyte);
+ break;
+
+ case 0xaa:
+ // ST
+ do_ST_op(get_ea_lit_imm(multibyte) , multibyte);
+ break;
+
+ case 0xac:
+ // LD
+ do_LD_op(get_ea_reg_indir() , multibyte);
+ break;
+
+ case 0xae:
+ // ST
+ do_ST_op(get_ea_reg_indir() , multibyte);
+ break;
+
+ case 0xb0:
+ // LD
+ do_LD_op(get_ea_lit_dir() , multibyte);
+ break;
+
+ case 0xb2:
+ // ST
+ do_ST_op(get_ea_lit_dir() , multibyte);
+ break;
+
+ case 0xb4:
+ // LD
+ do_LD_op(get_ea_idx_dir() , multibyte);
+ break;
+
+ case 0xb6:
+ // ST
+ do_ST_op(get_ea_idx_dir() , multibyte);
+ break;
+
+ case 0xb8:
+ // LD
+ do_LD_op(get_ea_lit_indir() , multibyte);
+ break;
+
+ case 0xba:
+ // ST
+ do_ST_op(get_ea_lit_indir() , multibyte);
+ break;
+
+ case 0xbc:
+ // LD
+ do_LD_op(get_ea_idx_indir() , multibyte);
+ break;
+
+ case 0xbe:
+ // ST
+ do_ST_op(get_ea_idx_indir() , multibyte);
+ break;
+
+ case 0xc0:
+ // CM
+ do_CM_op(get_ea_reg_imm() , multibyte);
+ break;
+
+ case 0xc2:
+ // AD
+ do_AD_op(get_ea_reg_imm() , multibyte);
+ break;
+
+ case 0xc4:
+ // SB
+ do_SB_op(get_ea_reg_imm() , multibyte);
+ break;
+
+ case 0xc8:
+ // CM
+ do_CM_op(get_ea_lit_imm(multibyte) , multibyte);
+ break;
+
+ case 0xca:
+ // AD
+ do_AD_op(get_ea_lit_imm(multibyte) , multibyte);
+ break;
+
+ case 0xcc:
+ // SB
+ do_SB_op(get_ea_lit_imm(multibyte) , multibyte);
+ break;
+
+ case 0xd0:
+ // CM
+ do_CM_op(get_ea_lit_dir() , multibyte);
+ break;
+
+ case 0xd2:
+ // AD
+ do_AD_op(get_ea_lit_dir() , multibyte);
+ break;
+
+ case 0xd4:
+ // SB
+ do_SB_op(get_ea_lit_dir() , multibyte);
+ break;
+
+ case 0xd8:
+ // CM
+ do_CM_op(get_ea_reg_dir() , multibyte);
+ break;
+
+ case 0xda:
+ // AD
+ do_AD_op(get_ea_reg_dir() , multibyte);
+ break;
+
+ case 0xdc:
+ // SB
+ do_SB_op(get_ea_reg_dir() , multibyte);
+ break;
+
+ default:
+ // Opcodes with 0xfc mask
+ switch (opcode & 0xfc) {
+ case 0xe0:
+ // PO
+ do_PO_op(multibyte, true, !BIT(opcode , 1));
+ break;
+
+ case 0xe4:
+ // PU
+ do_PU_op(multibyte, true, !BIT(opcode , 1));
+ break;
+
+ case 0xe8:
+ // PO
+ do_PO_op(multibyte, false, !BIT(opcode , 1));
+ break;
+
+ case 0xec:
+ // PU
+ do_PU_op(multibyte, false, !BIT(opcode , 1));
+ break;
+
+ default:
+ logerror("Unknown opcode %02x\n" , opcode);
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ }
+}
+
+void capricorn_cpu_device::take_interrupt()
+{
+ // According to O. De Smet emulator interrupt handling takes 15 cycles
+ m_icount -= 15;
+ push_pc();
+ uint8_t vector = (uint8_t)standard_irq_callback(0);
+ vector_to_pc(vector);
+}
diff --git a/src/devices/cpu/capricorn/capricorn.h b/src/devices/cpu/capricorn/capricorn.h
new file mode 100644
index 00000000000..c31bf6dfacc
--- /dev/null
+++ b/src/devices/cpu/capricorn/capricorn.h
@@ -0,0 +1,133 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+//
+// *****************************
+// Emulator for HP Capricorn CPU
+// *****************************
+//
+#ifndef MAME_CPU_CAPRICORN_CAPRICORN_H
+#define MAME_CPU_CAPRICORN_CAPRICORN_H
+
+#pragma once
+
+class capricorn_cpu_device : public cpu_device
+{
+public:
+ capricorn_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ uint8_t flatten_burst();
+
+protected:
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual u32 execute_min_cycles() const override
+ { return 2; }
+ virtual u32 execute_max_cycles() const override
+ { return 17; }
+ virtual u32 execute_input_lines() const override
+ { return 1; }
+ virtual void execute_run() override;
+ virtual void execute_set_input(int linenum, int state) override;
+
+ // device_memory_interface overrides
+ virtual std::vector<std::pair<int, const address_space_config *>> memory_space_config() const override;
+
+ // device_state_interface overrides
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+ // device_disasm_interface overrides
+ virtual u32 disasm_min_opcode_bytes() const override
+ { return 1; }
+ virtual u32 disasm_max_opcode_bytes() const override
+ { return 9; }
+ virtual offs_t disasm_disassemble(std::ostream &stream, offs_t pc, const u8 *oprom, const u8 *opram, u32 options) override;
+
+private:
+ address_space_config m_program_config;
+ address_space *m_program;
+ direct_read_data *m_direct;
+
+ int m_icount;
+
+ // State of processor
+ uint8_t m_reg[ 64 ];// Registers R00-R77
+ uint8_t m_arp; // ARP register (6 bits)
+ uint8_t m_drp; // DRP register (6 bits)
+ uint8_t m_reg_E; // E register (4 bits)
+ uint16_t m_flags; // Flags
+ uint16_t m_genpc; // PC
+
+ // Burst memory accesses
+ bool m_flatten; // Consecutive accesses to memory are "flattened"
+ uint16_t m_start_addr; // Start address of burst
+ uint16_t m_curr_addr; // Current address in burst
+
+ // Effective Addresses
+ // When b17 = 0, b15..b0 hold 16-bit memory address
+ // When b17 = 1, b5..b0 hold register index
+ typedef unsigned ea_addr_t;
+
+ void start_mem_burst(ea_addr_t addr);
+ uint16_t read_u16(ea_addr_t addr);
+ void write_u16(ea_addr_t addr , uint16_t v);
+ uint8_t RM(ea_addr_t& addr);
+ void WM(ea_addr_t& addr , uint8_t v);
+ uint8_t fetch();
+ void offset_pc(uint16_t offset);
+ void vector_to_pc(uint8_t vector);
+ void do_jump(bool condition);
+ uint8_t get_lower_boundary() const;
+ uint8_t get_upper_boundary() const;
+ void update_flags_right(uint8_t res);
+ void update_flags_left(uint8_t res);
+ void update_flags_every(uint8_t res);
+ ea_addr_t get_ea_reg_imm();
+ ea_addr_t get_ea_lit_imm(bool multibyte);
+ ea_addr_t get_ea_reg_dir();
+ ea_addr_t get_ea_lit_dir();
+ ea_addr_t get_ea_reg_indir();
+ ea_addr_t get_ea_idx_dir();
+ ea_addr_t get_ea_lit_indir();
+ ea_addr_t get_ea_idx_indir();
+
+ static uint8_t add_bcd_digits(uint8_t first , uint8_t second , bool& carry);
+ static uint8_t add_bcd_bytes(uint8_t first , uint8_t second , bool& carry);
+ static uint8_t sub_bcd_digits(uint8_t first , uint8_t second , bool& carry);
+ static uint8_t sub_bcd_bytes(uint8_t first , uint8_t second , bool& carry);
+
+ void do_AN_op(ea_addr_t ea);
+ void do_LD_op(ea_addr_t ea , bool multibyte);
+ void do_ST_op(ea_addr_t ea , bool multibyte);
+ void do_AD_op(ea_addr_t ea , bool multibyte);
+ void do_SB_op(ea_addr_t ea , bool multibyte);
+ void do_CM_op(ea_addr_t ea , bool multibyte);
+ void do_OR_op(bool multibyte);
+ void do_XR_op(bool multibyte);
+ void do_IC_op(bool multibyte);
+ void do_DC_op(bool multibyte);
+ void do_TC_op(bool multibyte);
+ void do_NC_op(bool multibyte);
+ void do_TS_op(bool multibyte);
+ void do_CL_op(bool multibyte);
+ void do_EL_op(bool multibyte);
+ void do_LL_op(bool multibyte);
+ void do_ER_op(bool multibyte);
+ void do_LR_op(bool multibyte);
+ void do_SAD_op();
+ void do_PAD_op();
+ void do_RTN_op();
+ void push_pc();
+ void do_JSB_op(ea_addr_t ea);
+ void do_PU_op(bool multibyte , bool direct , bool increment);
+ void do_PO_op(bool multibyte , bool direct , bool increment);
+
+ void execute_one(uint8_t opcode);
+ void take_interrupt();
+};
+
+DECLARE_DEVICE_TYPE(HP_CAPRICORN , capricorn_cpu_device);
+
+#endif // MAME_CPU_CAPRICORN_CAPRICORN_H
diff --git a/src/devices/cpu/capricorn/capricorn_dasm.cpp b/src/devices/cpu/capricorn/capricorn_dasm.cpp
new file mode 100644
index 00000000000..1d268b4f42a
--- /dev/null
+++ b/src/devices/cpu/capricorn/capricorn_dasm.cpp
@@ -0,0 +1,213 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+// ********************************************************************************
+// * HP Capricorn processor disassembler
+// ********************************************************************************
+
+#include "capricorn.h"
+
+typedef offs_t (*fn_dis_param)(std::ostream &stream , offs_t pc , const uint8_t *oprom);
+
+typedef struct {
+ uint8_t m_op_mask;
+ uint8_t m_opcode;
+ const char *m_mnemonic;
+ bool m_has_mb;
+ char m_addr_mode;
+ fn_dis_param m_param_fn;
+ uint32_t m_dasm_flags;
+} dis_entry_t;
+
+static void direct_addr(std::ostream &stream , const uint8_t *oprom)
+{
+ util::stream_format(stream , "$%02x%02x" , oprom[ 1 ] , oprom[ 0 ]);
+}
+
+static offs_t param_arp_drp(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ stream << "R";
+ util::stream_format(stream , "%02o" , oprom[ 0 ] & 0x3f);
+ return 0;
+}
+
+static offs_t param_dr(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ stream << "DR";
+ return 0;
+}
+
+static offs_t param_dr_ar(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ stream << "DR,AR";
+ return 0;
+}
+
+static offs_t param_dr_lit(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ stream << "DR,=";
+ // Here we assume that multi-byte instructions operate on 2 bytes because we
+ // have no way of knowing how many they are (the actual number of bytes is
+ // dynamically determined by the value of DRP register at run-time)
+ unsigned bytes = BIT(oprom[ 0 ] , 0) ? 2 : 1;
+
+ for (unsigned i = 1; i <= bytes; i++) {
+ util::stream_format(stream , "$%02x " , oprom[ i ]);
+ }
+
+ return bytes;
+}
+
+static offs_t param_dr_lit_dir(std::ostream &stream , offs_t pc ,const uint8_t *oprom)
+{
+ stream << "DR,=";
+ direct_addr(stream , &oprom[ 1 ]);
+ return 2;
+}
+
+static offs_t param_dr_idx_dir(std::ostream &stream , offs_t pc ,const uint8_t *oprom)
+{
+ stream << "DR,XAR,";
+ direct_addr(stream , &oprom[ 1 ]);
+ return 2;
+}
+
+static offs_t param_xr_lit(std::ostream &stream , offs_t pc ,const uint8_t *oprom)
+{
+ stream << "XR,";
+ direct_addr(stream , &oprom[ 1 ]);
+ return 2;
+}
+
+static offs_t param_lit_dir(std::ostream &stream , offs_t pc ,const uint8_t *oprom)
+{
+ stream << "=";
+ direct_addr(stream , &oprom[ 1 ]);
+ return 2;
+}
+
+static offs_t param_dr_id_ar(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ stream << "DR," << (BIT(oprom[ 0 ] , 1) ? '-' : '+') << "AR";
+ return 0;
+}
+
+static offs_t param_jmp_off(std::ostream &stream , offs_t pc , const uint8_t *oprom)
+{
+ uint16_t off = oprom[ 1 ];
+ if (BIT(off , 7)) {
+ off -= 0x100;
+ }
+ util::stream_format(stream , "$%04x" , (pc + 2 + off) & 0xffff);
+ return 1;
+}
+
+static const dis_entry_t dis_table[] = {
+ { 0xff , 0x01 , "ARP R*" , false , '\0' , nullptr , 0 },
+ { 0xc0 , 0x00 , "ARP" , false , '\0' , param_arp_drp , 0 },
+ { 0xff , 0x41 , "DRP R*" , false , '\0' , nullptr , 0 },
+ { 0xc0 , 0x40 , "DRP" , false , '\0' , param_arp_drp , 0 },
+ { 0xfe , 0x80 , "EL" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x82 , "ER" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x84 , "LL" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x86 , "LR" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x88 , "IC" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x8a , "DC" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x8c , "TC" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x8e , "NC" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x90 , "TS" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x92 , "CL" , true , '\0' , param_dr , 0 },
+ { 0xfe , 0x94 , "OR" , true , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0x96 , "XR" , true , '\0' , param_dr_ar , 0 },
+ { 0xff , 0x98 , "BIN" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x99 , "BCD" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x9a , "SAD" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x9b , "DCE" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x9c , "ICE" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x9d , "CLE" , false , '\0' , nullptr , 0 },
+ { 0xff , 0x9e , "RTN" , false , '\0' , nullptr , DASMFLAG_STEP_OUT },
+ { 0xff , 0x9f , "PAD" , false , '\0' , nullptr , 0 },
+ { 0xfe , 0xa0 , "LD" , true , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0xa2 , "ST" , true , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0xa4 , "LD" , true , 'D' , param_dr_ar , 0 },
+ { 0xfe , 0xa6 , "ST" , true , 'D' , param_dr_ar , 0 },
+ { 0xfe , 0xa8 , "LD" , true , '\0' , param_dr_lit , 0 },
+ { 0xfe , 0xaa , "ST" , true , '\0' , param_dr_lit , 0 },
+ { 0xfe , 0xac , "LD" , true , 'I' , param_dr_ar , 0 },
+ { 0xfe , 0xae , "ST" , true , 'I' , param_dr_ar , 0 },
+ { 0xfe , 0xb0 , "LD" , true , 'D' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xb2 , "ST" , true , 'D' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xb4 , "LD" , true , 'D' , param_dr_idx_dir , 0 },
+ { 0xfe , 0xb6 , "ST" , true , 'D' , param_dr_idx_dir , 0 },
+ { 0xfe , 0xb8 , "LD" , true , 'I' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xba , "ST" , true , 'I' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xbc , "LD" , true , 'I' , param_dr_idx_dir , 0 },
+ { 0xfe , 0xbe , "ST" , true , 'I' , param_dr_idx_dir , 0 },
+ { 0xfe , 0xc0 , "CM" , true , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0xc2 , "AD" , true , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0xc4 , "SB" , true , '\0' , param_dr_ar , 0 },
+ { 0xff , 0xc6 , "JSB" , false , '\0' , param_xr_lit , DASMFLAG_STEP_OVER },
+ { 0xff , 0xc7 , "ANM" , false , '\0' , param_dr_ar , 0 },
+ { 0xfe , 0xc8 , "CM" , true , '\0' , param_dr_lit , 0 },
+ { 0xfe , 0xca , "AD" , true , '\0' , param_dr_lit , 0 },
+ { 0xfe , 0xcc , "SB" , true , '\0' , param_dr_lit , 0 },
+ { 0xff , 0xce , "JSB" , false , '\0' , param_lit_dir , DASMFLAG_STEP_OVER },
+ { 0xff , 0xcf , "ANM" , false , '\0' , param_dr_lit , 0 },
+ { 0xfe , 0xd0 , "CM" , true , 'D' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xd2 , "AD" , true , 'D' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xd4 , "SB" , true , 'D' , param_dr_lit_dir , 0 },
+ { 0xff , 0xd7 , "ANM" , false , 'D' , param_dr_lit_dir , 0 },
+ { 0xfe , 0xd8 , "CM" , true , 'D' , param_dr_ar , 0 },
+ { 0xfe , 0xda , "AD" , true , 'D' , param_dr_ar , 0 },
+ { 0xfe , 0xdc , "SB" , true , 'D' , param_dr_ar , 0 },
+ { 0xff , 0xdf , "ANM" , false , 'D' , param_dr_ar , 0 },
+ { 0xfc , 0xe0 , "PO" , true , 'D' , param_dr_id_ar , 0 },
+ { 0xfc , 0xe4 , "PU" , true , 'D' , param_dr_id_ar , 0 },
+ { 0xfc , 0xe8 , "PO" , true , 'I' , param_dr_id_ar , 0 },
+ { 0xfc , 0xec , "PU" , true , 'I' , param_dr_id_ar , 0 },
+ { 0xff , 0xf0 , "JMP" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf1 , "JNO" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf2 , "JOD" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf3 , "JEV" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf4 , "JNG" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf5 , "JPS" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf6 , "JNZ" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf7 , "JZR" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf8 , "JEN" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xf9 , "JEZ" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xfa , "JNC" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xfb , "JCY" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xfc , "JLZ" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xfd , "JLN" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xfe , "JRZ" , false , '\0' , param_jmp_off , 0 },
+ { 0xff , 0xff , "JRN" , false , '\0' , param_jmp_off , 0 },
+ // *** END ***
+ {0 , 0 , nullptr , false , 0 , nullptr , 0 }
+};
+
+CPU_DISASSEMBLE(capricorn)
+{
+ const dis_entry_t *p;
+ uint8_t opcode = oprom[ 0 ];
+
+ for (p = dis_table; p->m_op_mask; p++) {
+ if ((opcode & p->m_op_mask) == p->m_opcode) {
+ offs_t res = 1 | p->m_dasm_flags | DASMFLAG_SUPPORTED;
+ stream << p->m_mnemonic;
+ if (p->m_has_mb) {
+ stream << (BIT(opcode , 0) ? 'M' : 'B');
+ }
+ if (p->m_addr_mode != '\0') {
+ stream << p->m_addr_mode;
+ }
+ if (p->m_param_fn != nullptr) {
+ stream << " ";
+ res += p->m_param_fn(stream , pc , oprom);
+ }
+ return res;
+ }
+ }
+
+ // Unknown opcode
+ stream << "???";
+ return 1 | DASMFLAG_SUPPORTED;
+}