diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index ccc964417d6..0616e9af48b 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -2246,6 +2246,7 @@ files { MAME_DIR .. "src/mame/drivers/hp2620.cpp", MAME_DIR .. "src/mame/drivers/hp700.cpp", MAME_DIR .. "src/mame/drivers/hp2640.cpp", + MAME_DIR .. "src/mame/drivers/hp9825.cpp", } createMESSProjects(_target, _subtarget, "hec2hrp") 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 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 void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK(n); + } + + template void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK(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 +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 +}; -#define HPHYBRID_IV_MASK 0xfff0 // IV mask - -#define HP_REG_SE_MASK 0x000f +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(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; + m_icount -= 2; + uint16_t fetch_addr = opcode & 0x1f; + if (BIT(opcode , 15)) { + 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) - uint16_t reg = RM(opcode & 0x1f); - if (BIT(opcode , 15)) { - m_icount -= 6; - return RM(add_mae(AEC_CASE_A , reg)); - } else { - return reg; - } + 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); - 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; + 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); + } + 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 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(); + // 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 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++; +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) +{ +} + +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(HPHYBRID_DB_BIT); + } else { + ptr_reg = &m_reg_C; + b_mask = BIT_MASK(HPHYBRID_CB_BIT); } - break; - 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++; - } - 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; - - 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--; - } - m_reg_B = n; - if (n < 12) { - BIT_CLR(m_flags , HPHYBRID_DC_BIT); - set_ar2(tmp_ar2); + 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(16); + } + tmp = RM(AEC_CASE_C , (uint16_t)(tmp_addr >> 1)); + if (BIT(tmp_addr , 0)) { + tmp &= 0xff; + } else { + tmp >>= 8; + } } 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; + // Word + tmp = RM(AEC_CASE_C , *ptr_reg); } - break; + WM(reg_addr , tmp); - case 0x73c0: - // CDC - m_icount -= 11; - BIT_CLR(m_flags , HPHYBRID_DC_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); + 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; + } } - set_ar2(tmp_ar2); - 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++; + } 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; + } } - set_ar2(tmp_ar2); + 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(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 0x7b00: - // MRX - set_ar1(do_mrxy(get_ar1())); - m_icount -= 62; + case 0x7108: + // SDI + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_DMADIR_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 0x7140: + // DBL + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_DB_BIT); break; - case 0x7b40: - // MRY - set_ar2(do_mrxy(get_ar2())); - m_icount -= 33; + case 0x7148: + // CBL + m_icount -= 6; + BIT_CLR(m_flags , HPHYBRID_CB_BIT); break; - 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); + case 0x7150: + // DBU + m_icount -= 6; + BIT_SET(m_flags , HPHYBRID_DB_BIT); break; - case 0x7b8f: - // MPY - do_mpy(); - break; - - 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; - - 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); + case 0x7158: + // CBU + m_icount -= 6; + BIT_SET(m_flags , HPHYBRID_CB_BIT); break; 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; + // Unrecognized instruction + return false; } } - - return m_reg_P + 1; + next_pc = m_reg_P + 1; + return true; } -std::unique_ptr hp_5061_3001_cpu_device::create_disassembler() +bool hp_5061_3011_cpu_device::read_non_common_reg(uint16_t addr , uint16_t& v) { - return std::make_unique(); + 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; + + default: + return false; + } +} + +bool hp_5061_3011_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +{ + return false; +} + +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; + + // 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; + } + + // Mystery solved: DMA is not automatically disabled at TC (test of 9845's graphic memory relies on this to work) +} + +std::unique_ptr hp_5061_3011_cpu_device::create_disassembler() +{ + return std::make_unique(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() +{ + 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 hp_5061_3001_cpu_device::create_disassembler() +{ + return std::make_unique(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; - - return m_reg_P + 1; + hp_hybrid_cpu_device::device_start(); + emc_start(); } -uint32_t hp_5061_3011_cpu_device::add_mae(aec_cases_t aec_case , uint16_t addr) +bool hp_09825_67907_cpu_device::execute_no_bpc(uint16_t opcode , uint16_t& next_pc) { - // No MAE on 3011 - return addr; + // 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); + } } -uint16_t hp_5061_3011_cpu_device::read_non_common_reg(uint16_t addr) +void hp_09825_67907_cpu_device::inc_dec_cd(uint16_t& cd_reg , bool increment , bool byte) { - // Non-existing registers are returned as 0 - return 0; + 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); + } + } } -void hp_5061_3011_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +bool hp_09825_67907_cpu_device::read_non_common_reg(uint16_t addr , uint16_t& v) { - // Non-existing registers are silently discarded + switch (addr) { + case HP_REG_DMAPA_ADDR: + v = m_dmapa; + return true; + + default: + return read_emc_reg(addr , v); + } +} + +bool hp_09825_67907_cpu_device::write_non_common_reg(uint16_t addr , uint16_t v) +{ + 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); +} + +std::unique_ptr hp_09825_67907_cpu_device::create_disassembler() +{ + return std::make_unique(m_relative_mode); } diff --git a/src/devices/cpu/hphybrid/hphybrid.h b/src/devices/cpu/hphybrid/hphybrid.h index 4b03035950a..5d29f77b515 100644 --- a/src/devices/cpu/hphybrid/hphybrid.h +++ b/src/devices/cpu/hphybrid/hphybrid.h @@ -8,15 +8,25 @@ // The HP hybrid processor series is composed of a few different models with different // capabilities. The series was derived from HP's own 2116 processor by translating a // discrete implementation of the 1960s into a multi-chip module (hence the "hybrid" name). -// This emulator currently supports both the 5061-3001 & the 5061-3011 versions. +// This emulator currently supports the 5061-3001, 5061-3011 and 09825-67907 versions. +// +// | *CPU* | *Addr bits* | *Multi-indirect* | *BPC* | *IOC* | *EMC* | *AEC* | *Used in* | +// |-------------+-------------+------------------+-------+-------+-------+-------+-----------| +// | 09825-67907 | 15 | Y | Y | Y | Y | N | HP 9825 | +// | 5061-3001 | 16 (+ext) | N | Y | Y | Y | Y | HP 9845 | +// | 5061-3011 | 16 | N | Y | Y | N | N | HP 64000 | // // For this emulator I mainly relied on these sources: +// - "The how they do dat manual": this manual has way more than you will ever want to know +// about the hybrid processors. It often gets to the single transistor detail level.. +// - "Reference manual for the CPD N-MOS II Processor": this is an internal HP manual that +// was targeted at the hw/sw user of the processors. // - http://www.hp9845.net/ website -// - HP manual "Assembly development ROM manual for the HP9845": this is the most precious -// and "enabling" resource of all +// - HP manual "Assembly development ROM manual for the HP9845" +// - US Patent 4,075,679 describing the HP9825 system // - US Patent 4,180,854 describing the HP9845 system // - Study of disassembly of firmware of HP64000 & HP9845 systems -// - hp9800e emulator for inspiration on implementing EMC instructions +// - hp9800e emulator (now go9800) for inspiration on implementing EMC instructions // - A lot of "educated" guessing #ifndef MAME_CPU_HPHYBRID_HPHYBRID_H @@ -37,15 +47,7 @@ #define HP_IOADDR_IC_SHIFT 0 // Compose an I/O address from PA & IC -#define HP_MAKE_IOADDR(pa , ic) (((pa) << HP_IOADDR_PA_SHIFT) | ((ic) << HP_IOADDR_IC_SHIFT)) - -// Set boot mode of 5061-3001: either normal (false) or as in HP9845 system (true) -#define MCFG_HPHYBRID_SET_9845_BOOT(_mode) \ - downcast(*device).set_boot_mode(_mode); - -// PA changed callback -#define MCFG_HPHYBRID_PA_CHANGED(_devcb) \ - downcast(*device).set_pa_changed_func(DEVCB_##_devcb); +inline constexpr unsigned HP_MAKE_IOADDR(unsigned pa , unsigned ic) { return ((pa << HP_IOADDR_PA_SHIFT) | (ic << HP_IOADDR_IC_SHIFT)); } class hp_hybrid_cpu_device : public cpu_device { @@ -57,7 +59,10 @@ public: uint8_t pa_r() const; - template devcb_base &set_pa_changed_func(Object &&cb) { return m_pa_changed_func.set_callback(std::forward(cb)); } + auto pa_changed_cb() { return m_pa_changed_func.bind(); } + + void set_relative_mode(bool rela) { m_relative_mode = rela; } + void set_rw_cycles(unsigned read_cycles , unsigned write_cycles) { m_r_cycles = read_cycles; m_w_cycles = write_cycles; } protected: hp_hybrid_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t addrwidth); @@ -67,16 +72,22 @@ protected: virtual void device_reset() override; // device_execute_interface overrides - virtual uint32_t execute_min_cycles() const override { return 6; } + virtual uint32_t execute_min_cycles() const override { return m_r_cycles; } virtual uint32_t execute_input_lines() const override { return 2; } virtual uint32_t execute_default_irq_vector(int inputnum) const override { return 0xff; } virtual void execute_run() override; virtual void execute_set_input(int inputnum, int state) override; uint16_t execute_one(uint16_t opcode); - uint16_t execute_one_sub(uint16_t opcode); - // Execute an instruction that doesn't belong to either BPC or IOC - virtual uint16_t execute_no_bpc_ioc(uint16_t opcode) = 0; + bool execute_one_bpc(uint16_t opcode , uint16_t& next_pc); + // Execute an instruction that doesn't belong to BPC + virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) = 0; + + // Add EMC state + void emc_start(); + + // Execute EMC instructions + bool execute_emc(uint16_t opcode , uint16_t& next_pc); // device_memory_interface overrides virtual space_config_vector memory_space_config() const override; @@ -84,9 +95,6 @@ protected: // device_state_interface overrides virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; - // device_disasm_interface overrides - virtual std::unique_ptr create_disassembler() override; - // Different cases of memory access // See patent @ pg 361 typedef enum { @@ -97,25 +105,42 @@ protected: } aec_cases_t; // do memory address extension - virtual uint32_t add_mae(aec_cases_t aec_case , uint16_t addr) = 0; + virtual uint32_t add_mae(aec_cases_t aec_case , uint16_t addr); - uint16_t remove_mae(uint32_t addr); + static uint16_t remove_mae(uint32_t addr); uint16_t RM(aec_cases_t aec_case , uint16_t addr); uint16_t RM(uint32_t addr); - virtual uint16_t read_non_common_reg(uint16_t addr) = 0; + virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) = 0; + bool read_emc_reg(uint16_t addr , uint16_t& v); void WM(aec_cases_t aec_case , uint16_t addr , uint16_t v); void WM(uint32_t addr , uint16_t v); - virtual void write_non_common_reg(uint16_t addr , uint16_t v) = 0; + virtual bool write_non_common_reg(uint16_t addr , uint16_t v) = 0; + bool write_emc_reg(uint16_t addr , uint16_t v); + + uint16_t RIO(uint8_t pa , uint8_t ic); + void WIO(uint8_t pa , uint8_t ic , uint16_t v); uint16_t fetch(); + virtual uint16_t get_indirect_target(uint32_t addr); + virtual void enter_isr(); + virtual void handle_dma() = 0; uint16_t get_skip_addr(uint16_t opcode , bool condition) const; + void update_pa(); + devcb_write8 m_pa_changed_func; + uint8_t m_last_pa; int m_icount; + uint32_t m_addr_mask; + uint16_t m_addr_mask_low16; + bool m_relative_mode; + unsigned m_r_cycles; + unsigned m_w_cycles; + bool m_boot_mode; bool m_forced_bsc_25; // State of processor @@ -134,39 +159,26 @@ protected: uint16_t m_dmac; // DMA counter uint16_t m_reg_I; // Instruction register uint32_t m_genpc; // Full PC + // EMC registers + uint16_t m_reg_ar2[ 4 ]; // AR2 register + uint16_t m_reg_se; // SE register (4 bits) + uint16_t m_reg_r25; // R25 register + uint16_t m_reg_r26; // R26 register + uint16_t m_reg_r27; // R27 register + + address_space *m_program; private: address_space_config m_program_config; address_space_config m_io_config; - address_space *m_program; memory_access_cache<1, -1, ENDIANNESS_BIG> *m_cache; address_space *m_io; uint32_t get_ea(uint16_t opcode); void do_add(uint16_t& addend1 , uint16_t addend2); - uint16_t get_skip_addr_sc(uint16_t opcode , uint16_t& v , unsigned n); - uint16_t get_skip_addr_sc(uint16_t opcode , uint32_t& v , unsigned n); - void do_pw(uint16_t opcode); + template uint16_t get_skip_addr_sc(uint16_t opcode , T& v , unsigned n); void check_for_interrupts(); - virtual void enter_isr(); - void handle_dma(); - - uint16_t RIO(uint8_t pa , uint8_t ic); - void WIO(uint8_t pa , uint8_t ic , uint16_t v); -}; - -class hp_5061_3001_cpu_device : public hp_hybrid_cpu_device -{ -public: - hp_5061_3001_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - void set_boot_mode(bool mode) { m_boot_mode = mode; } - -protected: - virtual void device_start() override; - virtual void device_reset() override; - virtual uint32_t execute_max_cycles() const override { return 237; } // FMP 15 static uint8_t do_dec_shift_r(uint8_t d1 , uint64_t& mantissa); static uint8_t do_dec_shift_l(uint8_t d12 , uint64_t& mantissa); @@ -177,25 +189,6 @@ protected: uint64_t do_mrxy(uint64_t ar); bool do_dec_add(bool carry_in , uint64_t& a , uint64_t b); void do_mpy(); - - virtual uint16_t execute_no_bpc_ioc(uint16_t opcode) override; - virtual std::unique_ptr create_disassembler() override; - virtual uint32_t add_mae(aec_cases_t aec_case, uint16_t addr) override; - virtual uint16_t read_non_common_reg(uint16_t addr) override; - virtual void write_non_common_reg(uint16_t addr , uint16_t v) override; - -private: - bool m_boot_mode; - - // Additional state of processor - uint16_t m_reg_ar2[ 4 ]; // AR2 register - uint16_t m_reg_se; // SE register (4 bits) - uint16_t m_reg_r25; // R25 register - uint16_t m_reg_r26; // R26 register - uint16_t m_reg_r27; // R27 register - uint16_t m_reg_aec[ 37 - 32 + 1 ]; // AEC registers R32-R37 - - virtual void enter_isr() override; }; class hp_5061_3011_cpu_device : public hp_hybrid_cpu_device @@ -204,15 +197,74 @@ public: hp_5061_3011_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); protected: + hp_5061_3011_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t addrwidth); + // TODO: fix virtual uint32_t execute_max_cycles() const override { return 25; } - virtual uint16_t execute_no_bpc_ioc(uint16_t opcode) override; - virtual uint32_t add_mae(aec_cases_t aec_case , uint16_t addr) override; - virtual uint16_t read_non_common_reg(uint16_t addr) override; - virtual void write_non_common_reg(uint16_t addr , uint16_t v) override; + virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override; + virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override; + virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override; + virtual void handle_dma() override; + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; +}; + +class hp_5061_3001_cpu_device : public hp_5061_3011_cpu_device +{ +public: + hp_5061_3001_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // Set boot mode of 5061-3001: either normal (false) or as in HP9845 system (true) + void set_9845_boot_mode(bool mode) { m_boot_mode = mode; } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + // TODO: fix + virtual uint32_t execute_max_cycles() const override { return 237; } // FMP 15 + + virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override; + virtual uint32_t add_mae(aec_cases_t aec_case, uint16_t addr) override; + virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override; + virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override; + virtual void enter_isr() override; + + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; +private: + // Additional state of processor + uint16_t m_reg_aec[ 37 - 32 + 1 ]; // AEC registers R32-R37 +}; + +class hp_09825_67907_cpu_device : public hp_hybrid_cpu_device +{ +public: + hp_09825_67907_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + // device-level overrides + virtual void device_start() override; + + // device_execute_interface overrides + // TODO: fix + virtual uint32_t execute_max_cycles() const override { return 237; } // FMP 15 + + virtual bool execute_no_bpc(uint16_t opcode , uint16_t& next_pc) override; + + // device_disasm_interface overrides + virtual std::unique_ptr create_disassembler() override; + + virtual bool read_non_common_reg(uint16_t addr , uint16_t& v) override; + virtual bool write_non_common_reg(uint16_t addr , uint16_t v) override; + virtual uint16_t get_indirect_target(uint32_t addr) override; + virtual void handle_dma() override; + +private: + void inc_dec_cd(uint16_t& cd_reg , bool increment , bool byte); }; DECLARE_DEVICE_TYPE(HP_5061_3001, hp_5061_3001_cpu_device) DECLARE_DEVICE_TYPE(HP_5061_3011, hp_5061_3011_cpu_device) +DECLARE_DEVICE_TYPE(HP_09825_67907 , hp_09825_67907_cpu_device) #endif // MAME_CPU_HPHYBRID_HPHYBRID_H diff --git a/src/devices/cpu/hphybrid/hphybrid_dasm.cpp b/src/devices/cpu/hphybrid/hphybrid_dasm.cpp index 74d833803c0..fc7b63559e0 100644 --- a/src/devices/cpu/hphybrid/hphybrid_dasm.cpp +++ b/src/devices/cpu/hphybrid/hphybrid_dasm.cpp @@ -8,86 +8,15 @@ #include "hphybrid_dasm.h" #include "hphybrid_defs.h" -void hp_hybrid_disassembler::addr_2_str(std::ostream &stream, uint16_t addr , bool indirect , bool is_3001) +void hp_hybrid_disassembler::addr_2_str(std::ostream &stream, uint16_t addr , bool indirect) { - util::stream_format(stream, "$%04x" , addr); - - if (is_3001) { - switch (addr) { - case HP_REG_AR1_ADDR: - stream << "(Ar1)"; - break; - - case HP_REG_AR1_ADDR + 1: - stream << "(Ar1_2)"; - break; - - case HP_REG_AR1_ADDR + 2: - stream << "(Ar1_3)"; - break; - - case HP_REG_AR1_ADDR + 3: - stream << "(Ar1_4)"; - break; - - case HP_REG_AR2_ADDR: - stream << "(Ar2)"; - break; - - case HP_REG_AR2_ADDR + 1: - stream << "(Ar2_2)"; - break; - - case HP_REG_AR2_ADDR + 2: - stream << "(Ar2_3)"; - break; - - case HP_REG_AR2_ADDR + 3: - stream << "(Ar2_4)"; - break; - - case HP_REG_SE_ADDR: - stream << "(SE)"; - break; - - case HP_REG_R25_ADDR: - stream << "(R25)"; - break; - - case HP_REG_R26_ADDR: - stream << "(R26)"; - break; - - case HP_REG_R27_ADDR: - stream << "(R27)"; - break; - - case HP_REG_R32_ADDR: - stream << "(R32)"; - break; - - case HP_REG_R33_ADDR: - stream << "(R33)"; - break; - - case HP_REG_R34_ADDR: - stream << "(R34)"; - break; - - case HP_REG_R35_ADDR: - stream << "(R35)"; - break; - - case HP_REG_R36_ADDR: - stream << "(R36)"; - break; - - case HP_REG_R37_ADDR: - stream << "(R37)"; - break; - } + if (m_flags & HP_HYBRID_DASM_HAS_15BITS) { + addr &= 0x7fff; } + util::stream_format(stream, "$%04x" , addr); + + // Common registers switch (addr) { case HP_REG_A_ADDR: stream << "(A)"; @@ -148,6 +77,116 @@ void hp_hybrid_disassembler::addr_2_str(std::ostream &stream, uint16_t addr , bo case HP_REG_D_ADDR: stream << "(D)"; break; + + default: + // EMC registers + if (m_flags & HP_HYBRID_DASM_HAS_EMC) { + if (m_flags & HP_HYBRID_DASM_HAS_15BITS) { + switch (addr) { + case HP_REG_AR1_ADDR & 0x7fff: + stream << "(Ar1)"; + break; + + case (HP_REG_AR1_ADDR & 0x7fff) + 1: + stream << "(Ar1_2)"; + break; + + case (HP_REG_AR1_ADDR & 0x7fff) + 2: + stream << "(Ar1_3)"; + break; + + case (HP_REG_AR1_ADDR & 0x7fff) + 3: + stream << "(Ar1_4)"; + break; + } + } else { + switch (addr) { + case HP_REG_AR1_ADDR: + stream << "(Ar1)"; + break; + + case HP_REG_AR1_ADDR + 1: + stream << "(Ar1_2)"; + break; + + case HP_REG_AR1_ADDR + 2: + stream << "(Ar1_3)"; + break; + + case HP_REG_AR1_ADDR + 3: + stream << "(Ar1_4)"; + break; + } + } + switch (addr) { + case HP_REG_AR2_ADDR: + stream << "(Ar2)"; + break; + + case HP_REG_AR2_ADDR + 1: + stream << "(Ar2_2)"; + break; + + case HP_REG_AR2_ADDR + 2: + stream << "(Ar2_3)"; + break; + + case HP_REG_AR2_ADDR + 3: + stream << "(Ar2_4)"; + break; + + case HP_REG_SE_ADDR: + stream << "(SE)"; + break; + + case HP_REG_R25_ADDR: + stream << "(R25)"; + break; + + case HP_REG_R26_ADDR: + stream << "(R26)"; + break; + + case HP_REG_R27_ADDR: + stream << "(R27)"; + break; + + default: + break; + } + } + // AEC registers + if (m_flags & HP_HYBRID_DASM_HAS_AEC) { + switch (addr) { + case HP_REG_R32_ADDR: + stream << "(R32)"; + break; + + case HP_REG_R33_ADDR: + stream << "(R33)"; + break; + + case HP_REG_R34_ADDR: + stream << "(R34)"; + break; + + case HP_REG_R35_ADDR: + stream << "(R35)"; + break; + + case HP_REG_R36_ADDR: + stream << "(R36)"; + break; + + case HP_REG_R37_ADDR: + stream << "(R37)"; + break; + + default: + break; + } + } + break; } if (indirect) { @@ -155,18 +194,23 @@ void hp_hybrid_disassembler::addr_2_str(std::ostream &stream, uint16_t addr , bo } } -void hp_hybrid_disassembler::param_none(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_none(std::ostream &stream, offs_t pc , uint16_t opcode) { } -void hp_hybrid_disassembler::param_loc(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_loc(std::ostream &stream, offs_t pc , uint16_t opcode) { uint16_t base; uint16_t off; if (opcode & 0x0400) { - // Current page - base = pc; + if (m_flags & HP_HYBRID_DASM_ABS_MODE) { + // Current page, absolute mode + base = (pc & 0xfc00) | 0x200; + } else { + // Current page, relative mode + base = pc; + } } else { // Base page base = 0; @@ -177,26 +221,26 @@ void hp_hybrid_disassembler::param_loc(std::ostream &stream, offs_t pc , uint16_ off -= 0x400; } - addr_2_str(stream, base + off , (opcode & 0x8000) != 0 , is_3001); + addr_2_str(stream, base + off , (opcode & 0x8000) != 0); } -void hp_hybrid_disassembler::param_addr32(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_addr32(std::ostream &stream, offs_t pc , uint16_t opcode) { - addr_2_str(stream, opcode & 0x1f , (opcode & 0x8000) != 0 , is_3001); + addr_2_str(stream, opcode & 0x1f , (opcode & 0x8000) != 0); } -void hp_hybrid_disassembler::param_skip(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_skip(std::ostream &stream, offs_t pc , uint16_t opcode) { uint16_t off = opcode & 0x3f; if (off & 0x20) { off -= 0x40; } - addr_2_str(stream , pc + off , false , is_3001); + addr_2_str(stream , pc + off , false); } -void hp_hybrid_disassembler::param_skip_sc(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_skip_sc(std::ostream &stream, offs_t pc , uint16_t opcode) { - param_skip(stream, pc, opcode , is_3001); + param_skip(stream, pc, opcode); if (opcode & 0x80) { if (opcode & 0x40) { @@ -207,7 +251,7 @@ void hp_hybrid_disassembler::param_skip_sc(std::ostream &stream, offs_t pc , uin } } -void hp_hybrid_disassembler::param_ret(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_ret(std::ostream &stream, offs_t pc , uint16_t opcode) { int off = opcode & 0x3f; @@ -221,14 +265,14 @@ void hp_hybrid_disassembler::param_ret(std::ostream &stream, offs_t pc , uint16_ } } -void hp_hybrid_disassembler::param_n16(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_n16(std::ostream &stream, offs_t pc , uint16_t opcode) { util::stream_format(stream , "%u" , (opcode & 0xf) + 1); } -void hp_hybrid_disassembler::param_reg_id(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001) +void hp_hybrid_disassembler::param_reg_id(std::ostream &stream, offs_t pc , uint16_t opcode) { - addr_2_str(stream, opcode & 7, false , is_3001); + addr_2_str(stream, opcode & 7, false); if (opcode & 0x80) { stream << ",D"; @@ -237,123 +281,131 @@ void hp_hybrid_disassembler::param_reg_id(std::ostream &stream, offs_t pc , uint } } -const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table[] = { - // *** BPC Instructions *** - {0xffff , 0x0000 , "NOP" , &hp_hybrid_disassembler::param_none , 0 }, - {0x7800 , 0x0000 , "LDA" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x0800 , "LDB" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x1000 , "CPA" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x1800 , "CPB" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x2000 , "ADA" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x2800 , "ADB" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x3000 , "STA" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x3800 , "STB" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x4000 , "JSM" , &hp_hybrid_disassembler::param_loc , STEP_OVER }, - {0x7800 , 0x4800 , "ISZ" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x5000 , "AND" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x5800 , "DSZ" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x6000 , "IOR" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7800 , 0x6800 , "JMP" , &hp_hybrid_disassembler::param_loc , 0 }, - {0x7fe0 , 0x7000 , "EXE" , &hp_hybrid_disassembler::param_addr32 , 0 }, - {0xffc0 , 0x7400 , "RZA" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7C00 , "RZB" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7440 , "RIA" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7C40 , "RIB" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7500 , "SZA" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7D00 , "SZB" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7540 , "SIA" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7D40 , "SIB" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7480 , "SFS" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7580 , "SFC" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7c80 , "SSS" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7d80 , "SSC" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7cc0 , "SHS" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xffc0 , 0x7dc0 , "SHC" , &hp_hybrid_disassembler::param_skip , 0 }, - {0xff00 , 0x7600 , "SLA" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0x7e00 , "SLB" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0x7700 , "RLA" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0x7f00 , "RLB" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xf400 , "SAP" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xfc00 , "SBP" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xf500 , "SAM" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xfd00 , "SBM" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xf600 , "SOC" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xf700 , "SOS" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xfe00 , "SEC" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xff00 , 0xff00 , "SES" , &hp_hybrid_disassembler::param_skip_sc , 0 }, - {0xffff , 0xf020 , "TCA" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0xf820 , "TCB" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0xf060 , "CMA" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0xf860 , "CMB" , &hp_hybrid_disassembler::param_none , 0 }, - {0xff80 , 0xf080 , "RET" , &hp_hybrid_disassembler::param_ret , STEP_OUT }, - {0xfff0 , 0xf100 , "AAR" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xfff0 , 0xf900 , "ABR" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xffff , 0xf14f , "CLA" , &hp_hybrid_disassembler::param_none , 0 }, - {0xfff0 , 0xf140 , "SAR" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xffff , 0xf94f , "CLB" , &hp_hybrid_disassembler::param_none , 0 }, - {0xfff0 , 0xf940 , "SBR" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xfff0 , 0xf180 , "SAL" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xfff0 , 0xf980 , "SBL" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xfff0 , 0xf1c0 , "RAR" , &hp_hybrid_disassembler::param_n16 , 0 }, - {0xfff0 , 0xf9c0 , "RBR" , &hp_hybrid_disassembler::param_n16 , 0 }, - // *** IOC Instructions *** - {0xffff , 0x7100 , "SDO" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7108 , "SDI" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7110 , "EIR" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7118 , "DIR" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7120 , "DMA" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7128 , "PCM" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7138 , "DDR" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7140 , "DBL" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7148 , "CBL" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7150 , "DBU" , &hp_hybrid_disassembler::param_none , 0 }, - {0xffff , 0x7158 , "CBU" , &hp_hybrid_disassembler::param_none , 0 }, - {0xff78 , 0x7160 , "PWC" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7168 , "PWD" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7960 , "PBC" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7968 , "PBD" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7170 , "WWC" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7178 , "WWD" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7970 , "WBC" , &hp_hybrid_disassembler::param_reg_id , 0 }, - {0xff78 , 0x7978 , "WBD" , &hp_hybrid_disassembler::param_reg_id , 0 }, - // *** END *** - {0 , 0 , nullptr , nullptr , 0 } +const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_common[] = { + // *** BPC Instructions *** + {0xffff , 0x0000 , "NOP" , &hp_hybrid_disassembler::param_none , 0 }, + {0x7800 , 0x0000 , "LDA" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x0800 , "LDB" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x1000 , "CPA" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x1800 , "CPB" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x2000 , "ADA" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x2800 , "ADB" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x3000 , "STA" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x3800 , "STB" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x4000 , "JSM" , &hp_hybrid_disassembler::param_loc , STEP_OVER }, + {0x7800 , 0x4800 , "ISZ" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x5000 , "AND" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x5800 , "DSZ" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x6000 , "IOR" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7800 , 0x6800 , "JMP" , &hp_hybrid_disassembler::param_loc , 0 }, + {0x7fe0 , 0x7000 , "EXE" , &hp_hybrid_disassembler::param_addr32 , 0 }, + {0xffc0 , 0x7400 , "RZA" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7C00 , "RZB" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7440 , "RIA" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7C40 , "RIB" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x74c0 , "SDS" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7500 , "SZA" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7D00 , "SZB" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7540 , "SIA" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7D40 , "SIB" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7480 , "SFS" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7580 , "SFC" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x75c0 , "SDC" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7c80 , "SSS" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7d80 , "SSC" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7cc0 , "SHS" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xffc0 , 0x7dc0 , "SHC" , &hp_hybrid_disassembler::param_skip , 0 }, + {0xff00 , 0x7600 , "SLA" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0x7e00 , "SLB" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0x7700 , "RLA" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0x7f00 , "RLB" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xf400 , "SAP" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xfc00 , "SBP" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xf500 , "SAM" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xfd00 , "SBM" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xf600 , "SOC" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xf700 , "SOS" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xfe00 , "SEC" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xff00 , 0xff00 , "SES" , &hp_hybrid_disassembler::param_skip_sc , 0 }, + {0xffff , 0xf020 , "TCA" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0xf820 , "TCB" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0xf060 , "CMA" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0xf860 , "CMB" , &hp_hybrid_disassembler::param_none , 0 }, + {0xff80 , 0xf080 , "RET" , &hp_hybrid_disassembler::param_ret , STEP_OUT }, + {0xfff0 , 0xf100 , "AAR" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xfff0 , 0xf900 , "ABR" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xffff , 0xf14f , "CLA" , &hp_hybrid_disassembler::param_none , 0 }, + {0xfff0 , 0xf140 , "SAR" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xffff , 0xf94f , "CLB" , &hp_hybrid_disassembler::param_none , 0 }, + {0xfff0 , 0xf940 , "SBR" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xfff0 , 0xf180 , "SAL" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xfff0 , 0xf980 , "SBL" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xfff0 , 0xf1c0 , "RAR" , &hp_hybrid_disassembler::param_n16 , 0 }, + {0xfff0 , 0xf9c0 , "RBR" , &hp_hybrid_disassembler::param_n16 , 0 }, + // *** IOC Instructions *** + {0xffff , 0x7110 , "EIR" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7118 , "DIR" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7120 , "DMA" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7128 , "PCM" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7138 , "DDR" , &hp_hybrid_disassembler::param_none , 0 }, + {0xff78 , 0x7160 , "PWC" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7168 , "PWD" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7960 , "PBC" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7968 , "PBD" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7170 , "WWC" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7178 , "WWD" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7970 , "WBC" , &hp_hybrid_disassembler::param_reg_id , 0 }, + {0xff78 , 0x7978 , "WBD" , &hp_hybrid_disassembler::param_reg_id , 0 }, + // *** END *** + {0 , 0 , nullptr , nullptr , 0 } +}; +const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_ioc16[] = { + // *** IOC-16 instructions *** + {0xffff , 0x7100 , "SDO" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7108 , "SDI" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7140 , "DBL" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7148 , "CBL" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7150 , "DBU" , &hp_hybrid_disassembler::param_none , 0 }, + {0xffff , 0x7158 , "CBU" , &hp_hybrid_disassembler::param_none , 0 }, + // *** END *** + {0 , 0 , nullptr , nullptr , 0 } +}; +const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_emc[] = { + // *** EMC Instructions *** + {0xffff , 0x7200 , "MWA" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7220 , "CMY" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7260 , "CMX" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7280 , "FXA" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xfff0 , 0x7300 , "XFR" , &hp_5061_3001_disassembler::param_n16 , 0 }, + {0xffff , 0x7340 , "NRM" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xfff0 , 0x7380 , "CLR" , &hp_5061_3001_disassembler::param_n16 , 0 }, + {0xffff , 0x73c0 , "CDC" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7a00 , "FMP" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7a21 , "FDV" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7b00 , "MRX" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7b21 , "DRS" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7b40 , "MRY" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7b61 , "MLY" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7b8f , "MPY" , &hp_5061_3001_disassembler::param_none , 0 }, + // *** END *** + {0 , 0 , nullptr , nullptr , 0 } +}; +const hp_hybrid_disassembler::dis_entry_t hp_hybrid_disassembler::dis_table_aec[] = { + // *** Undocumented AEC instructions of 5061-3001 *** + {0xffff , 0x7026 , "CIM" , &hp_5061_3001_disassembler::param_none , 0 }, + {0xffff , 0x7027 , "SIM" , &hp_5061_3001_disassembler::param_none , 0 }, + // *** END *** + {0 , 0 , nullptr , nullptr , 0 } }; -const hp_hybrid_disassembler::dis_entry_t hp_5061_3001_disassembler::dis_table_emc[] = { - // *** EMC Instructions *** - {0xffff , 0x7200 , "MWA" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7220 , "CMY" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7260 , "CMX" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7280 , "FXA" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xfff0 , 0x7300 , "XFR" , &hp_5061_3001_disassembler::param_n16 , 0 }, - {0xffff , 0x7340 , "NRM" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xfff0 , 0x7380 , "CLR" , &hp_5061_3001_disassembler::param_n16 , 0 }, - {0xffff , 0x73c0 , "CDC" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffc0 , 0x74c0 , "SDS" , &hp_5061_3001_disassembler::param_skip , 0 }, - {0xffc0 , 0x75c0 , "SDC" , &hp_5061_3001_disassembler::param_skip , 0 }, - {0xffff , 0x7a00 , "FMP" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7a21 , "FDV" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7b00 , "MRX" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7b21 , "DRS" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7b40 , "MRY" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7b61 , "MLY" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7b8f , "MPY" , &hp_5061_3001_disassembler::param_none , 0 }, - // *** Undocumented instructions of 5061-3001 *** - {0xffff , 0x7026 , "CIM" , &hp_5061_3001_disassembler::param_none , 0 }, - {0xffff , 0x7027 , "SIM" , &hp_5061_3001_disassembler::param_none , 0 }, - // *** END *** - {0 , 0 , nullptr , nullptr , 0 } -}; - -offs_t hp_hybrid_disassembler::disassemble_table(uint16_t opcode , offs_t pc , const dis_entry_t *table , bool is_3001 , std::ostream &stream) +offs_t hp_hybrid_disassembler::disassemble_table(uint16_t opcode , offs_t pc , const dis_entry_t *table , std::ostream &stream) { const dis_entry_t *p; for (p = table; p->m_op_mask; p++) { if ((opcode & p->m_op_mask) == p->m_opcode) { stream << p->m_mnemonic << " "; - (this->*(p->m_param_fn))(stream , pc , opcode , is_3001); + (this->*(p->m_param_fn))(stream , pc , opcode); return 1 | p->m_dasm_flags | SUPPORTED; } } @@ -366,10 +418,19 @@ offs_t hp_hybrid_disassembler::disassemble(std::ostream &stream, offs_t pc, cons uint16_t opcode = opcodes.r16(pc); offs_t res; - res = disassemble_table(opcode, pc, dis_table, false, stream); + res = disassemble_table(opcode, pc, dis_table_common, stream); - if (res == 0) - { + if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_15BITS) == 0) { + res = disassemble_table(opcode, pc, dis_table_ioc16, stream); + } + if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_EMC) != 0) { + res = disassemble_table(opcode, pc, dis_table_emc, stream); + } + if (res == 0 && (m_flags & HP_HYBRID_DASM_HAS_AEC) != 0) { + res = disassemble_table(opcode, pc, dis_table_aec, stream); + } + + if (res == 0) { // Unknown opcode stream << "???"; res = 1 | SUPPORTED; @@ -378,26 +439,28 @@ offs_t hp_hybrid_disassembler::disassemble(std::ostream &stream, offs_t pc, cons return res; } -offs_t hp_5061_3001_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +hp_hybrid_disassembler::hp_hybrid_disassembler(unsigned flags) + : m_flags(flags) { - uint16_t opcode = opcodes.r16(pc); - offs_t res; +} - res = disassemble_table(opcode, pc, dis_table_emc, true, stream); +hp_5061_3011_disassembler::hp_5061_3011_disassembler(bool relative_mode) + : hp_hybrid_disassembler(relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE) +{ +} - if (res == 0) - { - res = disassemble_table(opcode, pc, dis_table, true, stream); - } +hp_5061_3001_disassembler::hp_5061_3001_disassembler(bool relative_mode) + : hp_hybrid_disassembler(HP_HYBRID_DASM_HAS_EMC | + HP_HYBRID_DASM_HAS_AEC | + (relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE)) +{ +} - if (res == 0) - { - // Unknown opcode - stream << "???"; - res = 1 | SUPPORTED; - } - - return res; +hp_09825_67907_disassembler::hp_09825_67907_disassembler(bool relative_mode) + : hp_hybrid_disassembler(HP_HYBRID_DASM_HAS_15BITS | + HP_HYBRID_DASM_HAS_EMC | + (relative_mode ? 0 : HP_HYBRID_DASM_ABS_MODE)) +{ } u32 hp_hybrid_disassembler::opcode_alignment() const diff --git a/src/devices/cpu/hphybrid/hphybrid_dasm.h b/src/devices/cpu/hphybrid/hphybrid_dasm.h index 96994ae0632..4059a4aa90a 100644 --- a/src/devices/cpu/hphybrid/hphybrid_dasm.h +++ b/src/devices/cpu/hphybrid/hphybrid_dasm.h @@ -9,17 +9,24 @@ #pragma once +enum : unsigned { + HP_HYBRID_DASM_HAS_15BITS = 1, + HP_HYBRID_DASM_HAS_EMC = 2, + HP_HYBRID_DASM_HAS_AEC = 4, + HP_HYBRID_DASM_ABS_MODE = 8 +}; + class hp_hybrid_disassembler : public util::disasm_interface { public: - hp_hybrid_disassembler() = default; + hp_hybrid_disassembler(unsigned flags); virtual ~hp_hybrid_disassembler() = default; virtual u32 opcode_alignment() const override; virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; protected: - typedef void (hp_hybrid_disassembler::*fn_dis_param)(std::ostream &stream , offs_t pc , uint16_t opcode , bool is_3001); + typedef void (hp_hybrid_disassembler::*fn_dis_param)(std::ostream &stream , offs_t pc , uint16_t opcode); typedef struct { uint16_t m_op_mask; @@ -29,31 +36,44 @@ protected: uint32_t m_dasm_flags; } dis_entry_t; - static const dis_entry_t dis_table[]; + unsigned m_flags; + static const dis_entry_t dis_table_common[]; + static const dis_entry_t dis_table_ioc16[]; + static const dis_entry_t dis_table_emc[]; + static const dis_entry_t dis_table_aec[]; - void addr_2_str(std::ostream &stream, uint16_t addr , bool indirect , bool is_3001); - void param_none(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_loc(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_addr32(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_skip(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_skip_sc(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_ret(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_n16(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); - void param_reg_id(std::ostream &stream, offs_t pc , uint16_t opcode , bool is_3001); + void addr_2_str(std::ostream &stream, uint16_t addr , bool indirect); + void param_none(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_loc(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_addr32(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_skip(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_skip_sc(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_ret(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_n16(std::ostream &stream, offs_t pc , uint16_t opcode); + void param_reg_id(std::ostream &stream, offs_t pc , uint16_t opcode); - offs_t disassemble_table(uint16_t opcode , offs_t pc , const dis_entry_t *table , bool is_3001 , std::ostream &stream); + offs_t disassemble_table(uint16_t opcode , offs_t pc , const dis_entry_t *table , std::ostream &stream); }; class hp_5061_3001_disassembler : public hp_hybrid_disassembler { public: - hp_5061_3001_disassembler() = default; + hp_5061_3001_disassembler(bool relative_mode = true); virtual ~hp_5061_3001_disassembler() = default; +}; - virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; +class hp_5061_3011_disassembler : public hp_hybrid_disassembler +{ +public: + hp_5061_3011_disassembler(bool relative_mode = true); + virtual ~hp_5061_3011_disassembler() = default; +}; -protected: - static const dis_entry_t dis_table_emc[]; +class hp_09825_67907_disassembler : public hp_hybrid_disassembler +{ +public: + hp_09825_67907_disassembler(bool relative_mode = false); + virtual ~hp_09825_67907_disassembler() = default; }; #endif diff --git a/src/devices/cpu/hphybrid/hphybrid_defs.h b/src/devices/cpu/hphybrid/hphybrid_defs.h index 634a74b94a4..7b2c22a8661 100644 --- a/src/devices/cpu/hphybrid/hphybrid_defs.h +++ b/src/devices/cpu/hphybrid/hphybrid_defs.h @@ -2,37 +2,36 @@ #define MAME_CPU_HPHYBRID_HPHYBRID_DEFS_H // Addresses of memory mapped registers -#define HP_REG_A_ADDR 0x0000 -#define HP_REG_B_ADDR 0x0001 -#define HP_REG_P_ADDR 0x0002 -#define HP_REG_R_ADDR 0x0003 -#define HP_REG_R4_ADDR 0x0004 -#define HP_REG_R5_ADDR 0x0005 -#define HP_REG_R6_ADDR 0x0006 -#define HP_REG_R7_ADDR 0x0007 -#define HP_REG_IV_ADDR 0x0008 -#define HP_REG_PA_ADDR 0x0009 -#define HP_REG_W_ADDR 0x000A -#define HP_REG_DMAPA_ADDR 0x000B -#define HP_REG_DMAMA_ADDR 0x000C -#define HP_REG_DMAC_ADDR 0x000D -#define HP_REG_C_ADDR 0x000e -#define HP_REG_D_ADDR 0x000f -#define HP_REG_AR2_ADDR 0x0010 -#define HP_REG_SE_ADDR 0x0014 -#define HP_REG_R25_ADDR 0x0015 -#define HP_REG_R26_ADDR 0x0016 -#define HP_REG_R27_ADDR 0x0017 -#define HP_REG_R32_ADDR 0x001a -#define HP_REG_R33_ADDR 0x001b -#define HP_REG_R34_ADDR 0x001c -#define HP_REG_R35_ADDR 0x001d -#define HP_REG_R36_ADDR 0x001e -#define HP_REG_R37_ADDR 0x001f -#define HP_REG_LAST_ADDR 0x001f -#define HP_REG_AR1_ADDR 0xfff8 - -#define HP_REG_IV_MASK 0xfff0 -#define HP_REG_PA_MASK 0x000f +enum : uint16_t { + HP_REG_A_ADDR = 0x0000, + HP_REG_B_ADDR = 0x0001, + HP_REG_P_ADDR = 0x0002, + HP_REG_R_ADDR = 0x0003, + HP_REG_R4_ADDR = 0x0004, + HP_REG_R5_ADDR = 0x0005, + HP_REG_R6_ADDR = 0x0006, + HP_REG_R7_ADDR = 0x0007, + HP_REG_IV_ADDR = 0x0008, + HP_REG_PA_ADDR = 0x0009, + HP_REG_W_ADDR = 0x000A, + HP_REG_DMAPA_ADDR = 0x000B, + HP_REG_DMAMA_ADDR = 0x000C, + HP_REG_DMAC_ADDR = 0x000D, + HP_REG_C_ADDR = 0x000e, + HP_REG_D_ADDR = 0x000f, + HP_REG_AR2_ADDR = 0x0010, + HP_REG_SE_ADDR = 0x0014, + HP_REG_R25_ADDR = 0x0015, + HP_REG_R26_ADDR = 0x0016, + HP_REG_R27_ADDR = 0x0017, + HP_REG_R32_ADDR = 0x001a, + HP_REG_R33_ADDR = 0x001b, + HP_REG_R34_ADDR = 0x001c, + HP_REG_R35_ADDR = 0x001d, + HP_REG_R36_ADDR = 0x001e, + HP_REG_R37_ADDR = 0x001f, + HP_REG_LAST_ADDR = 0x001f, + HP_REG_AR1_ADDR = 0xfff8 +}; #endif // MAME_CPU_HPHYBRID_HPHYBRID_DEFS_H diff --git a/src/mame/drivers/hp64k.cpp b/src/mame/drivers/hp64k.cpp index 2034176ae86..23139325f1d 100644 --- a/src/mame/drivers/hp64k.cpp +++ b/src/mame/drivers/hp64k.cpp @@ -1375,11 +1375,12 @@ static void hp64k_floppies(device_slot_interface &device) } MACHINE_CONFIG_START(hp64k_state::hp64k) - MCFG_DEVICE_ADD("cpu" , HP_5061_3011 , 6250000) - MCFG_DEVICE_PROGRAM_MAP(cpu_mem_map) - MCFG_DEVICE_IO_MAP(cpu_io_map) - MCFG_DEVICE_IRQ_ACKNOWLEDGE_DRIVER(hp64k_state , hp64k_irq_callback) - MCFG_QUANTUM_TIME(attotime::from_hz(100)) + HP_5061_3011(config , m_cpu , 6250000); + m_cpu->set_rw_cycles(6 , 6); + m_cpu->set_relative_mode(true); + m_cpu->set_addrmap(AS_PROGRAM , &hp64k_state::cpu_mem_map); + m_cpu->set_addrmap(AS_IO , &hp64k_state::cpu_io_map); + m_cpu->set_irq_acknowledge_callback(FUNC(hp64k_state::hp64k_irq_callback)); // Actual keyboard refresh rate should be between 1 and 2 kHz MCFG_TIMER_DRIVER_ADD_PERIODIC("kb_timer", hp64k_state, hp64k_kb_scan, attotime::from_hz(100)) diff --git a/src/mame/drivers/hp9825.cpp b/src/mame/drivers/hp9825.cpp new file mode 100644 index 00000000000..251b256c9bf --- /dev/null +++ b/src/mame/drivers/hp9825.cpp @@ -0,0 +1,668 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi + +// ************************** +// Driver for HP 9825 systems +// ************************** +// +// **** Temporary header, will hopefully evolve into proper doc **** +// +// What's in: +// - Emulation of 9825B system +// - 12 kw of RAMs +// - 12 kw of system ROM +// - Keyboard (SHIFT LOCK & RESET not implemented) +// - Display & run light +// What's not yet in: +// - Internal & external expansion ROMs +// - Configurable RAM size +// - Printer +// - DC100 tape drive +// - I/O expansion slots: 98034 & 98035 modules from hp9845 emulation can be used here, too +// - Beeper +// +// 9825A & 9825T can also be emulated. At the moment I haven't all the necessary +// ROM dumps, though. + +#include "emu.h" +#include "cpu/hphybrid/hphybrid.h" +#include "machine/timer.h" +#include "hp9825.lh" + +// CPU clock (generated by a trimmered RC oscillator) +constexpr unsigned MAIN_CLOCK = 6000000; + +// KDP chip clock +constexpr unsigned KDP_CLOCK = MAIN_CLOCK / 4; + +// Bit manipulation +namespace { + template constexpr T BIT_MASK(unsigned n) + { + return (T)1U << n; + } + + template void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK(n); + } + + template void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK(n); + } +} + +class hp9825_state : public driver_device +{ +public: + hp9825_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_cpu(*this , "cpu") + , m_cursor_timer(*this , "cursor_timer") + , m_io_key(*this , "KEY%u" , 0) + , m_shift_key(*this , "KEY_SHIFT") + , m_display(*this , "char_%u_%u" , 0U , 0U) + , m_run_light(*this , "run_light") + { + } + + void hp9825b(machine_config &config); + +private: + required_device m_cpu; + required_device m_cursor_timer; + required_ioport_array<4> m_io_key; + required_ioport m_shift_key; + output_finder<32 , 7> m_display; + output_finder<> m_run_light; + + bool m_display_on; + uint8_t m_display_mem[ 32 ]; + uint8_t m_display_idx; + bool m_rpl_cursor; + bool m_cursor_blink; + bool m_any_cursor; + uint8_t m_scancode; + bool m_key_pressed; + bool m_autorepeating; + unsigned m_autorepeat_cnt; + uint8_t m_irl_pending; + uint8_t m_irh_pending; + + virtual void machine_start() override; + virtual void machine_reset() override; + + void cpu_io_map(address_map &map); + void cpu_mem_map(address_map &map); + + DECLARE_READ16_MEMBER(kb_scancode_r); + DECLARE_WRITE16_MEMBER(disp_w); + DECLARE_READ16_MEMBER(kdp_status_r); + DECLARE_WRITE16_MEMBER(kdp_control_w); + + void update_display(); + TIMER_DEVICE_CALLBACK_MEMBER(cursor_blink); + void kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx); + TIMER_DEVICE_CALLBACK_MEMBER(kb_scan); + + IRQ_CALLBACK_MEMBER(irq_callback); + void update_irq(); + void set_irq(uint8_t sc , int state); +}; + +void hp9825_state::machine_start() +{ + m_display.resolve(); + m_run_light.resolve(); + + save_item(NAME(m_display_on)); + save_item(NAME(m_display_mem)); + save_item(NAME(m_display_idx)); + save_item(NAME(m_scancode)); + save_item(NAME(m_irl_pending)); + save_item(NAME(m_irh_pending)); +} + +void hp9825_state::machine_reset() +{ + m_display_on = false; + m_display_idx = 0; + m_rpl_cursor = false; + m_cursor_timer->reset(); + m_cursor_blink = true; + update_display(); + m_scancode = 0; + m_key_pressed = false; + m_autorepeating = false; + m_autorepeat_cnt = 0; + m_irl_pending = 0; + m_irh_pending = 0; + update_irq(); +} + +void hp9825_state::cpu_io_map(address_map &map) +{ + map.unmap_value_low(); + map(HP_MAKE_IOADDR(0 , 0) , HP_MAKE_IOADDR(0 , 0)).rw(FUNC(hp9825_state::kb_scancode_r) , FUNC(hp9825_state::disp_w)); + map(HP_MAKE_IOADDR(0 , 1) , HP_MAKE_IOADDR(0 , 1)).rw(FUNC(hp9825_state::kdp_status_r) , FUNC(hp9825_state::kdp_control_w)); + // TODO: +} + +void hp9825_state::cpu_mem_map(address_map &map) +{ + map.unmap_value_low(); + map(0x0000 , 0x2fff).rom(); + map(0x5000 , 0x7fff).ram(); +} + +READ16_MEMBER(hp9825_state::kb_scancode_r) +{ + // TODO: + uint8_t res = m_scancode; + if (m_shift_key->read()) { + BIT_SET(res , 7); + } + set_irq(0 , false); + return res; +} + +WRITE16_MEMBER(hp9825_state::disp_w) +{ + // TODO: + if (m_display_on) { + m_display_on = false; + m_cursor_timer->reset(); + m_cursor_blink = true; + m_display_idx = 0; + update_display(); + } + m_display_mem[ m_display_idx++ ] = uint8_t(data); +} + +READ16_MEMBER(hp9825_state::kdp_status_r) +{ + // TODO: + return 8; +} + +WRITE16_MEMBER(hp9825_state::kdp_control_w) +{ + // TODO: + bool regen_display = false; + if (BIT(data , 1) && !m_display_on) { + m_display_on = true; + // Cursor should blink at 2^-19 the KDP clock + attotime cursor_half_period{ attotime::from_ticks(262144 , KDP_CLOCK) }; + m_cursor_timer->adjust(cursor_half_period , 0 , cursor_half_period); + regen_display = true; + } + if (BIT(data , 6) && !m_rpl_cursor) { + m_rpl_cursor = true; + regen_display = true; + } + if (BIT(data , 5) && m_rpl_cursor) { + m_rpl_cursor = false; + regen_display = true; + } + if (BIT(data , 4)) { + if (BIT(data , 3)) { + m_run_light = !m_run_light; + } else { + m_run_light = false; + } + } else if (BIT(data , 3)) { + m_run_light = true; + } + if (regen_display) { + update_display(); + } +} + +// The character generator was reverse engineered from images of printer & display test patterns. +// It is not guaranteed to be pixel-accurate though it looks quite close to the original. +static const uint8_t chargen[ 128 ][ 5 ] = { + { 0x08,0x1c,0x3e,0x7f,0x00 }, // 00 + { 0x30,0x48,0x45,0x40,0x30 }, // 01 + { 0x45,0x29,0x11,0x29,0x45 }, // 02 + { 0x7d,0x09,0x11,0x21,0x7d }, // 03 + { 0x38,0x44,0x44,0x38,0x44 }, // 04 + { 0x7c,0x2a,0x4a,0x4a,0x34 }, // 05 + { 0x7f,0x01,0x01,0x01,0x03 }, // 06 + { 0x7d,0x09,0x05,0x05,0x79 }, // 07 + { 0x60,0x58,0x46,0x58,0x60 }, // 08 + { 0x38,0x44,0x44,0x3c,0x04 }, // 09 + { 0x10,0x20,0x7f,0x20,0x10 }, // 0a + { 0x62,0x14,0x08,0x10,0x60 }, // 0b + { 0x40,0x3c,0x20,0x20,0x1c }, // 0c + { 0x08,0x1c,0x2a,0x08,0x08 }, // 0d + { 0x10,0x08,0x78,0x08,0x04 }, // 0e + { 0x08,0x55,0x7f,0x55,0x08 }, // 0f + { 0x3e,0x49,0x49,0x49,0x3e }, // 10 + { 0x5e,0x61,0x01,0x61,0x5e }, // 11 + { 0x30,0x4a,0x4d,0x49,0x30 }, // 12 + { 0x78,0x14,0x15,0x14,0x78 }, // 13 + { 0x38,0x44,0x45,0x3c,0x40 }, // 14 + { 0x78,0x15,0x14,0x15,0x78 }, // 15 + { 0x38,0x45,0x44,0x3d,0x40 }, // 16 + { 0x3c,0x43,0x42,0x43,0x3c }, // 17 + { 0x38,0x45,0x44,0x45,0x38 }, // 18 + { 0x3e,0x41,0x40,0x41,0x3e }, // 19 + { 0x3c,0x41,0x40,0x41,0x3c }, // 1a + { 0x7e,0x09,0x7f,0x49,0x49 }, // 1b + { 0x38,0x44,0x38,0x54,0x58 }, // 1c + { 0x12,0x19,0x15,0x12,0x00 }, // 1d + { 0x48,0x7e,0x49,0x41,0x42 }, // 1e + { 0x55,0x2a,0x55,0x2a,0x55 }, // 1f + { 0x00,0x00,0x00,0x00,0x00 }, // 20 + { 0x00,0x5f,0x00,0x00,0x00 }, // 21 + { 0x00,0x03,0x00,0x03,0x00 }, // 22 + { 0x14,0x7f,0x14,0x7f,0x14 }, // 23 + { 0x24,0x2a,0x7f,0x2a,0x12 }, // 24 + { 0x23,0x13,0x08,0x64,0x62 }, // 25 + { 0x36,0x49,0x56,0x20,0x50 }, // 26 + { 0x00,0x0b,0x07,0x00,0x00 }, // 27 + { 0x00,0x00,0x3e,0x41,0x00 }, // 28 + { 0x00,0x41,0x3e,0x00,0x00 }, // 29 + { 0x08,0x2a,0x1c,0x2a,0x08 }, // 2a + { 0x08,0x08,0x3e,0x08,0x08 }, // 2b + { 0x00,0x58,0x38,0x00,0x00 }, // 2c + { 0x08,0x08,0x08,0x08,0x08 }, // 2d + { 0x00,0x60,0x60,0x00,0x00 }, // 2e + { 0x20,0x10,0x08,0x04,0x02 }, // 2f + { 0x3e,0x51,0x49,0x45,0x3e }, // 30 + { 0x00,0x42,0x7f,0x40,0x00 }, // 31 + { 0x62,0x51,0x49,0x49,0x46 }, // 32 + { 0x22,0x41,0x49,0x49,0x36 }, // 33 + { 0x18,0x14,0x12,0x7f,0x10 }, // 34 + { 0x27,0x45,0x45,0x45,0x39 }, // 35 + { 0x3c,0x4a,0x49,0x49,0x30 }, // 36 + { 0x01,0x71,0x09,0x05,0x03 }, // 37 + { 0x36,0x49,0x49,0x49,0x36 }, // 38 + { 0x06,0x49,0x49,0x29,0x1e }, // 39 + { 0x00,0x36,0x36,0x00,0x00 }, // 3a + { 0x00,0x5b,0x3b,0x00,0x00 }, // 3b + { 0x00,0x08,0x14,0x22,0x41 }, // 3c + { 0x14,0x14,0x14,0x14,0x14 }, // 3d + { 0x41,0x22,0x14,0x08,0x00 }, // 3e + { 0x06,0x01,0x51,0x09,0x06 }, // 3f + { 0x3e,0x41,0x5d,0x55,0x1e }, // 40 + { 0x7e,0x09,0x09,0x09,0x7e }, // 41 + { 0x7f,0x49,0x49,0x49,0x36 }, // 42 + { 0x3e,0x41,0x41,0x41,0x22 }, // 43 + { 0x7f,0x41,0x41,0x41,0x3e }, // 44 + { 0x7f,0x49,0x49,0x49,0x41 }, // 45 + { 0x7f,0x09,0x09,0x09,0x01 }, // 46 + { 0x3e,0x41,0x41,0x51,0x72 }, // 47 + { 0x7f,0x08,0x08,0x08,0x7f }, // 48 + { 0x00,0x41,0x7f,0x41,0x00 }, // 49 + { 0x20,0x40,0x40,0x40,0x3f }, // 4a + { 0x7f,0x08,0x14,0x22,0x41 }, // 4b + { 0x7f,0x40,0x40,0x40,0x40 }, // 4c + { 0x7f,0x02,0x0c,0x02,0x7f }, // 4d + { 0x7f,0x04,0x08,0x10,0x7f }, // 4e + { 0x3e,0x41,0x41,0x41,0x3e }, // 4f + { 0x7f,0x09,0x09,0x09,0x06 }, // 50 + { 0x3e,0x41,0x51,0x21,0x5e }, // 51 + { 0x7f,0x09,0x19,0x29,0x46 }, // 52 + { 0x26,0x49,0x49,0x49,0x32 }, // 53 + { 0x01,0x01,0x7f,0x01,0x01 }, // 54 + { 0x3f,0x40,0x40,0x40,0x3f }, // 55 + { 0x07,0x18,0x60,0x18,0x07 }, // 56 + { 0x7f,0x20,0x10,0x20,0x7f }, // 57 + { 0x63,0x14,0x08,0x14,0x63 }, // 58 + { 0x03,0x04,0x78,0x04,0x03 }, // 59 + { 0x61,0x51,0x49,0x45,0x43 }, // 5a + { 0x00,0x00,0x7f,0x41,0x41 }, // 5b + { 0x20,0x7f,0x01,0x01,0x01 }, // 5c + { 0x41,0x41,0x7f,0x00,0x00 }, // 5d + { 0x04,0x02,0x7f,0x02,0x04 }, // 5e + { 0x40,0x40,0x40,0x40,0x40 }, // 5f + { 0x00,0x07,0x0b,0x00,0x00 }, // 60 + { 0x38,0x44,0x44,0x3c,0x40 }, // 61 + { 0x7f,0x48,0x44,0x44,0x38 }, // 62 + { 0x38,0x44,0x44,0x44,0x20 }, // 63 + { 0x38,0x44,0x44,0x48,0x7f }, // 64 + { 0x38,0x54,0x54,0x54,0x08 }, // 65 + { 0x08,0x7e,0x09,0x02,0x00 }, // 66 + { 0x08,0x14,0x54,0x54,0x3c }, // 67 + { 0x7f,0x08,0x04,0x04,0x78 }, // 68 + { 0x00,0x44,0x7d,0x40,0x00 }, // 69 + { 0x20,0x40,0x44,0x3d,0x00 }, // 6a + { 0x7f,0x10,0x28,0x44,0x00 }, // 6b + { 0x00,0x41,0x7f,0x40,0x00 }, // 6c + { 0x78,0x04,0x18,0x04,0x78 }, // 6d + { 0x7c,0x08,0x04,0x04,0x78 }, // 6e + { 0x38,0x44,0x44,0x44,0x38 }, // 6f + { 0x7c,0x14,0x24,0x24,0x18 }, // 70 + { 0x18,0x24,0x14,0x7c,0x40 }, // 71 + { 0x7c,0x08,0x04,0x04,0x00 }, // 72 + { 0x48,0x54,0x54,0x54,0x20 }, // 73 + { 0x04,0x3e,0x44,0x20,0x00 }, // 74 + { 0x3c,0x40,0x40,0x20,0x7c }, // 75 + { 0x1c,0x20,0x40,0x20,0x1c }, // 76 + { 0x3c,0x40,0x30,0x40,0x3c }, // 77 + { 0x44,0x28,0x10,0x28,0x44 }, // 78 + { 0x04,0x48,0x30,0x08,0x04 }, // 79 + { 0x44,0x64,0x54,0x4c,0x44 }, // 7a + { 0x08,0x7c,0x04,0x7c,0x02 }, // 7b + { 0x00,0x00,0x7f,0x00,0x00 }, // 7c + { 0x08,0x08,0x2a,0x1c,0x08 }, // 7d + { 0x41,0x63,0x55,0x49,0x63 }, // 7e + { 0x7f,0x08,0x08,0x08,0x08 } // 7f +}; + +void hp9825_state::update_display() +{ + m_any_cursor = false; + for (unsigned i = 0; i < 32; ++i) { + bool cursor_here = BIT(m_display_mem[ i ] , 7); + if (cursor_here) { + m_any_cursor = true; + } + bool show_cursor = m_cursor_blink && cursor_here; + uint8_t char_code = m_display_mem[ i ] & 0x7f; + for (unsigned j = 0; j < 7; ++j) { + uint8_t five_dots = 0; + if (m_display_on) { + for (unsigned col = 0; col < 5; col++) { + uint8_t char_col; + if (show_cursor) { + if (m_rpl_cursor) { + // Replace cursor: all pixels lit + char_col = ~0; + } else { + // Insert cursor: character code 0 + char_col = chargen[ 0 ][ col ]; + } + } else { + char_col = chargen[ char_code ][ col ]; + } + if (BIT(char_col , j)) { + BIT_SET(five_dots , col); + } + } + } + m_display[ i ][ j ] = five_dots; + } + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::cursor_blink) +{ + m_cursor_blink = !m_cursor_blink; + if (m_any_cursor) { + update_display(); + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::kb_scan) +{ + ioport_value input[ 4 ] + { m_io_key[ 0 ]->read(), + m_io_key[ 1 ]->read(), + m_io_key[ 2 ]->read(), + m_io_key[ 3 ]->read() + }; + + if (m_key_pressed) { + // Still pressed ? + m_key_pressed = BIT(input[ m_scancode / 32 ] , m_scancode % 32); + } else { + int max_seq_len = 0; + unsigned max_seq_idx = 0; + for (unsigned i = 0; i < 4; i++) { + kb_scan_ioport(input[ i ] , *m_io_key[ i ] , i << 5 , max_seq_len , max_seq_idx); + } + if (max_seq_len) { + m_scancode = max_seq_idx; + m_key_pressed = true; + set_irq(0 , true); + } + } + + if (m_key_pressed) { + auto prev_cnt = m_autorepeat_cnt; + m_autorepeat_cnt++; + // Auto-repeat initial delay & frequency are entirely guessed.. + if (BIT(m_autorepeat_cnt , 5)) { + // Initial delay passed + m_autorepeating = true; + } + if (m_autorepeating && BIT(~prev_cnt & m_autorepeat_cnt , 3)) { + // Repeat key every time bit 3 of autorepeat counter goes 0->1 + set_irq(0 , true); + } + } else { + m_autorepeating = false; + m_autorepeat_cnt = 0; + } +} + +void hp9825_state::kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx) +{ + while (pressed) { + unsigned bit_no = 31 - count_leading_zeros(pressed); + ioport_value mask = BIT_MASK(bit_no); + int seq_len = port.field(mask)->seq().length(); + if (seq_len > max_seq_len) { + max_seq_len = seq_len; + max_seq_idx = bit_no + idx_base; + } + pressed &= ~mask; + } +} + +IRQ_CALLBACK_MEMBER(hp9825_state::irq_callback) +{ + if (irqline == HPHYBRID_IRL) { + return m_irl_pending; + } else { + return m_irh_pending; + } +} + +void hp9825_state::update_irq() +{ + m_cpu->set_input_line(HPHYBRID_IRL , m_irl_pending != 0); + m_cpu->set_input_line(HPHYBRID_IRH , m_irh_pending != 0); +} + +void hp9825_state::set_irq(uint8_t sc , int state) +{ + unsigned bit_n = sc % 8; + + if (sc < 8) { + if (state) { + BIT_SET(m_irl_pending, bit_n); + } else { + BIT_CLR(m_irl_pending, bit_n); + } + } else { + if (state) { + BIT_SET(m_irh_pending, bit_n); + } else { + BIT_CLR(m_irh_pending, bit_n); + } + } + update_irq(); +} + +MACHINE_CONFIG_START(hp9825_state::hp9825b) + HP_09825_67907(config , m_cpu , MAIN_CLOCK); + // Just guessing... settings borrowed from hp9845 + m_cpu->set_rw_cycles(6 , 6); + m_cpu->set_relative_mode(false); + m_cpu->set_addrmap(AS_PROGRAM , &hp9825_state::cpu_mem_map); + m_cpu->set_addrmap(AS_IO , &hp9825_state::cpu_io_map); + m_cpu->set_irq_acknowledge_callback(FUNC(hp9825_state::irq_callback)); + + TIMER(config , m_cursor_timer , 0).configure_generic(timer_device::expired_delegate(FUNC(hp9825_state::cursor_blink) , this)); + + // Keyboard scan timer. A scan of the whole keyboard should take 2^14 KDP clocks. + TIMER(config , "kb_timer" , 0).configure_periodic(timer_device::expired_delegate(FUNC(hp9825_state::kb_scan) , this) , attotime::from_ticks(16384 , KDP_CLOCK)); + + config.set_default_layout(layout_hp9825); +MACHINE_CONFIG_END + +#define IOP_MASK(x) BIT_MASK((x)) + +static INPUT_PORTS_START(hp9825) + // Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, 102 are used. + // Keys are mapped on bit b of KEYn + // where b = (row & 1) << 4 + column, n = row >> 1 + // column = [0..15] + // row = [0..7] + // 4 more keys are not in the matrix: 2 SHIFTs, 1 SHIFT LOCK and RESET key. + // Fun fact: alphanumeric keys are arranged in the matrix so that their scancode (row/column number) + // equals the lower case ASCII code. The person in charge of routing the keyboard PCB + // must have loved this arrangement... + PORT_START("KEY0") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,0: N/U + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Stop") // 0,1: Stop + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Rewind") // 0,2: Rewind + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,3: N/U + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,4: N/U + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,5: N/U + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,6: N/U + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result") // 0,7: Result + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line insert") // 0,8: Line insert + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line delete") // 0,9: Line delete + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Execute") // 0,10: Execute + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line recall") // 0,11: Line recall + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Run") // 0,12: Run + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Store") // 0,13: Store + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display left") // 0,14: Display left + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display right") // 0,15: Display right + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Display down") // 1,0: Display down + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Display up") // 1,1: Display up + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Clear") // 1,2: Clear + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Print all") // 1,3: Print all + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Char back") // 1,4: Char back + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Char forward") // 1,5: Char forward + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Char ins/rpl") // 1,6: Char ins/rpl + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Char delete") // 1,7: Char delete + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Step") // 1,8: Step + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Continue") PORT_CHAR(13) // 1,9: Continue + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED) // 1,10: N/U + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("List") // 1,11: List + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line fetch") // 1,12: Line fetch + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Erase") // 1,13: Erase + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Record") // 1,14: Record + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Load") // 1,15: Load + + PORT_START("KEY1") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') // 2,0: Space + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,1: N/U + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,2: N/U + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,3: N/U + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,4: N/U + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,5: N/U + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,6: N/U + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED) // 2,7: N/U + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_NAME("Keypad (") // 2,8: KP ( + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_NAME("Keypad )") // 2,9: KP ) + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) // 2,10: KP * + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) // 2,11: KP + + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') // 2,12: , + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) // 2,13: KP - + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') // 2,14: . + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) // 2,15: KP / + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('\'') // 3,0: 0 + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') // 3,1: 1 + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') // 3,2: 2 + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') // 3,3: 3 + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') // 3,4: 4 + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') // 3,5: 5 + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') // 3,6: 6 + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('@') // 3,7: 7 + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('[') // 3,8: 8 + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(']') // 3,9: 9 + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED) // 3,10: N/U + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') // 3,11: ; + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED) // 3,12: N/U + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD)) // 3,13: = + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED) // 3,14: N/U + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR(':') // 3,15: ? + + PORT_START("KEY2") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED) // 4,0: N/U + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("f0") // 4,1: f0 + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("f1") // 4,2: f1 + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("f2") // 4,3: f2 + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("f3") // 4,4: f3 + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("f4") // 4,5: f4 + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("f5") // 4,6: f5 + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("f6") // 4,7: f6 + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("f7") // 4,8: f7 + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("f8") // 4,9: f8 + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("f9") // 4,10: f9 + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("f10") // 4,11: f10 + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("f11") // 4,12: f11 + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED) // 4,13: N/U + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) // 4,14: KP 0 + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) // 4,15: KP 1 + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) // 5,0: KP 2 + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) // 5,1: KP 3 + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) // 5,2: KP 4 + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) // 5,3: KP 5 + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) // 5,4: KP 6 + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) // 5,5: KP 7 + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) // 5,6: KP 8 + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) // 5,7: KP 9 + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) // 5,8: KP . + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) // 5,9: KP , + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,10: N/U + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,11: N/U + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,12: N/U + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,13: N/U + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_NAME("↑ √") // 5,14: ^ + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,15: N/U + + PORT_START("KEY3") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Enter exp _") // 6,0: Enter exp + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') // 6,1: A + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') // 6,2: B + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') // 6,3: C + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') // 6,4: D + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') // 6,5: E + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') // 6,6: F + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') // 6,7: G + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') // 6,8: H + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') // 6,9: I + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') // 6,10: J + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') // 6,11: K + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') // 6,12: L + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') // 6,13: M + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') // 6,14: N + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') // 6,15: O + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') // 7,0: P + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') // 7,1: Q + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') // 7,2: R + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') // 7,3: S + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') // 7,4: T + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') // 7,5: U + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') // 7,6: V + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') // 7,7: W + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') // 7,8: X + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') // 7,9: Y + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') // 7,10: Z + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("π |") // 7,11: Pi + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED) // 7,12: N/U + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME("→") // 7,13: Gazinta + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED) // 7,14: N/U + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED) // 7,15: N/U + + PORT_START("KEY_SHIFT") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // Shift +INPUT_PORTS_END + +ROM_START(hp9825b) + ROM_REGION(0x6000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE) + ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0)) + ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54)) + ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a)) +ROM_END + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +COMP(1980, hp9825b, 0, 0, hp9825b, hp9825, hp9825_state, empty_init, "Hewlett-Packard", "HP 9825B", MACHINE_NO_SOUND) diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp index f6e2e3fbf68..01c2caf375e 100644 --- a/src/mame/drivers/hp9845.cpp +++ b/src/mame/drivers/hp9845.cpp @@ -3737,15 +3737,19 @@ void hp9845_base_state::ppu_io_map(address_map &map) } MACHINE_CONFIG_START(hp9845_base_state::hp9845_base) - MCFG_DEVICE_ADD("lpu", HP_5061_3001, 5700000) - MCFG_DEVICE_PROGRAM_MAP(global_mem_map) - MCFG_HPHYBRID_SET_9845_BOOT(true) - MCFG_DEVICE_ADD("ppu", HP_5061_3001, 5700000) - MCFG_DEVICE_PROGRAM_MAP(global_mem_map) - MCFG_DEVICE_IO_MAP(ppu_io_map) - MCFG_HPHYBRID_SET_9845_BOOT(true) - MCFG_DEVICE_IRQ_ACKNOWLEDGE_DRIVER(hp9845_base_state , irq_callback) - MCFG_HPHYBRID_PA_CHANGED(WRITE8(*this, hp9845_base_state , pa_w)) + HP_5061_3001(config , m_lpu , 5700000); + m_lpu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map); + m_lpu->set_9845_boot_mode(true); + m_lpu->set_rw_cycles(6 , 6); + m_lpu->set_relative_mode(true); + HP_5061_3001(config , m_ppu , 5700000); + m_ppu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map); + m_ppu->set_addrmap(AS_IO , &hp9845_base_state::ppu_io_map); + m_ppu->set_9845_boot_mode(true); + m_ppu->set_rw_cycles(6 , 6); + m_ppu->set_relative_mode(true); + m_ppu->set_irq_acknowledge_callback(FUNC(hp9845_base_state::irq_callback)); + m_ppu->pa_changed_cb().set(FUNC(hp9845_base_state::pa_w)); // video hardware MCFG_SCREEN_ADD("screen", RASTER) diff --git a/src/mame/layout/hp9825.lay b/src/mame/layout/hp9825.lay new file mode 100644 index 00000000000..d73603d07f7 --- /dev/null +++ b/src/mame/layout/hp9825.lay @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index a0f51a6dc73..aa9e94a01f1 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -15286,6 +15286,9 @@ hp64k // @source:hp700.cpp hp700_92 // +@source:hp9825.cpp +hp9825b // HP 9825B + @source:hp9845.cpp hp9835a // hp9835b // diff --git a/src/mame/mess.flt b/src/mame/mess.flt index e3673b7b49a..eee3d5ef50c 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -291,6 +291,7 @@ hp48.cpp hp49gp.cpp hp64k.cpp hp700.cpp +hp9825.cpp hp9845.cpp hp9k.cpp hp9k_3xx.cpp diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 837f50e3e75..85d57327908 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -349,8 +349,9 @@ static const dasm_table_entry dasm_table[] = { "hd6309", be, 0, []() -> util::disasm_interface * { return new hd6309_disassembler; } }, { "hd63701", be, 0, []() -> util::disasm_interface * { return new m680x_disassembler(63701); } }, { "hmcs40", le, -1, []() -> util::disasm_interface * { return new hmcs40_disassembler; } }, - { "hp_hybrid", be, -1, []() -> util::disasm_interface * { return new hp_hybrid_disassembler; } }, { "hp_5061_3001", be, -1, []() -> util::disasm_interface * { return new hp_5061_3001_disassembler; } }, + { "hp_5061_3011", be, -1, []() -> util::disasm_interface * { return new hp_5061_3011_disassembler; } }, + { "hp_09825_67907", be, -1, []() -> util::disasm_interface * { return new hp_09825_67907_disassembler; } }, { "hyperstone", be, 0, []() -> util::disasm_interface * { return new hyperstone_disassembler(&hyperstone_unidasm); } }, { "i4004", le, 0, []() -> util::disasm_interface * { return new i4004_disassembler; } }, { "i4040", le, 0, []() -> util::disasm_interface * { return new i4040_disassembler; } },