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