diff --git a/src/nba/src/arm/arm7tdmi.hpp b/src/nba/src/arm/arm7tdmi.hpp index 277cd1af..7d37bea5 100644 --- a/src/nba/src/arm/arm7tdmi.hpp +++ b/src/nba/src/arm/arm7tdmi.hpp @@ -140,6 +140,8 @@ struct ARM7TDMI { bool is_banked = id >= 8 && id != 15; if(unlikely(ldm_usermode_conflict && is_banked)) { + // This array holds the current user/sys bank value only if the CPU wasn't in user or system mode all along during the user mode LDM instruction. + // We take care in the LDM implementation that this branch is only taken if that was the case. result |= state.bank[BANK_NONE][id - 8]; } @@ -154,6 +156,8 @@ struct ARM7TDMI { bool is_banked = id >= 8 && id != 15; if(unlikely(ldm_usermode_conflict && is_banked)) { + // This array holds the current user/sys bank value only if the CPU wasn't in user or system mode all along during the user mode LDM instruction. + // We take care in the LDM implementation that this branch is only taken if that was the case. state.bank[BANK_NONE][id - 8] = value; } diff --git a/src/nba/src/arm/handlers/handler32.inl b/src/nba/src/arm/handlers/handler32.inl index b99d00d9..034d31fe 100644 --- a/src/nba/src/arm/handlers/handler32.inl +++ b/src/nba/src/arm/handlers/handler32.inl @@ -544,7 +544,7 @@ void ARM_BlockDataTransfer(u32 instruction) { int base = (instruction >> 16) & 0xF; int list = instruction & 0xFFFF; - Mode mode; + Mode mode = state.cpsr.f.mode; bool transfer_pc = list & (1 << 15); int first = 0; int bytes = 0; @@ -571,10 +571,15 @@ void ARM_BlockDataTransfer(u32 instruction) { bytes = 64; } - bool switch_mode = user_mode && (!load || !transfer_pc); + /** + * Whether the instruction is a LDM^ instruction and we need to switch to user mode. + * Note that we only switch to user mode if we aren't in user or system mode already. + * This is important for emulation of the LDM user mode register bus conflict, + * since the implementation of this quirk in GetReg() and SetReg() only works if the CPU isn't in user or system mode anymore. + */ + const bool switch_mode = user_mode && (!load || !transfer_pc) && mode != MODE_USR && mode != MODE_SYS; if (switch_mode) { - mode = state.cpsr.f.mode; SwitchMode(MODE_USR); } @@ -633,10 +638,10 @@ void ARM_BlockDataTransfer(u32 instruction) { bus.Idle(); if (switch_mode) { - /* During the following two cycles of a usermode LDM, + /* During the following two cycles of a user mode LDM, * register accesses will go to both the user bank and original bank. */ - ldm_usermode_conflict = true; + ldm_usermode_conflict = mode != MODE_USR && mode != MODE_SYS; scheduler.Add(2, Scheduler::EventClass::ARM_ldm_usermode_conflict); }