diff options
Diffstat (limited to 'src/devices/cpu/hphybrid/hphybrid.cpp')
-rw-r--r-- | src/devices/cpu/hphybrid/hphybrid.cpp | 1654 |
1 files changed, 991 insertions, 663 deletions
diff --git a/src/devices/cpu/hphybrid/hphybrid.cpp b/src/devices/cpu/hphybrid/hphybrid.cpp index 280686f5bfa..75706bb1e7b 100644 --- a/src/devices/cpu/hphybrid/hphybrid.cpp +++ b/src/devices/cpu/hphybrid/hphybrid.cpp @@ -69,44 +69,71 @@ enum { HPHYBRID_R37 }; -#define BIT_MASK(n) (1U << (n)) +// Bit manipulation +namespace { + template<typename T> constexpr T BIT_MASK(unsigned n) + { + return (T)1U << n; + } -// Macros to clear/set single bits -#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n)) -#define BIT_SET(w , n) ((w) |= BIT_MASK(n)) + template<typename T> void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK<T>(n); + } + + template<typename T> void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK<T>(n); + } +} // Bits in m_flags -#define HPHYBRID_C_BIT 0 // Carry/extend -#define HPHYBRID_O_BIT 1 // Overflow -#define HPHYBRID_CB_BIT 2 // Cb -#define HPHYBRID_DB_BIT 3 // Db -#define HPHYBRID_INTEN_BIT 4 // Interrupt enable -#define HPHYBRID_DMAEN_BIT 5 // DMA enable -#define HPHYBRID_DMADIR_BIT 6 // DMA direction (1 = OUT) -#define HPHYBRID_HALT_BIT 7 // Halt flag -#define HPHYBRID_IRH_BIT 8 // IRH requested -#define HPHYBRID_IRL_BIT 9 // IRL requested -#define HPHYBRID_IRH_SVC_BIT 10 // IRH in service -#define HPHYBRID_IRL_SVC_BIT 11 // IRL in service -#define HPHYBRID_DMAR_BIT 12 // DMA request -#define HPHYBRID_STS_BIT 13 // Status flag -#define HPHYBRID_FLG_BIT 14 // "Flag" flag -#define HPHYBRID_DC_BIT 15 // Decimal carry -#define HPHYBRID_IM_BIT 16 // Interrupt mode - -#define HPHYBRID_IV_MASK 0xfff0 // IV mask - -#define HP_REG_SE_MASK 0x000f +enum : unsigned { + HPHYBRID_C_BIT = 0, // Carry/extend + HPHYBRID_O_BIT = 1, // Overflow + HPHYBRID_CB_BIT = 2, // Cb + HPHYBRID_DB_BIT = 3, // Db + HPHYBRID_INTEN_BIT = 4, // Interrupt enable + HPHYBRID_DMAEN_BIT = 5, // DMA enable + HPHYBRID_DMADIR_BIT = 6, // DMA direction (1 = OUT) + HPHYBRID_HALT_BIT = 7, // Halt flag + HPHYBRID_IRH_BIT = 8, // IRH requested + HPHYBRID_IRL_BIT = 9, // IRL requested + HPHYBRID_IRH_SVC_BIT = 10, // IRH in service + HPHYBRID_IRL_SVC_BIT = 11, // IRL in service + HPHYBRID_DMAR_BIT = 12, // DMA request + HPHYBRID_STS_BIT = 13, // Status flag + HPHYBRID_FLG_BIT = 14, // "Flag" flag + HPHYBRID_DC_BIT = 15, // Decimal carry + HPHYBRID_IM_BIT = 16 // Interrupt mode +}; + +constexpr uint16_t HP_REG_IV_MASK = 0xfff0; // IV mask +constexpr uint16_t HP_REG_PA_MASK = 0x000f; // PA mask +constexpr uint16_t HP_REG_SE_MASK = 0x000f; // SE mask #define CURRENT_PA (m_reg_PA[ 0 ]) -#define HP_RESET_ADDR 0x0020 +constexpr uint16_t HP_RESET_ADDR = 0x0020; // Part of r32-r37 that is actually output as address extension (6 bits of "BSC": block select code) -#define BSC_REG_MASK 0x3f +constexpr uint16_t BSC_REG_MASK = 0x3f; + +// Address mask of 15-bit processor +constexpr uint16_t ADDR_MASK_15BIT = 0x7fff; + +// Mask of MSB of registers +constexpr uint16_t REG_MSB_MASK = BIT_MASK<uint16_t>(15); + +// Memory, I/O & register access timings +constexpr unsigned DEF_MEM_R_CYCLES = 4; // Default memory read cycles +constexpr unsigned DEF_MEM_W_CYCLES = 4; // Default memory write cycles +constexpr unsigned REGISTER_RW_CYCLES = 5; // Internal register R/W cycles +constexpr unsigned IO_RW_CYCLES = 7; // I/O R/W cycles DEFINE_DEVICE_TYPE(HP_5061_3001, hp_5061_3001_cpu_device, "5061_3001", "Hewlett-Packard HP-5061-3001") DEFINE_DEVICE_TYPE(HP_5061_3011, hp_5061_3011_cpu_device, "5061_3011", "Hewlett-Packard HP-5061-3011") +DEFINE_DEVICE_TYPE(HP_09825_67907, hp_09825_67907_cpu_device, "09825_67907", "Hewlett-Packard HP-09825-67907") WRITE_LINE_MEMBER(hp_hybrid_cpu_device::dmar_w) { @@ -148,9 +175,15 @@ uint8_t hp_hybrid_cpu_device::pa_r(void) const hp_hybrid_cpu_device::hp_hybrid_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t addrwidth) : cpu_device(mconfig, type, tag, owner, clock) , m_pa_changed_func(*this) + , m_addr_mask((1U << addrwidth) - 1) + , m_relative_mode(true) + , m_r_cycles(DEF_MEM_R_CYCLES) + , m_w_cycles(DEF_MEM_W_CYCLES) + , m_boot_mode(false) , m_program_config("program", ENDIANNESS_BIG, 16, addrwidth, -1) , m_io_config("io", ENDIANNESS_BIG, 16, 6, -1) { + m_addr_mask_low16 = uint16_t(m_addr_mask & 0xffff); } device_memory_interface::space_config_vector hp_hybrid_cpu_device::memory_space_config() const @@ -163,24 +196,6 @@ device_memory_interface::space_config_vector hp_hybrid_cpu_device::memory_space_ void hp_hybrid_cpu_device::device_start() { - m_reg_A = 0; - m_reg_B = 0; - m_reg_P = HP_RESET_ADDR; - m_reg_R = 0; - m_reg_C = 0; - m_reg_D = 0; - m_reg_IV = 0; - m_reg_PA[ 0 ] = 0; - m_reg_PA[ 1 ] = 0; - m_reg_PA[ 2 ] = 0; - m_reg_W = 0; - m_flags = 0; - m_dmapa = 0; - m_dmama = 0; - m_dmac = 0; - m_reg_I = 0; - m_forced_bsc_25 = false; - { state_add(HPHYBRID_A, "A", m_reg_A); state_add(HPHYBRID_B, "B", m_reg_B); @@ -193,8 +208,8 @@ void hp_hybrid_cpu_device::device_start() state_add(STATE_GENSP, "GENSP", m_reg_R).noshow(); state_add(HPHYBRID_IV, "IV", m_reg_IV); state_add(HPHYBRID_PA, "PA", m_reg_PA[ 0 ]); - state_add(HPHYBRID_W, "W", m_reg_W).noshow(); - state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).noshow().formatstr("%9s"); + state_add(HPHYBRID_W, "W", m_reg_W).noshow(); + state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).noshow().formatstr("%12s"); state_add(HPHYBRID_DMAPA , "DMAPA" , m_dmapa).noshow(); state_add(HPHYBRID_DMAMA , "DMAMA" , m_dmama).noshow(); state_add(HPHYBRID_DMAC , "DMAC" , m_dmac).noshow(); @@ -230,9 +245,27 @@ void hp_hybrid_cpu_device::device_start() void hp_hybrid_cpu_device::device_reset() { + m_reg_A = 0; + m_reg_B = 0; m_reg_P = HP_RESET_ADDR; - m_reg_I = fetch(); + m_reg_R = 0; + m_reg_C = 0; + m_reg_D = 0; + m_reg_IV = 0; + m_reg_PA[ 0 ] = 0; + m_reg_PA[ 1 ] = 0; + m_reg_PA[ 2 ] = 0; + m_reg_W = 0; m_flags = 0; + m_dmapa = 0; + m_dmama = 0; + m_dmac = 0; + m_forced_bsc_25 = m_boot_mode; + + m_last_pa = ~0; + update_pa(); + + m_reg_I = fetch(); } void hp_hybrid_cpu_device::execute_run() @@ -272,30 +305,36 @@ uint16_t hp_hybrid_cpu_device::execute_one(uint16_t opcode) { if ((opcode & 0x7fe0) == 0x7000) { // EXE - m_icount -= 8; - // Indirect addressing in EXE instruction seems to use AEC case A instead of case C - // (because it's an opcode fetch) - uint16_t reg = RM(opcode & 0x1f); + m_icount -= 2; + uint16_t fetch_addr = opcode & 0x1f; if (BIT(opcode , 15)) { - m_icount -= 6; - return RM(add_mae(AEC_CASE_A , reg)); - } else { - return reg; + fetch_addr = get_indirect_target(fetch_addr); } + // Indirect addressing in EXE instruction seems to use AEC case A instead of case C + // (because it's an opcode fetch) + return RM(add_mae(AEC_CASE_A , fetch_addr)); } else { - m_reg_P = execute_one_sub(opcode); + uint16_t next_P; + if (!execute_one_bpc(opcode , next_P) && + !execute_no_bpc(opcode , next_P)) { + // Unrecognized instruction, make it a NOP + logerror("hp_hybrid: unknown opcode %04x @ %06x\n" , opcode , m_genpc); + next_P = m_reg_P + 1; + } + m_reg_P = next_P & m_addr_mask_low16; return fetch(); } } /** - * Execute 1 instruction (except EXE) + * Execute 1 BPC instruction (except EXE) * * @param opcode Opcode to be executed (no EXE instructions) + * @param[out] next_pc new value of P register * - * @return new value of P register + * @return true iff instruction executed */ -uint16_t hp_hybrid_cpu_device::execute_one_sub(uint16_t opcode) +bool hp_hybrid_cpu_device::execute_one_bpc(uint16_t opcode , uint16_t& next_pc) { uint32_t ea; uint16_t tmp; @@ -303,193 +342,220 @@ uint16_t hp_hybrid_cpu_device::execute_one_sub(uint16_t opcode) switch (opcode & 0x7800) { case 0x0000: // LDA - m_icount -= 13; + m_icount -= 1; m_reg_A = RM(get_ea(opcode)); break; case 0x0800: // LDB - m_icount -= 13; + m_icount -= 1; m_reg_B = RM(get_ea(opcode)); break; case 0x1000: // CPA - m_icount -= 16; + m_icount -= 4; if (m_reg_A != RM(get_ea(opcode))) { // Skip next instruction - return m_reg_P + 2; + next_pc = m_reg_P + 2; + return true; } break; case 0x1800: // CPB - m_icount -= 16; + m_icount -= 4; if (m_reg_B != RM(get_ea(opcode))) { // Skip next instruction - return m_reg_P + 2; + next_pc = m_reg_P + 2; + return true; } break; case 0x2000: // ADA - m_icount -= 13; + m_icount -= 1; do_add(m_reg_A , RM(get_ea(opcode))); break; case 0x2800: // ADB - m_icount -= 13; + m_icount -= 1; do_add(m_reg_B , RM(get_ea(opcode))); break; case 0x3000: // STA - m_icount -= 13; + m_icount -= 1; WM(get_ea(opcode) , m_reg_A); break; case 0x3800: // STB - m_icount -= 13; + m_icount -= 1; WM(get_ea(opcode) , m_reg_B); break; case 0x4000: // JSM - m_icount -= 17; - WM(AEC_CASE_C , ++m_reg_R , m_reg_P); - return remove_mae(get_ea(opcode)); + m_icount -= 5; + m_reg_R = (m_reg_R + 1) & m_addr_mask_low16; + WM(AEC_CASE_C , m_reg_R , m_reg_P); + next_pc = remove_mae(get_ea(opcode)); + return true; case 0x4800: // ISZ - m_icount -= 19; + m_icount -= 1; ea = get_ea(opcode); tmp = RM(ea) + 1; WM(ea , tmp); if (tmp == 0) { // Skip next instruction - return m_reg_P + 2; + next_pc = m_reg_P + 2; + return true; } break; case 0x5000: // AND - m_icount -= 13; + m_icount -= 1; m_reg_A &= RM(get_ea(opcode)); break; case 0x5800: // DSZ - m_icount -= 19; + m_icount -= 1; ea = get_ea(opcode); tmp = RM(ea) - 1; WM(ea , tmp); if (tmp == 0) { // Skip next instruction - return m_reg_P + 2; + next_pc = m_reg_P + 2; + return true; } break; case 0x6000: // IOR - m_icount -= 13; + m_icount -= 1; m_reg_A |= RM(get_ea(opcode)); break; case 0x6800: // JMP - m_icount -= 8; - return remove_mae(get_ea(opcode)); + m_icount -= 2; + next_pc = remove_mae(get_ea(opcode)); + return true; default: switch (opcode & 0xfec0) { case 0x7400: // RZA // SZA - m_icount -= 14; - return get_skip_addr(opcode , m_reg_A == 0); + m_icount -= 8; + next_pc = get_skip_addr(opcode , m_reg_A == 0); + return true; case 0x7440: // RIA // SIA - m_icount -= 14; - return get_skip_addr(opcode , m_reg_A++ == 0); + m_icount -= 8; + next_pc = get_skip_addr(opcode , m_reg_A++ == 0); + return true; case 0x7480: // SFS // SFC - m_icount -= 14; - return get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_FLG_BIT)); + m_icount -= 8; + next_pc = get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_FLG_BIT)); + return true; + + case 0x74c0: + // SDS + // SDC + m_icount -= 8; + next_pc = get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_DC_BIT)); + return true; case 0x7C00: // RZB // SZB - m_icount -= 14; - return get_skip_addr(opcode , m_reg_B == 0); + m_icount -= 8; + next_pc = get_skip_addr(opcode , m_reg_B == 0); + return true; case 0x7C40: // RIB // SIB - m_icount -= 14; - return get_skip_addr(opcode , m_reg_B++ == 0); + m_icount -= 8; + next_pc = get_skip_addr(opcode , m_reg_B++ == 0); + return true; case 0x7c80: // SSS // SSC - m_icount -= 14; - return get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_STS_BIT)); + m_icount -= 8; + next_pc = get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_STS_BIT)); + return true; case 0x7cc0: // SHS // SHC - m_icount -= 14; - return get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_HALT_BIT)); + m_icount -= 8; + next_pc = get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_HALT_BIT)); + return true; default: switch (opcode & 0xfe00) { case 0x7600: // SLA // RLA - m_icount -= 14; - return get_skip_addr_sc(opcode , m_reg_A , 0); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_reg_A , 0); + return true; case 0x7e00: // SLB // RLB - m_icount -= 14; - return get_skip_addr_sc(opcode , m_reg_B , 0); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_reg_B , 0); + return true; case 0xf400: // SAP // SAM - m_icount -= 14; - return get_skip_addr_sc(opcode , m_reg_A , 15); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_reg_A , 15); + return true; case 0xf600: // SOC // SOS - m_icount -= 14; - return get_skip_addr_sc(opcode , m_flags , HPHYBRID_O_BIT); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_flags , HPHYBRID_O_BIT); + return true; case 0xfc00: // SBP // SBM - m_icount -= 14; - return get_skip_addr_sc(opcode , m_reg_B , 15); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_reg_B , 15); + return true; case 0xfe00: // SEC // SES - m_icount -= 14; - return get_skip_addr_sc(opcode , m_flags , HPHYBRID_C_BIT); + m_icount -= 8; + next_pc = get_skip_addr_sc(opcode , m_flags , HPHYBRID_C_BIT); + return true; default: switch (opcode & 0xfff0) { case 0xf100: // AAR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); // A shift by 16 positions is equivalent to a shift by 15 tmp = tmp > 15 ? 15 : tmp; m_reg_A = ((m_reg_A ^ 0x8000) >> tmp) - (0x8000 >> tmp); @@ -498,7 +564,7 @@ uint16_t hp_hybrid_cpu_device::execute_one_sub(uint16_t opcode) case 0xf900: // ABR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); tmp = tmp > 15 ? 15 : tmp; m_reg_B = ((m_reg_B ^ 0x8000) >> tmp) - (0x8000 >> tmp); break; @@ -506,161 +572,123 @@ uint16_t hp_hybrid_cpu_device::execute_one_sub(uint16_t opcode) case 0xf140: // SAR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_A >>= tmp; break; case 0xf940: // SBR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_B >>= tmp; break; case 0xf180: // SAL tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_A <<= tmp; break; case 0xf980: // SBL tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_B <<= tmp; break; case 0xf1c0: // RAR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_A = (m_reg_A >> tmp) | (m_reg_A << (16 - tmp)); break; case 0xf9c0: // RBR tmp = (opcode & 0xf) + 1; - m_icount -= (9 + tmp); + m_icount -= (3 + tmp); m_reg_B = (m_reg_B >> tmp) | (m_reg_B << (16 - tmp)); break; default: - if ((opcode & 0xf760) == 0x7160) { - // Place/withdraw instructions - m_icount -= 23; - do_pw(opcode); - } else if ((opcode & 0xff80) == 0xf080) { + if ((opcode & 0xff80) == 0xf080) { // RET - m_icount -= 16; + m_icount -= 4; if (BIT(opcode , 6)) { // Pop PA stack if (BIT(m_flags , HPHYBRID_IRH_SVC_BIT)) { BIT_CLR(m_flags , HPHYBRID_IRH_SVC_BIT); memmove(&m_reg_PA[ 0 ] , &m_reg_PA[ 1 ] , HPHYBRID_INT_LVLS); - m_pa_changed_func((uint8_t)CURRENT_PA); + update_pa(); } else if (BIT(m_flags , HPHYBRID_IRL_SVC_BIT)) { BIT_CLR(m_flags , HPHYBRID_IRL_SVC_BIT); memmove(&m_reg_PA[ 0 ] , &m_reg_PA[ 1 ] , HPHYBRID_INT_LVLS); - m_pa_changed_func((uint8_t)CURRENT_PA); + update_pa(); } - tmp = RM(AEC_CASE_C , m_reg_R--) + (opcode & 0x1f); + } + tmp = RM(AEC_CASE_C , m_reg_R) + (opcode & 0x1f); + m_reg_R = (m_reg_R - 1) & m_addr_mask_low16; + if (BIT(opcode , 6)) { BIT_CLR(m_flags, HPHYBRID_IM_BIT); - } else { - tmp = RM(AEC_CASE_C , m_reg_R--) + (opcode & 0x1f); } - return BIT(opcode , 5) ? tmp - 0x20 : tmp; + next_pc = BIT(opcode , 5) ? tmp - 0x20 : tmp; + return true; } else { switch (opcode) { - case 0x7100: - // SDO - m_icount -= 12; - BIT_SET(m_flags , HPHYBRID_DMADIR_BIT); - break; - - case 0x7108: - // SDI - m_icount -= 12; - BIT_CLR(m_flags , HPHYBRID_DMADIR_BIT); - break; - case 0x7110: // EIR - m_icount -= 12; + m_icount -= 6; BIT_SET(m_flags , HPHYBRID_INTEN_BIT); break; case 0x7118: // DIR - m_icount -= 12; + m_icount -= 6; BIT_CLR(m_flags , HPHYBRID_INTEN_BIT); break; case 0x7120: // DMA - m_icount -= 12; + m_icount -= 6; BIT_SET(m_flags , HPHYBRID_DMAEN_BIT); break; case 0x7138: // DDR - m_icount -= 12; + m_icount -= 6; BIT_CLR(m_flags , HPHYBRID_DMAEN_BIT); break; - case 0x7140: - // DBL - m_icount -= 12; - BIT_CLR(m_flags , HPHYBRID_DB_BIT); - break; - - case 0x7148: - // CBL - m_icount -= 12; - BIT_CLR(m_flags , HPHYBRID_CB_BIT); - break; - - case 0x7150: - // DBU - m_icount -= 12; - BIT_SET(m_flags , HPHYBRID_DB_BIT); - break; - - case 0x7158: - // CBU - m_icount -= 12; - BIT_SET(m_flags , HPHYBRID_CB_BIT); - break; - case 0xf020: // TCA - m_icount -= 9; + m_icount -= 3; m_reg_A = ~m_reg_A; do_add(m_reg_A , 1); break; case 0xf060: // CMA - m_icount -= 9; + m_icount -= 3; m_reg_A = ~m_reg_A; break; case 0xf820: // TCB - m_icount -= 9; + m_icount -= 3; m_reg_B = ~m_reg_B; do_add(m_reg_B , 1); break; case 0xf860: // CMB - m_icount -= 9; + m_icount -= 3; m_reg_B = ~m_reg_B; break; default: - // Unrecognized instruction: pass it on for further processing (by EMC if present) - return execute_no_bpc_ioc(opcode); + // Unrecognized instruction: pass it on for further processing by other units + return false; } } } @@ -668,23 +696,241 @@ uint16_t hp_hybrid_cpu_device::execute_one_sub(uint16_t opcode) } } - return m_reg_P + 1; + next_pc = m_reg_P + 1; + return true; +} + +void hp_hybrid_cpu_device::emc_start() +{ + state_add(HPHYBRID_AR2, "Ar2" , m_reg_ar2[ 0 ]); + state_add(HPHYBRID_AR2_2, "Ar2_2" , m_reg_ar2[ 1 ]); + state_add(HPHYBRID_AR2_3, "Ar2_3" , m_reg_ar2[ 2 ]); + state_add(HPHYBRID_AR2_4, "Ar2_4" , m_reg_ar2[ 3 ]); + state_add(HPHYBRID_SE, "SE" , m_reg_se); + state_add(HPHYBRID_R25, "R25" , m_reg_r25).noshow(); + state_add(HPHYBRID_R26, "R26" , m_reg_r26).noshow(); + state_add(HPHYBRID_R27, "R27" , m_reg_r27).noshow(); + + save_item(NAME(m_reg_ar2[ 0 ])); + save_item(NAME(m_reg_ar2[ 1 ])); + save_item(NAME(m_reg_ar2[ 2 ])); + save_item(NAME(m_reg_ar2[ 3 ])); + save_item(NAME(m_reg_se)); + save_item(NAME(m_reg_r25)); + save_item(NAME(m_reg_r26)); + save_item(NAME(m_reg_r27)); +} + +bool hp_hybrid_cpu_device::execute_emc(uint16_t opcode , uint16_t& next_pc) +{ + // EMC instructions + uint8_t n; + uint16_t tmp1; + uint16_t tmp2; + uint64_t tmp_ar; + uint64_t tmp_ar2; + bool carry; + + switch (opcode & 0xfff0) { + case 0x7300: + // XFR + tmp1 = m_reg_A; + tmp2 = m_reg_B; + n = (opcode & 0xf) + 1; + m_icount -= 15; + while (n--) { + WM(AEC_CASE_C , tmp2 , RM(AEC_CASE_C , tmp1)); + tmp1++; + tmp2++; + } + break; + + case 0x7380: + // CLR + tmp1 = m_reg_A; + n = (opcode & 0xf) + 1; + m_icount -= 10; + while (n--) { + WM(AEC_CASE_C , tmp1 , 0); + tmp1++; + } + break; + + default: + switch (opcode) { + case 0x7200: + // MWA + m_icount -= 22; + tmp_ar2 = get_ar2(); + carry = do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , m_reg_B); + set_ar2(tmp_ar2); + if (carry) + BIT_SET(m_flags, HPHYBRID_DC_BIT); + else + BIT_CLR(m_flags, HPHYBRID_DC_BIT); + break; + + case 0x7220: + // CMY + m_icount -= 17; + tmp_ar2 = get_ar2(); + tmp_ar2 = 0x999999999999ULL - tmp_ar2; + do_dec_add(true , tmp_ar2 , 0); + set_ar2(tmp_ar2); + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + break; + + case 0x7260: + // CMX + m_icount -= 17; + tmp_ar = get_ar1(); + tmp_ar = 0x999999999999ULL - tmp_ar; + do_dec_add(true , tmp_ar , 0); + set_ar1(tmp_ar); + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + break; + + case 0x7280: + // FXA + m_icount -= 16; + tmp_ar2 = get_ar2(); + carry = do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , get_ar1()); + set_ar2(tmp_ar2); + if (carry) + BIT_SET(m_flags, HPHYBRID_DC_BIT); + else + BIT_CLR(m_flags, HPHYBRID_DC_BIT); + break; + + case 0x7340: + // NRM + tmp_ar2 = get_ar2(); + m_icount -= 17; + for (n = 0; n < 12 && (tmp_ar2 & 0xf00000000000ULL) == 0; n++) { + do_dec_shift_l(0 , tmp_ar2); + m_icount--; + } + m_reg_B = n; + if (n < 12) { + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + set_ar2(tmp_ar2); + } else { + BIT_SET(m_flags , HPHYBRID_DC_BIT); + // When ar2 is 0, total time is 69 cycles + // (salcazzo che cosa fa per altri 34 cicli) + m_icount -= 34; + } + break; + + case 0x73c0: + // CDC + m_icount -= 5; + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + break; + + case 0x7a00: + // FMP + m_icount -= 15; + m_reg_A = 0; + n = m_reg_B & 0xf; + if (n == 0) { + tmp_ar = 0; + } else { + m_icount -= 3; + tmp_ar = get_ar1(); + n--; + } + tmp_ar2 = get_ar2(); + do { + m_icount -= 13; + if (do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , tmp_ar)) { + m_reg_A++; + } + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + } while (n--); + set_ar2(tmp_ar2); + break; + + case 0x7a21: + // FDV + // This instruction keeps adding AR1 to AR2 until an overflow occurs. + // Register B will hold the number of iterations after the execution. + // Note that if AR1 is 0 overflow never happens and the processor hangs. + // Here we stop at 15 iterations (after all there are only 4 bits in the loop counter). + m_icount -= 13; + m_reg_B = 0; + tmp_ar = get_ar1(); + tmp_ar2 = get_ar2(); + while (m_reg_B < 15 && !do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , tmp_ar)) { + m_icount -= 13; + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + m_reg_B++; + } + set_ar2(tmp_ar2); + break; + + case 0x7b00: + // MRX + // Cycle count is incorrect for the case where B=0, as AR1 doesn't get read or written in real hw + set_ar1(do_mrxy(get_ar1())); + m_icount -= 20; + break; + + case 0x7b21: + // DRS + tmp_ar = get_ar1(); + m_icount -= 14; + m_reg_A = m_reg_se = do_dec_shift_r(0 , tmp_ar); + set_ar1(tmp_ar); + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + break; + + case 0x7b40: + // MRY + set_ar2(do_mrxy(get_ar2())); + m_icount -= 27; + break; + + case 0x7b61: + // MLY + tmp_ar2 = get_ar2(); + m_icount -= 26; + m_reg_A = m_reg_se = do_dec_shift_l(m_reg_A & 0xf , tmp_ar2); + set_ar2(tmp_ar2); + BIT_CLR(m_flags , HPHYBRID_DC_BIT); + break; + + case 0x7b8f: + // MPY + do_mpy(); + break; + + default: + // Unrecognized instruction + return false; + } + } + + next_pc = m_reg_P + 1; + return true; } void hp_hybrid_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const { if (entry.index() == STATE_GENFLAGS) { - str = string_format("%s %s %c %c", + str = string_format("%s %s %s %c %c", BIT(m_flags , HPHYBRID_DB_BIT) ? "Db":"..", BIT(m_flags , HPHYBRID_CB_BIT) ? "Cb":"..", + BIT(m_flags , HPHYBRID_DC_BIT) ? "DC":"..", BIT(m_flags , HPHYBRID_O_BIT) ? 'O':'.', BIT(m_flags , HPHYBRID_C_BIT) ? 'E':'.'); } } -std::unique_ptr<util::disasm_interface> hp_hybrid_cpu_device::create_disassembler() +uint32_t hp_hybrid_cpu_device::add_mae(aec_cases_t aec_case , uint16_t addr) { - return std::make_unique<hp_hybrid_disassembler>(); + // No MAE on 5061-3011 or 09825-67907 + return addr; } uint16_t hp_hybrid_cpu_device::remove_mae(uint32_t addr) @@ -699,26 +945,31 @@ uint16_t hp_hybrid_cpu_device::RM(aec_cases_t aec_case , uint16_t addr) uint16_t hp_hybrid_cpu_device::RM(uint32_t addr) { - uint16_t tmp; + addr &= m_addr_mask; uint16_t addr_wo_bsc = remove_mae(addr); if (addr_wo_bsc <= HP_REG_LAST_ADDR) { // Any access to internal registers removes forcing of BSC 2x m_forced_bsc_25 = false; - // Memory mapped registers that are present in both 3001 & 3011 + // Memory mapped BPC registers + uint16_t tmp; switch (addr_wo_bsc) { case HP_REG_A_ADDR: - return m_reg_A; + tmp = m_reg_A; + break; case HP_REG_B_ADDR: - return m_reg_B; + tmp = m_reg_B; + break; case HP_REG_P_ADDR: - return m_reg_P; + tmp = m_reg_P; + break; case HP_REG_R_ADDR: - return m_reg_R; + tmp = m_reg_R; + break; case HP_REG_R4_ADDR: case HP_REG_R5_ADDR: @@ -727,44 +978,79 @@ uint16_t hp_hybrid_cpu_device::RM(uint32_t addr) return RIO(CURRENT_PA , addr_wo_bsc - HP_REG_R4_ADDR); case HP_REG_IV_ADDR: - return m_reg_IV; + tmp = m_reg_IV; + break; case HP_REG_PA_ADDR: - return CURRENT_PA; + tmp = CURRENT_PA; + break; case HP_REG_W_ADDR: - return m_reg_W; - - case HP_REG_DMAPA_ADDR: - tmp = m_dmapa & HP_REG_PA_MASK; - if (BIT(m_flags , HPHYBRID_CB_BIT)) { - BIT_SET(tmp , 15); - } - if (BIT(m_flags , HPHYBRID_DB_BIT)) { - BIT_SET(tmp , 14); - } - return tmp; + tmp = m_reg_W; + break; case HP_REG_DMAMA_ADDR: - return m_dmama; + tmp = m_dmama; + break; case HP_REG_DMAC_ADDR: - return m_dmac; + tmp = m_dmac; + break; case HP_REG_C_ADDR: - return m_reg_C; + tmp = m_reg_C; + break; case HP_REG_D_ADDR: - return m_reg_D; + tmp = m_reg_D; + break; default: - return read_non_common_reg(addr_wo_bsc); + if (!read_non_common_reg(addr_wo_bsc , tmp)) { + // Non-existing registers are returned as 0 + tmp = 0; + } + break; } + m_icount -= REGISTER_RW_CYCLES; + return tmp; } else { + m_icount -= m_r_cycles; return m_cache->read_word(addr); } } +bool hp_hybrid_cpu_device::read_emc_reg(uint16_t addr , uint16_t& v) +{ + switch (addr) { + case HP_REG_AR2_ADDR: + case HP_REG_AR2_ADDR + 1: + case HP_REG_AR2_ADDR + 2: + case HP_REG_AR2_ADDR + 3: + v = m_reg_ar2[ addr - HP_REG_AR2_ADDR ]; + return true; + + case HP_REG_SE_ADDR: + v = m_reg_se; + return true; + + case HP_REG_R25_ADDR: + v = m_reg_r25; + return true; + + case HP_REG_R26_ADDR: + v = m_reg_r26; + return true; + + case HP_REG_R27_ADDR: + v = m_reg_r27; + return true; + + default: + return false; + } +} + void hp_hybrid_cpu_device::WM(aec_cases_t aec_case , uint16_t addr , uint16_t v) { WM(add_mae(aec_case , addr) , v); @@ -772,13 +1058,14 @@ void hp_hybrid_cpu_device::WM(aec_cases_t aec_case , uint16_t addr , uint16_t v) void hp_hybrid_cpu_device::WM(uint32_t addr , uint16_t v) { + addr &= m_addr_mask; uint16_t addr_wo_bsc = remove_mae(addr); if (addr_wo_bsc <= HP_REG_LAST_ADDR) { // Any access to internal registers removes forcing of BSC 2x m_forced_bsc_25 = false; - // Memory mapped registers + // Memory mapped BPC registers switch (addr_wo_bsc) { case HP_REG_A_ADDR: m_reg_A = v; @@ -789,11 +1076,11 @@ void hp_hybrid_cpu_device::WM(uint32_t addr , uint16_t v) break; case HP_REG_P_ADDR: - m_reg_P = v; + m_reg_P = v & m_addr_mask_low16; break; case HP_REG_R_ADDR: - m_reg_R = v; + m_reg_R = v & m_addr_mask_low16; break; case HP_REG_R4_ADDR: @@ -801,7 +1088,7 @@ void hp_hybrid_cpu_device::WM(uint32_t addr , uint16_t v) case HP_REG_R6_ADDR: case HP_REG_R7_ADDR: WIO(CURRENT_PA , addr_wo_bsc - HP_REG_R4_ADDR , v); - break; + return; case HP_REG_IV_ADDR: m_reg_IV = v & HP_REG_IV_MASK; @@ -809,7 +1096,7 @@ void hp_hybrid_cpu_device::WM(uint32_t addr , uint16_t v) case HP_REG_PA_ADDR: CURRENT_PA = v & HP_REG_PA_MASK; - m_pa_changed_func((uint8_t)CURRENT_PA); + update_pa(); break; case HP_REG_W_ADDR: @@ -840,17 +1127,56 @@ void hp_hybrid_cpu_device::WM(uint32_t addr , uint16_t v) write_non_common_reg(addr_wo_bsc , v); break; } + m_icount -= REGISTER_RW_CYCLES; } else { + m_icount -= m_w_cycles; m_program->write_word(addr , v); } } +bool hp_hybrid_cpu_device::write_emc_reg(uint16_t addr , uint16_t v) +{ + switch (addr) { + case HP_REG_AR2_ADDR: + case HP_REG_AR2_ADDR + 1: + case HP_REG_AR2_ADDR + 2: + case HP_REG_AR2_ADDR + 3: + m_reg_ar2[ addr - HP_REG_AR2_ADDR ] = v; + return true; + + case HP_REG_SE_ADDR: + m_reg_se = v & HP_REG_SE_MASK; + return true; + + case HP_REG_R25_ADDR: + m_reg_r25 = v; + return true; + + case HP_REG_R26_ADDR: + m_reg_r26 = v; + return true; + + case HP_REG_R27_ADDR: + m_reg_r27 = v; + return true; + + default: + return false; + } +} + uint16_t hp_hybrid_cpu_device::fetch(void) { m_genpc = add_mae(AEC_CASE_A , m_reg_P); return RM(m_genpc); } +uint16_t hp_hybrid_cpu_device::get_indirect_target(uint32_t addr) +{ + // Single-level indirect addressing on 5061-3011 or 5061-3001 + return RM(addr); +} + uint32_t hp_hybrid_cpu_device::get_ea(uint16_t opcode) { uint16_t base; @@ -858,8 +1184,13 @@ uint32_t hp_hybrid_cpu_device::get_ea(uint16_t opcode) aec_cases_t aec; if (BIT(opcode , 10)) { - // Current page - base = m_reg_P; + if (m_relative_mode) { + // Current page relative addressing + base = m_reg_P; + } else { + // Current page absolute addressing + base = (m_reg_P & 0xfc00) | 0x0200; + } aec = AEC_CASE_A; } else { // Base page @@ -873,14 +1204,14 @@ uint32_t hp_hybrid_cpu_device::get_ea(uint16_t opcode) } base += off; + uint32_t ea = add_mae(aec , base); if (BIT(opcode , 15)) { // Indirect addressing - m_icount -= 6; - return add_mae(AEC_CASE_C , RM(aec , base)); + return add_mae(AEC_CASE_C , get_indirect_target(ea)); } else { // Direct addressing - return add_mae(aec , base); + return ea; } } @@ -917,7 +1248,7 @@ uint16_t hp_hybrid_cpu_device::get_skip_addr(uint16_t opcode , bool condition) c } } -uint16_t hp_hybrid_cpu_device::get_skip_addr_sc(uint16_t opcode , uint16_t& v , unsigned n) +template<typename T> uint16_t hp_hybrid_cpu_device::get_skip_addr_sc(uint16_t opcode , T& v , unsigned n) { bool val = BIT(v , n); @@ -932,109 +1263,11 @@ uint16_t hp_hybrid_cpu_device::get_skip_addr_sc(uint16_t opcode , uint16_t& v , return get_skip_addr(opcode , val); } -uint16_t hp_hybrid_cpu_device::get_skip_addr_sc(uint16_t opcode , uint32_t& v , unsigned n) +void hp_hybrid_cpu_device::update_pa() { - bool val = BIT(v , n); - - if (BIT(opcode , 7)) { - if (BIT(opcode , 6)) { - BIT_SET(v , n); - } else { - BIT_CLR(v , n); - } - } - - return get_skip_addr(opcode , val); -} - -void hp_hybrid_cpu_device::do_pw(uint16_t opcode) -{ - uint16_t tmp; - uint16_t reg_addr = opcode & 7; - uint16_t *ptr_reg; - uint16_t b_mask; - - if (BIT(opcode , 3)) { - ptr_reg = &m_reg_D; - b_mask = BIT_MASK(HPHYBRID_DB_BIT); - } else { - ptr_reg = &m_reg_C; - b_mask = BIT_MASK(HPHYBRID_CB_BIT); - } - - if (BIT(opcode , 4)) { - // Withdraw - if (BIT(opcode , 11)) { - // Byte - uint32_t tmp_addr = (uint32_t)(*ptr_reg); - if (m_flags & b_mask) { - tmp_addr |= 0x10000; - } - tmp = RM(AEC_CASE_C , (uint16_t)(tmp_addr >> 1)); - if (BIT(tmp_addr , 0)) { - tmp &= 0xff; - } else { - tmp >>= 8; - } - } else { - // Word - tmp = RM(AEC_CASE_C , *ptr_reg); - } - WM(reg_addr , tmp); - - if (BIT(opcode , 7)) { - // Post-decrement - if ((*ptr_reg)-- == 0) { - m_flags ^= b_mask; - } - } else { - // Post-increment - if (++(*ptr_reg) == 0) { - m_flags ^= b_mask; - } - } - } else { - // Place - if (BIT(opcode , 7)) { - // Pre-decrement - if ((*ptr_reg)-- == 0) { - m_flags ^= b_mask; - } - } else { - // Pre-increment - if (++(*ptr_reg) == 0) { - m_flags ^= b_mask; - } - } - tmp = RM(reg_addr); - if (BIT(opcode , 11)) { - // Byte - uint32_t tmp_addr = (uint32_t)(*ptr_reg); - if (m_flags & b_mask) { - tmp_addr |= 0x10000; - } - if (tmp_addr <= (HP_REG_LAST_ADDR * 2 + 1)) { - // Single bytes can be written to registers. - // The addressed register gets the written byte in the proper position - // and a 0 in the other byte because access to registers is always done in - // 16 bits units. - if (BIT(tmp_addr , 0)) { - tmp &= 0xff; - } else { - tmp <<= 8; - } - WM(tmp_addr >> 1 , tmp); - } else { - // Extend address, form byte address - uint16_t val = (tmp_addr & 1) ? uint8_t(tmp) : (tmp << 8); - uint16_t mask = (tmp_addr & 1) ? 0x00ff : 0xff00; - tmp_addr = add_mae(AEC_CASE_C , tmp_addr >> 1); - m_program->write_word(tmp_addr , val, mask); - } - } else { - // Word - WM(AEC_CASE_C , *ptr_reg , tmp); - } + if (CURRENT_PA != m_last_pa) { + m_last_pa = CURRENT_PA; + m_pa_changed_func(m_last_pa); } } @@ -1081,19 +1314,24 @@ void hp_hybrid_cpu_device::check_for_interrupts(void) memmove(&m_reg_PA[ 1 ] , &m_reg_PA[ 0 ] , HPHYBRID_INT_LVLS); CURRENT_PA = new_PA; + update_pa(); - m_pa_changed_func((uint8_t)CURRENT_PA); - - // Is this correct? Patent @ pg 210 suggests that the whole interrupt recognition sequence - // lasts for 32 cycles - m_icount -= 32; + // Total time for int. ack execution = 12 + WM + RM * (1 + IND) + RR + // WM = memory write cycles + // RM = memory read cycles + // IND = count of indirections (1 in 3001/3011) + // RR = register read cycles + m_icount -= (12 + REGISTER_RW_CYCLES); // Allow special processing in 5061-3001 enter_isr(); // Do a double-indirect JSM IV,I instruction - WM(AEC_CASE_C , ++m_reg_R , m_reg_P); - m_reg_P = RM(AEC_CASE_C , m_reg_IV + CURRENT_PA); + // On 09825 there can be more than 2 levels of indirection + m_reg_R = (m_reg_R + 1) & m_addr_mask_low16; + WM(AEC_CASE_C , m_reg_R , m_reg_P); + uint32_t addr = add_mae(AEC_CASE_C , m_reg_IV + CURRENT_PA); + m_reg_P = get_indirect_target(addr); m_reg_I = fetch(); } @@ -1102,100 +1340,19 @@ void hp_hybrid_cpu_device::enter_isr(void) // Do nothing special } -void hp_hybrid_cpu_device::handle_dma(void) -{ - // Patent hints at the fact that terminal count is detected by bit 15 of dmac being 1 after decrementing - bool tc = BIT(--m_dmac , 15) != 0; - uint16_t tmp; - - if (BIT(m_flags , HPHYBRID_DMADIR_BIT)) { - // "Outward" DMA: memory -> peripheral - tmp = RM(AEC_CASE_D , m_dmama++); - WIO(m_dmapa , tc ? 2 : 0 , tmp); - m_icount -= 10; - } else { - // "Inward" DMA: peripheral -> memory - tmp = RIO(m_dmapa , tc ? 2 : 0); - WM(AEC_CASE_D , m_dmama++ , tmp); - m_icount -= 9; - } - - // Mystery solved: DMA is not automatically disabled at TC (test of 9845's graphic memory relies on this to work) -} - uint16_t hp_hybrid_cpu_device::RIO(uint8_t pa , uint8_t ic) { + m_icount -= IO_RW_CYCLES; return m_io->read_word(HP_MAKE_IOADDR(pa, ic)); } void hp_hybrid_cpu_device::WIO(uint8_t pa , uint8_t ic , uint16_t v) { + m_icount -= IO_RW_CYCLES; m_io->write_word(HP_MAKE_IOADDR(pa, ic) , v); } -hp_5061_3001_cpu_device::hp_5061_3001_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hp_hybrid_cpu_device(mconfig, HP_5061_3001, tag, owner, clock, 22) - , m_boot_mode(false) -{ -} - -void hp_5061_3001_cpu_device::device_start() -{ - hp_hybrid_cpu_device::device_start(); - - state_add(HPHYBRID_AR2, "Ar2" , m_reg_ar2[ 0 ]); - state_add(HPHYBRID_AR2_2, "Ar2_2" , m_reg_ar2[ 1 ]); - state_add(HPHYBRID_AR2_3, "Ar2_3" , m_reg_ar2[ 2 ]); - state_add(HPHYBRID_AR2_4, "Ar2_4" , m_reg_ar2[ 3 ]); - state_add(HPHYBRID_SE, "SE" , m_reg_se); - state_add(HPHYBRID_R25, "R25" , m_reg_r25).noshow(); - state_add(HPHYBRID_R26, "R26" , m_reg_r26).noshow(); - state_add(HPHYBRID_R27, "R27" , m_reg_r27).noshow(); - state_add(HPHYBRID_R32, "R32" , m_reg_aec[ 0 ]); - state_add(HPHYBRID_R33, "R33" , m_reg_aec[ 1 ]); - state_add(HPHYBRID_R34, "R34" , m_reg_aec[ 2 ]); - state_add(HPHYBRID_R35, "R35" , m_reg_aec[ 3 ]); - state_add(HPHYBRID_R36, "R36" , m_reg_aec[ 4 ]); - state_add(HPHYBRID_R37, "R37" , m_reg_aec[ 5 ]); - - save_item(NAME(m_reg_ar2[ 0 ])); - save_item(NAME(m_reg_ar2[ 1 ])); - save_item(NAME(m_reg_ar2[ 2 ])); - save_item(NAME(m_reg_ar2[ 3 ])); - save_item(NAME(m_reg_se)); - save_item(NAME(m_reg_r25)); - save_item(NAME(m_reg_r26)); - save_item(NAME(m_reg_r27)); - save_item(NAME(m_reg_aec[ 0 ])); - save_item(NAME(m_reg_aec[ 1 ])); - save_item(NAME(m_reg_aec[ 2 ])); - save_item(NAME(m_reg_aec[ 3 ])); - save_item(NAME(m_reg_aec[ 4 ])); - save_item(NAME(m_reg_aec[ 5 ])); -} - -void hp_5061_3001_cpu_device::device_reset() -{ - // Initial state of AEC registers: - // R32 0 - // R33 5 - // R34 0 - // R35 0 - // R36 0 - // R37 0 - m_reg_aec[ 0 ] = 0; - m_reg_aec[ 1 ] = 5; - m_reg_aec[ 2 ] = 0; - m_reg_aec[ 3 ] = 0; - m_reg_aec[ 4 ] = 0; - m_reg_aec[ 5 ] = 0; - - m_forced_bsc_25 = m_boot_mode; - - hp_hybrid_cpu_device::device_reset(); -} - -uint8_t hp_5061_3001_cpu_device::do_dec_shift_r(uint8_t d1 , uint64_t& mantissa) +uint8_t hp_hybrid_cpu_device::do_dec_shift_r(uint8_t d1 , uint64_t& mantissa) { uint8_t d12 = (uint8_t)(mantissa & 0xf); @@ -1204,7 +1361,7 @@ uint8_t hp_5061_3001_cpu_device::do_dec_shift_r(uint8_t d1 , uint64_t& mantissa) return d12; } -uint8_t hp_5061_3001_cpu_device::do_dec_shift_l(uint8_t d12 , uint64_t& mantissa) +uint8_t hp_hybrid_cpu_device::do_dec_shift_l(uint8_t d12 , uint64_t& mantissa) { uint8_t d1 = (uint8_t)((mantissa >> 44) & 0xf); @@ -1214,7 +1371,7 @@ uint8_t hp_5061_3001_cpu_device::do_dec_shift_l(uint8_t d12 , uint64_t& mantissa return d1; } -uint64_t hp_5061_3001_cpu_device::get_ar1(void) +uint64_t hp_hybrid_cpu_device::get_ar1(void) { uint32_t addr; uint64_t tmp; @@ -1229,7 +1386,7 @@ uint64_t hp_5061_3001_cpu_device::get_ar1(void) return tmp; } -void hp_5061_3001_cpu_device::set_ar1(uint64_t v) +void hp_hybrid_cpu_device::set_ar1(uint64_t v) { uint32_t addr; @@ -1241,7 +1398,7 @@ void hp_5061_3001_cpu_device::set_ar1(uint64_t v) WM(addr , (uint16_t)(v & 0xffff)); } -uint64_t hp_5061_3001_cpu_device::get_ar2(void) const +uint64_t hp_hybrid_cpu_device::get_ar2(void) const { uint64_t tmp; @@ -1254,7 +1411,7 @@ uint64_t hp_5061_3001_cpu_device::get_ar2(void) const return tmp; } -void hp_5061_3001_cpu_device::set_ar2(uint64_t v) +void hp_hybrid_cpu_device::set_ar2(uint64_t v) { m_reg_ar2[ 3 ] = (uint16_t)(v & 0xffff); v >>= 16; @@ -1263,7 +1420,7 @@ void hp_5061_3001_cpu_device::set_ar2(uint64_t v) m_reg_ar2[ 1 ] = (uint16_t)(v & 0xffff); } -uint64_t hp_5061_3001_cpu_device::do_mrxy(uint64_t ar) +uint64_t hp_hybrid_cpu_device::do_mrxy(uint64_t ar) { uint8_t n; @@ -1281,7 +1438,7 @@ uint64_t hp_5061_3001_cpu_device::do_mrxy(uint64_t ar) return ar; } -bool hp_5061_3001_cpu_device::do_dec_add(bool carry_in , uint64_t& a , uint64_t b) +bool hp_hybrid_cpu_device::do_dec_add(bool carry_in , uint64_t& a , uint64_t b) { uint64_t tmp = 0; unsigned i; @@ -1314,8 +1471,21 @@ bool hp_5061_3001_cpu_device::do_dec_add(bool carry_in , uint64_t& a , uint64_t return carry_in; } -void hp_5061_3001_cpu_device::do_mpy(void) +void hp_hybrid_cpu_device::do_mpy(void) { + // Count 0->1 and 1->0 transitions in A register + // Correct timing needs this count as real hw uses Booth's algorithm for multiplication + uint16_t tmp = m_reg_A; + uint16_t mask = ~0; + for (unsigned i = 0; i < 16 && tmp; ++i) { + if (BIT(tmp , 0)) { + tmp ^= mask; + m_icount -= 2; + } + tmp >>= 1; + mask >>= 1; + } + int32_t a = (int16_t)m_reg_A; int32_t b = (int16_t)m_reg_B; int32_t p = a * b; @@ -1323,227 +1493,287 @@ void hp_5061_3001_cpu_device::do_mpy(void) m_reg_A = (uint16_t)(p & 0xffff); m_reg_B = (uint16_t)((p >> 16) & 0xffff); - // Not entirely correct, timing depends on initial content of A register - m_icount -= 65; + m_icount -= 59; } -uint16_t hp_5061_3001_cpu_device::execute_no_bpc_ioc(uint16_t opcode) +// ******************************************************************************** +// hp_5061_3011_cpu_device +// ******************************************************************************** +hp_5061_3011_cpu_device::hp_5061_3011_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hp_hybrid_cpu_device(mconfig, HP_5061_3011, tag, owner, clock, 16) { - // EMC instructions - uint8_t n; - uint16_t tmp1; - uint16_t tmp2; - uint64_t tmp_ar; - uint64_t tmp_ar2; - bool carry; +} - switch (opcode & 0xfff0) { - case 0x7300: - // XFR - tmp1 = m_reg_A; - tmp2 = m_reg_B; - n = (opcode & 0xf) + 1; - m_icount -= 21; - while (n--) { - m_icount -= 12; - WM(AEC_CASE_C , tmp2 , RM(AEC_CASE_C , tmp1)); - tmp1++; - tmp2++; - } - break; +hp_5061_3011_cpu_device::hp_5061_3011_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t addrwidth) + : hp_hybrid_cpu_device(mconfig, type, tag, owner, clock, addrwidth) +{ +} - case 0x7380: - // CLR - tmp1 = m_reg_A; - n = (opcode & 0xf) + 1; - m_icount -= 16; - while (n--) { - m_icount -= 6; - WM(AEC_CASE_C , tmp1 , 0); - tmp1++; +bool hp_5061_3011_cpu_device::execute_no_bpc(uint16_t opcode , uint16_t& next_pc) +{ + // 16-bit IOC instructions + if ((opcode & 0xf760) == 0x7160) { + // Place/withdraw + uint16_t tmp; + uint16_t reg_addr = opcode & 7; + uint16_t *ptr_reg; + uint32_t b_mask; + + if (BIT(opcode , 3)) { + ptr_reg = &m_reg_D; + b_mask = BIT_MASK<uint32_t>(HPHYBRID_DB_BIT); + } else { + ptr_reg = &m_reg_C; + b_mask = BIT_MASK<uint32_t>(HPHYBRID_CB_BIT); } - break; - default: - switch (opcode) { - case 0x7200: - // MWA - m_icount -= 28; - tmp_ar2 = get_ar2(); - carry = do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , m_reg_B); - set_ar2(tmp_ar2); - if (carry) - BIT_SET(m_flags, HPHYBRID_DC_BIT); - else - BIT_CLR(m_flags, HPHYBRID_DC_BIT); - break; - - case 0x7220: - // CMY - m_icount -= 23; - tmp_ar2 = get_ar2(); - tmp_ar2 = 0x999999999999ULL - tmp_ar2; - do_dec_add(true , tmp_ar2 , 0); - set_ar2(tmp_ar2); - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - break; - - case 0x7260: - // CMX - m_icount -= 59; - tmp_ar = get_ar1(); - tmp_ar = 0x999999999999ULL - tmp_ar; - do_dec_add(true , tmp_ar , 0); - set_ar1(tmp_ar); - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - break; - - case 0x7280: - // FXA - m_icount -= 40; - tmp_ar2 = get_ar2(); - carry = do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , get_ar1()); - set_ar2(tmp_ar2); - if (carry) - BIT_SET(m_flags, HPHYBRID_DC_BIT); - else - BIT_CLR(m_flags, HPHYBRID_DC_BIT); - break; + if (BIT(opcode , 4)) { + // Withdraw + if (BIT(opcode , 11)) { + // Byte + uint32_t tmp_addr = (uint32_t)(*ptr_reg); + if (m_flags & b_mask) { + tmp_addr |= BIT_MASK<uint32_t>(16); + } + tmp = RM(AEC_CASE_C , (uint16_t)(tmp_addr >> 1)); + if (BIT(tmp_addr , 0)) { + tmp &= 0xff; + } else { + tmp >>= 8; + } + } else { + // Word + tmp = RM(AEC_CASE_C , *ptr_reg); + } + WM(reg_addr , tmp); - case 0x7340: - // NRM - tmp_ar2 = get_ar2(); - m_icount -= 23; - for (n = 0; n < 12 && (tmp_ar2 & 0xf00000000000ULL) == 0; n++) { - do_dec_shift_l(0 , tmp_ar2); - m_icount--; + if (BIT(opcode , 7)) { + // Post-decrement + if ((*ptr_reg)-- == 0) { + m_flags ^= b_mask; + } + } else { + // Post-increment + if (++(*ptr_reg) == 0) { + m_flags ^= b_mask; + } } - m_reg_B = n; - if (n < 12) { - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - set_ar2(tmp_ar2); + } else { + // Place + if (BIT(opcode , 7)) { + // Pre-decrement + if ((*ptr_reg)-- == 0) { + m_flags ^= b_mask; + } } else { - BIT_SET(m_flags , HPHYBRID_DC_BIT); - // When ar2 is 0, total time is 69 cycles - // (salcazzo che cosa fa per altri 34 cicli) - m_icount -= 34; + // Pre-increment + if (++(*ptr_reg) == 0) { + m_flags ^= b_mask; + } } + tmp = RM(reg_addr); + if (BIT(opcode , 11)) { + // Byte + uint32_t tmp_addr = (uint32_t)(*ptr_reg); + if (m_flags & b_mask) { + tmp_addr |= BIT_MASK<uint32_t>(16); + } + tmp = BIT(tmp_addr , 0) ? (tmp & 0xff) : (tmp << 8); + if (tmp_addr <= (HP_REG_LAST_ADDR * 2 + 1)) { + // Single bytes can be written to registers. + // The addressed register gets the written byte in the proper position + // and a 0 in the other byte because access to registers is always done in + // 16 bits units. + WM(tmp_addr >> 1 , tmp); + } else { + // Extend address, form byte address + uint16_t mask = BIT(tmp_addr , 0) ? 0x00ff : 0xff00; + tmp_addr = add_mae(AEC_CASE_C , tmp_addr >> 1); + m_program->write_word(tmp_addr , tmp , mask); + m_icount -= m_w_cycles; + } + } else { + // Word + WM(AEC_CASE_C , *ptr_reg , tmp); + } + } + m_icount -= 6; + } else { + switch (opcode) { + case 0x7100: + // SDO + m_icount -= 6; + BIT_SET(m_flags , HPHYBRID_DMADIR_BIT); break; - case 0x73c0: - // CDC - m_icount -= 11; - BIT_CLR(m_flags , HPHYBRID_DC_BIT); + case 0x7108: + // SDI + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_DMADIR_BIT); break; - case 0x7a00: - // FMP - m_icount -= 42; - m_reg_A = 0; - tmp_ar = get_ar1(); - tmp_ar2 = get_ar2(); - for (n = m_reg_B & 0xf; n > 0; n--) { - m_icount -= 13; - if (do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , tmp_ar)) - m_reg_A++; - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - } - set_ar2(tmp_ar2); + case 0x7140: + // DBL + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_DB_BIT); break; - case 0x7a21: - // FDV - // No doc mentions any limit on the iterations done by this instruction. - // Here we stop at 15 (after all there are only 4 bits in the loop counter). But is it correct? - m_icount -= 37; - m_reg_B = 0; - tmp_ar = get_ar1(); - tmp_ar2 = get_ar2(); - while (m_reg_B < 15 && !do_dec_add(BIT(m_flags , HPHYBRID_DC_BIT) , tmp_ar2 , tmp_ar)) { - m_icount -= 13; - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - m_reg_B++; - } - set_ar2(tmp_ar2); + case 0x7148: + // CBL + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_CB_BIT); break; - case 0x7b00: - // MRX - set_ar1(do_mrxy(get_ar1())); - m_icount -= 62; + case 0x7150: + // DBU + m_icount -= 6; + BIT_SET(m_flags , HPHYBRID_DB_BIT); break; - case 0x7b21: - // DRS - tmp_ar = get_ar1(); - m_icount -= 56; - m_reg_A = m_reg_se = do_dec_shift_r(0 , tmp_ar); - set_ar1(tmp_ar); - BIT_CLR(m_flags , HPHYBRID_DC_BIT); + case 0x7158: + // CBU + m_icount -= 6; + BIT_SET(m_flags , HPHYBRID_CB_BIT); break; - case 0x7b40: - // MRY - set_ar2(do_mrxy(get_ar2())); - m_icount -= 33; - break; + default: + // Unrecognized instruction + return false; + } + } + next_pc = m_reg_P + 1; + return true; +} - case 0x7b61: - // MLY - tmp_ar2 = get_ar2(); - m_icount -= 32; - m_reg_A = m_reg_se = do_dec_shift_l(m_reg_A & 0xf , tmp_ar2); - set_ar2(tmp_ar2); - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - break; +bool hp_5061_3011_cpu_device::read_non_common_reg(uint16_t addr , uint16_t& v) +{ + switch (addr) { + case HP_REG_DMAPA_ADDR: + v = m_dmapa & HP_REG_PA_MASK; + if (BIT(m_flags , HPHYBRID_CB_BIT)) { + BIT_SET(v , 15); + } + if (BIT(m_flags , HPHYBRID_DB_BIT)) { + BIT_SET(v , 14); + } + return true; - case 0x7b8f: - // MPY - do_mpy(); - break; + default: + return false; + } +} - case 0x7026: - // CIM - // Undocumented instruction, see beginning of this file - // Probably "Clear Interrupt Mode" - // No idea at all about exec. time: make it 9 cycles - m_icount -= 9; - BIT_CLR(m_flags, HPHYBRID_IM_BIT); - //logerror("hp-5061-3001: CIM, P = %06x flags = %05x\n" , m_genpc , m_flags); - break; +bool hp_5061_3011_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +{ + return false; +} - case 0x7027: - // SIM - // Undocumented instruction, see beginning of this file - // Probably "Set Interrupt Mode" - // No idea at all about exec. time: make it 9 cycles - m_icount -= 9; - BIT_SET(m_flags, HPHYBRID_IM_BIT); - //logerror("hp-5061-3001: SIM, P = %06x flags = %05x\n" , m_genpc , m_flags); - break; +void hp_5061_3011_cpu_device::handle_dma() +{ + // Patent hints at the fact that terminal count is detected by bit 15 of dmac being 1 after decrementing + bool tc = BIT(--m_dmac , 15) != 0; + uint16_t tmp; - default: - if ((opcode & 0xfec0) == 0x74c0) { - // SDS - // SDC - m_icount -= 14; - return get_skip_addr(opcode , !BIT(m_flags , HPHYBRID_DC_BIT)); - } else { - // Unrecognized instructions: NOP - // Execution time is fictional - logerror("hp-5061-3001: unknown opcode %04x @ %06x\n" , opcode , m_genpc); - m_icount -= 6; - } - break; - } + // Timing here assumes that DMA transfers are isolated and not done in bursts + if (BIT(m_flags , HPHYBRID_DMADIR_BIT)) { + // "Outward" DMA: memory -> peripheral + tmp = RM(AEC_CASE_D , m_dmama++); + WIO(m_dmapa , tc ? 2 : 0 , tmp); + } else { + // "Inward" DMA: peripheral -> memory + tmp = RIO(m_dmapa , tc ? 2 : 0); + WM(AEC_CASE_D , m_dmama++ , tmp); + m_icount += 1; } - return m_reg_P + 1; + // Mystery solved: DMA is not automatically disabled at TC (test of 9845's graphic memory relies on this to work) } -std::unique_ptr<util::disasm_interface> hp_5061_3001_cpu_device::create_disassembler() +std::unique_ptr<util::disasm_interface> hp_5061_3011_cpu_device::create_disassembler() +{ + return std::make_unique<hp_5061_3011_disassembler>(m_relative_mode); +} + +// ******************************************************************************** +// hp_5061_3001_cpu_device +// ******************************************************************************** +hp_5061_3001_cpu_device::hp_5061_3001_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hp_5061_3011_cpu_device(mconfig, HP_5061_3001, tag, owner, clock, 22) +{ +} + +void hp_5061_3001_cpu_device::device_start() { - return std::make_unique<hp_5061_3001_disassembler>(); + hp_hybrid_cpu_device::device_start(); + emc_start(); + + state_add(HPHYBRID_R32, "R32" , m_reg_aec[ 0 ]); + state_add(HPHYBRID_R33, "R33" , m_reg_aec[ 1 ]); + state_add(HPHYBRID_R34, "R34" , m_reg_aec[ 2 ]); + state_add(HPHYBRID_R35, "R35" , m_reg_aec[ 3 ]); + state_add(HPHYBRID_R36, "R36" , m_reg_aec[ 4 ]); + state_add(HPHYBRID_R37, "R37" , m_reg_aec[ 5 ]); + save_item(NAME(m_reg_aec[ 0 ])); + save_item(NAME(m_reg_aec[ 1 ])); + save_item(NAME(m_reg_aec[ 2 ])); + save_item(NAME(m_reg_aec[ 3 ])); + save_item(NAME(m_reg_aec[ 4 ])); + save_item(NAME(m_reg_aec[ 5 ])); +} + +void hp_5061_3001_cpu_device::device_reset() +{ + // Initial state of AEC registers: + // R32 0 + // R33 5 + // R34 0 + // R35 0 + // R36 0 + // R37 0 + m_reg_aec[ 0 ] = 0; + m_reg_aec[ 1 ] = 5; + m_reg_aec[ 2 ] = 0; + m_reg_aec[ 3 ] = 0; + m_reg_aec[ 4 ] = 0; + m_reg_aec[ 5 ] = 0; + + hp_hybrid_cpu_device::device_reset(); +} + +bool hp_5061_3001_cpu_device::execute_no_bpc(uint16_t opcode , uint16_t& next_pc) +{ + // Try to execute opcode as an IOC-16 instruction first then as an EMC one + if (hp_5061_3011_cpu_device::execute_no_bpc(opcode , next_pc) || + execute_emc(opcode , next_pc)) { + return true; + } + + // AEC instructions + switch (opcode) { + case 0x7026: + // CIM + // Undocumented instruction, see beginning of this file + // Probably "Clear Interrupt Mode" + // No idea at all about exec. time: make it 9 cycles (6 are in opcode fetch) + m_icount -= 3; + BIT_CLR(m_flags, HPHYBRID_IM_BIT); + break; + + case 0x7027: + // SIM + // Undocumented instruction, see beginning of this file + // Probably "Set Interrupt Mode" + // No idea at all about exec. time: make it 9 cycles (6 are in opcode fetch) + m_icount -= 3; + BIT_SET(m_flags, HPHYBRID_IM_BIT); + break; + + default: + return false; + } + + next_pc = m_reg_P + 1; + return true; } uint32_t hp_5061_3001_cpu_device::add_mae(aec_cases_t aec_case , uint16_t addr) @@ -1616,66 +1846,27 @@ uint32_t hp_5061_3001_cpu_device::add_mae(aec_cases_t aec_case , uint16_t addr) return (uint32_t)addr | ((uint32_t)aec_reg << 16); } -uint16_t hp_5061_3001_cpu_device::read_non_common_reg(uint16_t addr) +bool hp_5061_3001_cpu_device::read_non_common_reg(uint16_t addr , uint16_t& v) { switch (addr) { - case HP_REG_AR2_ADDR: - case HP_REG_AR2_ADDR + 1: - case HP_REG_AR2_ADDR + 2: - case HP_REG_AR2_ADDR + 3: - return m_reg_ar2[ addr - HP_REG_AR2_ADDR ]; - - case HP_REG_SE_ADDR: - return m_reg_se; - - case HP_REG_R25_ADDR: - return m_reg_r25; - - case HP_REG_R26_ADDR: - return m_reg_r26; - - case HP_REG_R27_ADDR: - return m_reg_r27; - case HP_REG_R32_ADDR: case HP_REG_R33_ADDR: case HP_REG_R34_ADDR: case HP_REG_R35_ADDR: case HP_REG_R36_ADDR: case HP_REG_R37_ADDR: - return m_reg_aec[ addr - HP_REG_R32_ADDR ]; + v = m_reg_aec[ addr - HP_REG_R32_ADDR ]; + return true; default: - return 0; + return hp_5061_3011_cpu_device::read_non_common_reg(addr , v) || + read_emc_reg(addr , v); } } -void hp_5061_3001_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +bool hp_5061_3001_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) { switch (addr) { - case HP_REG_AR2_ADDR: - case HP_REG_AR2_ADDR + 1: - case HP_REG_AR2_ADDR + 2: - case HP_REG_AR2_ADDR + 3: - m_reg_ar2[ addr - HP_REG_AR2_ADDR ] = v; - break; - - case HP_REG_SE_ADDR: - m_reg_se = v & HP_REG_SE_MASK; - break; - - case HP_REG_R25_ADDR: - m_reg_r25 = v; - break; - - case HP_REG_R26_ADDR: - m_reg_r26 = v; - break; - - case HP_REG_R27_ADDR: - m_reg_r27 = v; - break; - case HP_REG_R32_ADDR: case HP_REG_R33_ADDR: case HP_REG_R34_ADDR: @@ -1683,46 +1874,183 @@ void hp_5061_3001_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) case HP_REG_R36_ADDR: case HP_REG_R37_ADDR: m_reg_aec[ addr - HP_REG_R32_ADDR ] = v; - break; + return true; default: - break; + return hp_5061_3011_cpu_device::write_non_common_reg(addr , v) || + write_emc_reg(addr , v); } } +std::unique_ptr<util::disasm_interface> hp_5061_3001_cpu_device::create_disassembler() +{ + return std::make_unique<hp_5061_3001_disassembler>(m_relative_mode); +} + void hp_5061_3001_cpu_device::enter_isr(void) { // Set interrupt mode when entering an ISR BIT_SET(m_flags, HPHYBRID_IM_BIT); } -hp_5061_3011_cpu_device::hp_5061_3011_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : hp_hybrid_cpu_device(mconfig, HP_5061_3011, tag, owner, clock, 16) +// ******************************************************************************** +// hp_09825_67907_cpu_device +// ******************************************************************************** +hp_09825_67907_cpu_device::hp_09825_67907_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hp_hybrid_cpu_device(mconfig , HP_09825_67907 , tag , owner , clock , 15) { } -uint16_t hp_5061_3011_cpu_device::execute_no_bpc_ioc(uint16_t opcode) +void hp_09825_67907_cpu_device::device_start() { - // Unrecognized instructions: NOP - // Execution time is fictional - m_icount -= 6; + hp_hybrid_cpu_device::device_start(); + emc_start(); +} - return m_reg_P + 1; +bool hp_09825_67907_cpu_device::execute_no_bpc(uint16_t opcode , uint16_t& next_pc) +{ + // 15-bit IOC instructions + if ((opcode & 0xf760) == 0x7160) { + // Place/withdraw + uint16_t tmp; + uint16_t reg_addr = opcode & 7; + uint16_t *ptr_reg; + + if (BIT(opcode , 3)) { + ptr_reg = &m_reg_D; + } else { + ptr_reg = &m_reg_C; + } + + if (BIT(opcode , 4)) { + // Withdraw + tmp = RM(AEC_CASE_C , *ptr_reg); + if (BIT(opcode , 11)) { + // Byte + if (BIT(*ptr_reg , 15)) { + tmp >>= 8; + } else { + tmp &= 0xff; + } + } + WM(reg_addr , tmp); + + // Post inc/dec + inc_dec_cd(*ptr_reg , !BIT(opcode , 7) , BIT(opcode , 11)); + } else { + // Place + + // Pre inc/dec + inc_dec_cd(*ptr_reg , !BIT(opcode , 7) , BIT(opcode , 11)); + + tmp = RM(reg_addr); + uint16_t tmp_addr = *ptr_reg & ADDR_MASK_15BIT; + if (BIT(opcode , 11)) { + // Byte + tmp = BIT(*ptr_reg , 15) ? (tmp << 8) : (tmp & 0xff); + if (tmp_addr <= HP_REG_LAST_ADDR) { + // Single bytes can be written to registers. + // The addressed register gets the written byte in the proper position + // and a 0 in the other byte because access to registers is always done in + // 16 bits units. + WM(tmp_addr , tmp); + } else { + uint16_t mask = BIT(*ptr_reg , 15) ? 0xff00 : 0x00ff; + m_program->write_word(tmp_addr , tmp , mask); + m_icount -= m_w_cycles; + } + } else { + // Word + WM(AEC_CASE_C , tmp_addr , tmp); + } + } + m_icount -= 6; + next_pc = m_reg_P + 1; + return true; + } else { + return execute_emc(opcode , next_pc); + } } -uint32_t hp_5061_3011_cpu_device::add_mae(aec_cases_t aec_case , uint16_t addr) +void hp_09825_67907_cpu_device::inc_dec_cd(uint16_t& cd_reg , bool increment , bool byte) { - // No MAE on 3011 - return addr; + bool propagate; + if (byte) { + // Byte + // Toggle bit 15 + cd_reg ^= REG_MSB_MASK; + // When incrementing, propagate to 15 LSBs when bit 15 goes 0->1 + propagate = (cd_reg & REG_MSB_MASK) != 0; + if (!increment) { + // When decrementing, propagate when bit 15 goes 1->0 + propagate = !propagate; + } + } else { + // Word + propagate = true; + } + if (propagate) { + if (increment) { + cd_reg = (cd_reg & ~ADDR_MASK_15BIT) | ((cd_reg + 1) & ADDR_MASK_15BIT); + } else { + cd_reg = (cd_reg & ~ADDR_MASK_15BIT) | ((cd_reg - 1) & ADDR_MASK_15BIT); + } + } +} + +bool hp_09825_67907_cpu_device::read_non_common_reg(uint16_t addr , uint16_t& v) +{ + switch (addr) { + case HP_REG_DMAPA_ADDR: + v = m_dmapa; + return true; + + default: + return read_emc_reg(addr , v); + } } -uint16_t hp_5061_3011_cpu_device::read_non_common_reg(uint16_t addr) +bool hp_09825_67907_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) { - // Non-existing registers are returned as 0 - return 0; + return write_emc_reg(addr , v); +} + +uint16_t hp_09825_67907_cpu_device::get_indirect_target(uint32_t addr) +{ + uint16_t tmp; + bool ind; + + // Multi-level indirect addressing + // TODO: It wouldn't hurt to have some limit on iterations + do { + tmp = RM(addr); + ind = BIT(tmp , 15); + addr = tmp & ADDR_MASK_15BIT; + } while (ind); + + return tmp; +} + +void hp_09825_67907_cpu_device::handle_dma() +{ + bool tc = BIT(--m_dmac , 15) != 0; + uint16_t tmp; + + // Timing here assumes that DMA transfers are isolated and not done in bursts + if (BIT(m_dmama , 15)) { + // "Outward" DMA: memory -> peripheral + tmp = RM(AEC_CASE_D , m_dmama); + WIO(m_dmapa , tc ? 2 : 0 , tmp); + } else { + // "Inward" DMA: peripheral -> memory + tmp = RIO(m_dmapa , tc ? 2 : 0); + WM(AEC_CASE_D , m_dmama , tmp); + m_icount += 1; + } + m_dmama = (m_dmama & ~ADDR_MASK_15BIT) | ((m_dmama + 1) & ADDR_MASK_15BIT); } -void hp_5061_3011_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +std::unique_ptr<util::disasm_interface> hp_09825_67907_cpu_device::create_disassembler() { - // Non-existing registers are silently discarded + return std::make_unique<hp_09825_67907_disassembler>(m_relative_mode); } |