cpu/e132xs: More fixes and optimisation:

* Fixed failing to call the debugger instruction hook for the first
  instruction following an interrupt, exception or trap.
* Use UML branches to emulate non-delayed intra-block branches, avoiding
  the expensive "hash jump".
* Re-worked the instruction description code:
  - Calculate static branch targets for more instructions.
  - Flag instructions that may cause mode changes.
  - Don't be so eager to end an instruction sequence.
  - Removed the local register input/output flags - FP may no be the
    same when executing the code as when describing instructions.
* Fixed interpreter incorrectly setting ILC when an interrupt
  immediately follows a RET instruction.
* Fixed recompiler flag calculation regressions, and optimised a little.
This commit is contained in:
Vas Crabb 2025-03-27 08:07:52 +11:00
parent 08fae4612b
commit 128c29c52f
6 changed files with 234 additions and 344 deletions

View File

@ -734,7 +734,6 @@ void hyperstone_device::execute_trap(uint8_t trapno)
void hyperstone_device::execute_int(uint32_t addr)
{
const uint8_t reg = GET_FP + GET_FL;
SET_ILC(m_instruction_length);
const uint32_t oldSR = SR;
SET_FL(2);
@ -1040,7 +1039,7 @@ void hyperstone_device::device_start()
m_drcuml->symbol_add(&m_core->arg1, sizeof(uint32_t), "arg1");
/* initialize the front-end helper */
m_drcfe = std::make_unique<e132xs_frontend>(this, COMPILE_BACKWARDS_BYTES, COMPILE_FORWARDS_BYTES, SINGLE_INSTRUCTION_MODE ? 1 : COMPILE_MAX_SEQUENCE);
m_drcfe = std::make_unique<e132xs_frontend>(*this, COMPILE_BACKWARDS_BYTES, COMPILE_FORWARDS_BYTES, SINGLE_INSTRUCTION_MODE ? 1 : COMPILE_MAX_SEQUENCE);
/* mark the cache dirty so it is updated on next execute */
m_cache_dirty = true;
@ -1498,12 +1497,6 @@ void hyperstone_device::execute_run()
while (m_core->icount > 0)
{
#if E132XS_LOG_INTERPRETER_REGS
dump_registers();
#endif
debugger_instruction_hook(PC);
if (--m_core->intblock <= 0)
{
m_core->intblock = 0;
@ -1513,6 +1506,12 @@ void hyperstone_device::execute_run()
check_interrupts<NO_TIMER>();
}
#if E132XS_LOG_INTERPRETER_REGS
dump_registers();
#endif
debugger_instruction_hook(PC);
OP = m_pr16(PC);
PC += 2;

View File

@ -478,7 +478,7 @@ private:
void static_generate_exception(drcuml_block &block, uml::code_label &label);
void static_generate_interrupt_checks(drcuml_block &block, uml::code_label &label);
void generate_interrupt_checks(drcuml_block &block, uml::code_label &labelnum, bool with_timer, int take_int, int take_timer);
void generate_branch(drcuml_block &block, uml::parameter mode, uml::parameter targetpc, const opcode_desc *desc);
void generate_branch(drcuml_block &block, compiler_state &compiler, uml::parameter mode, uml::parameter targetpc, const opcode_desc *desc);
void generate_update_cycles(drcuml_block &block);
void generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast);
void generate_sequence_instruction(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc);
@ -504,6 +504,7 @@ private:
void generate_update_flags_addsub(drcuml_block &block, compiler_state &compiler, uml::parameter sr);
void generate_update_flags_addsubc(drcuml_block &block, compiler_state &compiler, uml::parameter sr);
void generate_update_flags_addsubs(drcuml_block &block, compiler_state &compiler, uml::parameter sr);
void generate_update_flags_cmp(drcuml_block &block, compiler_state &compiler, uml::parameter sr);
template <trap_exception_or_int TYPE> void generate_trap_exception_or_int(drcuml_block &block, uml::code_label &label, uml::parameter trapno);
void generate_int(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc, uint32_t addr);

View File

@ -219,9 +219,6 @@ void hyperstone_device::code_compile_block(uint8_t mode, offs_t pc)
/* loop until we get through all instruction sequences */
for (seqhead = desclist; seqhead != nullptr; seqhead = seqlast->next())
{
const opcode_desc *curdesc;
uint32_t nextpc;
/* add a code log entry */
if (m_drcuml->logging())
block.append_comment("-------------------------");
@ -263,12 +260,13 @@ void hyperstone_device::code_compile_block(uint8_t mode, offs_t pc)
UML_MOV(block, I7, 0);
/* iterate over instructions in the sequence and compile them */
for (curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next())
for (const opcode_desc *curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next())
{
generate_sequence_instruction(block, compiler, curdesc);
generate_update_cycles(block);
}
uint32_t nextpc;
if (seqlast->flags & OPFLAG_RETURN_TO_START) /* if we need to return to the start, do it */
nextpc = pc;
else /* otherwise we just go to the next instruction */
@ -696,7 +694,7 @@ void hyperstone_device::log_add_disasm_comment(drcuml_block &block, uint32_t pc,
generate_branch
------------------------------------------------------------------*/
void hyperstone_device::generate_branch(drcuml_block &block, uml::parameter mode, uml::parameter targetpc, const opcode_desc *desc)
void hyperstone_device::generate_branch(drcuml_block &block, compiler_state &compiler, uml::parameter mode, uml::parameter targetpc, const opcode_desc *desc)
{
// clobbers I0 and I1 if mode is BRANCH_TARGET_DYNAMIC
@ -705,17 +703,26 @@ void hyperstone_device::generate_branch(drcuml_block &block, uml::parameter mode
generate_update_cycles(block);
// update the cycles and jump through the hash table to the target
const uml::parameter pc = (targetpc != BRANCH_TARGET_DYNAMIC) ? targetpc : DRC_PC;
const uml::parameter m = (mode != BRANCH_TARGET_DYNAMIC) ? mode : uml::I0;
if (mode == BRANCH_TARGET_DYNAMIC)
if (desc && (mode == compiler.m_mode) && (desc->flags & OPFLAG_INTRABLOCK_BRANCH))
{
UML_MOV(block, I0, DRC_SR);
UML_ROLAND(block, I1, I0, 32 - T_SHIFT + 1, 0x2);
UML_ROLAND(block, I0, I0, 32 - S_SHIFT, 0x1);
UML_OR(block, I0, I0, I1);
assert(desc->targetpc != BRANCH_TARGET_DYNAMIC);
UML_JMP(block, desc->targetpc | 0x80000000);
}
else
{
// jump through the hash table to the target
const uml::parameter pc = (targetpc != BRANCH_TARGET_DYNAMIC) ? targetpc : DRC_PC;
const uml::parameter m = (mode != BRANCH_TARGET_DYNAMIC) ? mode : uml::I0;
if (mode == BRANCH_TARGET_DYNAMIC)
{
UML_MOV(block, I0, DRC_SR);
UML_ROLAND(block, I1, I0, 32 - T_SHIFT + 1, 0x2);
UML_ROLAND(block, I0, I0, 32 - S_SHIFT, 0x1);
UML_OR(block, I0, I0, I1);
}
UML_HASHJMP(block, m, pc, *m_nocode);
}
UML_HASHJMP(block, m, pc, *m_nocode);
}
@ -731,19 +738,7 @@ void hyperstone_device::generate_sequence_instruction(drcuml_block &block, compi
log_add_disasm_comment(block, desc->pc, desc->opptr.w[0]);
// set the PC map variable
const offs_t expc = (desc->flags & OPFLAG_IN_DELAY_SLOT) ? (desc->pc - 3) : desc->pc;
UML_MAPVAR(block, MAPVAR_PC, expc);
#if E132XS_LOG_DRC_REGS
UML_CALLC(block, &c_funcs::dump_registers, this);
#endif
// if we are debugging, call the debugger
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
//save_fast_iregs(block);
UML_DEBUG(block, desc->pc);
}
UML_MAPVAR(block, MAPVAR_PC, desc->pc);
// check for pending interrupts
UML_SUB(block, I0, mem(&m_core->intblock), 1);
@ -751,6 +746,17 @@ void hyperstone_device::generate_sequence_instruction(drcuml_block &block, compi
UML_MOV(block, mem(&m_core->intblock), I0);
UML_CALLHc(block, uml::COND_LE, *m_interrupt_checks);
#if E132XS_LOG_DRC_REGS
UML_CALLC(block, &c_funcs::dump_registers, this);
#endif
// if we are debugging, call the debugger
if (machine().debug_flags & DEBUG_FLAG_ENABLED)
{
//save_fast_iregs(block);
UML_DEBUG(block, desc->pc);
}
if (!(desc->flags & OPFLAG_VIRTUAL_NOOP))
{
// compile the instruction

View File

@ -390,15 +390,15 @@ void hyperstone_device::generate_set_dst(drcuml_block &block, compiler_state &co
if (src.is_int_register() && (desc->targetpc == BRANCH_TARGET_DYNAMIC))
{
UML_AND(block, src, src, ~uint32_t(1));
generate_branch(block, compiler.m_mode, src, desc);
generate_branch(block, compiler, compiler.m_mode, src, desc);
}
else if (src.is_immediate() && (desc->targetpc == BRANCH_TARGET_DYNAMIC))
{
generate_branch(block, compiler.m_mode, src.immediate() & ~uint32_t(1), desc);
generate_branch(block, compiler, compiler.m_mode, src.immediate() & ~uint32_t(1), desc);
}
else
{
generate_branch(block, compiler.m_mode, desc->targetpc, desc);
generate_branch(block, compiler, compiler.m_mode, desc->targetpc, desc);
}
}
}
@ -468,6 +468,26 @@ void hyperstone_device::generate_update_flags_addsubs(drcuml_block &block, compi
UML_ROLINS(block, sr, I1, 0, (V_MASK | N_MASK | Z_MASK));
}
void hyperstone_device::generate_update_flags_cmp(drcuml_block &block, compiler_state &compiler, uml::parameter sr)
{
// expects UML flags set by ADD/SUB
// clobbers I0, I1, I3 and I4
UML_SETc(block, uml::COND_V, I0); // I0 = ...V
UML_SETc(block, uml::COND_L, I1); // I1 = ...N
UML_SETc(block, uml::COND_Z, I3); // I3 = ...Z
UML_SETc(block, uml::COND_C, I4); // I4 = ...C
UML_AND(block, sr, sr, ~(V_MASK | N_MASK | Z_MASK | C_MASK));
UML_SHL(block, I0, I0, V_SHIFT); // I0 = V...
UML_SHL(block, I1, I1, N_SHIFT); // I1 = .N..
UML_SHL(block, I3, I3, Z_SHIFT); // I3 = ..Z.
UML_SHL(block, I4, I4, C_SHIFT); // I4 = ...C
UML_OR(block, I0, I0, I1); // I0 = VN..
UML_OR(block, I3, I3, I4); // I1 = ..ZC
UML_OR(block, sr, sr, I0);
UML_OR(block, sr, sr, I3);
}
template <hyperstone_device::trap_exception_or_int TYPE>
void hyperstone_device::generate_trap_exception_or_int(drcuml_block &block, uml::code_label &label, uml::parameter trapno)
{
@ -508,8 +528,8 @@ void hyperstone_device::generate_trap_exception_or_int(drcuml_block &block, uml:
UML_ADD(block, I7, I7, mem(&m_core->clock_cycles_2)); // assume exception dispatch takes two cycles
UML_MOV(block, DRC_PC, I0); // branch to exception handler
generate_branch(block, 1, uml::I0, nullptr); // T cleared and S set - mode will always be 1
generate_update_cycles(block);
UML_HASHJMP(block, 1, I0, *m_nocode); // T cleared and S set - mode will always be 1
}
void hyperstone_device::generate_int(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc, uint32_t addr)
@ -601,7 +621,7 @@ void hyperstone_device::generate_software(drcuml_block &block, compiler_state &c
UML_ROLINS(block, I0, I4, FP_SHIFT, FP_MASK); // SET_FP(reg)
UML_MOV(block, DRC_SR, I0);
generate_branch(block, compiler.m_mode & 0x1, uml::I5, desc); // T cleared - only keep S in bit zero of mode
generate_branch(block, compiler, compiler.m_mode & 0x1, uml::I5, desc); // T cleared - only keep S in bit zero of mode
}
@ -815,7 +835,7 @@ void hyperstone_device::generate_movd(drcuml_block &block, compiler_state &compi
UML_MOV(block, mem(&SP), I0); // SP = I0
UML_LABEL(block, done_ret);
generate_branch(block, BRANCH_TARGET_DYNAMIC, desc->targetpc, nullptr); // don't pass desc - must not update ILC and P
generate_branch(block, compiler, BRANCH_TARGET_DYNAMIC, desc->targetpc, nullptr); // don't pass desc - must not update ILC and P
return;
}
else if (SRC_GLOBAL && (src_code == SR_REGISTER)) // Rd doesn't denote PC and Rs denotes SR
@ -1135,9 +1155,9 @@ void hyperstone_device::generate_cmp(drcuml_block &block, compiler_state &compil
generate_load_src_addsub(block, compiler, SRC_GLOBAL, src_code, uml::I1, uml::I1, uml::I2);
generate_load_operand(block, compiler, DST_GLOBAL, dst_code, uml::I0, uml::I3);
UML_SUB(block, I0, I0, I1);
UML_CMP(block, I0, I1);
generate_update_flags_addsub(block, compiler, uml::I2);
generate_update_flags_cmp(block, compiler, uml::I2);
UML_MOV(block, DRC_SR, I2);
}
@ -1230,7 +1250,7 @@ void hyperstone_device::generate_mov(drcuml_block &block, compiler_state &compil
{
UML_AND(block, DRC_SR, DRC_SR, ~M_MASK);
UML_AND(block, I5, I5, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I5, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I5, desc);
}
UML_JMP(block, done);
@ -1606,9 +1626,9 @@ void hyperstone_device::generate_cmpi(drcuml_block &block, compiler_state &compi
generate_load_operand(block, compiler, DST_GLOBAL, dst_code, uml::I0, uml::I0);
UML_SUB(block, I0, I0, src);
UML_CMP(block, I0, src);
generate_update_flags_addsub(block, compiler, uml::I2);
generate_update_flags_cmp(block, compiler, uml::I2);
UML_MOV(block, DRC_SR, I2);
}
@ -1629,43 +1649,43 @@ void hyperstone_device::generate_movi(drcuml_block &block, compiler_state &compi
generate_check_delay_pc(block, compiler, desc);
int done;
if (DST_GLOBAL)
UML_AND(block, I2, DRC_SR, ~(Z_MASK | N_MASK));
if (!src)
UML_OR(block, I2, I2, Z_MASK);
else if (src & 0x80000000)
UML_OR(block, I2, I2, N_MASK);
#if MISSIONCRAFT_FLAGS
UML_AND(block, I2, I2, ~V_MASK);
#endif
if (DST_GLOBAL && !BIT(compiler.m_mode, 0))
{
done = compiler.m_labelnum++;
if (!BIT(compiler.m_mode, 0))
{
const int no_exception = compiler.m_labelnum++;
UML_TEST(block, DRC_SR, H_MASK);
UML_JMPc(block, uml::COND_Z, no_exception);
UML_EXH(block, *m_exception, EXCEPTION_PRIVILEGE_ERROR);
UML_JMP(block, done);
UML_LABEL(block, no_exception);
}
const int no_exception = compiler.m_labelnum++;
UML_TEST(block, I2, H_MASK);
UML_JMPc(block, uml::COND_Z, no_exception);
UML_EXH(block, *m_exception, EXCEPTION_PRIVILEGE_ERROR);
UML_LABEL(block, no_exception);
}
UML_AND(block, DRC_SR, DRC_SR, ~(Z_MASK | N_MASK));
if (src)
UML_OR(block, DRC_SR, DRC_SR, (src & 0x80000000) ? (Z_MASK | N_MASK) : Z_MASK);
#if MISSIONCRAFT_FLAGS
UML_AND(block, DRC_SR, DRC_SR, ~V_MASK);
#endif
UML_MOV(block, DRC_SR, I2);
if (DST_GLOBAL)
{
const int highglobal = compiler.m_labelnum++;
const int done = compiler.m_labelnum++;
UML_TEST(block, DRC_SR, H_MASK);
UML_TEST(block, I2, H_MASK);
UML_JMPc(block, uml::COND_NZ, highglobal);
generate_set_global_register_low(block, compiler, dst_code, src);
if (dst_code == PC_REGISTER)
{
UML_AND(block, DRC_SR, DRC_SR, ~M_MASK);
generate_branch(block, compiler.m_mode, src & ~uint32_t(1), desc);
generate_branch(block, compiler, compiler.m_mode, src & ~uint32_t(1), desc);
}
else
{
UML_JMP(block, done);
}
UML_JMP(block, done);
UML_LABEL(block, highglobal);
UML_AND(block, DRC_SR, DRC_SR, ~H_MASK);
@ -2612,7 +2632,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2634,7 +2654,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2659,7 +2679,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2688,7 +2708,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2708,7 +2728,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2744,7 +2764,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == 0)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -2766,7 +2786,7 @@ void hyperstone_device::generate_ldxx1(drcuml_block &block, compiler_state &comp
if (src_code == PC_REGISTER)
{
UML_AND(block, I1, I1, ~uint32_t(1));
generate_branch(block, compiler.m_mode, uml::I1, desc);
generate_branch(block, compiler, compiler.m_mode, uml::I1, desc);
}
}
else
@ -3602,7 +3622,7 @@ void hyperstone_device::generate_ldwp(drcuml_block &block, compiler_state &compi
UML_STORE(block, (void *)m_core->local_regs, I2, I3, SIZE_DWORD, SCALE_x4);
if (src_code == PC_REGISTER)
generate_branch(block, compiler.m_mode, desc->targetpc, desc);
generate_branch(block, compiler, compiler.m_mode, desc->targetpc, desc);
}
else
{
@ -3657,7 +3677,7 @@ void hyperstone_device::generate_lddp(drcuml_block &block, compiler_state &compi
UML_STORE(block, (void *)m_core->local_regs, I2, I3, SIZE_DWORD, SCALE_x4);
if (src_code == PC_REGISTER || (src_code + 1) == PC_REGISTER)
generate_branch(block, compiler.m_mode, desc->targetpc, desc);
generate_branch(block, compiler, compiler.m_mode, desc->targetpc, desc);
}
else
{
@ -3875,22 +3895,17 @@ void hyperstone_device::generate_b(drcuml_block &block, compiler_state &compiler
{
static const uint32_t condition_masks[6] = { V_MASK, Z_MASK, C_MASK, C_MASK | Z_MASK, N_MASK, N_MASK | Z_MASK };
int done = compiler.m_labelnum++;
uml::condition_t condition = COND_SET ? uml::COND_Z : uml::COND_NZ;
int skip;
const int skip = compiler.m_labelnum++;
UML_TEST(block, DRC_SR, condition_masks[CONDITION]);
UML_JMPc(block, condition, skip = compiler.m_labelnum++);
UML_JMPc(block, condition, skip);
generate_br(block, compiler, desc);
UML_JMP(block, done);
UML_LABEL(block, skip);
generate_ignore_pcrel(block, desc);
generate_check_delay_pc(block, compiler, desc);
UML_MOV(block, I7, mem(&m_core->clock_cycles_1));
UML_LABEL(block, done);
}
@ -3905,7 +3920,7 @@ void hyperstone_device::generate_br(drcuml_block &block, compiler_state &compile
UML_ADD(block, DRC_PC, DRC_PC, target);
UML_AND(block, DRC_SR, DRC_SR, ~M_MASK);
generate_branch(block, compiler.m_mode, desc->targetpc, desc);
generate_branch(block, compiler, compiler.m_mode, desc->targetpc, desc);
// TODO: correct cycle count
}
@ -4077,7 +4092,7 @@ void hyperstone_device::generate_call(drcuml_block &block, compiler_state &compi
UML_MOV(block, mem(&m_core->intblock), 2);
generate_branch(block, compiler.m_mode, uml::I2, nullptr);
generate_branch(block, compiler, compiler.m_mode, uml::I2, nullptr);
//TODO: add interrupt locks, errors, ....
}

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
class e132xs_frontend : public drc_frontend
{
public:
e132xs_frontend(hyperstone_device *e132xs, uint32_t window_start, uint32_t window_end, uint32_t max_sequence);
e132xs_frontend(hyperstone_device &cpu, uint32_t window_start, uint32_t window_end, uint32_t max_sequence);
void flush();
protected:
@ -19,15 +19,15 @@ protected:
virtual bool describe(opcode_desc &desc, const opcode_desc *prev) override;
private:
inline uint16_t read_word(opcode_desc &desc);
inline uint16_t read_imm1(opcode_desc &desc);
inline uint16_t read_imm2(opcode_desc &desc);
inline uint32_t read_ldstxx_imm(opcode_desc &desc);
inline uint32_t read_limm(opcode_desc &desc, uint16_t op);
inline int32_t decode_pcrel(opcode_desc &desc, uint16_t op);
inline int32_t decode_call(opcode_desc &desc);
uint16_t read_word(opcode_desc &desc);
uint16_t read_imm1(opcode_desc &desc);
uint16_t read_imm2(opcode_desc &desc);
uint32_t read_ldstxx_imm(opcode_desc &desc);
uint32_t read_limm(opcode_desc &desc, uint16_t op);
int32_t decode_pcrel(opcode_desc &desc, uint16_t op);
int32_t decode_call(opcode_desc &desc);
hyperstone_device *m_cpu;
hyperstone_device &m_cpu;
};
#endif /* MAME_CPU_E132XS_E132XSFE_H */
#endif // MAME_CPU_E132XS_E132XSFE_H