From 241320803272ba8f7b7365b224663669a22a1afb Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:10:02 +1000 Subject: [PATCH 01/68] Added sub bitmask to read32 call, allowing for SWR/SWL calls to limit msan check for sub-32 bit init Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/gte_aa64.cc | 2 +- src/core/DynaRec_aa64/instructions.cc | 54 +++++++++++++-------------- src/core/DynaRec_aa64/recompiler.h | 4 +- src/core/DynaRec_x64/gte_x64.cc | 2 +- src/core/DynaRec_x64/instructions.cc | 40 ++++++++++---------- src/core/DynaRec_x64/recompiler.h | 3 +- src/core/DynaRec_x64/symbols.cc | 2 +- src/core/psxmem.cc | 3 +- src/core/psxmem.h | 6 ++- 9 files changed, 61 insertions(+), 55 deletions(-) diff --git a/src/core/DynaRec_aa64/gte_aa64.cc b/src/core/DynaRec_aa64/gte_aa64.cc index af325923b..6af1f490a 100644 --- a/src/core/DynaRec_aa64/gte_aa64.cc +++ b/src/core/DynaRec_aa64/gte_aa64.cc @@ -257,7 +257,7 @@ void DynaRecCPU::recLWC2(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); } - call(read32Wrapper); + call(read32Wrapper<>); switch (_Rt_) { case 15: case 30: diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 455b8d053..295b1aa28 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -496,7 +496,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper<>); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -512,7 +512,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper<>); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -524,10 +524,10 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<>); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -544,10 +544,10 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with shifted value } } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<>); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -578,7 +578,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper<>); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -594,7 +594,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper<>); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -606,10 +606,10 @@ void DynaRecCPU::recLWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<>); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -626,10 +626,10 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with newly read value } } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper<>); // Read from the aligned address, result in eax if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -703,7 +703,7 @@ void DynaRecCPU::recompileLoad(uint32_t code) { call(read16Wrapper); break; case 32: - call(read32Wrapper); + call(read32Wrapper<>); break; default: PCSX::g_system->message("Invalid size for memory load in dynarec. Instruction %08x\n", m_regs.code); @@ -1360,7 +1360,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); + call(read32Wrapper<0xFFFF0000>); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1374,19 +1374,19 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); + call(read32Wrapper<0xFFFF0000>); gen.andImm(w0, w0, mask); // Mask read value allocateReg(_Rt_); // Allocate $rt // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1405,10 +1405,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Mov(arg1, w5); call(write32Wrapper); } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1441,7 +1441,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); + call(read32Wrapper<0x0000FFFF>); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1455,19 +1455,19 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); + call(read32Wrapper<0x0000FFFF>); gen.andImm(w0, w0, mask); // Mask read value - allocateReg(_Rt_); // Allocate $rt + allocateReg(_Rt_); // Allocate $rt gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1487,10 +1487,10 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg1, w5); call(write32Wrapper); } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index 64c89390f..8eb81f84b 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -18,6 +18,7 @@ ***************************************************************************/ #pragma once +#include #include "core/r3000a.h" #if defined(DYNAREC_AA64) @@ -82,7 +83,8 @@ static uint8_t read8Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read8(address); } static uint16_t read16Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read16(address); } -static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +template +static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/gte_x64.cc b/src/core/DynaRec_x64/gte_x64.cc index a61d3920c..8b872d10f 100644 --- a/src/core/DynaRec_x64/gte_x64.cc +++ b/src/core/DynaRec_x64/gte_x64.cc @@ -316,7 +316,7 @@ void DynaRecCPU::recLWC2(uint32_t code) { gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); } - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); switch (_Rt_) { case 15: // SXYP gen.mov(rcx, qword[contextPointer + COP2_DATA_OFFSET(13)]); // SXY0 = SXY1 and SXY1 = SXY2 diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 42ce460cb..e103b7f1a 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -700,7 +700,7 @@ void DynaRecCPU::recompileLoadWithDelay(uint32_t code, LoadDelayDependencyType t callMemoryFunc(&PCSX::Memory::read16); break; case 32: - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); break; } @@ -770,7 +770,7 @@ void DynaRecCPU::recompileLoad(uint32_t code) { callMemoryFunc(&PCSX::Memory::read16); break; case 32: - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); break; } @@ -809,7 +809,7 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.and_(arg2, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address return; } @@ -826,7 +826,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -840,7 +840,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -853,7 +853,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -873,7 +873,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -902,7 +902,7 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.and_(arg2, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address return; } @@ -919,7 +919,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -933,7 +933,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + callMemoryFunc(&PCSX::Memory::read32<>); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -946,7 +946,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -966,7 +966,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1145,7 +1145,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + call(read32Wrapper<0xFFFF0000>); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value @@ -1159,7 +1159,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + call(read32Wrapper<0xFFFF0000>); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1172,7 +1172,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1206,7 +1206,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1251,7 +1251,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + call(read32Wrapper<0x0000FFFF>); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value @@ -1265,7 +1265,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + call(read32Wrapper<0x0000FFFF>); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1278,7 +1278,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1312,7 +1312,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index 8e77501ff..b5d64b70f 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -83,7 +83,8 @@ #define _ImmU_ _fImmU_(code) #define _ImmLU_ _fImmLU_(code) -static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +template +static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index 3897ff67a..0bf7b6851 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -97,7 +97,7 @@ void DynaRecCPU::makeSymbols() { REGISTER_CLASS_FUNCTION(PCSX::Memory::read8, "read8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read16, "read16"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32<>, "read32"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write8, "write8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write16, "write16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write32, "write32"); diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index a40a621a9..d4ce49dbd 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -387,6 +387,7 @@ uint16_t PCSX::Memory::read16(uint32_t address) { return 0xffff; } +template uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) { if (readType == ReadType::Data) g_emulator->m_cpu->m_regs.cycle += 1; const uint32_t page = address >> 16; @@ -395,7 +396,7 @@ uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) { if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { - switch (msanGetStatus<4>(address)) { + switch (msanGetStatus<4, msan_sub_bitmask>(address)) { case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 082567b47..5f5078b57 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -66,6 +67,7 @@ class Memory { uint8_t read8(uint32_t address); uint16_t read16(uint32_t address); + template uint32_t read32(uint32_t address, ReadType = ReadType::Data); void write8(uint32_t address, uint32_t value); void write16(uint32_t address, uint32_t value); @@ -91,10 +93,10 @@ class Memory { uint32_t msanSetChainPtr(uint32_t headerAddr, uint32_t ptrToNext, uint32_t size); uint32_t msanGetChainPtr(uint32_t addr) const; - template + template MsanStatus msanGetStatus(uint32_t addr) const { uint32_t bitmapIndex = (addr - c_msanStart) / 8; - uint32_t bitmask = ((1 << length) - 1) << addr % 8; + uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { From 97c8e493652496d1adb32980fd0e9f774074750d Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:48:49 +1000 Subject: [PATCH 02/68] Fixed template linking issues with non-inline specialisation, moved to constant compile-time optimised args Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/gte_aa64.cc | 2 +- src/core/DynaRec_aa64/instructions.cc | 36 +++++++++++++-------------- src/core/DynaRec_aa64/recompiler.h | 5 ++-- src/core/DynaRec_x64/gte_x64.cc | 2 +- src/core/DynaRec_x64/instructions.cc | 36 +++++++++++++-------------- src/core/DynaRec_x64/recompiler.h | 5 ++-- src/core/DynaRec_x64/symbols.cc | 5 +++- src/core/psxmem.cc | 6 ++--- src/core/psxmem.h | 11 +++++--- 9 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/core/DynaRec_aa64/gte_aa64.cc b/src/core/DynaRec_aa64/gte_aa64.cc index 6af1f490a..af325923b 100644 --- a/src/core/DynaRec_aa64/gte_aa64.cc +++ b/src/core/DynaRec_aa64/gte_aa64.cc @@ -257,7 +257,7 @@ void DynaRecCPU::recLWC2(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); } - call(read32Wrapper<>); + call(read32Wrapper); switch (_Rt_) { case 15: case 30: diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 295b1aa28..8f2b1d877 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -496,7 +496,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<>); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -512,7 +512,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<>); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -527,7 +527,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<>); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -547,7 +547,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<>); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -578,7 +578,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper<>); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -594,7 +594,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<>); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -609,7 +609,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<>); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -629,7 +629,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<>); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -703,7 +703,7 @@ void DynaRecCPU::recompileLoad(uint32_t code) { call(read16Wrapper); break; case 32: - call(read32Wrapper<>); + call(read32Wrapper); break; default: PCSX::g_system->message("Invalid size for memory load in dynarec. Instruction %08x\n", m_regs.code); @@ -1360,7 +1360,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper<0xFFFF0000>); + call(read32MaskedWrapper<0xFFFF0000>); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1374,7 +1374,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper<0xFFFF0000>); + call(read32MaskedWrapper<0xFFFF0000>); gen.andImm(w0, w0, mask); // Mask read value allocateReg(_Rt_); // Allocate $rt @@ -1386,7 +1386,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in w0 + call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1408,7 +1408,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in w0 + call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1441,7 +1441,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper<0x0000FFFF>); + call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1455,7 +1455,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper<0x0000FFFF>); + call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(w0, w0, mask); // Mask read value allocateReg(_Rt_); // Allocate $rt @@ -1464,10 +1464,10 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in w0 + call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1490,7 +1490,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in w0 + call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index 8eb81f84b..49c65897a 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -83,8 +83,9 @@ static uint8_t read8Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read8(address); } static uint16_t read16Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read16(address); } -template -static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +template +static uint32_t read32MaskedWrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/gte_x64.cc b/src/core/DynaRec_x64/gte_x64.cc index 8b872d10f..a61d3920c 100644 --- a/src/core/DynaRec_x64/gte_x64.cc +++ b/src/core/DynaRec_x64/gte_x64.cc @@ -316,7 +316,7 @@ void DynaRecCPU::recLWC2(uint32_t code) { gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); } - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); switch (_Rt_) { case 15: // SXYP gen.mov(rcx, qword[contextPointer + COP2_DATA_OFFSET(13)]); // SXY0 = SXY1 and SXY1 = SXY2 diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index e103b7f1a..108882c34 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -700,7 +700,7 @@ void DynaRecCPU::recompileLoadWithDelay(uint32_t code, LoadDelayDependencyType t callMemoryFunc(&PCSX::Memory::read16); break; case 32: - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); break; } @@ -770,7 +770,7 @@ void DynaRecCPU::recompileLoad(uint32_t code) { callMemoryFunc(&PCSX::Memory::read16); break; case 32: - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); break; } @@ -826,7 +826,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -840,7 +840,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -853,7 +853,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -873,7 +873,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -919,7 +919,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -933,7 +933,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32<>); + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -946,7 +946,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -966,7 +966,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address, result in eax + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1145,7 +1145,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<0xFFFF0000>); + call(read32MaskedWrapper<0xFFFF0000>); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value @@ -1159,7 +1159,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<0xFFFF0000>); + call(read32MaskedWrapper<0xFFFF0000>); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1172,7 +1172,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in eax + call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1206,7 +1206,7 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper<0xFFFF0000>); // Read from the aligned address, result in eax + call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1251,7 +1251,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<0x0000FFFF>); + call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value @@ -1265,7 +1265,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper<0x0000FFFF>); + call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1278,7 +1278,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in eax + call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1312,7 +1312,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper<0x0000FFFF>); // Read from the aligned address, result in eax + call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index b5d64b70f..714cf7219 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -83,8 +83,9 @@ #define _ImmU_ _fImmU_(code) #define _ImmLU_ _fImmLU_(code) -template -static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } +template +static uint32_t read32MaskedWrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index 0bf7b6851..0979b5098 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -97,7 +97,10 @@ void DynaRecCPU::makeSymbols() { REGISTER_CLASS_FUNCTION(PCSX::Memory::read8, "read8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read16, "read16"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32<>, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked<0xFFFF0000>, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked<0x0000FFFF>, "read32"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write8, "write8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write16, "write16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write32, "write32"); diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index d4ce49dbd..83f83a010 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -21,6 +21,7 @@ #include +#include #include #include @@ -387,8 +388,7 @@ uint16_t PCSX::Memory::read16(uint32_t address) { return 0xffff; } -template -uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) { +uint32_t PCSX::Memory::read32Masked(uint32_t address, const uint32_t msan_sub_bitmask, ReadType readType) { if (readType == ReadType::Data) g_emulator->m_cpu->m_regs.cycle += 1; const uint32_t page = address >> 16; const auto pointer = (uint8_t *)m_readLUT[page]; @@ -396,7 +396,7 @@ uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) { if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { - switch (msanGetStatus<4, msan_sub_bitmask>(address)) { + switch (msanGetStatus<4>(address, msan_sub_bitmask)) { case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 5f5078b57..acc904b77 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -67,8 +67,8 @@ class Memory { uint8_t read8(uint32_t address); uint16_t read16(uint32_t address); - template - uint32_t read32(uint32_t address, ReadType = ReadType::Data); + uint32_t read32Masked(uint32_t address, const uint32_t msan_sub_bitmask, ReadType = ReadType::Data); + inline uint32_t read32(uint32_t address, ReadType = ReadType::Data) { return read32Masked(address, UINT32_MAX); }; void write8(uint32_t address, uint32_t value); void write16(uint32_t address, uint32_t value); void write32(uint32_t address, uint32_t value); @@ -93,8 +93,8 @@ class Memory { uint32_t msanSetChainPtr(uint32_t headerAddr, uint32_t ptrToNext, uint32_t size); uint32_t msanGetChainPtr(uint32_t addr) const; - template - MsanStatus msanGetStatus(uint32_t addr) const { + template + MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; MsanStatus bestCase = MsanStatus::OK; @@ -116,6 +116,9 @@ class Memory { return bestCase; } + template + inline MsanStatus msanGetStatus(uint32_t addr) const { return msanGetStatus(addr, sub_bitmask); }; + // if the write is valid, marks the address as initialized, otherwise returns false template bool msanValidateWrite(uint32_t addr) { From 01dc987b577cd19e89f49068d5ca43f503238eb0 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 15 Jun 2026 22:42:52 +1000 Subject: [PATCH 03/68] Cleaned up whitespace changes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 38 +++++++++++++-------------- src/core/DynaRec_x64/instructions.cc | 4 +-- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 8f2b1d877..e07782d0c 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -496,7 +496,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -512,7 +512,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -524,10 +524,10 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -544,10 +544,10 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with shifted value } } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -578,7 +578,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -594,7 +594,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -606,10 +606,10 @@ void DynaRecCPU::recLWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + call(read32Wrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -626,10 +626,10 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with newly read value } } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1381,9 +1381,9 @@ void DynaRecCPU::recSWL(uint32_t code) { // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 @@ -1405,7 +1405,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Mov(arg1, w5); call(write32Wrapper); } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 @@ -1458,13 +1458,13 @@ void DynaRecCPU::recSWR(uint32_t code) { call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(w0, w0, mask); // Mask read value - allocateReg(_Rt_); // Allocate $rt + allocateReg(_Rt_); // Allocate $rt gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 @@ -1487,7 +1487,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg1, w5); call(write32Wrapper); } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg + allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 108882c34..958b871bb 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -809,7 +809,7 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.and_(arg2, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address return; } @@ -902,7 +902,7 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.and_(arg2, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32<>); // Read from the aligned address + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address return; } From ac2eecaf0258e08879f3dd187995db75f19f207f Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:34:44 +1000 Subject: [PATCH 04/68] Emitted x64/aarch64 assembly for read32Masked call in SWR/SWL callsa Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 18 +++++++++++++++--- src/core/DynaRec_aa64/recompiler.h | 3 +-- src/core/DynaRec_x64/instructions.cc | 24 ++++++++++++++++++++---- src/core/DynaRec_x64/recompiler.h | 3 +-- src/core/DynaRec_x64/symbols.cc | 4 +--- src/core/psxmem.h | 14 ++++++++++++++ 6 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index e07782d0c..4fd0ca759 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1360,7 +1360,8 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32MaskedWrapper<0xFFFF0000>); + gen.Mov(arg2, mask); // Mask in arg2 (??) + call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1374,7 +1375,8 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32MaskedWrapper<0xFFFF0000>); + gen.Mov(arg2, mask); // Mask in arg2 (??) + call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value allocateReg(_Rt_); // Allocate $rt @@ -1386,7 +1388,12 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 + gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 + gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Ldr(arg2, Operand(x3, LSR, 32)); // Load mask value + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1408,6 +1415,11 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it + gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 + gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Ldr(arg2, Operand(x3, LSR, 32)); // Load mask value call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index 49c65897a..4e8937833 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -84,8 +84,7 @@ static uint8_t read8Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read8(address); } static uint16_t read16Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read16(address); } static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } -template -static uint32_t read32MaskedWrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } +static uint32_t read32MaskedWrapper(uint32_t address, uint32_t msan_sub_bitmask) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 958b871bb..0aa053079 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1145,7 +1145,8 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32MaskedWrapper<0xFFFF0000>); + gen.mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value @@ -1159,7 +1160,8 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32MaskedWrapper<0xFFFF0000>); + gen.mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1171,8 +1173,15 @@ void DynaRecCPU::recSWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in eax + gen.and_(edx, 3); // edx = low 2 bits of address + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // rcx = mask now + gen.mov(arg2, rcx); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1205,8 +1214,15 @@ void DynaRecCPU::recSWL(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in eax + gen.and_(edx, 3); // edx = low 2 bits of address + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // rcx = mask now + gen.mov(arg2, rcx); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index 714cf7219..cab1be136 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -84,8 +84,7 @@ #define _ImmLU_ _fImmLU_(code) static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } -template -static uint32_t read32MaskedWrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } +static uint32_t read32MaskedWrapper(uint32_t address, uint32_t msan_sub_bitmask) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index 0979b5098..2fe769d18 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -98,9 +98,7 @@ void DynaRecCPU::makeSymbols() { REGISTER_CLASS_FUNCTION(PCSX::Memory::read8, "read8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read16, "read16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read32, "read32"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked, "read32"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked<0xFFFF0000>, "read32"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked<0x0000FFFF>, "read32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked, "read32"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write8, "write8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write16, "write16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write32, "write32"); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index acc904b77..2fed608a1 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "core/psxemulator.h" #include "support/eventbus.h" @@ -95,11 +97,19 @@ class Memory { template MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { + std::cout << "Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; + std::cout << "Bitmask " << std::bitset<32>(bitmask) << std::endl; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { + std::cout << "Next bitmask " + << std::bitset<24>(nextBitmask) + << " Init bitmap entry " + << std::bitset<24>(m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { + std::cout << "Usable bitmap entry " + << std::bitset<24>(m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return MsanStatus::UNUSABLE; } @@ -107,7 +117,11 @@ class Memory { } bitmask &= 0xff; } + std::cout << "Full init bitmap entry " + << std::bitset<32>(m_msanInitializedBitmap[bitmapIndex] & bitmask) << std::endl; if ((m_msanInitializedBitmap[bitmapIndex] & bitmask) != bitmask) [[unlikely]] { + std::cout << "Full usable bitmap entry " + << std::bitset<32>(m_msanUsableBitmap[bitmapIndex] & bitmask) << std::endl; if ((m_msanUsableBitmap[bitmapIndex] & bitmask) != bitmask) { return MsanStatus::UNUSABLE; } From 3b754b70da9992b7321a65cf6619f22240c6f64e Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:39:10 +1000 Subject: [PATCH 05/68] Moved mask into arg2 for constant address cases in SWR Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 10 +++++++--- src/core/DynaRec_x64/instructions.cc | 12 ++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 4fd0ca759..ecd759933 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1453,7 +1453,8 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32MaskedWrapper<0x0000FFFF>); + gen.Mov(arg2, mask); // Mask in arg2 (??) + call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value @@ -1467,6 +1468,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) + gen.Mov(arg2, mask); // Mask in arg2 (??) call(read32MaskedWrapper<0x0000FFFF>); gen.andImm(w0, w0, mask); // Mask read value @@ -1479,7 +1481,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 + // TODO: Emit assebly for mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1502,7 +1505,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in w0 + // TODO: Emit assebly for mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 0aa053079..d5f7d1aef 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1267,7 +1267,8 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32MaskedWrapper<0x0000FFFF>); + gen.mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value @@ -1281,7 +1282,8 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32MaskedWrapper<0x0000FFFF>); + gen.mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1294,7 +1296,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in eax + // TODO: Emit assebly for mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1328,7 +1331,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper<0x0000FFFF>); // Read from the aligned address, result in eax + // TODO: Emit assebly for mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); From acf346bf78f683b6b05b50c487ce97afe2aacd9f Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:39:30 +1000 Subject: [PATCH 06/68] Moved mask into arg2 for constant address cases in SWR Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 4 ++-- src/core/DynaRec_x64/instructions.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index ecd759933..e3e27ff20 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1481,7 +1481,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - // TODO: Emit assebly for mask in arg2 + // TODO: Emit assembly for mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1505,7 +1505,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - // TODO: Emit assebly for mask in arg2 + // TODO: Emit assembly for mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index d5f7d1aef..6c6f3c8b3 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1296,7 +1296,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - // TODO: Emit assebly for mask in arg2 + // TODO: Emit assembly for mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1331,7 +1331,7 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - // TODO: Emit assebly for mask in arg2 + // TODO: Emit assembly for mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt From a488eace61a917aad31c6e0309e523e6af55b283 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:34:19 +1000 Subject: [PATCH 07/68] Moved mask into arg2 for constant address cases in SWR Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index e3e27ff20..adf6d6d49 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1469,7 +1469,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) gen.Mov(arg2, mask); // Mask in arg2 (??) - call(read32MaskedWrapper<0x0000FFFF>); + call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value allocateReg(_Rt_); // Allocate $rt From 4c220d33cfb3f0cabded448c8ee2a16c0460c52b Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 14:26:52 +1000 Subject: [PATCH 08/68] Fixed loading of mask on aarch64 Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index adf6d6d49..ec1c3cae0 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1387,13 +1387,13 @@ void DynaRecCPU::recSWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Ldr(arg2, Operand(x3, LSR, 32)); // Load mask value - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, Operand(x3, LSR, 32)); // Load mask value + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1414,13 +1414,13 @@ void DynaRecCPU::recSWL(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Ldr(arg2, Operand(x3, LSR, 32)); // Load mask value - call(read32MaskedWrapper<0xFFFF0000>); // Read from the aligned address, result in w0 + gen.Mov(arg2, Operand(x3, LSR, 32)); // Load mask value + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1480,8 +1480,12 @@ void DynaRecCPU::recSWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w2, arg1, 3); // w2 = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - // TODO: Emit assembly for mask in arg2 + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 + gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(arg2, Operand(x3, LSR, 32)); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1504,8 +1508,12 @@ void DynaRecCPU::recSWR(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w2, arg1, 3); // arg2 = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - // TODO: Emit assembly for mask in arg2 + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 + gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(arg2, Operand(x3, LSR, 32)); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt From 52d0b572d4afeab1ce3ccf75c20e1270699e8134 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:36:12 +1000 Subject: [PATCH 09/68] Testing masking derived from LUT Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index ec1c3cae0..7c2b83504 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1387,12 +1387,13 @@ void DynaRecCPU::recSWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // edx = low 2 bits of address - gen.And(arg1, arg1, ~3); // Force align it + gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, Operand(x3, LSR, 32)); // Load mask value + gen.Mov(x0, Operand(x3, LSR, 32)); // Load mask value + gen.Mov(arg2, w0); call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1414,12 +1415,13 @@ void DynaRecCPU::recSWL(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // edx = low 2 bits of address - gen.And(arg1, arg1, ~3); // Force align it + gen.And(w2, arg1, 3); // edx = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, Operand(x3, LSR, 32)); // Load mask value + gen.Mov(x0, Operand(x3, LSR, 32)); // Load mask value + gen.Mov(arg2, w0); call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1480,13 +1482,14 @@ void DynaRecCPU::recSWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // w2 = low 2 bits of address - gen.And(arg1, arg1, ~3); // Force align it + gen.And(w2, arg1, 3); // w2 = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, Operand(x3, LSR, 32)); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(x3, Operand(x3, LSR, 32)); // Mask in arg2 + gen.Mov(arg2, w3); + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1508,13 +1511,14 @@ void DynaRecCPU::recSWR(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // arg2 = low 2 bits of address - gen.And(arg1, arg1, ~3); // Force align it + gen.And(w2, arg1, 3); // w2 = low 2 bits of address + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 - gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + gen.Ldr(x3, MemOperand(x3, x2, LSR, 29)); // Load the mask from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, Operand(x3, LSR, 32)); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(x3, Operand(x3, LSR, 32)); // Mask in arg2 + gen.Mov(arg2, w3); + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); From 129b4d2a8d918b9168a34b228cd8a96c8ed17720 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:17:42 +1000 Subject: [PATCH 10/68] Implemented masking for LWL/LWR calls Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 109 ++++++++------- src/core/DynaRec_x64/instructions.cc | 188 +++++++++++++++----------- src/core/psxmem.h | 8 +- 3 files changed, 170 insertions(+), 135 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 7c2b83504..27a1fe30c 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -18,6 +18,7 @@ ***************************************************************************/ #include "recompiler.h" +#include "vixl/src/aarch64/operands-aarch64.h" #if defined(DYNAREC_AA64) #define BAILZERO(x) \ if (!(x)) { \ @@ -496,7 +497,8 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + gen.Mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -512,7 +514,8 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + gen.Mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -524,10 +527,16 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w1, arg1, 3); // Get low 2 bits + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT + gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask + gen.Mov(arg2, w3); // Mask in arg2 + gen.And(arg1, arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -543,11 +552,17 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.And(m_gprs[_Rt_].allocatedReg.X(), m_gprs[_Rt_].allocatedReg.X(), Operand(x3, LSR, 32)); gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with shifted value } - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w1, arg1, 3); // Get low 2 bits + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT + gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask + gen.Mov(arg2, w3); // Mask in arg2 + gen.And(arg1, arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -578,7 +593,8 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - call(read32Wrapper); // Read value returned in w0 + gen.Mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -594,7 +610,8 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); // Read value returned in w0 + gen.Mov(arg2, mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -606,10 +623,16 @@ void DynaRecCPU::recLWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in w0 + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w1, arg1, 3); // Get low 2 bits + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT + gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask + gen.Mov(arg2, w3); // Mask in arg2 + gen.And(arg1, arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -625,11 +648,17 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.And(m_gprs[_Rt_].allocatedReg.X(), m_gprs[_Rt_].allocatedReg.X(), Operand(x3, LSR, 32)); gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with newly read value } - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(w1, arg1, 3); // Get low 2 bits + gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT + gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 + // bits of the unaligned addr. + gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask + gen.Mov(arg2, w3); // Mask in arg2 + gen.And(arg1, arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in eax if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1360,7 +1389,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, mask); // Mask in arg2 (??) + gen.Mov(arg2, 0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); @@ -1375,7 +1404,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, mask); // Mask in arg2 (??) + gen.Mov(arg2, 0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value @@ -1387,13 +1416,8 @@ void DynaRecCPU::recSWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // edx = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 - gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x0, Operand(x3, LSR, 32)); // Load mask value - gen.Mov(arg2, w0); + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1415,13 +1439,8 @@ void DynaRecCPU::recSWL(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // edx = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 - gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x0, Operand(x3, LSR, 32)); // Load mask value - gen.Mov(arg2, w0); + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1455,7 +1474,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, mask); // Mask in arg2 (??) + gen.Mov(arg2, 0); // Mask in arg2(??) call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); @@ -1470,7 +1489,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, mask); // Mask in arg2 (??) + gen.Mov(arg2, 0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value @@ -1482,13 +1501,8 @@ void DynaRecCPU::recSWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // w2 = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 - gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Mask in arg2 - gen.Mov(arg2, w3); + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1511,13 +1525,8 @@ void DynaRecCPU::recSWR(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w2, arg1, 3); // w2 = low 2 bits of address gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Base to mask and shift lookup table in x3 - gen.Ldr(x3, MemOperand(x3, x2, LSR, 29)); // Load the mask from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Mask in arg2 - gen.Mov(arg2, w3); + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 6c6f3c8b3..dfdadaa4f 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -802,14 +802,15 @@ void DynaRecCPU::recLWL(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg2, address & ~3); // Aligned address in arg2 + gen.mov(arg1, address & ~3); // Aligned address in arg1 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); return; } @@ -825,8 +826,9 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -839,8 +841,9 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -850,10 +853,17 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); + gen.and_(edx, 3); // Get the low 2 bits + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // ecx = mask now + gen.mov(arg2, ecx); // Mask in arg2 + gen.and_(arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -863,17 +873,24 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); + gen.and_(edx, 3); // Get the low 2 bits + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // ecx = mask now + gen.mov(arg2, ecx); // Mask in arg2 + gen.and_(arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -882,12 +899,12 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } } @@ -895,14 +912,15 @@ void DynaRecCPU::recLWR(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg2, address & ~3); // Aligned address in arg2 + gen.mov(arg1, address & ~3); // Aligned address in arg1 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it } - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); return; } @@ -918,8 +936,9 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -932,21 +951,29 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); - gen.andImm(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, mask); // Mask the previous $rt value - gen.shr(eax, shift); // Shift the value read from the aligned address - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Or $rt with shifted value - } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant + gen.andImm(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, mask); // Mask the previous $rt value + gen.shr(eax, shift); // Shift the value read from the aligned address + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Or $rt with shifted value + } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); + gen.and_(edx, 3); // Get the low 2 bits + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // ecx = mask now + gen.mov(arg1, ecx); // Mask in arg2 + gen.and_(arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -956,17 +983,24 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(edx, arg1); + gen.and_(edx, 3); // Get the low 2 bits + gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(rcx, 32); // ecx = mask now + gen.mov(arg1, ecx); // Mask in arg2 + gen.and_(arg1, ~3); // Force align it + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -975,12 +1009,12 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } } @@ -1145,7 +1179,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, mask); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value @@ -1160,7 +1194,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, mask); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value @@ -1175,12 +1209,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - gen.and_(edx, 3); // edx = low 2 bits of address - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // rcx = mask now - gen.mov(arg2, rcx); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1216,12 +1245,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - gen.and_(edx, 3); // edx = low 2 bits of address - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // rcx = mask now - gen.mov(arg2, rcx); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1267,7 +1291,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, mask); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value @@ -1282,7 +1306,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, mask); // Mask in arg2 + gen.mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value @@ -1296,8 +1320,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - // TODO: Emit assembly for mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1331,8 +1355,8 @@ void DynaRecCPU::recSWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - // TODO: Emit assembly for mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 2fed608a1..e55d80eda 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -104,12 +104,12 @@ class Memory { MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { std::cout << "Next bitmask " - << std::bitset<24>(nextBitmask) + << std::bitset<32>(nextBitmask) << " Init bitmap entry " - << std::bitset<24>(m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; + << std::bitset<32>(m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { std::cout << "Usable bitmap entry " - << std::bitset<24>(m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; + << std::bitset<32>(m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return MsanStatus::UNUSABLE; } @@ -138,6 +138,8 @@ class Memory { bool msanValidateWrite(uint32_t addr) { uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = ((1 << length) - 1) << addr % 8; + std::cout << "Bitmask " << std::bitset<32>(bitmask) + << " Next bitmask " << std::bitset<32>(bitmask >> 8) << std::endl; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; From 1613ac3510960341de3f4ab40c04d03d43b5a2db Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:18:23 +1000 Subject: [PATCH 11/68] Implemented masking for LWL/LWR calls Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 27a1fe30c..8383c434e 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -18,7 +18,6 @@ ***************************************************************************/ #include "recompiler.h" -#include "vixl/src/aarch64/operands-aarch64.h" #if defined(DYNAREC_AA64) #define BAILZERO(x) \ if (!(x)) { \ From 1c9c384b2dfd1ac8dfd0c849b441ff2b6793069f Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:22:06 +1000 Subject: [PATCH 12/68] Removed testing logs Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxmem.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/core/psxmem.h b/src/core/psxmem.h index e55d80eda..b0ddd4390 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,12 +19,9 @@ #pragma once -#include #include #include #include -#include -#include #include "core/psxemulator.h" #include "support/eventbus.h" @@ -97,19 +94,11 @@ class Memory { template MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { - std::cout << "Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; - std::cout << "Bitmask " << std::bitset<32>(bitmask) << std::endl; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { - std::cout << "Next bitmask " - << std::bitset<32>(nextBitmask) - << " Init bitmap entry " - << std::bitset<32>(m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { - std::cout << "Usable bitmap entry " - << std::bitset<32>(m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) << std::endl; if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return MsanStatus::UNUSABLE; } @@ -117,11 +106,7 @@ class Memory { } bitmask &= 0xff; } - std::cout << "Full init bitmap entry " - << std::bitset<32>(m_msanInitializedBitmap[bitmapIndex] & bitmask) << std::endl; if ((m_msanInitializedBitmap[bitmapIndex] & bitmask) != bitmask) [[unlikely]] { - std::cout << "Full usable bitmap entry " - << std::bitset<32>(m_msanUsableBitmap[bitmapIndex] & bitmask) << std::endl; if ((m_msanUsableBitmap[bitmapIndex] & bitmask) != bitmask) { return MsanStatus::UNUSABLE; } @@ -138,8 +123,6 @@ class Memory { bool msanValidateWrite(uint32_t addr) { uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = ((1 << length) - 1) << addr % 8; - std::cout << "Bitmask " << std::bitset<32>(bitmask) - << " Next bitmask " << std::bitset<32>(bitmask >> 8) << std::endl; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; From 53742a9307cf7c46e9266a13f8f1f344f2015cd2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:22:46 +1000 Subject: [PATCH 13/68] Removed testing logs Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxmem.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index 83f83a010..7d4cc811b 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -21,7 +21,6 @@ #include -#include #include #include From 0ecaf37a5fe1a944683dcf19d153929a099778cd Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:28:18 +1000 Subject: [PATCH 14/68] Added write32Masked call and refactored swl/swr calls to use it, allowing for sub-32-bit MSAN triggers Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 28 +++++++++++++++++---------- src/core/DynaRec_aa64/recompiler.h | 1 + src/core/DynaRec_x64/instructions.cc | 28 +++++++++++++++++---------- src/core/DynaRec_x64/recompiler.h | 1 + src/core/psxmem.cc | 4 ++-- src/core/psxmem.h | 11 ++++++++--- 6 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 8383c434e..3fc386dbb 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1394,8 +1394,9 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value - gen.Mov(arg1, alignedAddress); // Address in arg2 again - call(write32Wrapper); + gen.Mov(arg1, alignedAddress); // Address in arg1 again + gen.Mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1411,7 +1412,8 @@ void DynaRecCPU::recSWL(uint32_t code) { // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + gen.Mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1434,7 +1436,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - call(write32Wrapper); + gen.Mov(arg3, w0); + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1457,7 +1460,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - call(write32Wrapper); + gen.Mov(arg3, w0); + call(write32MaskedWrapper); } } @@ -1479,8 +1483,9 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value - gen.Mov(arg1, alignedAddress); // Address in arg2 again - call(write32Wrapper); + gen.Mov(arg1, alignedAddress); // Address in arg1 again + gen.Mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1496,7 +1501,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - call(write32Wrapper); // Write back + gen.Mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1520,7 +1526,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - call(write32Wrapper); + gen.Mov(arg3, w0); + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1544,7 +1551,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - call(write32Wrapper); + gen.Mov(arg3, w0); + call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index 4e8937833..d00875631 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -93,6 +93,7 @@ static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { static void write8Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write8(address, value); } static void write16Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write16(address, value); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } +static void write32MaskedWrapper(uint32_t address, uint32_t value, uint32_t msan_sub_bitmask) { PCSX::g_emulator->m_mem->write32Masked(address, value, msan_sub_bitmask); } using DynarecCallback = void (*)(); // A function pointer to JIT-emitted code diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index dfdadaa4f..da7e4af03 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1184,9 +1184,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value - gen.mov(arg1, alignedAddress); // Address in arg2 again + gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - call(write32Wrapper); + gen.mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1203,7 +1204,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shr(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - call(write32Wrapper); // Write back + gen.mov(arg3, mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1239,7 +1241,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, eax); + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1275,7 +1278,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, eax); + call(write32MaskedWrapper); } } @@ -1296,9 +1300,10 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value - gen.mov(arg1, alignedAddress); // Address in arg2 again + gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - call(write32Wrapper); + gen.mov(arg3, mask); /// Mask in arg3 + call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1315,7 +1320,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shlImm(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - call(write32Wrapper); // Write back + gen.mov(arg3, mask); /// Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1350,7 +1356,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, eax); + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1385,7 +1392,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, eax); + call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index cab1be136..2873e7957 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -86,6 +86,7 @@ static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } static uint32_t read32MaskedWrapper(uint32_t address, uint32_t msan_sub_bitmask) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } +static void write32MaskedWrapper(uint32_t address, uint32_t value, uint32_t msan_sub_bitmask) { PCSX::g_emulator->m_mem->write32Masked(address, value, msan_sub_bitmask); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); } diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index 7d4cc811b..8536e8ca0 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -586,7 +586,7 @@ void PCSX::Memory::write16(uint32_t address, uint32_t value) { } } -void PCSX::Memory::write32(uint32_t address, uint32_t value) { +void PCSX::Memory::write32Masked(uint32_t address, uint32_t value, const uint32_t msan_sub_bitmask) { g_emulator->m_cpu->m_regs.cycle += 1; const uint32_t page = address >> 16; const auto pointer = (uint8_t *)m_writeLUT[page]; @@ -594,7 +594,7 @@ void PCSX::Memory::write32(uint32_t address, uint32_t value) { if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { - if (msanValidateWrite<4>(address)) { + if (msanValidateWrite<4>(address, msan_sub_bitmask)) { *(uint32_t *)&m_msanRAM[address - c_msanStart] = SWAP_LEu32(value); } else { g_system->log(LogClass::CPU, _("32-bit write to unusable msan memory: %8.8lx\n"), address); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index b0ddd4390..a238c1a56 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -70,7 +71,8 @@ class Memory { inline uint32_t read32(uint32_t address, ReadType = ReadType::Data) { return read32Masked(address, UINT32_MAX); }; void write8(uint32_t address, uint32_t value); void write16(uint32_t address, uint32_t value); - void write32(uint32_t address, uint32_t value); + void write32Masked(uint32_t address, uint32_t value, const uint32_t msan_sub_bitmask); + inline void write32(uint32_t address, uint32_t value) { return write32Masked(address, value, UINT32_MAX); }; const void *pointerRead(uint32_t address); const void *pointerWrite(uint32_t address, int size); @@ -120,9 +122,9 @@ class Memory { // if the write is valid, marks the address as initialized, otherwise returns false template - bool msanValidateWrite(uint32_t addr) { + bool msanValidateWrite(uint32_t addr, const uint32_t sub_bitmask) { uint32_t bitmapIndex = (addr - c_msanStart) / 8; - uint32_t bitmask = ((1 << length) - 1) << addr % 8; + uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; @@ -137,6 +139,9 @@ class Memory { return true; } + template + inline bool msanValidateWrite(uint32_t addr) { return msanValidateWrite(addr, sub_bitmask); }; + void msanDmaWrite(uint32_t addr, uint32_t size) { if (!msanInitialized() || !inMsanRange(addr)) return; addr -= c_msanStart; From 48bdfd09f222edf819be16b62248c99866902da7 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:29:34 +1000 Subject: [PATCH 15/68] Registered write32Masked in symbols Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_x64/symbols.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index 2fe769d18..bd16b167c 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -102,6 +102,7 @@ void DynaRecCPU::makeSymbols() { REGISTER_CLASS_FUNCTION(PCSX::Memory::write8, "write8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write16, "write16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write32, "write32"); + REGISTER_CLASS_FUNCTION(PCSX::Memory::write32Masked, "write32"); REGISTER_CLASS_FUNCTION(PCSX::R3000Acpu::branchTest, "branch_test"); REGISTER_FUNCTION(exceptionWrapper, "fire_exception"); From 0722b5cc2550d08bfcc1e962f7df326dd435f515 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 18:30:18 +1000 Subject: [PATCH 16/68] Fixed register cloberring when applying shift + mask in SWR/SWL Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 56 ++++++++++++++------------- src/core/DynaRec_x64/instructions.cc | 20 ++++++---- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 3fc386dbb..e7025eb10 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -496,7 +496,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, mask); // Mask in arg2 + gen.Mov(arg2, ~mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -513,7 +513,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, mask); // Mask in arg2 + gen.Mov(arg2, ~mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -528,12 +528,12 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w1, arg1, 3); // Get low 2 bits + gen.And(x1, arg1, 3); // Get low 2 bits gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mov(arg2, w3); // Mask in arg2 + gen.Mvn(arg2, w3); // Mask in arg2 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -554,12 +554,12 @@ void DynaRecCPU::recLWL(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w1, arg1, 3); // Get low 2 bits + gen.And(x1, arg1, 3); // Get low 2 bits gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mov(arg2, w3); // Mask in arg2 + gen.Mvn(arg2, w3); // Mask in arg2 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -592,7 +592,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, mask); // Mask in arg2 + gen.Mov(arg2, ~mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -609,7 +609,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, mask); // Mask in arg2 + gen.Mov(arg2, ~mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -624,12 +624,12 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w1, arg1, 3); // Get low 2 bits + gen.And(x1, arg1, 3); // Get low 2 bits gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mov(arg2, w3); // Mask in arg2 + gen.Mvn(arg2, w3); // Mask in arg2 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -650,12 +650,12 @@ void DynaRecCPU::recLWR(uint32_t code) { } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(w1, arg1, 3); // Get low 2 bits + gen.And(x1, arg1, 3); // Get low 2 bits gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mov(arg2, w3); // Mask in arg2 + gen.Mvn(arg2, w3); // Mask in arg2 gen.And(arg1, arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in eax @@ -1395,7 +1395,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg1 again - gen.Mov(arg3, mask); // Mask in arg3 + gen.Mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1412,7 +1412,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, mask); // Mask in arg3 + gen.Mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1433,10 +1433,11 @@ void DynaRecCPU::recSWL(uint32_t code) { // bits of the unaligned addr. gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt gen.Lsr(arg2, arg2, w3); // Shift rt value - gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value + gen.Mov(x4, Operand(x3, LSR, 32)); + gen.Mvn(arg3, w4); + gen.And(x0, x0, x4); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, w0); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1457,10 +1458,11 @@ void DynaRecCPU::recSWL(uint32_t code) { // bits of the unaligned addr. gen.Lsr(arg2, m_gprs[_Rt_].allocatedReg, w3); // arg2 = shifted $rt - gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value + gen.Mov(x4, Operand(x3, LSR, 32)); + gen.Mvn(arg3, w4); + gen.And(x0, x0, x4); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - gen.Mov(arg3, w0); call(write32MaskedWrapper); } } @@ -1484,7 +1486,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg1 again - gen.Mov(arg3, mask); // Mask in arg3 + gen.Mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1501,7 +1503,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, mask); // Mask in arg3 + gen.Mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1521,12 +1523,13 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt - gen.Lsl(arg2, arg2, w3); // Shift rt value - gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value + gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt + gen.Lsl(arg2, arg2, w3); // Shift rt value + gen.Mov(x4, Operand(x3, LSR, 32)); + gen.Mvn(arg3, w4); + gen.And(x0, x0, x4); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, w0); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1548,10 +1551,11 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg2, m_gprs[_Rt_].allocatedReg); // arg2 = $rt gen.Lsl(arg2, arg2, w3); // Shift rt value - gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value + gen.Mov(x4, Operand(x3, LSR, 32)); + gen.Mvn(arg3, w4); + gen.And(x0, x0, x4); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Mov(arg3, w0); call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index da7e4af03..f9413b955 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1186,7 +1186,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, mask); // Mask in arg3 + gen.mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1204,7 +1204,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shr(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, mask); // Mask in arg3 + gen.mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1241,7 +1241,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, eax); + gen.mov(arg3, ecx); + gen.not_(arg3); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1278,7 +1279,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, eax); + gen.mov(arg3, ecx); + gen.not_(arg3); call(write32MaskedWrapper); } } @@ -1302,7 +1304,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, mask); /// Mask in arg3 + gen.mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1320,7 +1322,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shlImm(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, mask); /// Mask in arg3 + gen.mov(arg3, ~mask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1356,7 +1358,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, eax); + gen.mov(arg3, ecx); + gen.not_(arg3); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1392,7 +1395,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, eax); + gen.mov(arg3, ecx); + gen.not_(arg3); call(write32MaskedWrapper); } } From 9d880e6667bba9c2bdefa0fb6c51ff45b4268f32 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:28:18 +1000 Subject: [PATCH 17/68] Restored x64 instructions and fixed aarch64 msan mask to depend in immediate value instead of mask from LUT Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 142 ++++++++--------- src/core/DynaRec_x64/instructions.cc | 216 ++++++++++---------------- src/core/psxmem.h | 6 + 3 files changed, 147 insertions(+), 217 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index e7025eb10..b5c6b9dac 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -487,6 +487,7 @@ void DynaRecCPU::recLWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0x00FFFFFF00000018, 0x0000FFFF00000010, 0x000000FF00000008, 0}; + const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -496,7 +497,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, ~mask); // Mask in arg2 + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -510,11 +511,11 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; - const auto shift = LWL_SHIFT[address & 3]; + const uint32_t shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, ~mask); // Mask in arg2 - call(read32MaskedWrapper); // Read value returned in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32Wrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -526,16 +527,11 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(x1, arg1, 3); // Get low 2 bits - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT - gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mvn(arg2, w3); // Mask in arg2 - gen.And(arg1, arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, arg1, ~3); // Force align it + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -551,17 +547,12 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.And(m_gprs[_Rt_].allocatedReg.X(), m_gprs[_Rt_].allocatedReg.X(), Operand(x3, LSR, 32)); gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with shifted value } - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(x1, arg1, 3); // Get low 2 bits - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT - gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mvn(arg2, w3); // Mask in arg2 - gen.And(arg1, arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, arg1, ~3); // Force align it + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -583,6 +574,7 @@ void DynaRecCPU::recLWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0xFF00000000000008, 0xFFFF000000000010, 0xFFFFFF0000000018}; + const uint32_t msan_mask = 0b1111 >> _Imm_; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -592,7 +584,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, ~mask); // Mask in arg2 + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -609,7 +601,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, ~mask); // Mask in arg2 + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -622,15 +614,10 @@ void DynaRecCPU::recLWR(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(x1, arg1, 3); // Get low 2 bits - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT - gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mvn(arg2, w3); // Mask in arg2 - gen.And(arg1, arg1, ~3); // Force align it + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, arg1, ~3); // Force align it + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -647,16 +634,11 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.And(m_gprs[_Rt_].allocatedReg.X(), m_gprs[_Rt_].allocatedReg.X(), Operand(x3, LSR, 32)); gen.Orr(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, w0); // Merge with newly read value } - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(x1, arg1, 3); // Get low 2 bits - gen.Mov(x3, (uintptr_t)&MASKS_AND_SHIFTS); // Form PC-relative address to mask and shift LUT - gen.Ldr(x3, MemOperand(x3, x1, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 - // bits of the unaligned addr. - gen.Mov(x3, Operand(x3, LSR, 32)); // Extract mask - gen.Mvn(arg2, w3); // Mask in arg2 - gen.And(arg1, arg1, ~3); // Force align it + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, arg1, ~3); // Force align it + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax if (_Rt_) { @@ -1380,6 +1362,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; + const uint32_t msan_mask = (0b1110 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1388,14 +1371,14 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value - gen.Mov(arg1, alignedAddress); // Address in arg1 again - gen.Mov(arg3, ~mask); // Mask in arg3 + gen.Mov(arg1, alignedAddress); // Address in arg2 again + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1404,7 +1387,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value @@ -1412,14 +1395,14 @@ void DynaRecCPU::recSWL(uint32_t code) { // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + gen.Mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1433,18 +1416,17 @@ void DynaRecCPU::recSWL(uint32_t code) { // bits of the unaligned addr. gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt gen.Lsr(arg2, arg2, w3); // Shift rt value - gen.Mov(x4, Operand(x3, LSR, 32)); - gen.Mvn(arg3, w4); - gen.And(x0, x0, x4); // Mask read value + gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1458,11 +1440,10 @@ void DynaRecCPU::recSWL(uint32_t code) { // bits of the unaligned addr. gen.Lsr(arg2, m_gprs[_Rt_].allocatedReg, w3); // arg2 = shifted $rt - gen.Mov(x4, Operand(x3, LSR, 32)); - gen.Mvn(arg3, w4); - gen.And(x0, x0, x4); // Mask read value + gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1471,6 +1452,7 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; + const uint32_t msan_mask = 0b0111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1479,14 +1461,14 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0); // Mask in arg2(??) + gen.Mov(arg2, 0x0); call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value - gen.Mov(arg1, alignedAddress); // Address in arg1 again - gen.Mov(arg3, ~mask); // Mask in arg3 + gen.Mov(arg1, alignedAddress); // Address in arg2 again + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1495,7 +1477,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value @@ -1503,14 +1485,14 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + gen.Mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.And(arg1, arg1, ~3); // Force align it + gen.Mov(arg2, 0x0); + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1523,20 +1505,19 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Ldr(x3, MemOperand(x3, x2, LSL, 3)); // Load the mask and shift from LUT by indexing using the bottom 2 // bits of the unaligned addr. - gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt - gen.Lsl(arg2, arg2, w3); // Shift rt value - gen.Mov(x4, Operand(x3, LSR, 32)); - gen.Mvn(arg3, w4); - gen.And(x0, x0, x4); // Mask read value + gen.Mov(arg2, m_gprs[_Rt_].val); // arg2 = $rt + gen.Lsl(arg2, arg2, w3); // Shift rt value + gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it + gen.And(arg1, arg1, ~3); // Force align it gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1551,11 +1532,10 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Mov(arg2, m_gprs[_Rt_].allocatedReg); // arg2 = $rt gen.Lsl(arg2, arg2, w3); // Shift rt value - gen.Mov(x4, Operand(x3, LSR, 32)); - gen.Mvn(arg3, w4); - gen.And(x0, x0, x4); // Mask read value + gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index f9413b955..42ce460cb 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -802,15 +802,14 @@ void DynaRecCPU::recLWL(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg1, address & ~3); // Aligned address in arg1 + gen.mov(arg2, address & ~3); // Aligned address in arg2 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it } - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address return; } @@ -826,9 +825,8 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + gen.mov(arg2, alignedAddress); // Address in arg2 + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -841,9 +839,8 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; - gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + gen.mov(arg2, alignedAddress); // Address in arg2 + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -853,17 +850,10 @@ void DynaRecCPU::recLWL(uint32_t code) { } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); - gen.and_(edx, 3); // Get the low 2 bits - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // ecx = mask now - gen.mov(arg2, ecx); // Mask in arg2 - gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in eax + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -873,24 +863,17 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); - gen.and_(edx, 3); // Get the low 2 bits - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // ecx = mask now - gen.mov(arg2, ecx); // Mask in arg2 - gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in eax + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -899,12 +882,12 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shl(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } } @@ -912,15 +895,14 @@ void DynaRecCPU::recLWR(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg1, address & ~3); // Aligned address in arg1 + gen.mov(arg2, address & ~3); // Aligned address in arg2 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it } - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address return; } @@ -936,9 +918,8 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + gen.mov(arg2, alignedAddress); // Address in arg2 + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -951,29 +932,21 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; - gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + gen.mov(arg2, alignedAddress); // Address in arg2 + callMemoryFunc(&PCSX::Memory::read32); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); - gen.andImm(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, mask); // Mask the previous $rt value - gen.shr(eax, shift); // Shift the value read from the aligned address - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Or $rt with shifted value - } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant + gen.andImm(m_gprs[_Rt_].allocatedReg, m_gprs[_Rt_].allocatedReg, mask); // Mask the previous $rt value + gen.shr(eax, shift); // Shift the value read from the aligned address + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Or $rt with shifted value + } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant const uint32_t previousValue = m_gprs[_Rt_].val; - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); - gen.and_(edx, 3); // Get the low 2 bits - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // ecx = mask now - gen.mov(arg1, ecx); // Mask in arg2 - gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in eax + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -983,24 +956,17 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value - } else { // Nothing is constant - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); - gen.and_(edx, 3); // Get the low 2 bits - gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(rcx, 32); // ecx = mask now - gen.mov(arg1, ecx); // Mask in arg2 - gen.and_(arg1, ~3); // Force align it - call(read32MaskedWrapper); // Read from the aligned address, result in eax + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + } else { // Nothing is constant + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 + gen.and_(arg2, ~3); // Force align it + callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1009,12 +975,12 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in edx again gen.and_(edx, 3); // Get the low 2 bits gen.lea(rcx, qword[rip + MASKS_AND_SHIFTS]); // Base to mask and shift lookup table in rcx - gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of - // the unaligned addr. - gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) - gen.shr(rcx, 32); // ecx = mask now - gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value - gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value + gen.mov(rcx, qword[rcx + rdx * 8]); // Load the mask and shift from LUT by indexing using the bottom 2 bits of + // the unaligned addr. + gen.shr(eax, cl); // Shift the read value by the shift amount (This relies on x86 masking shift behavior) + gen.shr(rcx, 32); // ecx = mask now + gen.and_(m_gprs[_Rt_].allocatedReg, ecx); // Mask previous $rt value + gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } } @@ -1179,15 +1145,13 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + call(read32Wrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value - gen.mov(arg1, alignedAddress); // Address in arg1 again + gen.mov(arg1, alignedAddress); // Address in arg2 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + call(write32Wrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1195,8 +1159,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + call(read32Wrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1204,15 +1167,12 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shr(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1241,16 +1201,12 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, ecx); - gen.not_(arg3); - call(write32MaskedWrapper); + call(write32Wrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.mov(edx, arg1); gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1279,9 +1235,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, ecx); - gen.not_(arg3); - call(write32MaskedWrapper); + call(write32Wrapper); } } @@ -1297,15 +1251,13 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + call(read32Wrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value - gen.mov(arg1, alignedAddress); // Address in arg1 again + gen.mov(arg1, alignedAddress); // Address in arg2 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); + call(write32Wrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1313,8 +1265,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); + call(read32Wrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1322,14 +1273,12 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shlImm(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, ~mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + call(write32Wrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1358,15 +1307,12 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, ecx); - gen.not_(arg3); - call(write32MaskedWrapper); + call(write32Wrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + call(read32Wrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1395,9 +1341,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, ecx); - gen.not_(arg3); - call(write32MaskedWrapper); + call(write32Wrapper); } } diff --git a/src/core/psxmem.h b/src/core/psxmem.h index a238c1a56..7b9b782ea 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "core/psxemulator.h" #include "support/eventbus.h" @@ -96,8 +98,10 @@ class Memory { template MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { + std::cout << "[READ] Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; + std::cout << "[READ] Bitmask " << std::bitset<32>(bitmask) << std::endl; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { @@ -123,8 +127,10 @@ class Memory { // if the write is valid, marks the address as initialized, otherwise returns false template bool msanValidateWrite(uint32_t addr, const uint32_t sub_bitmask) { + std::cout << "[WRITE] Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; + std::cout << "[WRITE] Bitmask " << std::bitset<32>(bitmask) << std::endl; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; From 0c2b11ff5d42b54c177178fefff58ecf63253893 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:50:48 +1000 Subject: [PATCH 18/68] Fixed MSAN mask choosing registered bytes instead of memory-read bytes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index b5c6b9dac..86bbf103a 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1362,7 +1362,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; - const uint32_t msan_mask = (0b1110 << _Imm_) & 0b1111; + const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1452,7 +1452,7 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; - const uint32_t msan_mask = 0b0111 >> (3 - _Imm_); + const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; From 61e6842f92c5b33cfe8ffa5ff1c3be7299fd5f59 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:17:24 +1000 Subject: [PATCH 19/68] Implemented x64 equivalent of SWL/SWR and LWL/LWR msan fixes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 28 +++--- src/core/DynaRec_x64/instructions.cc | 122 ++++++++++++++++---------- 2 files changed, 89 insertions(+), 61 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 86bbf103a..c250970ed 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -1401,8 +1401,8 @@ void DynaRecCPU::recSWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1419,14 +1419,14 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1443,7 +1443,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1485,14 +1485,14 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, msan_mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + gen.Mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, 0x0); - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1510,14 +1510,14 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, 0x0); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1535,7 +1535,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 42ce460cb..3cfb3aa27 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -799,17 +799,18 @@ void DynaRecCPU::recLHU(uint32_t code) { recompileLoad<16, false>(code); } void DynaRecCPU::recLW(uint32_t code) { recompileLoad<32, true>(code); } void DynaRecCPU::recLWL(uint32_t code) { + const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg2, address & ~3); // Aligned address in arg2 + gen.mov(arg1, address & ~3); // Aligned address in arg1 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it } - - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address return; } @@ -825,8 +826,9 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -839,8 +841,9 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -851,9 +854,10 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -871,9 +875,10 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -892,17 +897,18 @@ void DynaRecCPU::recLWL(uint32_t code) { } void DynaRecCPU::recLWR(uint32_t code) { + const uint32_t msan_mask = 0b1111 >> _Imm_; if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg2, address & ~3); // Aligned address in arg2 + gen.mov(arg1, address & ~3); // Aligned address in arg1 } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it } - - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address return; } @@ -918,8 +924,9 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; const uint32_t previousValue = m_gprs[_Rt_].val; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -932,8 +939,9 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; - gen.mov(arg2, alignedAddress); // Address in arg2 - callMemoryFunc(&PCSX::Memory::read32); + gen.mov(arg1, alignedAddress); // Address in arg1 + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback m_gprs[_Rt_].setWriteback(true); @@ -944,9 +952,10 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -964,9 +973,10 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg2, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg2 - gen.and_(arg2, ~3); // Force align it - callMemoryFunc(&PCSX::Memory::read32); // Read from the aligned address, result in eax + gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, ~3); // Force align it + gen.mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1137,6 +1147,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; + const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1145,13 +1156,15 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val >> shift); // Shift $rt and or with read value - gen.mov(arg1, alignedAddress); // Address in arg2 again + gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1159,7 +1172,8 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1167,12 +1181,14 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shr(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - call(write32Wrapper); // Write back + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1201,12 +1217,14 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1235,7 +1253,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } } @@ -1243,6 +1262,7 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; + const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1251,13 +1271,15 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.or_(eax, m_gprs[_Rt_].val << shift); // Shift $rt and or with read value gen.mov(arg1, alignedAddress); // Address in arg2 again gen.mov(arg2, eax); // Address to write to in arg2 - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; @@ -1265,7 +1287,8 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - call(read32Wrapper); + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); gen.andImm(eax, eax, mask); // Mask read value gen.mov(arg1, alignedAddress); // Aligned address in arg1 again @@ -1273,12 +1296,14 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shlImm(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - call(write32Wrapper); // Write back + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt allocateReg(_Rs_); @@ -1307,12 +1332,14 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - call(read32Wrapper); // Read from the aligned address, result in eax + gen.mov(arg2, 0x0); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt alloc_rt_rs(code); @@ -1341,7 +1368,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - call(write32Wrapper); + gen.mov(arg3, msan_mask); // Mask in arg3 + call(write32MaskedWrapper); } } From 0738a12b7edbb8f4924af67bf982cd15ed1acaf0 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:18:00 +1000 Subject: [PATCH 20/68] Removed debug logs Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxmem.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 7b9b782ea..a238c1a56 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include "core/psxemulator.h" #include "support/eventbus.h" @@ -98,10 +96,8 @@ class Memory { template MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { - std::cout << "[READ] Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; - std::cout << "[READ] Bitmask " << std::bitset<32>(bitmask) << std::endl; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { @@ -127,10 +123,8 @@ class Memory { // if the write is valid, marks the address as initialized, otherwise returns false template bool msanValidateWrite(uint32_t addr, const uint32_t sub_bitmask) { - std::cout << "[WRITE] Sub bitmask " << std::bitset<32>(sub_bitmask) << std::endl; uint32_t bitmapIndex = (addr - c_msanStart) / 8; uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; - std::cout << "[WRITE] Bitmask " << std::bitset<32>(bitmask) << std::endl; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; From b3067715e1bb42a6c769aad68806a231dd12fbcd Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:24:16 +1000 Subject: [PATCH 21/68] Revered useless change Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index c250970ed..d98e3d109 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -511,7 +511,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; - const uint32_t shift = LWL_SHIFT[address & 3]; + const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 gen.Mov(arg2, msan_mask); // Mask in arg2 From 03d6ce17102ca850b620136e1f8242f218602d74 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:59:37 +1000 Subject: [PATCH 22/68] Whitespace fixes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index d98e3d109..34d0b455e 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -497,8 +497,8 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read value returned in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -514,7 +514,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msan_mask); // Mask in arg2 call(read32Wrapper); // Read value returned in w0 if (_Rt_) { @@ -530,8 +530,8 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -551,8 +551,8 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -584,8 +584,8 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read value returned in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -601,8 +601,8 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read value returned in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { allocateReg(_Rt_); // Allocate $rt with writeback @@ -617,8 +617,8 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in w0 + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -638,8 +638,8 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address, result in eax + gen.Mov(arg2, msan_mask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address, result in eax if (_Rt_) { // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1371,14 +1371,14 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0x0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val >> shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg2 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1387,7 +1387,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const auto shift = SWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0x0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value @@ -1396,7 +1396,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again gen.Mov(arg3, msan_mask); // Mask in arg3 - call(write32MaskedWrapper); // Write back + call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 @@ -1461,14 +1461,14 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0x0); + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(arg2, w0, mask); // Mask read value gen.Mov(w2, m_gprs[_Rt_].val << shift); gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg2 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msan_mask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1477,7 +1477,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const auto shift = SWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, 0x0); // Mask in arg2 + gen.Mov(arg2, 0x0); // Mask in arg2 call(read32MaskedWrapper); gen.andImm(w0, w0, mask); // Mask read value From f1aef19b617d36a31457d1aa46a536ab7d871ec3 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:04:34 +1000 Subject: [PATCH 23/68] Fixed LWR read mask Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 2 +- src/core/DynaRec_x64/instructions.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 34d0b455e..e2bcd31b0 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -574,7 +574,7 @@ void DynaRecCPU::recLWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0xFF00000000000008, 0xFFFF000000000010, 0xFFFFFF0000000018}; - const uint32_t msan_mask = 0b1111 >> _Imm_; + const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 3cfb3aa27..90d9431d4 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -897,7 +897,7 @@ void DynaRecCPU::recLWL(uint32_t code) { } void DynaRecCPU::recLWR(uint32_t code) { - const uint32_t msan_mask = 0b1111 >> _Imm_; + const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; From 9e4c90235b0100a730bf47a41f7e89a47b847c1b Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 13:06:37 +1000 Subject: [PATCH 24/68] Removed unused ctsdint header includes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/recompiler.h | 1 - src/core/psxmem.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index d00875631..678f2d2ad 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -18,7 +18,6 @@ ***************************************************************************/ #pragma once -#include #include "core/r3000a.h" #if defined(DYNAREC_AA64) diff --git a/src/core/psxmem.h b/src/core/psxmem.h index a238c1a56..17dc06298 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,7 +19,6 @@ #pragma once -#include #include #include #include From cdc40e36ae0513475e5ecfc870bdb714735c620d Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 16:50:08 +1000 Subject: [PATCH 25/68] Merged masked read32/write32 into base call to avoid redundant symbols and inline duplications per-compilation unit Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 40 +++++++++++----------- src/core/DynaRec_aa64/recompiler.h | 8 +++-- src/core/DynaRec_x64/instructions.cc | 48 +++++++++++++-------------- src/core/DynaRec_x64/recompiler.h | 8 +++-- src/core/DynaRec_x64/symbols.cc | 4 +-- src/core/psxmem.cc | 8 ++--- src/core/psxmem.h | 21 ++++-------- 7 files changed, 69 insertions(+), 68 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index e2bcd31b0..b05fd917e 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -487,7 +487,7 @@ void DynaRecCPU::recLWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0x00FFFFFF00000018, 0x0000FFFF00000010, 0x000000FF00000008, 0}; - const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); + const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -497,7 +497,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -514,7 +514,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32Wrapper); // Read value returned in w0 if (_Rt_) { @@ -530,7 +530,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -551,7 +551,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -574,7 +574,7 @@ void DynaRecCPU::recLWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0xFF00000000000008, 0xFFFF000000000010, 0xFFFFFF0000000018}; - const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -584,7 +584,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -601,7 +601,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.Mov(arg1, alignedAddress); // Address in arg1 - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read value returned in w0 if (_Rt_) { @@ -617,7 +617,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -638,7 +638,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msan_mask); // Mask in arg2 + gen.Mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax if (_Rt_) { @@ -1362,7 +1362,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; - const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); + const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1378,7 +1378,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg2 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1395,7 +1395,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // Value to write back to memory in $arg2 gen.Orr(arg2, w0, Operand(m_gprs[_Rt_].allocatedReg, LSR, shift)); gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1419,7 +1419,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1443,7 +1443,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1452,7 +1452,7 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; - const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1468,7 +1468,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Orr(arg2, arg2, w2); // Shift $rt and or with read value gen.Mov(arg1, alignedAddress); // Address in arg2 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1485,7 +1485,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.Lsl(arg2, m_gprs[_Rt_].allocatedReg, shift); // arg2 = shifted $rt gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, alignedAddress); // Aligned address in arg1 again - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1510,7 +1510,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1535,7 +1535,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Mov(arg3, msan_mask); // Mask in arg3 + gen.Mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_aa64/recompiler.h b/src/core/DynaRec_aa64/recompiler.h index 678f2d2ad..02a500711 100644 --- a/src/core/DynaRec_aa64/recompiler.h +++ b/src/core/DynaRec_aa64/recompiler.h @@ -83,7 +83,9 @@ static uint8_t read8Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read8(address); } static uint16_t read16Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read16(address); } static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } -static uint32_t read32MaskedWrapper(uint32_t address, uint32_t msan_sub_bitmask) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } +static uint32_t read32MaskedWrapper(uint32_t address, const uint32_t msanSubBitmask) { + return PCSX::g_emulator->m_mem->read32(address, PCSX::Memory::ReadType::Data, msanSubBitmask); +} static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); @@ -92,7 +94,9 @@ static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { static void write8Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write8(address, value); } static void write16Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write16(address, value); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } -static void write32MaskedWrapper(uint32_t address, uint32_t value, uint32_t msan_sub_bitmask) { PCSX::g_emulator->m_mem->write32Masked(address, value, msan_sub_bitmask); } +static void write32MaskedWrapper(uint32_t address, uint32_t value, const uint32_t msanSubBitmask) { + PCSX::g_emulator->m_mem->write32(address, value, msanSubBitmask); +} using DynarecCallback = void (*)(); // A function pointer to JIT-emitted code diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 90d9431d4..e471ec15c 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -799,7 +799,7 @@ void DynaRecCPU::recLHU(uint32_t code) { recompileLoad<16, false>(code); } void DynaRecCPU::recLW(uint32_t code) { recompileLoad<32, true>(code); } void DynaRecCPU::recLWL(uint32_t code) { - const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); + const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -809,8 +809,8 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it } - gen.mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address + gen.mov(arg2, msanReadMask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address return; } @@ -827,7 +827,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback @@ -842,7 +842,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const auto shift = LWL_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback @@ -856,7 +856,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -877,7 +877,7 @@ void DynaRecCPU::recLWL(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -897,7 +897,7 @@ void DynaRecCPU::recLWL(uint32_t code) { } void DynaRecCPU::recLWR(uint32_t code) { - const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -907,8 +907,8 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it } - gen.mov(arg2, msan_mask); // Mask in arg2 - call(read32MaskedWrapper); // Read from the aligned address + gen.mov(arg2, msanReadMask); // Mask in arg2 + call(read32MaskedWrapper); // Read from the aligned address return; } @@ -925,7 +925,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback @@ -940,7 +940,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const auto shift = LWR_SHIFT[address & 3]; gen.mov(arg1, alignedAddress); // Address in arg1 - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); allocateReg(_Rt_); // Allocate $rt with writeback @@ -954,7 +954,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -975,7 +975,7 @@ void DynaRecCPU::recLWR(uint32_t code) { allocateReg(_Rs_); // Allocate address reg gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msan_mask); // Mask in arg2 + gen.mov(arg2, msanReadMask); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1147,7 +1147,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; - const uint32_t msan_mask = 0b1111 >> (3 - _Imm_); + const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1163,7 +1163,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, alignedAddress); // Address in arg1 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1181,7 +1181,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shr(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1217,7 +1217,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1253,7 +1253,7 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1262,7 +1262,7 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; - const uint32_t msan_mask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1278,7 +1278,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, alignedAddress); // Address in arg2 again gen.mov(arg2, eax); // Address to write to in arg2 - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else if (m_gprs[_Rs_].isConst()) { // Only address is constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; @@ -1296,7 +1296,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg2, m_gprs[_Rt_].allocatedReg); // Move rt to arg2 gen.shlImm(arg2, shift); // Shift rt value gen.or_(arg2, eax); // Or with read value - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); // Write back } else if (m_gprs[_Rt_].isConst()) { // Only previous rt value is constant allocateReg(_Rs_); // Allocate address reg @@ -1332,7 +1332,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1368,7 +1368,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msan_mask); // Mask in arg3 + gen.mov(arg3, msanWriteMask); // Mask in arg3 call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index 2873e7957..df5f86b49 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -84,9 +84,13 @@ #define _ImmLU_ _fImmLU_(code) static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } -static uint32_t read32MaskedWrapper(uint32_t address, uint32_t msan_sub_bitmask) { return PCSX::g_emulator->m_mem->read32Masked(address, msan_sub_bitmask); } +static uint32_t read32MaskedWrapper(uint32_t address, const uint32_t msanSubBitmask) { + return PCSX::g_emulator->m_mem->read32Masked(address, PCSX::Memory::ReadType::Data, msanSubBitmask); +} static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } -static void write32MaskedWrapper(uint32_t address, uint32_t value, uint32_t msan_sub_bitmask) { PCSX::g_emulator->m_mem->write32Masked(address, value, msan_sub_bitmask); } +static void write32MaskedWrapper(uint32_t address, uint32_t value, const uint32_t msanSubBitmask) { + PCSX::g_emulator->m_mem->write32Masked(address, value, msanSubBitmask); +} static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); } diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index bd16b167c..0a05675e7 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -93,16 +93,16 @@ void DynaRecCPU::makeSymbols() { } REGISTER_FUNCTION(read32Wrapper, "read32_wrapper"); + REGISTER_FUNCTION(read32MaskedWrapper, "read32_masked_wrapper"); REGISTER_FUNCTION(write32Wrapper, "write32_wrapper"); + REGISTER_FUNCTION(write32MaskedWrapper, "write32_masked_wrapper"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read8, "read8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read16, "read16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::read32, "read32"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::read32Masked, "read32"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write8, "write8"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write16, "write16"); REGISTER_CLASS_FUNCTION(PCSX::Memory::write32, "write32"); - REGISTER_CLASS_FUNCTION(PCSX::Memory::write32Masked, "write32"); REGISTER_CLASS_FUNCTION(PCSX::R3000Acpu::branchTest, "branch_test"); REGISTER_FUNCTION(exceptionWrapper, "fire_exception"); diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index 8536e8ca0..80c34a943 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -387,7 +387,7 @@ uint16_t PCSX::Memory::read16(uint32_t address) { return 0xffff; } -uint32_t PCSX::Memory::read32Masked(uint32_t address, const uint32_t msan_sub_bitmask, ReadType readType) { +uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType, const uint32_t msanSubBitmask) { if (readType == ReadType::Data) g_emulator->m_cpu->m_regs.cycle += 1; const uint32_t page = address >> 16; const auto pointer = (uint8_t *)m_readLUT[page]; @@ -395,7 +395,7 @@ uint32_t PCSX::Memory::read32Masked(uint32_t address, const uint32_t msan_sub_bi if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { - switch (msanGetStatus<4>(address, msan_sub_bitmask)) { + switch (msanGetStatus<4>(address, msanSubBitmask)) { case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); @@ -586,7 +586,7 @@ void PCSX::Memory::write16(uint32_t address, uint32_t value) { } } -void PCSX::Memory::write32Masked(uint32_t address, uint32_t value, const uint32_t msan_sub_bitmask) { +void PCSX::Memory::write32(uint32_t address, uint32_t value, const uint32_t msanSubBitmask) { g_emulator->m_cpu->m_regs.cycle += 1; const uint32_t page = address >> 16; const auto pointer = (uint8_t *)m_writeLUT[page]; @@ -594,7 +594,7 @@ void PCSX::Memory::write32Masked(uint32_t address, uint32_t value, const uint32_ if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { - if (msanValidateWrite<4>(address, msan_sub_bitmask)) { + if (msanValidateWrite<4>(address, msanSubBitmask)) { *(uint32_t *)&m_msanRAM[address - c_msanStart] = SWAP_LEu32(value); } else { g_system->log(LogClass::CPU, _("32-bit write to unusable msan memory: %8.8lx\n"), address); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 17dc06298..796f7dc4e 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -66,12 +67,10 @@ class Memory { uint8_t read8(uint32_t address); uint16_t read16(uint32_t address); - uint32_t read32Masked(uint32_t address, const uint32_t msan_sub_bitmask, ReadType = ReadType::Data); - inline uint32_t read32(uint32_t address, ReadType = ReadType::Data) { return read32Masked(address, UINT32_MAX); }; + uint32_t read32(uint32_t address, ReadType = ReadType::Data, const uint32_t msanSubBitmask = UINT32_MAX); void write8(uint32_t address, uint32_t value); void write16(uint32_t address, uint32_t value); - void write32Masked(uint32_t address, uint32_t value, const uint32_t msan_sub_bitmask); - inline void write32(uint32_t address, uint32_t value) { return write32Masked(address, value, UINT32_MAX); }; + void write32(uint32_t address, uint32_t value, const uint32_t msanSubBitmask = UINT32_MAX); const void *pointerRead(uint32_t address); const void *pointerWrite(uint32_t address, int size); @@ -94,9 +93,9 @@ class Memory { uint32_t msanGetChainPtr(uint32_t addr) const; template - MsanStatus msanGetStatus(uint32_t addr, const uint32_t sub_bitmask) const { + MsanStatus msanGetStatus(uint32_t addr, const uint32_t subBitmask = UINT32_MAX) const { uint32_t bitmapIndex = (addr - c_msanStart) / 8; - uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; + uint32_t bitmask = (((1 << length) - 1) << addr % 8) & subBitmask; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { @@ -116,14 +115,11 @@ class Memory { return bestCase; } - template - inline MsanStatus msanGetStatus(uint32_t addr) const { return msanGetStatus(addr, sub_bitmask); }; - // if the write is valid, marks the address as initialized, otherwise returns false template - bool msanValidateWrite(uint32_t addr, const uint32_t sub_bitmask) { + bool msanValidateWrite(uint32_t addr, const uint32_t subBitmask = UINT32_MAX) { uint32_t bitmapIndex = (addr - c_msanStart) / 8; - uint32_t bitmask = (((1 << length) - 1) << addr % 8) & sub_bitmask; + uint32_t bitmask = (((1 << length) - 1) << addr % 8) & subBitmask; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return false; @@ -138,9 +134,6 @@ class Memory { return true; } - template - inline bool msanValidateWrite(uint32_t addr) { return msanValidateWrite(addr, sub_bitmask); }; - void msanDmaWrite(uint32_t addr, uint32_t size) { if (!msanInitialized() || !inMsanRange(addr)) return; addr -= c_msanStart; From 45487a3654d49cc120a2b2d7d350a649486a73c2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 17:19:26 +1000 Subject: [PATCH 26/68] Merged masked read32/write32 into base call to avoid redundant symbols and inline duplications per-compilation unit Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_x64/recompiler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/DynaRec_x64/recompiler.h b/src/core/DynaRec_x64/recompiler.h index df5f86b49..8f4ae12ac 100644 --- a/src/core/DynaRec_x64/recompiler.h +++ b/src/core/DynaRec_x64/recompiler.h @@ -85,11 +85,11 @@ static uint32_t read32Wrapper(uint32_t address) { return PCSX::g_emulator->m_mem->read32(address); } static uint32_t read32MaskedWrapper(uint32_t address, const uint32_t msanSubBitmask) { - return PCSX::g_emulator->m_mem->read32Masked(address, PCSX::Memory::ReadType::Data, msanSubBitmask); + return PCSX::g_emulator->m_mem->read32(address, PCSX::Memory::ReadType::Data, msanSubBitmask); } static void write32Wrapper(uint32_t address, uint32_t value) { PCSX::g_emulator->m_mem->write32(address, value); } static void write32MaskedWrapper(uint32_t address, uint32_t value, const uint32_t msanSubBitmask) { - PCSX::g_emulator->m_mem->write32Masked(address, value, msanSubBitmask); + PCSX::g_emulator->m_mem->write32(address, value, msanSubBitmask); } static void SPU_writeRegisterWrapper(uint32_t addr, uint16_t value) { PCSX::g_emulator->m_spu->writeRegister(addr, value); From 3cee0a00fe450fc3441377b20ee59fe180513398 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 17:45:34 +1000 Subject: [PATCH 27/68] Refactored interpreted CPU LWL/LWR/SWL/SWR to use new masked variants of read32/write32 Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxinterpreter.cc | 42 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/core/psxinterpreter.cc b/src/core/psxinterpreter.cc index d7a0654c3..feff723ff 100644 --- a/src/core/psxinterpreter.cc +++ b/src/core/psxinterpreter.cc @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ +#include "core/DynaRec_aa64/recompiler.h" #include "core/callstacks.h" #include "core/debug.h" #include "core/disr3000a.h" @@ -25,6 +26,7 @@ #include "core/pgxp_debug.h" #include "core/pgxp_gte.h" #include "core/psxemulator.h" +#include "core/psxmem.h" #include "core/r3000a.h" #include "tracy/Tracy.hpp" @@ -935,7 +937,8 @@ void InterpretedCPU::psxLW(uint32_t code) { void InterpretedCPU::psxLWL(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; - uint32_t mem = PCSX::g_emulator->m_mem->read32(addr & ~3); + const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); + uint32_t mem = read32MaskedWrapper(addr & ~3, msanReadMask); // load delay = 1 latency if (!_Rt_) return; @@ -953,7 +956,8 @@ void InterpretedCPU::psxLWL(uint32_t code) { void InterpretedCPU::psxLWR(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; - uint32_t mem = PCSX::g_emulator->m_mem->read32(addr & ~3); + const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; + uint32_t mem = read32MaskedWrapper(addr & ~3, msanReadMask); // load delay = 1 latency if (!_Rt_) return; @@ -998,15 +1002,14 @@ void InterpretedCPU::psxSWL(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; addr ^= shift; - uint32_t mem; - // special handling to avoid msan interpreting this as a read - if (PCSX::g_emulator->m_mem->msanInitialized() && PCSX::g_emulator->m_mem->inMsanRange(addr)) { - mem = *(uint32_t *)&PCSX::g_emulator->m_mem->m_msanRAM[addr - PCSX::Memory::c_msanStart]; - } else { - mem = PCSX::g_emulator->m_mem->read32(addr); - } - - PCSX::g_emulator->m_mem->write32(addr, (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift])); + // 0x0 mask avoids msan interpreting this as a read + uint32_t mem = read32MaskedWrapper(addr, 0x0); + const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); + write32MaskedWrapper( + addr, + (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift]), + msanWriteMask + ); /* Mem = 1234. Reg = abcd 0 123a (reg >> 24) | (mem & 0xffffff00) @@ -1020,15 +1023,14 @@ void InterpretedCPU::psxSWR(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; addr ^= shift; - uint32_t mem; - // special handling to avoid msan interpreting this as a read - if (PCSX::g_emulator->m_mem->msanInitialized() && PCSX::g_emulator->m_mem->inMsanRange(addr)) { - mem = *(uint32_t *)&PCSX::g_emulator->m_mem->m_msanRAM[addr - PCSX::Memory::c_msanStart]; - } else { - mem = PCSX::g_emulator->m_mem->read32(addr); - } - - PCSX::g_emulator->m_mem->write32(addr, (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift])); + // 0x0 mask avoids msan interpreting this as a read + uint32_t mem = read32MaskedWrapper(addr, 0x0); + const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; + write32MaskedWrapper( + addr, + (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift]), + msanWriteMask + ); /* Mem = 1234. Reg = abcd From 09e817a9dca27b44074e303fe80601e980945cee Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 19:10:52 +1000 Subject: [PATCH 28/68] Fixed read32/write32 calls in interpreted CPU and fixed warnings for C-style casts in x64 symbols construction Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_x64/symbols.cc | 5 +++-- src/core/psxinterpreter.cc | 29 ++++++++++++++++++++++------- src/core/psxmem.h | 1 - 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index 0a05675e7..be9e3ea19 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -26,9 +26,10 @@ #include "fmt/format.h" #define REGISTER_VARIABLE(variable, name, size) \ - m_symbols += fmt::format("{} {} {}\n", (void*)&(variable), (name), (size)) + m_symbols += fmt::format("{} {} {}\n", static_cast(&(variable)), (name), (size)) -#define REGISTER_FUNCTION(function, name) m_symbols += fmt::format("{} {}\n", (void*)&(function), (name)); +#define REGISTER_FUNCTION(function, name) \ + m_symbols += fmt::format("{} {}\n", static_cast(&(function)), (name)); #define REGISTER_CLASS_FUNCTION(function, name) \ { \ diff --git a/src/core/psxinterpreter.cc b/src/core/psxinterpreter.cc index feff723ff..9dd85ac25 100644 --- a/src/core/psxinterpreter.cc +++ b/src/core/psxinterpreter.cc @@ -17,7 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ -#include "core/DynaRec_aa64/recompiler.h" #include "core/callstacks.h" #include "core/debug.h" #include "core/disr3000a.h" @@ -938,7 +937,11 @@ void InterpretedCPU::psxLWL(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); - uint32_t mem = read32MaskedWrapper(addr & ~3, msanReadMask); + uint32_t mem = PCSX::g_emulator->m_mem->read32( + addr & ~3, + PCSX::Memory::ReadType::Data, + msanReadMask + ); // load delay = 1 latency if (!_Rt_) return; @@ -957,7 +960,11 @@ void InterpretedCPU::psxLWR(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; - uint32_t mem = read32MaskedWrapper(addr & ~3, msanReadMask); + uint32_t mem = PCSX::g_emulator->m_mem->read32( + addr & ~3, + PCSX::Memory::ReadType::Data, + msanReadMask + ); // load delay = 1 latency if (!_Rt_) return; @@ -1003,9 +1010,13 @@ void InterpretedCPU::psxSWL(uint32_t code) { uint32_t shift = addr & 3; addr ^= shift; // 0x0 mask avoids msan interpreting this as a read - uint32_t mem = read32MaskedWrapper(addr, 0x0); + uint32_t mem = PCSX::g_emulator->m_mem->read32( + addr, + PCSX::Memory::ReadType::Data, + 0x0 + ); const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); - write32MaskedWrapper( + PCSX::g_emulator->m_mem->write32( addr, (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift]), msanWriteMask @@ -1024,9 +1035,13 @@ void InterpretedCPU::psxSWR(uint32_t code) { uint32_t shift = addr & 3; addr ^= shift; // 0x0 mask avoids msan interpreting this as a read - uint32_t mem = read32MaskedWrapper(addr, 0x0); + uint32_t mem = PCSX::g_emulator->m_mem->read32( + addr, + PCSX::Memory::ReadType::Data, + 0x0 + ); const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; - write32MaskedWrapper( + PCSX::g_emulator->m_mem->write32( addr, (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift]), msanWriteMask diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 796f7dc4e..7d951f5b5 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -19,7 +19,6 @@ #pragma once -#include #include #include #include From 8dc8b859cd46e6aa094241edc2294ead14e03b4d Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 19:46:03 +1000 Subject: [PATCH 29/68] Disambiguated partial and fully initialised reads with MSAN Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/gpu.cc | 10 ++++++++++ src/core/psxhw.h | 5 +++++ src/core/psxmem.cc | 15 +++++++++++++++ src/core/psxmem.h | 12 ++++++++---- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/core/gpu.cc b/src/core/gpu.cc index cf9067c24..2f6b35c12 100644 --- a/src/core/gpu.cc +++ b/src/core/gpu.cc @@ -327,6 +327,11 @@ uint32_t PCSX::GPU::gpuDmaChainSize(uint32_t addr) { if (usingMsan && PCSX::Memory::inMsanRange(addr)) { addr &= 0xfffffffc; switch (g_emulator->m_mem->msanGetStatus<4>(addr)) { + case PCSX::MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::GPU, + _("GPU DMA went into uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + addr); + break; case PCSX::MsanStatus::UNINITIALIZED: g_system->log(LogClass::GPU, _("GPU DMA went into usable but uninitialized msan memory: %8.8lx\n"), addr); @@ -596,6 +601,11 @@ void PCSX::GPU::chainedDMAWrite(const uint32_t *memory, uint32_t hwAddr) { addr &= 0xfffffffc; const uint32_t *headerPtr = (uint32_t *)(g_emulator->m_mem->m_msanRAM + (addr - PCSX::Memory::c_msanStart)); switch (g_emulator->m_mem->msanGetStatus<4>(addr)) { + case PCSX::MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::GPU, + _("GPU DMA went into uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + addr); + break; case PCSX::MsanStatus::UNINITIALIZED: g_system->log(LogClass::GPU, _("GPU DMA went into usable but uninitialized msan memory: %8.8lx\n"), addr); diff --git a/src/core/psxhw.h b/src/core/psxhw.h index 167c5d00e..27f000e8c 100644 --- a/src/core/psxhw.h +++ b/src/core/psxhw.h @@ -78,6 +78,11 @@ class HW { if (usingMsan && PCSX::Memory::inMsanRange(madr)) { madr &= 0xfffffffc; switch (g_emulator->m_mem->msanGetStatus<4>(madr)) { + case MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::GPU, + _("GPU DMA went into uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + madr); + break; case PCSX::MsanStatus::UNINITIALIZED: g_system->log(LogClass::GPU, _("GPU DMA went into usable but uninitialized msan memory: %8.8lx\n"), diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index 80c34a943..944de2f5d 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -297,6 +297,11 @@ uint8_t PCSX::Memory::read8(uint32_t address) { if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) [[unlikely]] { switch (msanGetStatus<1>(address)) { + case MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::CPU, + _("8-bit read from uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + address); + break; case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("8-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); @@ -348,6 +353,11 @@ uint16_t PCSX::Memory::read16(uint32_t address) { if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { switch (msanGetStatus<2>(address)) { + case MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::CPU, + _("16-bit read from uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + address); + break; case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("16-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); @@ -396,6 +406,11 @@ uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType, const uint32_ if (pointer != nullptr) { if (msanInitialized() && inMsanRange(address)) { switch (msanGetStatus<4>(address, msanSubBitmask)) { + case MsanStatus::PARTIALLY_INITIALIZED: + g_system->log(LogClass::CPU, + _("32-bit read from uninitialized bytes in usable, partially initialized msan memory: %8.8lx\n"), + address); + break; case MsanStatus::UNINITIALIZED: g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 7d951f5b5..2b5580657 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -50,6 +50,8 @@ namespace PCSX { enum class MsanStatus { UNUSABLE, // memory that hasn't been allocated or has been freed UNINITIALIZED, // allocated memory that has never been written to, has undefined contents + PARTIALLY_INITIALIZED, // allocated memory that has had 1-3 bytes written to via SWR/SWL and + // an read to the address load at least one ofthe unwritten bytes OK // free to use }; @@ -97,19 +99,21 @@ class Memory { uint32_t bitmask = (((1 << length) - 1) << addr % 8) & subBitmask; MsanStatus bestCase = MsanStatus::OK; if (uint32_t nextBitmask = bitmask >> 8) [[unlikely]] { - if ((m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { + const uint8_t nextInitEntry = m_msanInitializedBitmap[bitmapIndex + 1] & nextBitmask; + if (nextInitEntry != nextBitmask) { if ((m_msanUsableBitmap[bitmapIndex + 1] & nextBitmask) != nextBitmask) { return MsanStatus::UNUSABLE; } - bestCase = MsanStatus::UNINITIALIZED; + bestCase = nextInitEntry ? MsanStatus::PARTIALLY_INITIALIZED : MsanStatus::UNINITIALIZED; } bitmask &= 0xff; } - if ((m_msanInitializedBitmap[bitmapIndex] & bitmask) != bitmask) [[unlikely]] { + const uint8_t initEntry = m_msanInitializedBitmap[bitmapIndex] & bitmask; + if (initEntry != bitmask) [[unlikely]] { if ((m_msanUsableBitmap[bitmapIndex] & bitmask) != bitmask) { return MsanStatus::UNUSABLE; } - return MsanStatus::UNINITIALIZED; + return initEntry ? MsanStatus::PARTIALLY_INITIALIZED : MsanStatus::UNINITIALIZED; } return bestCase; } From 3d9f7c8ce8520426b3ebbcd3510e4ef87747eb46 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 19:49:14 +1000 Subject: [PATCH 30/68] Disambiguated partial and fully initialised reads with MSAN Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxmem.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 2b5580657..f8864f51a 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -48,11 +48,11 @@ namespace PCSX { enum class MsanStatus { - UNUSABLE, // memory that hasn't been allocated or has been freed - UNINITIALIZED, // allocated memory that has never been written to, has undefined contents - PARTIALLY_INITIALIZED, // allocated memory that has had 1-3 bytes written to via SWR/SWL and - // an read to the address load at least one ofthe unwritten bytes - OK // free to use + UNUSABLE, // memory that hasn't been allocated or has been freed + UNINITIALIZED, // allocated memory that has never been written to, has undefined contents + PARTIALLY_INITIALIZED, // allocated memory that has had 1-3 bytes written to via SWR/SWL and + // an read to the address load at least one ofthe unwritten bytes + OK // free to use }; class Memory { From aacc53520c81dc997a675360d7adc278e53e4fed Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 20:31:55 +1000 Subject: [PATCH 31/68] Changed static_cast to reinterpret_cast for variable/function registration Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_x64/symbols.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/DynaRec_x64/symbols.cc b/src/core/DynaRec_x64/symbols.cc index be9e3ea19..5398229c3 100644 --- a/src/core/DynaRec_x64/symbols.cc +++ b/src/core/DynaRec_x64/symbols.cc @@ -26,10 +26,10 @@ #include "fmt/format.h" #define REGISTER_VARIABLE(variable, name, size) \ - m_symbols += fmt::format("{} {} {}\n", static_cast(&(variable)), (name), (size)) + m_symbols += fmt::format("{} {} {}\n", reinterpret_cast(&(variable)), (name), (size)) #define REGISTER_FUNCTION(function, name) \ - m_symbols += fmt::format("{} {}\n", static_cast(&(function)), (name)); + m_symbols += fmt::format("{} {}\n", reinterpret_cast(&(function)), (name)); #define REGISTER_CLASS_FUNCTION(function, name) \ { \ From 1298529c895eedfbbc66e6d7ee42f48596c31de1 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sat, 20 Jun 2026 21:21:29 +1000 Subject: [PATCH 32/68] Fixed comment on partial initialisation for MSAN status Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/psxmem.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/psxmem.h b/src/core/psxmem.h index f8864f51a..708fe0455 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -50,8 +50,8 @@ namespace PCSX { enum class MsanStatus { UNUSABLE, // memory that hasn't been allocated or has been freed UNINITIALIZED, // allocated memory that has never been written to, has undefined contents - PARTIALLY_INITIALIZED, // allocated memory that has had 1-3 bytes written to via SWR/SWL and - // an read to the address load at least one ofthe unwritten bytes + PARTIALLY_INITIALIZED, // allocated memory that has had some subset of bytes being read initialised + // but not all of them OK // free to use }; From 2449d4fd0cc2e26607756882c06c9068e415c19a Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:47:45 +1000 Subject: [PATCH 33/68] WIP MSAN test suites Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/Makefile | 4 + src/mips/tests/msan_invalid/Makefile | 51 ++++ src/mips/tests/msan_invalid/msan_invalid.c | 146 +++++++++ src/mips/tests/msan_valid/Makefile | 51 ++++ src/mips/tests/msan_valid/msan_valid.c | 336 +++++++++++++++++++++ tests/pcsxrunner/msan.cc | 254 ++++++++++++++++ 6 files changed, 842 insertions(+) create mode 100644 src/mips/tests/msan_invalid/Makefile create mode 100644 src/mips/tests/msan_invalid/msan_invalid.c create mode 100644 src/mips/tests/msan_valid/Makefile create mode 100644 src/mips/tests/msan_valid/msan_valid.c create mode 100644 tests/pcsxrunner/msan.cc diff --git a/src/mips/tests/Makefile b/src/mips/tests/Makefile index 6006a8841..ff38e61b8 100644 --- a/src/mips/tests/Makefile +++ b/src/mips/tests/Makefile @@ -38,6 +38,8 @@ all: $(MAKE) -C libc all $(MAKE) -C memcpy all $(MAKE) -C memset all + $(MAKE) -C msan_invalid all + $(MAKE) -C msan_valid all $(MAKE) -C pcdrv all $(MAKE) -C psyqo all $(MAKE) -C regwrites all @@ -83,6 +85,8 @@ clean: $(MAKE) -C libc clean $(MAKE) -C memcpy clean $(MAKE) -C memset clean + $(MAKE) -C msan_invalid clean + $(MAKE) -C msan_valid clean $(MAKE) -C pcdrv clean $(MAKE) -C psyqo clean $(MAKE) -C regwrites clean diff --git a/src/mips/tests/msan_invalid/Makefile b/src/mips/tests/msan_invalid/Makefile new file mode 100644 index 000000000..8d6758940 --- /dev/null +++ b/src/mips/tests/msan_invalid/Makefile @@ -0,0 +1,51 @@ +TARGET = msan-invalid +USE_FUNCTION_SECTIONS = false +TYPE = ps-exe + +SRCS = \ +../uC-sdk-glue/BoardConsole.c \ +../uC-sdk-glue/BoardInit.c \ +../uC-sdk-glue/init.c \ +\ +../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ +../../../../third_party/uC-sdk/libc/src/errno.c \ +../../../../third_party/uC-sdk/libc/src/initfini.c \ +../../../../third_party/uC-sdk/libc/src/malloc.c \ +../../../../third_party/uC-sdk/libc/src/qsort.c \ +../../../../third_party/uC-sdk/libc/src/rand.c \ +../../../../third_party/uC-sdk/libc/src/reent.c \ +../../../../third_party/uC-sdk/libc/src/stdio.c \ +../../../../third_party/uC-sdk/libc/src/string.c \ +../../../../third_party/uC-sdk/libc/src/strto.c \ +../../../../third_party/uC-sdk/libc/src/unistd.c \ +../../../../third_party/uC-sdk/libc/src/xprintf.c \ +../../../../third_party/uC-sdk/libc/src/xscanf.c \ +../../../../third_party/uC-sdk/libc/src/yscanf.c \ +../../../../third_party/uC-sdk/os/src/devfs.c \ +../../../../third_party/uC-sdk/os/src/filesystem.c \ +../../../../third_party/uC-sdk/os/src/fio.c \ +../../../../third_party/uC-sdk/os/src/hash-djb2.c \ +../../../../third_party/uC-sdk/os/src/init.c \ +../../../../third_party/uC-sdk/os/src/osdebug.c \ +../../../../third_party/uC-sdk/os/src/romfs.c \ +../../../../third_party/uC-sdk/os/src/sbrk.c \ + + +CPPFLAGS = -DNOFLOATINGPOINT +CPPFLAGS += -I. +CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include +CPPFLAGS += -I../../../../third_party/uC-sdk/os/include +CPPFLAGS += -I../../../../third_party/libcester/include +CPPFLAGS += -I../../openbios/uC-sdk-glue + +ifeq ($(PCSX_TESTS),true) +CPPFLAGS += -DPCSX_TESTS=1 +endif + +SRCS += \ +../../common/syscalls/printf.s \ +../../common/crt0/uC-sdk-crt0.s \ +../cop0/exceptions.cpp \ +msan_invalid.c \ + +include ../../common.mk diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c new file mode 100644 index 000000000..de9d13f1f --- /dev/null +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -0,0 +1,146 @@ +/* + +MIT License + +Copyright (c) 2020 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#ifndef PCSX_TESTS +#define PCSX_TESTS 0 +#endif + +#if PCSX_TESTS +#define CESTER_MAYBE_TEST CESTER_SKIP_TEST +#else +#define CESTER_MAYBE_TEST CESTER_TEST +#endif + +#include "common/hardware/dma.h" +#include "common/hardware/hwregs.h" +#include "common/hardware/psxhw.h" + +#undef unix +#define CESTER_NO_SIGNAL +#define CESTER_NO_TIME +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#include "exotic/cester.h" + +// clang-format off + +CESTER_BEFORE_ALL(msan_invalid_tests, + pcsx_initMsan(); +) + +CESTER_AFTER_ALL(msan_invalid_tests, + pcsx_resetMsan(); +) + +// clang-format on + +CESTER_TEST(msan_32bit_sw_lw, msan_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344; + register uint32_t result = 0; + __asm__ __volatile__( + "sw %1, 0(%2)", + "lw %0, 0(%2)" + : "=r"(result) + : "r"(value), "r"(mem_32_bit) + ); + pcsx_msanFree(mem_32_bit); + EXPECT_EQ(result, value); +) + +// SWL -> LWL + +#define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ + CESTER_TEST(name, msan_invalid_tests \ + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ + register uint32_t value = 0x11223344; \ + __asm__ __volatile__( \ + "sw" #sw_suffix " %0, " #store_off "(%1)", \ + : \ + : "r"(value), "r"(mem_32_bit) \ + ); \ + register uint32_t result = 0xAABBCCDD; \ + __asm__ __volatile__( \ + "lw" lw_suffix " %0 " #load_off "(%1)", \ + : "=r"(result) \ + : "r"(mem_32_bit) \ + ); \ + ASSERT_EQ(result, 0xAABBCCDD); \ + pcsx_msanFree(mem_32_bit); \ + SUCCESS(); \ + ) + +// SWL => LWL +INVALID_SWX_LWX(msan_8bit_swl_16bit_lwl, l, l, 0, 1) +INVALID_SWX_LWX(msan_8bit_swl_24bit_lwl, l, l, 0, 2) +INVALID_SWX_LWX(msan_8bit_swl_32bit_lwl, l, l, 0, 3) + +INVALID_SWX_LWX(msan_16bit_swl_24bit_lwl, l, l, 1, 2) +INVALID_SWX_LWX(msan_16bit_swl_32bit_lwl, l, l, 1, 3) + +INVALID_SWX_LWX(msan_24bit_swl_32bit_lwl, l, l, 2, 3) + +// SWL => LWR +INVALID_SWX_LWX(msan_8bit_swl_32bit_lwr, l, r, 0, 0) +INVALID_SWX_LWX(msan_8bit_swl_24bit_lwr, l, r, 0, 1) +INVALID_SWX_LWX(msan_8bit_swl_16bit_lwr, l, r, 0, 2) +INVALID_SWX_LWX(msan_8bit_swl_8bit_lwr, l, r, 0, 3) + +INVALID_SWX_LWX(msan_16bit_swl_32bit_lwr, l, r, 1, 0) +INVALID_SWX_LWX(msan_16bit_swl_24bit_lwr, l, r, 1, 1) +INVALID_SWX_LWX(msan_16bit_swl_16bit_lwr, l, r, 1, 2) +INVALID_SWX_LWX(msan_16bit_swl_8bit_lwr, l, r, 1, 3) + +INVALID_SWX_LWX(msan_24bit_swl_32bit_lwr, l, r, 2, 0) +INVALID_SWX_LWX(msan_24bit_swl_24bit_lwr, l, r, 2, 1) +INVALID_SWX_LWX(msan_24bit_swl_16bit_lwr, l, r, 2, 2) +INVALID_SWX_LWX(msan_24bit_swl_8bit_lwr, l, r, 2, 3) + +// SWR => LWR +INVALID_SWX_LWX(msan_8bit_swr_32bit_lwr, r, r, 3, 0) +INVALID_SWX_LWX(msan_8bit_swr_24bit_lwr, r, r, 3, 1) +INVALID_SWX_LWX(msan_8bit_swr_16bit_lwr, r, r, 3, 2) + +INVALID_SWX_LWX(msan_16bit_swr_32bit_lwr, r, r, 2, 0) +INVALID_SWX_LWX(msan_16bit_swr_24bit_lwr, r, r, 2, 1) + +INVALID_SWX_LWX(msan_24bit_swr_32bit_lwr, r, r, 3, 0) + +// SWR => LWL +INVALID_SWX_LWX(msan_8bit_swr_32bit_lwl, r, l, 3, 0) +INVALID_SWX_LWX(msan_8bit_swr_24bit_lwl, r, l, 3, 1) +INVALID_SWX_LWX(msan_8bit_swr_16bit_lwl, r, l, 3, 2) +INVALID_SWX_LWX(msan_8bit_swr_8bit_lwl, r, l, 3, 3) + +INVALID_SWX_LWX(msan_16bit_swr_32bit_lwl, r, l, 2, 0) +INVALID_SWX_LWX(msan_16bit_swr_24bit_lwl, r, l, 2, 1) +INVALID_SWX_LWX(msan_16bit_swr_16bit_lwl, r, l, 2, 2) +INVALID_SWX_LWX(msan_16bit_swr_8bit_lwl, r, l, 2, 3) + +INVALID_SWX_LWX(msan_24bit_swr_32bit_lwl, r, l, 1, 0) +INVALID_SWX_LWX(msan_24bit_swr_24bit_lwl, r, l, 1, 1) +INVALID_SWX_LWX(msan_24bit_swr_16bit_lwl, r, l, 1, 2) +INVALID_SWX_LWX(msan_24bit_swr_8bit_lwl, r, l, 1, 3) diff --git a/src/mips/tests/msan_valid/Makefile b/src/mips/tests/msan_valid/Makefile new file mode 100644 index 000000000..6e9cb6958 --- /dev/null +++ b/src/mips/tests/msan_valid/Makefile @@ -0,0 +1,51 @@ +TARGET = msan-valid +USE_FUNCTION_SECTIONS = false +TYPE = ps-exe + +SRCS = \ +../uC-sdk-glue/BoardConsole.c \ +../uC-sdk-glue/BoardInit.c \ +../uC-sdk-glue/init.c \ +\ +../../../../third_party/uC-sdk/libc/src/cxx-glue.c \ +../../../../third_party/uC-sdk/libc/src/errno.c \ +../../../../third_party/uC-sdk/libc/src/initfini.c \ +../../../../third_party/uC-sdk/libc/src/malloc.c \ +../../../../third_party/uC-sdk/libc/src/qsort.c \ +../../../../third_party/uC-sdk/libc/src/rand.c \ +../../../../third_party/uC-sdk/libc/src/reent.c \ +../../../../third_party/uC-sdk/libc/src/stdio.c \ +../../../../third_party/uC-sdk/libc/src/string.c \ +../../../../third_party/uC-sdk/libc/src/strto.c \ +../../../../third_party/uC-sdk/libc/src/unistd.c \ +../../../../third_party/uC-sdk/libc/src/xprintf.c \ +../../../../third_party/uC-sdk/libc/src/xscanf.c \ +../../../../third_party/uC-sdk/libc/src/yscanf.c \ +../../../../third_party/uC-sdk/os/src/devfs.c \ +../../../../third_party/uC-sdk/os/src/filesystem.c \ +../../../../third_party/uC-sdk/os/src/fio.c \ +../../../../third_party/uC-sdk/os/src/hash-djb2.c \ +../../../../third_party/uC-sdk/os/src/init.c \ +../../../../third_party/uC-sdk/os/src/osdebug.c \ +../../../../third_party/uC-sdk/os/src/romfs.c \ +../../../../third_party/uC-sdk/os/src/sbrk.c \ + + +CPPFLAGS = -DNOFLOATINGPOINT +CPPFLAGS += -I. +CPPFLAGS += -I../../../../third_party/uC-sdk/libc/include +CPPFLAGS += -I../../../../third_party/uC-sdk/os/include +CPPFLAGS += -I../../../../third_party/libcester/include +CPPFLAGS += -I../../openbios/uC-sdk-glue + +ifeq ($(PCSX_TESTS),true) +CPPFLAGS += -DPCSX_TESTS=1 +endif + +SRCS += \ +../../common/syscalls/printf.s \ +../../common/crt0/uC-sdk-crt0.s \ +../cop0/exceptions.cpp \ +msan_valid.c \ + +include ../../common.mk diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c new file mode 100644 index 000000000..773c2a55b --- /dev/null +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -0,0 +1,336 @@ +/* + +MIT License + +Copyright (c) 2020 PCSX-Redux authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include "mips/common/hardware/pcsxhw.h" +#ifndef PCSX_TESTS +#define PCSX_TESTS 0 +#endif + +#if PCSX_TESTS +#define CESTER_MAYBE_TEST CESTER_SKIP_TEST +#else +#define CESTER_MAYBE_TEST CESTER_TEST +#endif + +#include "common/hardware/dma.h" +#include "common/hardware/hwregs.h" +#include "common/hardware/psxhw.h" + +#undef unix +#define CESTER_NO_SIGNAL +#define CESTER_NO_TIME +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#include "exotic/cester.h" + +// clang-format off + +CESTER_BEFORE_ALL(msan_valid_tests, + pcsx_initMsan(); +) + +CESTER_AFTER_ALL(msan_valid_tests, + pcsx_resetMsan(); +) + +// clang-format on + +CESTER_TEST(msan_32bit_sw_lw, msan_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + register uint32_t result = 0; + __asm__ __volatile__( + "sw %1, 0(%2)", + "lw %0, 0(%2)" + : "=r"(result) + : "r"(value), "r"(mem_32_bit) + ); + pcsx_msanFree(mem_32_bit); + EXPECT_EQ(result, value); +) + +// SWL -> LWL + +CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store upper byte 0x11 + __asm__ __volatile__( + "swl %0, 0(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x11BBCCDD); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store upper 2 bytes 0x1122 + __asm__ __volatile__( + "swl %0, 1(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x22BBCCDD); + result = 0 + __asm__ __volatile__( + "lwl %0 1(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x1122CCDD); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store upper 3 bytes 0x112233 + __asm__ __volatile__( + "swl %0, 2(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x33BBCCDD); + result = 0 + __asm__ __volatile__( + "lwl %0 1(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x2233CCDD); + result = 0 + __asm__ __volatile__( + "lwl %0 2(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x112233DD); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store all 4 bytes 0x11223344 + __asm__ __volatile__( + "swl %0, 3(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x44BBCCDD); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 1(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x3344CCDD); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 2(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x223344DD); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 3(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x11223344); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lw %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x11223344); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +// SWR -> LWR + +CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store lower byte 0x44 + __asm__ __volatile__( + "swr %0, 3(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 3(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABBCC44); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store lower 2 bytes 0x3344 + __asm__ __volatile__( + "swr %0, 2(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 3(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABBCC44); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 2(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABB3344); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store lower 3 bytes 0x223344 + __asm__ __volatile__( + "swr %0, 1(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 3(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABBCC44); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 2(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABB3344); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 1(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAA223344); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + +CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests + register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t value = 0x11223344 + // Store lower 3 bytes 0x11223344 + __asm__ __volatile__( + "swr %0, 0(%1)", + : + : "r"(value), "r"(mem_32_bit) + ); + register uint32_t result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 3(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABBCC44); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 2(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAABB3344); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 1(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0xAA223344); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lwl %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x11223344); + result = 0xAABBCCDD; + __asm__ __volatile__( + "lw %0 0(%1)", + : "=r"(result) + : "r"(mem_32_bit) + ); + ASSERT_EQ(result, 0x11223344); + pcsx_msanFree(mem_32_bit); + SUCCESS(); +) + diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc new file mode 100644 index 000000000..ff2938bc6 --- /dev/null +++ b/tests/pcsxrunner/msan.cc @@ -0,0 +1,254 @@ +/*************************************************************************** + * Copyright (C) 2021 PCSX-Redux authors * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "main/main.h" +#include "core/psxmem.h" +#include "core/system.h" +#include "core/psxemulator.h" +#include "support/eventbus.h" + +static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); + +TEST(CPU, InterpreterValid) { + using namespace std::chrono_literals; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + "-luacov", "-loadexe", "src/mips/tests/cpu/msan-valid.ps-exe"); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS + && ret.load() != INT_MIN + && !PCSX::g_system->quitting()) { + if (!PCSX::g_system->running()) { + FAIL() << "Emulator should never pause in valid MSAN cases"; + } + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (thread.joinable()) { + thread.join(); + } + EXPECT_EQ(ret.load(), 0); +} + +TEST(CPU, DynarecValid) { + using namespace std::chrono_literals; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", + "-luacov", "-loadexe", "src/mips/tests/cpu/msan-valid.ps-exe"); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS + && ret.load() != INT_MIN + && !PCSX::g_system->quitting()) { + if (!PCSX::g_system->running()) { + FAIL() << "Emulator should never pause in valid MSAN cases"; + } + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (thread.joinable()) { + thread.join(); + } + EXPECT_EQ(ret.load(), 0); +} + +inline uint16_t inspectMsanInitializedBitmap(const uint32_t address) { + const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; + return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; +} + +inline uint8_t inspectMsanUsableBitmap(const uint32_t address) { + const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; + return PCSX::g_emulator->m_mem->m_msanUsableBitmap[bitmapIndex]; +} + +static unsigned int nextMsanCheckIndex = 0; +static uint8_t SWX_EXPECTED_BITMASKS[36] = { + 0b0001, // swl 0 (8-bit) => lwl 1 (16-bit) + 0b0001, // swl 0 (8-bit) => lwl 2 (24-bit) + 0b0001, // swl 0 (8-bit) => lwl 3 (32-bit) + // + 0b0011, // swl 1 (16-bit) => lwl 2 (24-bit) + 0b0011, // swl 1 (16-bit) => lwl 3 (32-bit) + + 0b0111, // swl 2 (24-bit) => lwl 3 (32-bit) + + 0b0001, // swl (8-bit) => lwr (32-bit) + 0b0001, // swl (8-bit) => lwr (24-bit) + 0b0001, // swl (8-bit) => lwr (16-bit) + 0b0001, // swl (8-bit) => lwr (8-bit) + + 0b0011, // swl (16-bit) => lwr (32-bit) + 0b0011, // swl (16-bit) => lwr (24-bit) + 0b0011, // swl (16-bit) => lwr (16-bit) + 0b0011, // swl (16-bit) => lwr (8-bit) + + 0b0111, // swl (24-bit) => lwr (32-bit) + 0b0111, // swl (24-bit) => lwr (24-bit) + 0b0111, // swl (24-bit) => lwr (16-bit) + 0b0111, // swl (24-bit) => lwr (8-bit) + + 0b1000, // swr (8-bit) => lwr (32-bit) + 0b1000, // swr (8-bit) => lwr (24-bit) + 0b1000, // swr (8-bit) => lwr (16-bit) + + 0b1100, // swr (16-bit) => lwr (32-bit) + 0b1100, // swr (16-bit) => lwr (24-bit) + + 0b1110, // swr (24-bit) => lwr (32-bit) + + 0b1000, // swr (8-bit) => lwl (32-bit) + 0b1000, // swr (8-bit) => lwl (24-bit) + 0b1000, // swr (8-bit) => lwl (16-bit) + 0b1000, // swr (8-bit) => lwl (8-bit) + + 0b1100, // swr (16-bit) => lwl (32-bit) + 0b1100, // swr (16-bit) => lwl (24-bit) + 0b1100, // swr (16-bit) => lwl (16-bit) + 0b1100, // swr (16-bit) => lwl (8-bit) + + 0b1110, // swr (24-bit) => lwl (32-bit) + 0b1110, // swr (24-bit) => lwl (24-bit) + 0b1110, // swr (24-bit) => lwl (16-bit) + 0b1110, // swr (24-bit) => lwl (8-bit) +}; + +std::optional nextMsanTest(const std::string& msg) { + if (PCSX::g_system->running()) { + return "Expected emulator to be paused"; + } + auto allocCount = PCSX::g_emulator->m_mem->m_msanAllocs.size(); + if (allocCount != 1) { + std::stringstream returnMsg; + returnMsg << "Expected 1 MSAN allocation, got"; + returnMsg << allocCount; + return returnMsg.str(); + } else if (nextMsanCheckIndex >= 36) { + std::stringstream returnMsg; + returnMsg << "Invalid MSAN check test index: "; + returnMsg << nextMsanCheckIndex; + return returnMsg.str(); + } + auto alloc = PCSX::g_emulator->m_mem->m_msanAllocs.begin(); + std::stringstream ss; + ss << "32-bit read from uninitialized bytes in usable, partially initialized msan memory: 0x" << std::hex << alloc->first; + std::string expectedMsg = ss.str(); + if (msg != expectedMsg) { + std::stringstream returnMsg; + returnMsg << "Inavlid MSAN event logged, expected: "; + returnMsg << expectedMsg; + returnMsg << ", got :"; + returnMsg << msg; + return returnMsg.str(); + } + PCSX::g_system->resume(); + return std::nullopt; +} + +TEST(CPU, InterpreterInvalid) { + using namespace std::chrono_literals; + nextMsanCheckIndex = 0; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + "-luacov", "-loadexe", "src/mips/tests/cpu/msan-invalid.ps-exe"); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + std::chrono::milliseconds elapsed(0); + std::optional result = std::nullopt; + PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); + listener.listen([&](const PCSX::Events::LogMessage& event) { + if (event.logClass != PCSX::LogClass::CPU + || event.message.starts_with("32-bit read")) { + return; + } + nextMsanTest(event.message); + }); + PCSX::g_system->resume(); + while (elapsed < _30s_MILLIS + && ret.load() != INT_MIN + && !PCSX::g_system->quitting()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (thread.joinable()) { + thread.join(); + } + if (result.has_value()) { + FAIL() << result.value(); + } + const int exit_code = ret.load(); + if (exit_code == INT_MIN) { + FAIL() << "Test timed out"; + } + EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; +} + +TEST(CPU, DynarecInvalid) { + using namespace std::chrono_literals; + nextMsanCheckIndex = 0; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", + "-luacov", "-loadexe", "src/mips/tests/cpu/msan-invalid.ps-exe"); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + std::chrono::milliseconds elapsed(0); + std::optional result = std::nullopt; + PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); + listener.listen([](const PCSX::Events::LogMessage& event) { + if (event.logClass != PCSX::LogClass::CPU + || event.message.starts_with("32-bit read")) { + return; + } + nextMsanTest(event.message); + }); + PCSX::g_system->resume(); + while (elapsed < _30s_MILLIS + && ret.load() != INT_MIN + && !PCSX::g_system->quitting()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (thread.joinable()) { + thread.join(); + } + if (result.has_value()) { + FAIL() << result.value(); + } + const int exit_code = ret.load(); + if (exit_code == INT_MIN) { + FAIL() << "Test timed out"; + } + EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; +} From a61f8dd614b5d93885a1d21d13bd90548da383f2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:51:58 +1000 Subject: [PATCH 34/68] WIP MSAN test suites Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index ff2938bc6..6a5c68f9d 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -44,9 +44,8 @@ TEST(CPU, InterpreterValid) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN - && !PCSX::g_system->quitting()) { - if (!PCSX::g_system->running()) { + && ret.load() != INT_MIN) { + if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { FAIL() << "Emulator should never pause in valid MSAN cases"; } std::this_thread::sleep_for(200ms); @@ -68,9 +67,8 @@ TEST(CPU, DynarecValid) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN - && !PCSX::g_system->quitting()) { - if (!PCSX::g_system->running()) { + && ret.load() != INT_MIN) { + if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { FAIL() << "Emulator should never pause in valid MSAN cases"; } std::this_thread::sleep_for(200ms); @@ -196,8 +194,7 @@ TEST(CPU, InterpreterInvalid) { }); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN - && !PCSX::g_system->quitting()) { + && ret.load() != INT_MIN) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -235,8 +232,7 @@ TEST(CPU, DynarecInvalid) { }); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN - && !PCSX::g_system->quitting()) { + && ret.load() != INT_MIN) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } From 7ff46741bba5c47f10fa9c5f802f9ea3ccb55a89 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:58:04 +1000 Subject: [PATCH 35/68] Added missing bitmap check to MSAN tests Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 6a5c68f9d..1bec176b7 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -18,6 +18,7 @@ ***************************************************************************/ #include +#include #include #include #include @@ -80,16 +81,11 @@ TEST(CPU, DynarecValid) { EXPECT_EQ(ret.load(), 0); } -inline uint16_t inspectMsanInitializedBitmap(const uint32_t address) { +inline uint8_t inspectMsanInitializedBitmap(const uint32_t address) { const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; } -inline uint8_t inspectMsanUsableBitmap(const uint32_t address) { - const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; - return PCSX::g_emulator->m_mem->m_msanUsableBitmap[bitmapIndex]; -} - static unsigned int nextMsanCheckIndex = 0; static uint8_t SWX_EXPECTED_BITMASKS[36] = { 0b0001, // swl 0 (8-bit) => lwl 1 (16-bit) @@ -169,6 +165,15 @@ std::optional nextMsanTest(const std::string& msg) { returnMsg << msg; return returnMsg.str(); } + const uint8_t expectedInitBitmap = SWX_EXPECTED_BITMASKS[nextMsanCheckIndex]; + const uint8_t actualInitBitmap = inspectMsanInitializedBitmap(alloc->first); + if (expectedInitBitmap != actualInitBitmap) { + std::stringstream ss; + ss << "Initialized bitmap for address 0x" << std::hex << alloc->first + << " mismatch: 0b" << std::bitset<8>(expectedInitBitmap) + << " != 0b " << std::bitset<8>(actualInitBitmap); + return ss.str(); + } PCSX::g_system->resume(); return std::nullopt; } From 7386cab170fc79e1f0dbb4850f8c15f4ed648171 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:10:27 +1000 Subject: [PATCH 36/68] Fixed compilation issue with pcsxhw.h header and log line Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/Makefile | 1 - src/mips/tests/msan_invalid/msan_invalid.c | 2 +- src/mips/tests/msan_valid/msan_valid.c | 2 +- tests/pcsxrunner/msan.cc | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mips/tests/msan_invalid/Makefile b/src/mips/tests/msan_invalid/Makefile index 8d6758940..970ade403 100644 --- a/src/mips/tests/msan_invalid/Makefile +++ b/src/mips/tests/msan_invalid/Makefile @@ -43,7 +43,6 @@ CPPFLAGS += -DPCSX_TESTS=1 endif SRCS += \ -../../common/syscalls/printf.s \ ../../common/crt0/uC-sdk-crt0.s \ ../cop0/exceptions.cpp \ msan_invalid.c \ diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index de9d13f1f..bab89d1fe 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -36,7 +36,7 @@ SOFTWARE. #include "common/hardware/dma.h" #include "common/hardware/hwregs.h" -#include "common/hardware/psxhw.h" +#include "common/hardware/pcsxhw.h" #undef unix #define CESTER_NO_SIGNAL diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index 773c2a55b..84b730c54 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -37,7 +37,7 @@ SOFTWARE. #include "common/hardware/dma.h" #include "common/hardware/hwregs.h" -#include "common/hardware/psxhw.h" +#include "common/hardware/pcsxhw.h" #undef unix #define CESTER_NO_SIGNAL diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 1bec176b7..06539bb38 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -171,7 +171,7 @@ std::optional nextMsanTest(const std::string& msg) { std::stringstream ss; ss << "Initialized bitmap for address 0x" << std::hex << alloc->first << " mismatch: 0b" << std::bitset<8>(expectedInitBitmap) - << " != 0b " << std::bitset<8>(actualInitBitmap); + << " != 0b" << std::bitset<8>(actualInitBitmap); return ss.str(); } PCSX::g_system->resume(); From 89b9119bfabe4591aeaca283877e03a5714d68ca Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:30:36 +1000 Subject: [PATCH 37/68] Fixed test format Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 4 ++-- src/mips/tests/msan_valid/msan_valid.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index bab89d1fe..335a9d8cc 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -57,7 +57,7 @@ CESTER_AFTER_ALL(msan_invalid_tests, // clang-format on -CESTER_TEST(msan_32bit_sw_lw, msan_tests +CESTER_TEST(msan_32bit_sw_lw, msan_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344; register uint32_t result = 0; @@ -74,7 +74,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests // SWL -> LWL #define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ - CESTER_TEST(name, msan_invalid_tests \ + CESTER_TEST(name, msan_invalid_tests, \ register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ register uint32_t value = 0x11223344; \ __asm__ __volatile__( \ diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index 84b730c54..f6259a9d9 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -58,7 +58,7 @@ CESTER_AFTER_ALL(msan_valid_tests, // clang-format on -CESTER_TEST(msan_32bit_sw_lw, msan_tests +CESTER_TEST(msan_32bit_sw_lw, msan_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 register uint32_t result = 0; @@ -74,7 +74,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests // SWL -> LWL -CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests +CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper byte 0x11 @@ -94,7 +94,7 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests +CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 2 bytes 0x1122 @@ -121,7 +121,7 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests +CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 3 bytes 0x112233 @@ -155,7 +155,7 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests +CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store all 4 bytes 0x11223344 @@ -205,7 +205,7 @@ CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests // SWR -> LWR -CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests +CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower byte 0x44 @@ -225,7 +225,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests +CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 2 bytes 0x3344 @@ -252,7 +252,7 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests +CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x223344 @@ -286,7 +286,7 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests SUCCESS(); ) -CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests +CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x11223344 From 0ea228e8a4526c890a3fb8f599a6736ef713cf8f Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:46:48 +1000 Subject: [PATCH 38/68] Fixed pointer case and ASM clobber decl (=r -> +r) Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 18 +-- src/mips/tests/msan_valid/msan_valid.c | 144 ++++++++++----------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index 335a9d8cc..478359206 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -58,12 +58,12 @@ CESTER_AFTER_ALL(msan_invalid_tests, // clang-format on CESTER_TEST(msan_32bit_sw_lw, msan_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344; - register uint32_t result = 0; + register volatile uint32_t result = 0; __asm__ __volatile__( - "sw %1, 0(%2)", - "lw %0, 0(%2)" + "sw %1, 0(%2);", + "lw %0, 0(%2);" : "=r"(result) : "r"(value), "r"(mem_32_bit) ); @@ -75,17 +75,17 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, #define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ CESTER_TEST(name, msan_invalid_tests, \ - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ register uint32_t value = 0x11223344; \ __asm__ __volatile__( \ - "sw" #sw_suffix " %0, " #store_off "(%1)", \ + "sw" #sw_suffix " %0, " #store_off "(%1);" \ : \ : "r"(value), "r"(mem_32_bit) \ ); \ - register uint32_t result = 0xAABBCCDD; \ + register volatile uint32_t result = 0xAABBCCDD; \ __asm__ __volatile__( \ - "lw" lw_suffix " %0 " #load_off "(%1)", \ - : "=r"(result) \ + "lw" lw_suffix " %0 " #load_off "(%1);" \ + : "+r"(result) \ : "r"(mem_32_bit) \ ); \ ASSERT_EQ(result, 0xAABBCCDD); \ diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index f6259a9d9..dc719e4ba 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -59,12 +59,12 @@ CESTER_AFTER_ALL(msan_valid_tests, // clang-format on CESTER_TEST(msan_32bit_sw_lw, msan_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 - register uint32_t result = 0; + register volatile uint32_t result = 0; __asm__ __volatile__( - "sw %1, 0(%2)", - "lw %0, 0(%2)" + "sw %1, 0(%2);" + "lw %0, 0(%2);" : "=r"(result) : "r"(value), "r"(mem_32_bit) ); @@ -75,18 +75,18 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, // SWL -> LWL CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper byte 0x11 __asm__ __volatile__( - "swl %0, 0(%1)", + "swl %0, 0(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 0(%1)", - : "=r"(result) + "lwl %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x11BBCCDD); @@ -95,25 +95,25 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 2 bytes 0x1122 __asm__ __volatile__( - "swl %0, 1(%1)", + "swl %0, 1(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 0(%1)", - : "=r"(result) + "lwl %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x22BBCCDD); result = 0 __asm__ __volatile__( - "lwl %0 1(%1)", - : "=r"(result) + "lwl %0 1(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x1122CCDD); @@ -122,32 +122,32 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 3 bytes 0x112233 __asm__ __volatile__( - "swl %0, 2(%1)", + "swl %0, 2(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 0(%1)", - : "=r"(result) + "lwl %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x33BBCCDD); result = 0 __asm__ __volatile__( - "lwl %0 1(%1)", - : "=r"(result) + "lwl %0 1(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x2233CCDD); result = 0 __asm__ __volatile__( - "lwl %0 2(%1)", - : "=r"(result) + "lwl %0 2(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x112233DD); @@ -156,46 +156,46 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store all 4 bytes 0x11223344 __asm__ __volatile__( - "swl %0, 3(%1)", + "swl %0, 3(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 0(%1)", - : "=r"(result) + "lwl %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x44BBCCDD); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 1(%1)", - : "=r"(result) + "lwl %0 1(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x3344CCDD); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 2(%1)", - : "=r"(result) + "lwl %0 2(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x223344DD); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 3(%1)", - : "=r"(result) + "lwl %0 3(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x11223344); result = 0xAABBCCDD; __asm__ __volatile__( - "lw %0 0(%1)", - : "=r"(result) + "lw %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x11223344); @@ -206,18 +206,18 @@ CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, // SWR -> LWR CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower byte 0x44 __asm__ __volatile__( - "swr %0, 3(%1)", + "swr %0, 3(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 3(%1)", - : "=r"(result) + "lwl %0 3(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABBCC44); @@ -226,25 +226,25 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 2 bytes 0x3344 __asm__ __volatile__( - "swr %0, 2(%1)", + "swr %0, 2(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 3(%1)", - : "=r"(result) + "lwl %0 3(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 2(%1)", - : "=r"(result) + "lwl %0 2(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABB3344); @@ -253,32 +253,32 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x223344 __asm__ __volatile__( - "swr %0, 1(%1)", + "swr %0, 1(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 3(%1)", - : "=r"(result) + "lwl %0 3(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 2(%1)", - : "=r"(result) + "lwl %0 2(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABB3344); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 1(%1)", - : "=r"(result) + "lwl %0 1(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAA223344); @@ -287,46 +287,46 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, - register uint32_t mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); + register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x11223344 __asm__ __volatile__( - "swr %0, 0(%1)", + "swr %0, 0(%1)" : : "r"(value), "r"(mem_32_bit) ); - register uint32_t result = 0xAABBCCDD; + register volatile uint32_t result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 3(%1)", - : "=r"(result) + "lwl %0 3(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 2(%1)", - : "=r"(result) + "lwl %0 2(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAABB3344); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 1(%1)", - : "=r"(result) + "lwl %0 1(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0xAA223344); result = 0xAABBCCDD; __asm__ __volatile__( - "lwl %0 0(%1)", - : "=r"(result) + "lwl %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x11223344); result = 0xAABBCCDD; __asm__ __volatile__( - "lw %0 0(%1)", - : "=r"(result) + "lw %0 0(%1)" + : "+r"(result) : "r"(mem_32_bit) ); ASSERT_EQ(result, 0x11223344); From 02720644f7c7ea106d8f9ace5bdc1674f1eaeb84 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:12:17 +1000 Subject: [PATCH 39/68] Stringified lw_suffix in INVALID_SWX_LWX macro Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index 478359206..084046aeb 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -62,7 +62,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, register uint32_t value = 0x11223344; register volatile uint32_t result = 0; __asm__ __volatile__( - "sw %1, 0(%2);", + "sw %1, 0(%2);" "lw %0, 0(%2);" : "=r"(result) : "r"(value), "r"(mem_32_bit) @@ -84,7 +84,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, ); \ register volatile uint32_t result = 0xAABBCCDD; \ __asm__ __volatile__( \ - "lw" lw_suffix " %0 " #load_off "(%1);" \ + "lw" #lw_suffix " %0 " #load_off "(%1);" \ : "+r"(result) \ : "r"(mem_32_bit) \ ); \ From 372957a1058a2529c780495e20f5fa38ba02b2df Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:42:43 +1000 Subject: [PATCH 40/68] Fixed LWX asm instruction missing comma Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index 084046aeb..2f8e7ccbb 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -71,8 +71,6 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, EXPECT_EQ(result, value); ) -// SWL -> LWL - #define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ CESTER_TEST(name, msan_invalid_tests, \ register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ @@ -84,7 +82,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, ); \ register volatile uint32_t result = 0xAABBCCDD; \ __asm__ __volatile__( \ - "lw" #lw_suffix " %0 " #load_off "(%1);" \ + "lw" #lw_suffix " %0, " #load_off "(%1);" \ : "+r"(result) \ : "r"(mem_32_bit) \ ); \ From b1af11618fa64766c7989aee2d40c274754d39f1 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:28:06 +1000 Subject: [PATCH 41/68] Fixed assert macros and test setup/teardown Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 21 +++--- src/mips/tests/msan_valid/msan_valid.c | 83 +++++++++------------- 2 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index 2f8e7ccbb..5e3a90843 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -47,6 +47,16 @@ SOFTWARE. // clang-format off +static uint32_t* mem_32_bit = NULL; + +CESTER_BEFORE_EACH(msan_invalid_tests, + mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); +) + +CESTER_AFTER_EACH(msan_invalid_tests, + pcsx_msanFree(mem_32_bit); +) + CESTER_BEFORE_ALL(msan_invalid_tests, pcsx_initMsan(); ) @@ -57,8 +67,7 @@ CESTER_AFTER_ALL(msan_invalid_tests, // clang-format on -CESTER_TEST(msan_32bit_sw_lw, msan_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); +CESTER_TEST(msan_32bit_sw_lw, msan_invalid_tests, register uint32_t value = 0x11223344; register volatile uint32_t result = 0; __asm__ __volatile__( @@ -67,13 +76,11 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, : "=r"(result) : "r"(value), "r"(mem_32_bit) ); - pcsx_msanFree(mem_32_bit); - EXPECT_EQ(result, value); + cester_assert_equal(result, value); ) #define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ CESTER_TEST(name, msan_invalid_tests, \ - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); \ register uint32_t value = 0x11223344; \ __asm__ __volatile__( \ "sw" #sw_suffix " %0, " #store_off "(%1);" \ @@ -86,9 +93,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, : "+r"(result) \ : "r"(mem_32_bit) \ ); \ - ASSERT_EQ(result, 0xAABBCCDD); \ - pcsx_msanFree(mem_32_bit); \ - SUCCESS(); \ + cester_assert_equal(result, 0xAABBCCDD); \ ) // SWL => LWL diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index dc719e4ba..bab5f90c4 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -24,7 +24,6 @@ SOFTWARE. */ -#include "mips/common/hardware/pcsxhw.h" #ifndef PCSX_TESTS #define PCSX_TESTS 0 #endif @@ -48,6 +47,16 @@ SOFTWARE. // clang-format off +static uint32_t* mem_32_bit = NULL; + +CESTER_BEFORE_EACH(msan_valid_tests, + mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); +) + +CESTER_AFTER_EACH(msan_valid_tests, + pcsx_msanFree(mem_32_bit); +) + CESTER_BEFORE_ALL(msan_valid_tests, pcsx_initMsan(); ) @@ -59,7 +68,6 @@ CESTER_AFTER_ALL(msan_valid_tests, // clang-format on CESTER_TEST(msan_32bit_sw_lw, msan_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 register volatile uint32_t result = 0; __asm__ __volatile__( @@ -68,14 +76,12 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, : "=r"(result) : "r"(value), "r"(mem_32_bit) ); - pcsx_msanFree(mem_32_bit); - EXPECT_EQ(result, value); + cester_assert_equal(result, value); ) // SWL -> LWL CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper byte 0x11 __asm__ __volatile__( @@ -89,13 +95,10 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x11BBCCDD); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0x11BBCCDD); ) CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 2 bytes 0x1122 __asm__ __volatile__( @@ -109,20 +112,17 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x22BBCCDD); + cester_assert_equal(result, 0x22BBCCDD); result = 0 __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x1122CCDD); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0x1122CCDD); ) CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store upper 3 bytes 0x112233 __asm__ __volatile__( @@ -136,27 +136,24 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x33BBCCDD); + cester_assert_equal(result, 0x33BBCCDD); result = 0 __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x2233CCDD); + cester_assert_equal(result, 0x2233CCDD); result = 0 __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x112233DD); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0x112233DD); ) CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store all 4 bytes 0x11223344 __asm__ __volatile__( @@ -170,43 +167,40 @@ CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x44BBCCDD); + cester_assert_equal(result, 0x44BBCCDD); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x3344CCDD); + cester_assert_equal(result, 0x3344CCDD); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x223344DD); + cester_assert_equal(result, 0x223344DD); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 3(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x11223344); + cester_assert_equal(result, 0x11223344); result = 0xAABBCCDD; __asm__ __volatile__( "lw %0 0(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x11223344); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0x11223344); ) // SWR -> LWR CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower byte 0x44 __asm__ __volatile__( @@ -220,13 +214,10 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABBCC44); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0xAABBCC44); ) CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 2 bytes 0x3344 __asm__ __volatile__( @@ -240,20 +231,17 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABBCC44); + cester_assert_equal(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABB3344); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0xAABB3344); ) CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x223344 __asm__ __volatile__( @@ -267,27 +255,24 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABBCC44); + cester_assert_equal(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABB3344); + cester_assert_equal(result, 0xAABB3344); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAA223344); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0xAA223344); ) CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, - register uint32_t* mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); register uint32_t value = 0x11223344 // Store lower 3 bytes 0x11223344 __asm__ __volatile__( @@ -301,36 +286,34 @@ CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABBCC44); + cester_assert_equal(result, 0xAABBCC44); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAABB3344); + cester_assert_equal(result, 0xAABB3344); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0xAA223344); + cester_assert_equal(result, 0xAA223344); result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 0(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x11223344); + cester_assert_equal(result, 0x11223344); result = 0xAABBCCDD; __asm__ __volatile__( "lw %0 0(%1)" : "+r"(result) : "r"(mem_32_bit) ); - ASSERT_EQ(result, 0x11223344); - pcsx_msanFree(mem_32_bit); - SUCCESS(); + cester_assert_equal(result, 0x11223344); ) From cbca77f0f7488942617e1bd3cad2961a3fdd02b3 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:58:34 +1000 Subject: [PATCH 42/68] Fixed CESTER_X_EACH macro usage and moved mem_32_bit into CESTER_BODY Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_invalid/msan_invalid.c | 8 +++++--- src/mips/tests/msan_valid/msan_valid.c | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan_invalid/msan_invalid.c index 5e3a90843..d6674d737 100644 --- a/src/mips/tests/msan_invalid/msan_invalid.c +++ b/src/mips/tests/msan_invalid/msan_invalid.c @@ -47,13 +47,15 @@ SOFTWARE. // clang-format off -static uint32_t* mem_32_bit = NULL; +CESTER_BODY( + static uint32_t* mem_32_bit = NULL; +) -CESTER_BEFORE_EACH(msan_invalid_tests, +CESTER_BEFORE_EACH(msan_invalid_tests, testname, testindex, mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); ) -CESTER_AFTER_EACH(msan_invalid_tests, +CESTER_AFTER_EACH(msan_invalid_tests, testname, testindex, pcsx_msanFree(mem_32_bit); ) diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index bab5f90c4..e6264e2c3 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -47,13 +47,15 @@ SOFTWARE. // clang-format off -static uint32_t* mem_32_bit = NULL; +CESTER_BODY( + static uint32_t* mem_32_bit = NULL; +) -CESTER_BEFORE_EACH(msan_valid_tests, +CESTER_BEFORE_EACH(msan_valid_tests, testname, testindex, mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); ) -CESTER_AFTER_EACH(msan_valid_tests, +CESTER_AFTER_EACH(msan_valid_tests, testname, testindex, pcsx_msanFree(mem_32_bit); ) From ed522a501f7cd8cc70d55dc88717162fc13b8739 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:29:21 +1000 Subject: [PATCH 43/68] Fixed missing semi-colons and reset values for result before assembly calls Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_valid/msan_valid.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index e6264e2c3..22a490ce6 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -70,7 +70,7 @@ CESTER_AFTER_ALL(msan_valid_tests, // clang-format on CESTER_TEST(msan_32bit_sw_lw, msan_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; register volatile uint32_t result = 0; __asm__ __volatile__( "sw %1, 0(%2);" @@ -84,7 +84,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, // SWL -> LWL CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store upper byte 0x11 __asm__ __volatile__( "swl %0, 0(%1)" @@ -101,7 +101,7 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store upper 2 bytes 0x1122 __asm__ __volatile__( "swl %0, 1(%1)" @@ -115,7 +115,7 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, : "r"(mem_32_bit) ); cester_assert_equal(result, 0x22BBCCDD); - result = 0 + result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) @@ -125,7 +125,7 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store upper 3 bytes 0x112233 __asm__ __volatile__( "swl %0, 2(%1)" @@ -139,14 +139,14 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, : "r"(mem_32_bit) ); cester_assert_equal(result, 0x33BBCCDD); - result = 0 + result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 1(%1)" : "+r"(result) : "r"(mem_32_bit) ); cester_assert_equal(result, 0x2233CCDD); - result = 0 + result = 0xAABBCCDD; __asm__ __volatile__( "lwl %0 2(%1)" : "+r"(result) @@ -156,7 +156,7 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, ) CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store all 4 bytes 0x11223344 __asm__ __volatile__( "swl %0, 3(%1)" @@ -203,7 +203,7 @@ CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, // SWR -> LWR CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store lower byte 0x44 __asm__ __volatile__( "swr %0, 3(%1)" @@ -220,7 +220,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store lower 2 bytes 0x3344 __asm__ __volatile__( "swr %0, 2(%1)" @@ -244,7 +244,7 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store lower 3 bytes 0x223344 __asm__ __volatile__( "swr %0, 1(%1)" @@ -275,7 +275,7 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, ) CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, - register uint32_t value = 0x11223344 + register uint32_t value = 0x11223344; // Store lower 3 bytes 0x11223344 __asm__ __volatile__( "swr %0, 0(%1)" From 25a34b7f67a78b5d3d46b864428351ce637153f5 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:07:21 +1000 Subject: [PATCH 44/68] Fixed syntax issues with valid tests and simplified with macro Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_valid/msan_valid.c | 177 +++++-------------------- 1 file changed, 31 insertions(+), 146 deletions(-) diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index 22a490ce6..9444be8c0 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -81,6 +81,15 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, cester_assert_equal(result, value); ) +#define LWX(load_suffix, offset, expected_result) \ + result = 0xAABBCCDD; \ + __asm__ __volatile__( \ + "lw" #load_suffix " %0, " #offset "(%1)" \ + : "+r"(result) \ + : "r"(mem_32_bit) \ + ); \ + cester_assert_equal(result, expected_result) + // SWL -> LWL CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, @@ -92,12 +101,7 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x11BBCCDD); + LWX(l, 0, 0x11BBCCDD); ) CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, @@ -109,19 +113,8 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x22BBCCDD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 1(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x1122CCDD); + LWX(l, 0, 0x22BBCCDD); + LWX(l, 1, 0x1122CCDD); ) CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, @@ -133,26 +126,9 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x33BBCCDD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 1(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x2233CCDD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 2(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x112233DD); + LWX(l, 0, 0x33BBCCDD); + LWX(l, 1, 0x2233CCDD); + LWX(l, 2, 0x112233DD); ) CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, @@ -164,40 +140,11 @@ CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x44BBCCDD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 1(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x3344CCDD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 2(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x223344DD); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 3(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x11223344); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lw %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x11223344); + LWX(l, 0, 0x44BBCCDD); + LWX(l, 1, 0x3344CCDD); + LWX(l, 2, 0x223344DD); + LWX(l, 3, 0x11223344); + LWX(, 0, 0x11223344); ) // SWR -> LWR @@ -211,12 +158,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 3(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABBCC44); + LWX(r, 3, 0xAABBCC44) ) CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, @@ -228,19 +170,8 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 3(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABBCC44); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 2(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABB3344); + LWX(r, 3, 0xAABBCC44); + LWX(r, 2, 0xAABB3344); ) CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, @@ -252,26 +183,9 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 3(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABBCC44); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 2(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABB3344); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 1(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAA223344); + LWX(r, 3, 0xAABBCC44); + LWX(r, 2, 0xAABB3344); + LWX(r, 1, 0xAA223344); ) CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, @@ -283,39 +197,10 @@ CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 3(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABBCC44); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 2(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAABB3344); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 1(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0xAA223344); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lwl %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x11223344); - result = 0xAABBCCDD; - __asm__ __volatile__( - "lw %0 0(%1)" - : "+r"(result) - : "r"(mem_32_bit) - ); - cester_assert_equal(result, 0x11223344); + LWX(r, 3, 0xAABBCC44); + LWX(r, 2, 0xAABB3344); + LWX(r, 1, 0xAA223344); + LWX(r, 0, 0x11223344); + LWX(, 0, 0x11223344); ) From 73e0152ce1e033e97a38845779945ef18227c4b2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:07:54 +1000 Subject: [PATCH 45/68] Fixed syntax issues with valid tests and simplified with macro Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan_valid/msan_valid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan_valid/msan_valid.c index 9444be8c0..e1782a42d 100644 --- a/src/mips/tests/msan_valid/msan_valid.c +++ b/src/mips/tests/msan_valid/msan_valid.c @@ -158,7 +158,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - LWX(r, 3, 0xAABBCC44) + LWX(r, 3, 0xAABBCC44); ) CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, From 1f825a546e70917badf982174a387adbd9bcadf0 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:39:45 +1000 Subject: [PATCH 46/68] Fixed test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/{msan_invalid => msan-invalid}/Makefile | 1 - src/mips/tests/{msan_invalid => msan-invalid}/msan_invalid.c | 0 src/mips/tests/{msan_valid => msan-valid}/Makefile | 2 -- src/mips/tests/{msan_valid => msan-valid}/msan_valid.c | 0 tests/pcsxrunner/msan.cc | 4 ++-- 5 files changed, 2 insertions(+), 5 deletions(-) rename src/mips/tests/{msan_invalid => msan-invalid}/Makefile (98%) rename src/mips/tests/{msan_invalid => msan-invalid}/msan_invalid.c (100%) rename src/mips/tests/{msan_valid => msan-valid}/Makefile (96%) rename src/mips/tests/{msan_valid => msan-valid}/msan_valid.c (100%) diff --git a/src/mips/tests/msan_invalid/Makefile b/src/mips/tests/msan-invalid/Makefile similarity index 98% rename from src/mips/tests/msan_invalid/Makefile rename to src/mips/tests/msan-invalid/Makefile index 970ade403..91f5fe748 100644 --- a/src/mips/tests/msan_invalid/Makefile +++ b/src/mips/tests/msan-invalid/Makefile @@ -44,7 +44,6 @@ endif SRCS += \ ../../common/crt0/uC-sdk-crt0.s \ -../cop0/exceptions.cpp \ msan_invalid.c \ include ../../common.mk diff --git a/src/mips/tests/msan_invalid/msan_invalid.c b/src/mips/tests/msan-invalid/msan_invalid.c similarity index 100% rename from src/mips/tests/msan_invalid/msan_invalid.c rename to src/mips/tests/msan-invalid/msan_invalid.c diff --git a/src/mips/tests/msan_valid/Makefile b/src/mips/tests/msan-valid/Makefile similarity index 96% rename from src/mips/tests/msan_valid/Makefile rename to src/mips/tests/msan-valid/Makefile index 6e9cb6958..d10c8b85c 100644 --- a/src/mips/tests/msan_valid/Makefile +++ b/src/mips/tests/msan-valid/Makefile @@ -43,9 +43,7 @@ CPPFLAGS += -DPCSX_TESTS=1 endif SRCS += \ -../../common/syscalls/printf.s \ ../../common/crt0/uC-sdk-crt0.s \ -../cop0/exceptions.cpp \ msan_valid.c \ include ../../common.mk diff --git a/src/mips/tests/msan_valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c similarity index 100% rename from src/mips/tests/msan_valid/msan_valid.c rename to src/mips/tests/msan-valid/msan_valid.c diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 06539bb38..bdeb1737f 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -39,7 +39,7 @@ TEST(CPU, InterpreterValid) { using namespace std::chrono_literals; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", - "-luacov", "-loadexe", "src/mips/tests/cpu/msan-valid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); @@ -62,7 +62,7 @@ TEST(CPU, DynarecValid) { using namespace std::chrono_literals; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/cpu/msan-valid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); From ab41d9b3412097e9db8cfa3620cd7f492e0bc07e Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:40:09 +1000 Subject: [PATCH 47/68] Fixed test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index bdeb1737f..e79f2ea87 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -183,7 +183,7 @@ TEST(CPU, InterpreterInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", - "-luacov", "-loadexe", "src/mips/tests/cpu/msan-invalid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); @@ -221,7 +221,7 @@ TEST(CPU, DynarecInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/cpu/msan-invalid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); From 43663972a856b87dc51b0eaf5afbb1db5b7eb29e Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:57:32 +1000 Subject: [PATCH 48/68] Handled elapsed time exceeded to terminate test early Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index e79f2ea87..b82e44387 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -52,6 +52,9 @@ TEST(CPU, InterpreterValid) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } if (thread.joinable()) { thread.join(); } @@ -75,6 +78,9 @@ TEST(CPU, DynarecValid) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } if (thread.joinable()) { thread.join(); } @@ -203,6 +209,9 @@ TEST(CPU, InterpreterInvalid) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } if (thread.joinable()) { thread.join(); } @@ -241,6 +250,9 @@ TEST(CPU, DynarecInvalid) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } if (thread.joinable()) { thread.join(); } From 265fb86c9e62871a8ace4dc01bb9177cea553d4b Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:26:10 +1000 Subject: [PATCH 49/68] Fixed makefile builds for msan-invalid and msan-valid Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mips/tests/Makefile b/src/mips/tests/Makefile index ff38e61b8..b08b5a254 100644 --- a/src/mips/tests/Makefile +++ b/src/mips/tests/Makefile @@ -38,8 +38,8 @@ all: $(MAKE) -C libc all $(MAKE) -C memcpy all $(MAKE) -C memset all - $(MAKE) -C msan_invalid all - $(MAKE) -C msan_valid all + $(MAKE) -C msan-invalid all + $(MAKE) -C msan-valid all $(MAKE) -C pcdrv all $(MAKE) -C psyqo all $(MAKE) -C regwrites all @@ -85,8 +85,8 @@ clean: $(MAKE) -C libc clean $(MAKE) -C memcpy clean $(MAKE) -C memset clean - $(MAKE) -C msan_invalid clean - $(MAKE) -C msan_valid clean + $(MAKE) -C msan-invalid clean + $(MAKE) -C msan-valid clean $(MAKE) -C pcdrv clean $(MAKE) -C psyqo clean $(MAKE) -C regwrites clean From deb7f2ddf469bf4e893f7983c4b391f536920dbe Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:59:56 +1000 Subject: [PATCH 50/68] Fixed MSAN positive tests Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-valid/msan_valid.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index e1782a42d..898c19461 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -131,7 +131,7 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, LWX(l, 2, 0x112233DD); ) -CESTER_TEST(msan_swl_8_to_lwl, msan_valid_tests, +CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, msan_valid_tests, register uint32_t value = 0x11223344; // Store all 4 bytes 0x11223344 __asm__ __volatile__( @@ -161,7 +161,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, LWX(r, 3, 0xAABBCC44); ) -CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, msan_valid_tests, register uint32_t value = 0x11223344; // Store lower 2 bytes 0x3344 __asm__ __volatile__( @@ -170,11 +170,11 @@ CESTER_TEST(msan_8bit_swr_8_to_16_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - LWX(r, 3, 0xAABBCC44); - LWX(r, 2, 0xAABB3344); + LWX(r, 3, 0xAABBCC33); + LWX(r, 2, 0xAABB4433); ) -CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, msan_valid_tests, register uint32_t value = 0x11223344; // Store lower 3 bytes 0x223344 __asm__ __volatile__( @@ -183,12 +183,12 @@ CESTER_TEST(msan_8bit_swr_8_to_24_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - LWX(r, 3, 0xAABBCC44); - LWX(r, 2, 0xAABB3344); - LWX(r, 1, 0xAA223344); + LWX(r, 3, 0xAABBCC22); + LWX(r, 2, 0xAABB3322); + LWX(r, 1, 0xAA443322); ) -CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, msan_valid_tests, register uint32_t value = 0x11223344; // Store lower 3 bytes 0x11223344 __asm__ __volatile__( @@ -197,10 +197,10 @@ CESTER_TEST(msan_8bit_swr_8_to_32_bit_lwr, msan_valid_tests, : "r"(value), "r"(mem_32_bit) ); register volatile uint32_t result = 0xAABBCCDD; - LWX(r, 3, 0xAABBCC44); - LWX(r, 2, 0xAABB3344); - LWX(r, 1, 0xAA223344); - LWX(r, 0, 0x11223344); - LWX(, 0, 0x11223344); + LWX(r, 3, 0xAABBCC11); + LWX(r, 2, 0xAABB2211); + LWX(r, 1, 0xAA332211); + LWX(r, 0, 0x44332211); + LWX(, 0, 0x44332211); ) From c118bb118016757317d606fa16b758e2d08da1c2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:03:25 +1000 Subject: [PATCH 51/68] Fixed MSAN positive tests Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-valid/msan_valid.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index 898c19461..d0442503c 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -171,7 +171,7 @@ CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, msan_valid_tests, ); register volatile uint32_t result = 0xAABBCCDD; LWX(r, 3, 0xAABBCC33); - LWX(r, 2, 0xAABB4433); + LWX(r, 2, 0xAABB3344); ) CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, msan_valid_tests, @@ -184,8 +184,8 @@ CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, msan_valid_tests, ); register volatile uint32_t result = 0xAABBCCDD; LWX(r, 3, 0xAABBCC22); - LWX(r, 2, 0xAABB3322); - LWX(r, 1, 0xAA443322); + LWX(r, 2, 0xAABB2233); + LWX(r, 1, 0xAA223344); ) CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, msan_valid_tests, @@ -198,9 +198,9 @@ CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, msan_valid_tests, ); register volatile uint32_t result = 0xAABBCCDD; LWX(r, 3, 0xAABBCC11); - LWX(r, 2, 0xAABB2211); - LWX(r, 1, 0xAA332211); - LWX(r, 0, 0x44332211); - LWX(, 0, 0x44332211); + LWX(r, 2, 0xAABB1122); + LWX(r, 1, 0xAA112233); + LWX(r, 0, 0x11223344); + LWX(, 0, 0x11223344); ) From bd230e2fdc73055ee0074604368ac27614561354 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:05:58 +1000 Subject: [PATCH 52/68] Fixed return value exit conditon from loop Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index b82e44387..50194af67 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -45,7 +45,7 @@ TEST(CPU, InterpreterValid) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN) { + && ret.load() == INT_MIN) { if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { FAIL() << "Emulator should never pause in valid MSAN cases"; } @@ -71,7 +71,7 @@ TEST(CPU, DynarecValid) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN) { + && ret.load() == INT_MIN) { if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { FAIL() << "Emulator should never pause in valid MSAN cases"; } @@ -205,7 +205,7 @@ TEST(CPU, InterpreterInvalid) { }); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN) { + && ret.load() == INT_MIN) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -246,7 +246,7 @@ TEST(CPU, DynarecInvalid) { }); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS - && ret.load() != INT_MIN) { + && ret.load() == INT_MIN) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } From e7f75053d0c203c9e0edd9a49ced6e5980e59483 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:31:25 +1000 Subject: [PATCH 53/68] Simplified valid test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 49 +++++----------------------------------- 1 file changed, 6 insertions(+), 43 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 50194af67..4f06c224c 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -33,60 +33,23 @@ #include "core/psxemulator.h" #include "support/eventbus.h" -static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); TEST(CPU, InterpreterValid) { - using namespace std::chrono_literals; - std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); - std::thread thread([&](){ - ret.store(invoker.invoke()); - }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS - && ret.load() == INT_MIN) { - if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { - FAIL() << "Emulator should never pause in valid MSAN cases"; - } - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); - } - if (thread.joinable()) { - thread.join(); - } - EXPECT_EQ(ret.load(), 0); + const int ret = invoker.invoke(); + EXPECT_EQ(ret, 0); } TEST(CPU, DynarecValid) { - using namespace std::chrono_literals; - std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); - std::thread thread([&](){ - ret.store(invoker.invoke()); - }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS - && ret.load() == INT_MIN) { - if (!PCSX::g_system->quitting() || !PCSX::g_system->running()) { - FAIL() << "Emulator should never pause in valid MSAN cases"; - } - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); - } - if (thread.joinable()) { - thread.join(); - } - EXPECT_EQ(ret.load(), 0); + const int ret = invoker.invoke(); + EXPECT_EQ(ret, 0); } +static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); + inline uint8_t inspectMsanInitializedBitmap(const uint32_t address) { const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; From d6c8647097121b137cefc1fff1864c61619be2ef Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:38:30 +1000 Subject: [PATCH 54/68] Simplified valid test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 76 ++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 4f06c224c..ac69b448f 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -33,23 +33,78 @@ #include "core/psxemulator.h" #include "support/eventbus.h" +static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); TEST(CPU, InterpreterValid) { - MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + using namespace std::chrono_literals; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); - const int ret = invoker.invoke(); - EXPECT_EQ(ret, 0); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); + std::atomic_bool logMessageRecieved = false; + listener.listen([&](const PCSX::Events::LogMessage& event) { + if (event.logClass == PCSX::LogClass::CPU + && event.message.starts_with("32-bit read")) { + logMessageRecieved.store(true); + } + }); + std::chrono::milliseconds elapsed(0); + PCSX::g_system->resume(); + while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } + if (thread.joinable()) { + thread.join(); + } + const int exit_code = ret.load(); + if (exit_code == INT_MIN) { + FAIL() << "Test timed out"; + } + EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; } TEST(CPU, DynarecValid) { - MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", + using namespace std::chrono_literals; + std::atomic_int ret(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); - const int ret = invoker.invoke(); - EXPECT_EQ(ret, 0); + std::thread thread([&](){ + ret.store(invoker.invoke()); + }); + PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); + std::atomic_bool logMessageRecieved = false; + listener.listen([&](const PCSX::Events::LogMessage& event) { + if (event.logClass == PCSX::LogClass::CPU + && event.message.starts_with("32-bit read")) { + logMessageRecieved.store(true); + } + }); + std::chrono::milliseconds elapsed(0); + PCSX::g_system->resume(); + while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } + if (elapsed >= _30s_MILLIS) { + PCSX::g_system->quit(); + } + if (thread.joinable()) { + thread.join(); + } + const int exit_code = ret.load(); + if (exit_code == INT_MIN) { + FAIL() << "Test timed out"; + } + EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; } -static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); - inline uint8_t inspectMsanInitializedBitmap(const uint32_t address) { const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; @@ -144,6 +199,7 @@ std::optional nextMsanTest(const std::string& msg) { return ss.str(); } PCSX::g_system->resume(); + nextMsanCheckIndex++; return std::nullopt; } @@ -161,7 +217,7 @@ TEST(CPU, InterpreterInvalid) { PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { if (event.logClass != PCSX::LogClass::CPU - || event.message.starts_with("32-bit read")) { + || !event.message.starts_with("32-bit read")) { return; } nextMsanTest(event.message); @@ -202,7 +258,7 @@ TEST(CPU, DynarecInvalid) { PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([](const PCSX::Events::LogMessage& event) { if (event.logClass != PCSX::LogClass::CPU - || event.message.starts_with("32-bit read")) { + || !event.message.starts_with("32-bit read")) { return; } nextMsanTest(event.message); From 26be900b22d5b8b4cea9c4a4a1ce634b98e07866 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:38:46 +1000 Subject: [PATCH 55/68] Simplified valid test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index ac69b448f..955bfb8dc 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -212,7 +212,6 @@ TEST(CPU, InterpreterInvalid) { std::thread thread([&](){ ret.store(invoker.invoke()); }); - std::chrono::milliseconds elapsed(0); std::optional result = std::nullopt; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { @@ -222,6 +221,7 @@ TEST(CPU, InterpreterInvalid) { } nextMsanTest(event.message); }); + std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN) { @@ -253,7 +253,6 @@ TEST(CPU, DynarecInvalid) { std::thread thread([&](){ ret.store(invoker.invoke()); }); - std::chrono::milliseconds elapsed(0); std::optional result = std::nullopt; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([](const PCSX::Events::LogMessage& event) { @@ -263,6 +262,7 @@ TEST(CPU, DynarecInvalid) { } nextMsanTest(event.message); }); + std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN) { From fef195f1ac7c86868244a0f438d21de9e2b7c65e Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:41:57 +1000 Subject: [PATCH 56/68] Simplified valid test invocation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 955bfb8dc..724278b74 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -171,7 +171,7 @@ std::optional nextMsanTest(const std::string& msg) { returnMsg << "Expected 1 MSAN allocation, got"; returnMsg << allocCount; return returnMsg.str(); - } else if (nextMsanCheckIndex >= 36) { + } else if (nextMsanCheckIndex >= std::size(SWX_EXPECTED_BITMASKS)) { std::stringstream returnMsg; returnMsg << "Invalid MSAN check test index: "; returnMsg << nextMsanCheckIndex; From 40b91fa98d6fa2b28b4789e086b663252712bac4 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:04:52 +1000 Subject: [PATCH 57/68] Added MainInvoker::isInStartup check and refactored MSAN tests to ensure consistent setup with event bus Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/main/main.cc | 14 ++++++++++++- src/main/main.h | 1 + tests/pcsxrunner/msan.cc | 44 ++++++++++++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/main/main.cc b/src/main/main.cc index f52d833f1..b961adefc 100644 --- a/src/main/main.cc +++ b/src/main/main.cc @@ -17,10 +17,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ +#include "main/main.h" +#include #include #include #include #include +#include #include #include "core/arguments.h" @@ -30,6 +33,7 @@ #include "core/psxemulator.h" #include "core/r3000a.h" #include "core/sstate.h" +#include "core/system.h" #include "core/ui.h" #include "flags.h" #include "fmt/chrono.h" @@ -155,7 +159,7 @@ class SystemImpl final : public PCSX::System { void useLogfile(const PCSX::u8string &filename) { m_logfile.setFile(new PCSX::UvFile(filename, PCSX::FileOps::TRUNCATE)); } - bool m_inStartup = true; + std::atomic_bool m_inStartup = true; }; struct Cleaner { @@ -484,3 +488,11 @@ runner.init({ return exitCode; } + +bool MainInvoker::isInStartup() { + if (PCSX::g_system == nullptr || PCSX::g_emulator == nullptr) { + return false; + } + SystemImpl* system = reinterpret_cast(PCSX::g_system); + return system->m_inStartup; +} diff --git a/src/main/main.h b/src/main/main.h index 8a4f662f3..03004c36d 100644 --- a/src/main/main.h +++ b/src/main/main.h @@ -50,6 +50,7 @@ class MainInvoker { fprintf(stderr, "PCSX-Redux test finished with exit code %d\n", r); return r; } + bool isInStartup(); private: int m_count; diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 724278b74..082788b7a 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -43,15 +43,20 @@ TEST(CPU, InterpreterValid) { std::thread thread([&](){ ret.store(invoker.invoke()); }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); std::atomic_bool logMessageRecieved = false; listener.listen([&](const PCSX::Events::LogMessage& event) { if (event.logClass == PCSX::LogClass::CPU && event.message.starts_with("32-bit read")) { - logMessageRecieved.store(true); + logMessageRecieved = true; } }); - std::chrono::milliseconds elapsed(0); + elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { std::this_thread::sleep_for(200ms); @@ -63,6 +68,9 @@ TEST(CPU, InterpreterValid) { if (thread.joinable()) { thread.join(); } + if (logMessageRecieved) { + FAIL() << "Unexpected MSAN log encountered"; + } const int exit_code = ret.load(); if (exit_code == INT_MIN) { FAIL() << "Test timed out"; @@ -78,15 +86,20 @@ TEST(CPU, DynarecValid) { std::thread thread([&](){ ret.store(invoker.invoke()); }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); std::atomic_bool logMessageRecieved = false; listener.listen([&](const PCSX::Events::LogMessage& event) { if (event.logClass == PCSX::LogClass::CPU && event.message.starts_with("32-bit read")) { - logMessageRecieved.store(true); + logMessageRecieved = true; } }); - std::chrono::milliseconds elapsed(0); + elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { std::this_thread::sleep_for(200ms); @@ -98,6 +111,9 @@ TEST(CPU, DynarecValid) { if (thread.joinable()) { thread.join(); } + if (logMessageRecieved) { + FAIL() << "Unexpected MSAN log encountered"; + } const int exit_code = ret.load(); if (exit_code == INT_MIN) { FAIL() << "Test timed out"; @@ -185,7 +201,7 @@ std::optional nextMsanTest(const std::string& msg) { std::stringstream returnMsg; returnMsg << "Inavlid MSAN event logged, expected: "; returnMsg << expectedMsg; - returnMsg << ", got :"; + returnMsg << ", got: "; returnMsg << msg; return returnMsg.str(); } @@ -208,10 +224,15 @@ TEST(CPU, InterpreterInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", - "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } std::optional result = std::nullopt; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { @@ -221,7 +242,7 @@ TEST(CPU, InterpreterInvalid) { } nextMsanTest(event.message); }); - std::chrono::milliseconds elapsed(0); + elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN) { @@ -249,10 +270,15 @@ TEST(CPU, DynarecInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); + std::chrono::milliseconds elapsed(0); + while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + std::this_thread::sleep_for(200ms); + elapsed += std::chrono::milliseconds(200ms); + } std::optional result = std::nullopt; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([](const PCSX::Events::LogMessage& event) { @@ -262,7 +288,7 @@ TEST(CPU, DynarecInvalid) { } nextMsanTest(event.message); }); - std::chrono::milliseconds elapsed(0); + elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && ret.load() == INT_MIN) { From 1011074d4189e59fabd7139301e7855d4564f77b Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:04:59 +1000 Subject: [PATCH 58/68] Added MainInvoker::isInStartup check and refactored MSAN tests to ensure consistent setup with event bus Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/main/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/main.cc b/src/main/main.cc index b961adefc..0521a6c22 100644 --- a/src/main/main.cc +++ b/src/main/main.cc @@ -491,7 +491,7 @@ runner.init({ bool MainInvoker::isInStartup() { if (PCSX::g_system == nullptr || PCSX::g_emulator == nullptr) { - return false; + return true; } SystemImpl* system = reinterpret_cast(PCSX::g_system); return system->m_inStartup; From c72c696eeef6dc3a6d27a516d9450000ed8db371 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 23 Jun 2026 23:24:44 +1000 Subject: [PATCH 59/68] Test fixes Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-invalid/msan_invalid.c | 12 ------------ src/mips/tests/msan-valid/msan_valid.c | 12 ------------ tests/pcsxrunner/msan.cc | 12 ++++++------ 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/mips/tests/msan-invalid/msan_invalid.c b/src/mips/tests/msan-invalid/msan_invalid.c index d6674d737..9388776c3 100644 --- a/src/mips/tests/msan-invalid/msan_invalid.c +++ b/src/mips/tests/msan-invalid/msan_invalid.c @@ -24,18 +24,6 @@ SOFTWARE. */ -#ifndef PCSX_TESTS -#define PCSX_TESTS 0 -#endif - -#if PCSX_TESTS -#define CESTER_MAYBE_TEST CESTER_SKIP_TEST -#else -#define CESTER_MAYBE_TEST CESTER_TEST -#endif - -#include "common/hardware/dma.h" -#include "common/hardware/hwregs.h" #include "common/hardware/pcsxhw.h" #undef unix diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index d0442503c..d85ed6fe0 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -24,18 +24,6 @@ SOFTWARE. */ -#ifndef PCSX_TESTS -#define PCSX_TESTS 0 -#endif - -#if PCSX_TESTS -#define CESTER_MAYBE_TEST CESTER_SKIP_TEST -#else -#define CESTER_MAYBE_TEST CESTER_TEST -#endif - -#include "common/hardware/dma.h" -#include "common/hardware/hwregs.h" #include "common/hardware/pcsxhw.h" #undef unix diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 082788b7a..cbc29e896 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -44,7 +44,7 @@ TEST(CPU, InterpreterValid) { ret.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -87,7 +87,7 @@ TEST(CPU, DynarecValid) { ret.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -224,12 +224,12 @@ TEST(CPU, InterpreterInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", - "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -270,12 +270,12 @@ TEST(CPU, DynarecInvalid) { nextMsanCheckIndex = 0; std::atomic_int ret(INT_MIN); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); + "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ ret.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && !invoker.isInStartup()) { + while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } From 8358fda7d2ecb0f6acfa104e4453da14e41b4108 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 24 Jun 2026 13:49:10 +1000 Subject: [PATCH 60/68] Reduced test code duplication and fixed missing result handling Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-invalid/msan_invalid.c | 1 + tests/pcsxrunner/msan.cc | 162 +++++++-------------- 2 files changed, 52 insertions(+), 111 deletions(-) diff --git a/src/mips/tests/msan-invalid/msan_invalid.c b/src/mips/tests/msan-invalid/msan_invalid.c index 9388776c3..2266f8ae6 100644 --- a/src/mips/tests/msan-invalid/msan_invalid.c +++ b/src/mips/tests/msan-invalid/msan_invalid.c @@ -83,6 +83,7 @@ CESTER_TEST(msan_32bit_sw_lw, msan_invalid_tests, : "+r"(result) \ : "r"(mem_32_bit) \ ); \ + /* Should leave our existing registered value untouched */ \ cester_assert_equal(result, 0xAABBCCDD); \ ) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index cbc29e896..446a95760 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -17,12 +17,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ +#include #include #include #include #include #include #include +#include #include #include #include @@ -35,30 +37,32 @@ static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); -TEST(CPU, InterpreterValid) { +// ==== VALID ==== + +void validTestLoop(const char* type) { using namespace std::chrono_literals; - std::atomic_int ret(INT_MIN); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + std::atomic_int exitCode(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ - ret.store(invoker.invoke()); + exitCode.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); std::atomic_bool logMessageRecieved = false; listener.listen([&](const PCSX::Events::LogMessage& event) { - if (event.logClass == PCSX::LogClass::CPU - && event.message.starts_with("32-bit read")) { + if (event.logClass == PCSX::LogClass::CPU) { logMessageRecieved = true; } }); elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); - while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { + while (elapsed < _30s_MILLIS && exitCode.load() == INT_MIN && !logMessageRecieved) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } @@ -68,59 +72,20 @@ TEST(CPU, InterpreterValid) { if (thread.joinable()) { thread.join(); } - if (logMessageRecieved) { - FAIL() << "Unexpected MSAN log encountered"; - } - const int exit_code = ret.load(); - if (exit_code == INT_MIN) { - FAIL() << "Test timed out"; - } - EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; + ASSERT_FALSE(logMessageRecieved.load()) << "Unexpected MSAN log encountered"; + EXPECT_EQ(exitCode.load(), 0); +} + +TEST(CPU, InterpreterValid) { + validTestLoop("-interpreter"); } TEST(CPU, DynarecValid) { - using namespace std::chrono_literals; - std::atomic_int ret(INT_MIN); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); - std::thread thread([&](){ - ret.store(invoker.invoke()); - }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && invoker.isInStartup()) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); - std::atomic_bool logMessageRecieved = false; - listener.listen([&](const PCSX::Events::LogMessage& event) { - if (event.logClass == PCSX::LogClass::CPU - && event.message.starts_with("32-bit read")) { - logMessageRecieved = true; - } - }); - elapsed = std::chrono::milliseconds(0); - PCSX::g_system->resume(); - while (elapsed < _30s_MILLIS && ret.load() == INT_MIN && !logMessageRecieved) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); - } - if (thread.joinable()) { - thread.join(); - } - if (logMessageRecieved) { - FAIL() << "Unexpected MSAN log encountered"; - } - const int exit_code = ret.load(); - if (exit_code == INT_MIN) { - FAIL() << "Test timed out"; - } - EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; + validTestLoop("-dynarec"); } +// ==== INVALID ==== + inline uint8_t inspectMsanInitializedBitmap(const uint32_t address) { const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; @@ -207,6 +172,8 @@ std::optional nextMsanTest(const std::string& msg) { } const uint8_t expectedInitBitmap = SWX_EXPECTED_BITMASKS[nextMsanCheckIndex]; const uint8_t actualInitBitmap = inspectMsanInitializedBitmap(alloc->first); + std::cout << "Expected bitmap: " << std::bitset<8>(expectedInitBitmap) + << " Actual bitmap: " << std::bitset<8>(actualInitBitmap) << std::endl; if (expectedInitBitmap != actualInitBitmap) { std::stringstream ss; ss << "Initialized bitmap for address 0x" << std::hex << alloc->first @@ -219,35 +186,50 @@ std::optional nextMsanTest(const std::string& msg) { return std::nullopt; } -TEST(CPU, InterpreterInvalid) { +void invalidTestLoop(const char* type) { using namespace std::chrono_literals; nextMsanCheckIndex = 0; - std::atomic_int ret(INT_MIN); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-interpreter", + using namespace std::chrono_literals; + std::atomic_int exitCode(INT_MIN); + MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ - ret.store(invoker.invoke()); + exitCode.store(invoker.invoke()); }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS && invoker.isInStartup()) { std::this_thread::sleep_for(200ms); elapsed += std::chrono::milliseconds(200ms); } + ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; std::optional result = std::nullopt; + std::mutex resultMutex; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { if (event.logClass != PCSX::LogClass::CPU || !event.message.starts_with("32-bit read")) { + std::stringstream ss; + ss << "Unexpected log message encountered: " << event.message; + std::lock_guard guard(resultMutex); + result = ss.str(); return; } - nextMsanTest(event.message); + std::cout << "Event encountered: " << event.message << std::endl; + std::lock_guard guard(resultMutex); + result = nextMsanTest(event.message); }); elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS - && ret.load() == INT_MIN) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); + && exitCode.load() == INT_MIN) { + { + std::lock_guard guard(resultMutex); + if (result.has_value()) { + break; + } + } + std::this_thread::sleep_for(50ms); + elapsed += std::chrono::milliseconds(50ms); } if (elapsed >= _30s_MILLIS) { PCSX::g_system->quit(); @@ -258,55 +240,13 @@ TEST(CPU, InterpreterInvalid) { if (result.has_value()) { FAIL() << result.value(); } - const int exit_code = ret.load(); - if (exit_code == INT_MIN) { - FAIL() << "Test timed out"; - } - EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; + EXPECT_EQ(exitCode.load(), 0); +} + +TEST(CPU, InterpreterInvalid) { + invalidTestLoop("-interpreter"); } TEST(CPU, DynarecInvalid) { - using namespace std::chrono_literals; - nextMsanCheckIndex = 0; - std::atomic_int ret(INT_MIN); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", "-dynarec", - "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); - std::thread thread([&](){ - ret.store(invoker.invoke()); - }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && invoker.isInStartup()) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - std::optional result = std::nullopt; - PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); - listener.listen([](const PCSX::Events::LogMessage& event) { - if (event.logClass != PCSX::LogClass::CPU - || !event.message.starts_with("32-bit read")) { - return; - } - nextMsanTest(event.message); - }); - elapsed = std::chrono::milliseconds(0); - PCSX::g_system->resume(); - while (elapsed < _30s_MILLIS - && ret.load() == INT_MIN) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); - } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); - } - if (thread.joinable()) { - thread.join(); - } - if (result.has_value()) { - FAIL() << result.value(); - } - const int exit_code = ret.load(); - if (exit_code == INT_MIN) { - FAIL() << "Test timed out"; - } - EXPECT_EQ(exit_code, 0) << "Mismatch in expected exit code"; + invalidTestLoop("-dynarec"); } From 6c6011ad1d2c24d53bcbd5de77414fd907c75b68 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:15:33 +1000 Subject: [PATCH 61/68] Added missing test assertion failure propagation from subroutine Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 446a95760..1ce3464f4 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -49,8 +49,8 @@ void validTestLoop(const char* type) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS && invoker.isInStartup()) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); + std::this_thread::sleep_for(10ms); + elapsed += std::chrono::milliseconds(10ms); } ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); @@ -63,8 +63,8 @@ void validTestLoop(const char* type) { elapsed = std::chrono::milliseconds(0); PCSX::g_system->resume(); while (elapsed < _30s_MILLIS && exitCode.load() == INT_MIN && !logMessageRecieved) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); + std::this_thread::sleep_for(10ms); + elapsed += std::chrono::milliseconds(10ms); } if (elapsed >= _30s_MILLIS) { PCSX::g_system->quit(); @@ -77,11 +77,11 @@ void validTestLoop(const char* type) { } TEST(CPU, InterpreterValid) { - validTestLoop("-interpreter"); + EXPECT_NO_FATAL_FAILURE(validTestLoop("-interpreter")); } TEST(CPU, DynarecValid) { - validTestLoop("-dynarec"); + EXPECT_NO_FATAL_FAILURE(validTestLoop("-dynarec")); } // ==== INVALID ==== @@ -198,8 +198,8 @@ void invalidTestLoop(const char* type) { }); std::chrono::milliseconds elapsed(0); while (elapsed < _30s_MILLIS && invoker.isInStartup()) { - std::this_thread::sleep_for(200ms); - elapsed += std::chrono::milliseconds(200ms); + std::this_thread::sleep_for(10ms); + elapsed += std::chrono::milliseconds(10ms); } ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; std::optional result = std::nullopt; @@ -228,8 +228,8 @@ void invalidTestLoop(const char* type) { break; } } - std::this_thread::sleep_for(50ms); - elapsed += std::chrono::milliseconds(50ms); + std::this_thread::sleep_for(10ms); + elapsed += std::chrono::milliseconds(10ms); } if (elapsed >= _30s_MILLIS) { PCSX::g_system->quit(); @@ -244,9 +244,9 @@ void invalidTestLoop(const char* type) { } TEST(CPU, InterpreterInvalid) { - invalidTestLoop("-interpreter"); + EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-interpreter")); } TEST(CPU, DynarecInvalid) { - invalidTestLoop("-dynarec"); + EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-dynarec")); } From 4dc172696f5d082b6384d59069d7931873371591 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:35:14 +1000 Subject: [PATCH 62/68] Temp stop GH actions indefinite running Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mips/tests/Makefile b/src/mips/tests/Makefile index b08b5a254..93cd575e9 100644 --- a/src/mips/tests/Makefile +++ b/src/mips/tests/Makefile @@ -38,8 +38,8 @@ all: $(MAKE) -C libc all $(MAKE) -C memcpy all $(MAKE) -C memset all - $(MAKE) -C msan-invalid all - $(MAKE) -C msan-valid all + # $(MAKE) -C msan-invalid all + # $(MAKE) -C msan-valid all $(MAKE) -C pcdrv all $(MAKE) -C psyqo all $(MAKE) -C regwrites all @@ -85,8 +85,8 @@ clean: $(MAKE) -C libc clean $(MAKE) -C memcpy clean $(MAKE) -C memset clean - $(MAKE) -C msan-invalid clean - $(MAKE) -C msan-valid clean + # $(MAKE) -C msan-invalid clean + # $(MAKE) -C msan-valid clean $(MAKE) -C pcdrv clean $(MAKE) -C psyqo clean $(MAKE) -C regwrites clean From e2ef4a4bb2b4bf1eeac4b698fba2a38444fb5f30 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:07:38 +1000 Subject: [PATCH 63/68] Temp stop GH actions indefinite running Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- tests/pcsxrunner/msan.cc | 86 +++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 1ce3464f4..7b25085f3 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -21,9 +21,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -35,54 +35,59 @@ #include "core/psxemulator.h" #include "support/eventbus.h" -static constexpr std::chrono::milliseconds _30s_MILLIS = std::chrono::duration_cast(std::chrono::seconds(30)); +using namespace std::chrono_literals; + // ==== VALID ==== void validTestLoop(const char* type) { - using namespace std::chrono_literals; - std::atomic_int exitCode(INT_MIN); + std::atomic_int exitCode(std::numeric_limits::min()); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ exitCode.store(invoker.invoke()); + std::cout << "Invoker thread complete" << std::endl; }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && invoker.isInStartup()) { + while (invoker.isInStartup()) { + std::cout << "Awaiting startup" << std::endl; std::this_thread::sleep_for(10ms); - elapsed += std::chrono::milliseconds(10ms); } - ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); std::atomic_bool logMessageRecieved = false; listener.listen([&](const PCSX::Events::LogMessage& event) { + std::cout << "Event encountered: " << event.message << std::endl; if (event.logClass == PCSX::LogClass::CPU) { logMessageRecieved = true; } }); - elapsed = std::chrono::milliseconds(0); + std::cout << "Resuming" << std::endl; + std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); - while (elapsed < _30s_MILLIS && exitCode.load() == INT_MIN && !logMessageRecieved) { + while (elapsed < 30s + && exitCode.load() == std::numeric_limits::min() + && !logMessageRecieved) { + std::cout << "Elapsed: " << elapsed << std::endl; std::this_thread::sleep_for(10ms); - elapsed += std::chrono::milliseconds(10ms); + elapsed += 10ms; } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); + if (elapsed >= 30s) { + PCSX::g_system->quit(1); } if (thread.joinable()) { + std::cout << "Joining invoker thread" << std::endl; thread.join(); } ASSERT_FALSE(logMessageRecieved.load()) << "Unexpected MSAN log encountered"; EXPECT_EQ(exitCode.load(), 0); } -TEST(CPU, InterpreterValid) { - EXPECT_NO_FATAL_FAILURE(validTestLoop("-interpreter")); -} - -TEST(CPU, DynarecValid) { - EXPECT_NO_FATAL_FAILURE(validTestLoop("-dynarec")); -} +// TEST(CPU, InterpreterValid) { +// EXPECT_NO_FATAL_FAILURE(validTestLoop("-interpreter")); +// } +// +// TEST(CPU, DynarecValid) { +// EXPECT_NO_FATAL_FAILURE(validTestLoop("-dynarec")); +// } // ==== INVALID ==== @@ -187,25 +192,23 @@ std::optional nextMsanTest(const std::string& msg) { } void invalidTestLoop(const char* type) { - using namespace std::chrono_literals; nextMsanCheckIndex = 0; - using namespace std::chrono_literals; - std::atomic_int exitCode(INT_MIN); + std::atomic_int exitCode(std::numeric_limits::min()); MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ exitCode.store(invoker.invoke()); + std::cout << "Invoker thread complete" << std::endl; }); - std::chrono::milliseconds elapsed(0); - while (elapsed < _30s_MILLIS && invoker.isInStartup()) { + while (invoker.isInStartup()) { + std::cout << "Awaiting startup" << std::endl; std::this_thread::sleep_for(10ms); - elapsed += std::chrono::milliseconds(10ms); } - ASSERT_LT(elapsed, _30s_MILLIS) << "Test timed out waiting for system to start"; std::optional result = std::nullopt; std::mutex resultMutex; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { + std::cout << "Event encountered: " << event.message << std::endl; if (event.logClass != PCSX::LogClass::CPU || !event.message.starts_with("32-bit read")) { std::stringstream ss; @@ -214,14 +217,14 @@ void invalidTestLoop(const char* type) { result = ss.str(); return; } - std::cout << "Event encountered: " << event.message << std::endl; std::lock_guard guard(resultMutex); result = nextMsanTest(event.message); }); - elapsed = std::chrono::milliseconds(0); + std::cout << "Resuming" << std::endl; + std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); - while (elapsed < _30s_MILLIS - && exitCode.load() == INT_MIN) { + while (elapsed < 30s && exitCode.load() == std::numeric_limits::min()) { + std::cout << "Elapsed: " << elapsed << std::endl; { std::lock_guard guard(resultMutex); if (result.has_value()) { @@ -229,12 +232,13 @@ void invalidTestLoop(const char* type) { } } std::this_thread::sleep_for(10ms); - elapsed += std::chrono::milliseconds(10ms); + elapsed += 10ms; } - if (elapsed >= _30s_MILLIS) { - PCSX::g_system->quit(); + if (elapsed >= 30s) { + PCSX::g_system->quit(1); } if (thread.joinable()) { + std::cout << "Joining invoker thread" << std::endl; thread.join(); } if (result.has_value()) { @@ -243,10 +247,10 @@ void invalidTestLoop(const char* type) { EXPECT_EQ(exitCode.load(), 0); } -TEST(CPU, InterpreterInvalid) { - EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-interpreter")); -} - -TEST(CPU, DynarecInvalid) { - EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-dynarec")); -} +// TEST(CPU, InterpreterInvalid) { +// EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-interpreter")); +// } +// +// TEST(CPU, DynarecInvalid) { +// EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-dynarec")); +// } From ca111019aec8a129f3b1e999a9c6ad142af0b2d5 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:45:29 +1000 Subject: [PATCH 64/68] Improved tests and WIP migration from direct _Imm_ usage to address & 3 for MSAN mask calculation Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 56 ++++-- src/core/DynaRec_x64/instructions.cc | 1 + src/core/psxmem.cc | 2 + src/mips/tests/Makefile | 8 +- src/mips/tests/msan-invalid/msan_invalid.c | 45 ++--- src/mips/tests/msan-valid/msan_valid.c | 49 ++--- tests/pcsxrunner/msan.cc | 206 +++++++++++---------- 7 files changed, 201 insertions(+), 166 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index b05fd917e..827dc5311 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -487,13 +487,13 @@ void DynaRecCPU::recLWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0x00FFFFFF00000018, 0x0000FFFF00000010, 0x000000FF00000008, 0}; - const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; + const uint32_t msanReadMask = 0b1111 >> (3 - (address & 3)); const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 @@ -512,6 +512,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; + const uint32_t msanReadMask = 0b1111 >> (3 - (address & 3)); gen.Mov(arg1, alignedAddress); // Address in arg1 gen.Mov(arg2, msanReadMask); // Mask in arg2 @@ -528,9 +529,11 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 + gen.Sub(arg2, 3, arg2); + gen.Lsr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -549,9 +552,11 @@ void DynaRecCPU::recLWL(uint32_t code) { } } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 + gen.Sub(arg2, 3, arg2); + gen.Lsr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -574,13 +579,13 @@ void DynaRecCPU::recLWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0xFF00000000000008, 0xFFFF000000000010, 0xFFFFFF0000000018}; - const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; + const uint32_t msanReadMask = (0b1111 << (address & 3)) & 0b1111; const uint32_t previousValue = m_gprs[_Rt_].val; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) @@ -599,6 +604,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; + const uint32_t msanReadMask = (0b1111 << (address & 3)) & 0b1111; gen.Mov(arg1, alignedAddress); // Address in arg1 gen.Mov(arg2, msanReadMask); // Mask in arg2 @@ -615,9 +621,11 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 + gen.Lsl(arg2, 0b1111, arg2); + gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -636,9 +644,11 @@ void DynaRecCPU::recLWR(uint32_t code) { } } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, arg1, ~3); // Force align it - gen.Mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 + gen.Lsl(arg2, 0b1111, arg2); + gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in eax if (_Rt_) { @@ -1362,13 +1372,13 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; - const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWL_MASK[address & 3]; const auto shift = SWL_SHIFT[address & 3]; + const uint32_t msanWriteMask = 0b1111 >> (3 - (address & 3)); gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) gen.Mov(arg2, 0x0); // Mask in arg2 @@ -1385,6 +1395,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWL_MASK[address & 3]; const auto shift = SWL_SHIFT[address & 3]; + const uint32_t msanWriteMask = 0b1111 >> (3 - (address & 3)); gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) gen.Mov(arg2, 0x0); // Mask in arg2 @@ -1419,7 +1430,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msanWriteMask); // Mask in arg3 + gen.Sub(arg3, 3, w2); // Mask in arg3 + gen.Lsr(arg3, 0b1111, arg3); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1443,7 +1455,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - gen.Mov(arg3, msanWriteMask); // Mask in arg3 + gen.Sub(arg3, 3, w2); // Mask in arg3 + gen.Lsr(arg3, 0b1111, arg3); call(write32MaskedWrapper); } } @@ -1452,13 +1465,13 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; - const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWR_MASK[address & 3]; const auto shift = SWR_SHIFT[address & 3]; + const uint32_t msanWriteMask = (0b1111 << (address & 3)) & 0b1111; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) gen.Mov(arg2, 0x0); // Mask in arg2 @@ -1475,6 +1488,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWR_MASK[address & 3]; const auto shift = SWR_SHIFT[address & 3]; + const uint32_t msanWriteMask = (0b1111 << (address & 3)) & 0b1111; gen.Mov(arg1, alignedAddress); // Address in arg1 (w0) gen.Mov(arg2, 0x0); // Mask in arg2 @@ -1510,7 +1524,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Mov(arg3, msanWriteMask); // Mask in arg3 + gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 + gen.And(arg3, 0b1111); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1535,7 +1550,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Mov(arg3, msanWriteMask); // Mask in arg3 + gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 + gen.And(arg3, 0b1111); call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index e471ec15c..74fcf7bc9 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -1147,6 +1147,7 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; + // TODO: Port Aarch64 changes using address & 3 instead of _Imm_ to here. const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index 944de2f5d..3bbe10111 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -27,6 +27,7 @@ #include "core/pio-cart.h" #include "core/psxhw.h" #include "core/r3000a.h" +#include "core/system.h" #include "mips/common/util/encoder.hh" #include "support/file.h" #include "supportpsx/binloader.h" @@ -899,6 +900,7 @@ uint32_t PCSX::Memory::msanAlloc(uint32_t size) { // Check if we still have enough memory. if (m_msanPtr + actualSize > c_msanSize) { + g_system->printf(_("m_msanPtr: 0x%x actualSize: %zu, c_msanSize: %zu\n"), m_msanPtr, actualSize, c_msanSize); g_system->printf(_("Out of memory in MsanAlloc\n")); g_system->pause(); return 0; diff --git a/src/mips/tests/Makefile b/src/mips/tests/Makefile index 93cd575e9..b08b5a254 100644 --- a/src/mips/tests/Makefile +++ b/src/mips/tests/Makefile @@ -38,8 +38,8 @@ all: $(MAKE) -C libc all $(MAKE) -C memcpy all $(MAKE) -C memset all - # $(MAKE) -C msan-invalid all - # $(MAKE) -C msan-valid all + $(MAKE) -C msan-invalid all + $(MAKE) -C msan-valid all $(MAKE) -C pcdrv all $(MAKE) -C psyqo all $(MAKE) -C regwrites all @@ -85,8 +85,8 @@ clean: $(MAKE) -C libc clean $(MAKE) -C memcpy clean $(MAKE) -C memset clean - # $(MAKE) -C msan-invalid clean - # $(MAKE) -C msan-valid clean + $(MAKE) -C msan-invalid clean + $(MAKE) -C msan-valid clean $(MAKE) -C pcdrv clean $(MAKE) -C psyqo clean $(MAKE) -C regwrites clean diff --git a/src/mips/tests/msan-invalid/msan_invalid.c b/src/mips/tests/msan-invalid/msan_invalid.c index 2266f8ae6..3dd5e6666 100644 --- a/src/mips/tests/msan-invalid/msan_invalid.c +++ b/src/mips/tests/msan-invalid/msan_invalid.c @@ -23,9 +23,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +#include #include "common/hardware/pcsxhw.h" +#if PCSX_TESTS +#define CESTER_MAYBE_TEST CESTER_SKIP_TEST +#else +#define CESTER_MAYBE_TEST CESTER_TEST +#endif + #undef unix #define CESTER_NO_SIGNAL #define CESTER_NO_TIME @@ -35,43 +41,28 @@ SOFTWARE. // clang-format off -CESTER_BODY( - static uint32_t* mem_32_bit = NULL; -) - -CESTER_BEFORE_EACH(msan_invalid_tests, testname, testindex, - mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); +CESTER_BEFORE_EACH(test_instance, test_name, index, + test_instance->arg = pcsx_msanAlloc(sizeof(uint32_t)); ) -CESTER_AFTER_EACH(msan_invalid_tests, testname, testindex, - pcsx_msanFree(mem_32_bit); +CESTER_AFTER_EACH(test_instance, test_name, index, + pcsx_msanFree(test_instance->arg); ) -CESTER_BEFORE_ALL(msan_invalid_tests, +CESTER_BEFORE_ALL(test_instance, pcsx_initMsan(); ) -CESTER_AFTER_ALL(msan_invalid_tests, +CESTER_AFTER_ALL(test_instance, pcsx_resetMsan(); ) // clang-format on -CESTER_TEST(msan_32bit_sw_lw, msan_invalid_tests, - register uint32_t value = 0x11223344; - register volatile uint32_t result = 0; - __asm__ __volatile__( - "sw %1, 0(%2);" - "lw %0, 0(%2);" - : "=r"(result) - : "r"(value), "r"(mem_32_bit) - ); - cester_assert_equal(result, value); -) - #define INVALID_SWX_LWX(name, sw_suffix, lw_suffix, store_off, load_off) \ - CESTER_TEST(name, msan_invalid_tests, \ - register uint32_t value = 0x11223344; \ + CESTER_MAYBE_TEST(name, test_instance, \ + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; \ + const register uint32_t value = 0x11223344; \ __asm__ __volatile__( \ "sw" #sw_suffix " %0, " #store_off "(%1);" \ : \ @@ -85,6 +76,10 @@ CESTER_TEST(msan_32bit_sw_lw, msan_invalid_tests, ); \ /* Should leave our existing registered value untouched */ \ cester_assert_equal(result, 0xAABBCCDD); \ + /* BUG: For some reason the pointer value in test_instance->arg */ \ + /* is mutated from the 0x20000000+ range to the 0x80000000+ address range. */ \ + /* This is to restore the correct address.*/ \ + test_instance->arg = mem_32_bit; \ ) // SWL => LWL diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index d85ed6fe0..0dacb5fa1 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -23,7 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +#include #include "common/hardware/pcsxhw.h" #undef unix @@ -34,30 +34,27 @@ SOFTWARE. #include "exotic/cester.h" // clang-format off - -CESTER_BODY( - static uint32_t* mem_32_bit = NULL; +#if PCSX_TESTS +CESTER_BEFORE_EACH(test_instance, testname, testindex, + test_instance->arg = pcsx_msanAlloc(sizeof(uint32_t)); ) -CESTER_BEFORE_EACH(msan_valid_tests, testname, testindex, - mem_32_bit = (uint32_t*) pcsx_msanAlloc(sizeof(uint32_t)); +CESTER_AFTER_EACH(test_instance, testname, testindex, + pcsx_msanFree(test_instance->arg); ) -CESTER_AFTER_EACH(msan_valid_tests, testname, testindex, - pcsx_msanFree(mem_32_bit); -) - -CESTER_BEFORE_ALL(msan_valid_tests, +CESTER_BEFORE_ALL(test_instance, pcsx_initMsan(); ) -CESTER_AFTER_ALL(msan_valid_tests, +CESTER_AFTER_ALL(test_instance, pcsx_resetMsan(); ) - +#endif // clang-format on -CESTER_TEST(msan_32bit_sw_lw, msan_tests, +CESTER_TEST(msan_32bit_sw_lw, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; register volatile uint32_t result = 0; __asm__ __volatile__( @@ -80,7 +77,8 @@ CESTER_TEST(msan_32bit_sw_lw, msan_tests, // SWL -> LWL -CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, +CESTER_TEST(msan_8bit_swl_8bit_lwl, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper byte 0x11 __asm__ __volatile__( @@ -92,7 +90,8 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, msan_valid_tests, LWX(l, 0, 0x11BBCCDD); ) -CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, +CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper 2 bytes 0x1122 __asm__ __volatile__( @@ -105,7 +104,8 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, msan_valid_tests, LWX(l, 1, 0x1122CCDD); ) -CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, +CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper 3 bytes 0x112233 __asm__ __volatile__( @@ -119,7 +119,8 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, msan_valid_tests, LWX(l, 2, 0x112233DD); ) -CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, msan_valid_tests, +CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store all 4 bytes 0x11223344 __asm__ __volatile__( @@ -137,7 +138,8 @@ CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, msan_valid_tests, // SWR -> LWR -CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, +CESTER_TEST(msan_8bit_swr_8bit_lwr, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower byte 0x44 __asm__ __volatile__( @@ -149,7 +151,8 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, msan_valid_tests, LWX(r, 3, 0xAABBCC44); ) -CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 2 bytes 0x3344 __asm__ __volatile__( @@ -162,7 +165,8 @@ CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, msan_valid_tests, LWX(r, 2, 0xAABB3344); ) -CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 3 bytes 0x223344 __asm__ __volatile__( @@ -176,7 +180,8 @@ CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, msan_valid_tests, LWX(r, 1, 0xAA223344); ) -CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, msan_valid_tests, +CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, test_instance, + register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 3 bytes 0x11223344 __asm__ __volatile__( diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 7b25085f3..6a57f3927 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -22,12 +22,11 @@ #include #include #include -#include #include #include #include -#include #include +#include "fmt/printf.h" #include "gtest/gtest.h" #include "main/main.h" #include "core/psxmem.h" @@ -37,67 +36,80 @@ using namespace std::chrono_literals; +inline static const bool isMsanLog(const PCSX::Events::LogMessage& event) { + return event.logClass == PCSX::LogClass::CPU + && (event.message.contains("-bit read") || event.message.contains("-bit write")); +} // ==== VALID ==== -void validTestLoop(const char* type) { +void execValidTest(const char* type) { std::atomic_int exitCode(std::numeric_limits::min()); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, + MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-valid/msan-valid.ps-exe"); std::thread thread([&](){ exitCode.store(invoker.invoke()); - std::cout << "Invoker thread complete" << std::endl; }); - while (invoker.isInStartup()) { - std::cout << "Awaiting startup" << std::endl; - std::this_thread::sleep_for(10ms); - } + while (invoker.isInStartup()); PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); - std::atomic_bool logMessageRecieved = false; + std::atomic_bool shouldFail = false; + std::atomic_bool msanTriggered = false; listener.listen([&](const PCSX::Events::LogMessage& event) { - std::cout << "Event encountered: " << event.message << std::endl; - if (event.logClass == PCSX::LogClass::CPU) { - logMessageRecieved = true; + if (isMsanLog(event)) { + msanTriggered = true; + shouldFail = true; } }); - std::cout << "Resuming" << std::endl; std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); - while (elapsed < 30s - && exitCode.load() == std::numeric_limits::min() - && !logMessageRecieved) { - std::cout << "Elapsed: " << elapsed << std::endl; + while (elapsed < 30s + && exitCode.load() == std::numeric_limits::min()) { + if (msanTriggered) { + // Allow the tests to continue, even after triggering an MSAN + // log. This means the test results are printed and we still + // show a failure. + std::chrono::milliseconds awaitPauseElapsed(0); + while (awaitPauseElapsed < 5s && PCSX::g_system->running()) { + std::cout << "Awaiting pause" << std::endl; + std::this_thread::sleep_for(10ms); + awaitPauseElapsed += 10ms; + } + if (awaitPauseElapsed >= 5s) { + PCSX::g_system->quit(3); + break; + } + PCSX::g_system->resume(); + msanTriggered = false; + } std::this_thread::sleep_for(10ms); elapsed += 10ms; } - if (elapsed >= 30s) { + if (elapsed >= 30s || shouldFail) { PCSX::g_system->quit(1); } if (thread.joinable()) { - std::cout << "Joining invoker thread" << std::endl; thread.join(); } - ASSERT_FALSE(logMessageRecieved.load()) << "Unexpected MSAN log encountered"; - EXPECT_EQ(exitCode.load(), 0); + ASSERT_FALSE(shouldFail.load()) << "Unexpected MSAN log encountered"; + ASSERT_EQ(exitCode.load(), 0); } -// TEST(CPU, InterpreterValid) { -// EXPECT_NO_FATAL_FAILURE(validTestLoop("-interpreter")); -// } -// -// TEST(CPU, DynarecValid) { -// EXPECT_NO_FATAL_FAILURE(validTestLoop("-dynarec")); -// } +TEST(MSAN, InterpreterValid) { + ASSERT_NO_FATAL_FAILURE(execValidTest("-interpreter")); +} + +TEST(MSAN, DynarecValid) { + ASSERT_NO_FATAL_FAILURE(execValidTest("-dynarec")); +} // ==== INVALID ==== -inline uint8_t inspectMsanInitializedBitmap(const uint32_t address) { +inline static const uint8_t inspectMsanInitializedBitmap(const uint32_t address) { const uint32_t bitmapIndex = (address - PCSX::g_emulator->m_mem->c_msanStart) / 8; return PCSX::g_emulator->m_mem->m_msanInitializedBitmap[bitmapIndex]; } -static unsigned int nextMsanCheckIndex = 0; -static uint8_t SWX_EXPECTED_BITMASKS[36] = { +static constexpr uint8_t SWX_EXPECTED_BITMASKS[36] = { 0b0001, // swl 0 (8-bit) => lwl 1 (16-bit) 0b0001, // swl 0 (8-bit) => lwl 2 (24-bit) 0b0001, // swl 0 (8-bit) => lwl 3 (32-bit) @@ -147,89 +159,94 @@ static uint8_t SWX_EXPECTED_BITMASKS[36] = { 0b1110, // swr (24-bit) => lwl (8-bit) }; -std::optional nextMsanTest(const std::string& msg) { - if (PCSX::g_system->running()) { - return "Expected emulator to be paused"; +inline std::string rtrim(std::string s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); + return s; +} + +std::optional nextMsanTest(const std::string& msg, + std::atomic_uint* nextMsanCheckIndex) { + if (!PCSX::g_system->running()) { + return fmt::sprintf("[Test index: %d] Expected emulator to not have reached pause state yet", nextMsanCheckIndex->load()); } - auto allocCount = PCSX::g_emulator->m_mem->m_msanAllocs.size(); + const size_t allocCount = PCSX::g_emulator->m_mem->m_msanAllocs.size(); if (allocCount != 1) { - std::stringstream returnMsg; - returnMsg << "Expected 1 MSAN allocation, got"; - returnMsg << allocCount; - return returnMsg.str(); - } else if (nextMsanCheckIndex >= std::size(SWX_EXPECTED_BITMASKS)) { - std::stringstream returnMsg; - returnMsg << "Invalid MSAN check test index: "; - returnMsg << nextMsanCheckIndex; - return returnMsg.str(); + return fmt::sprintf("[Test index: %d] Expected 1 MSAN allocation, got %zu", nextMsanCheckIndex->load(), allocCount); } - auto alloc = PCSX::g_emulator->m_mem->m_msanAllocs.begin(); - std::stringstream ss; - ss << "32-bit read from uninitialized bytes in usable, partially initialized msan memory: 0x" << std::hex << alloc->first; - std::string expectedMsg = ss.str(); - if (msg != expectedMsg) { - std::stringstream returnMsg; - returnMsg << "Inavlid MSAN event logged, expected: "; - returnMsg << expectedMsg; - returnMsg << ", got: "; - returnMsg << msg; - return returnMsg.str(); + const uint32_t msanAllocAddr = PCSX::g_emulator->m_mem->m_msanAllocs.begin()->first + PCSX::Memory::c_msanStart; + const std::string expectedMsg = fmt::sprintf("32-bit read from uninitialized bytes in usable, partially initialized msan memory: %8.8lx", msanAllocAddr); + const std::string trimmedMsg = rtrim(msg); + if (trimmedMsg != expectedMsg) { + return fmt::sprintf("[Test index: %d] Invalid MSAN event logged, expected: \"%s\", got: \"%s\"", nextMsanCheckIndex->load(), expectedMsg, trimmedMsg); } - const uint8_t expectedInitBitmap = SWX_EXPECTED_BITMASKS[nextMsanCheckIndex]; - const uint8_t actualInitBitmap = inspectMsanInitializedBitmap(alloc->first); - std::cout << "Expected bitmap: " << std::bitset<8>(expectedInitBitmap) - << " Actual bitmap: " << std::bitset<8>(actualInitBitmap) << std::endl; + const uint8_t expectedInitBitmap = SWX_EXPECTED_BITMASKS[nextMsanCheckIndex->load()]; + const uint8_t actualInitBitmap = inspectMsanInitializedBitmap(msanAllocAddr); if (expectedInitBitmap != actualInitBitmap) { - std::stringstream ss; - ss << "Initialized bitmap for address 0x" << std::hex << alloc->first - << " mismatch: 0b" << std::bitset<8>(expectedInitBitmap) - << " != 0b" << std::bitset<8>(actualInitBitmap); - return ss.str(); + return fmt::sprintf( + "[Test index: %d] Initialized bitmap for address 0x%8.8lx mismatch: 0b%s != 0b%s", + nextMsanCheckIndex->load(), + msanAllocAddr, + std::bitset<8>(expectedInitBitmap).to_string(), + std::bitset<8>(actualInitBitmap).to_string() + ); } - PCSX::g_system->resume(); - nextMsanCheckIndex++; + nextMsanCheckIndex->fetch_add(1); return std::nullopt; } -void invalidTestLoop(const char* type) { - nextMsanCheckIndex = 0; +void execInvalidTest(const char* type) { std::atomic_int exitCode(std::numeric_limits::min()); - MainInvoker invoker("-no-ui", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, + MainInvoker invoker("-no-ui", "-run", "-bios", "src/mips/openbios/openbios.bin", "-testmode", type, "-luacov", "-loadexe", "src/mips/tests/msan-invalid/msan-invalid.ps-exe"); std::thread thread([&](){ exitCode.store(invoker.invoke()); - std::cout << "Invoker thread complete" << std::endl; }); - while (invoker.isInStartup()) { - std::cout << "Awaiting startup" << std::endl; - std::this_thread::sleep_for(10ms); - } + while (invoker.isInStartup()); + std::atomic_bool resumeSystem = false; std::optional result = std::nullopt; std::mutex resultMutex; + std::atomic_uint nextMsanCheckIndex = 0; PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { - std::cout << "Event encountered: " << event.message << std::endl; - if (event.logClass != PCSX::LogClass::CPU - || !event.message.starts_with("32-bit read")) { - std::stringstream ss; - ss << "Unexpected log message encountered: " << event.message; - std::lock_guard guard(resultMutex); - result = ss.str(); + if (PCSX::g_system->quitting() || !isMsanLog(event)) { return; } std::lock_guard guard(resultMutex); - result = nextMsanTest(event.message); + result = nextMsanTest(event.message, &nextMsanCheckIndex); + resumeSystem = !result.has_value(); }); - std::cout << "Resuming" << std::endl; std::chrono::milliseconds elapsed(0); PCSX::g_system->resume(); - while (elapsed < 30s && exitCode.load() == std::numeric_limits::min()) { - std::cout << "Elapsed: " << elapsed << std::endl; + while (elapsed < 30s + && nextMsanCheckIndex <= std::size(SWX_EXPECTED_BITMASKS) + && exitCode.load() == std::numeric_limits::min()) { { std::lock_guard guard(resultMutex); if (result.has_value()) { + PCSX::g_system->quit(2); break; } + // When an MSAN log is published, it will be handled + // first before a pause is triggered. Thus we need to + // wait until the pause is triggered and then ensure the + // emulator continues. This must be differentiated from + // the case where we haven't handled an MSAN log. + if (resumeSystem) { + std::chrono::milliseconds awaitPauseElapsed(0); + while (awaitPauseElapsed < 5s && PCSX::g_system->running()) { + std::cout << "Awaiting pause" << std::endl; + std::this_thread::sleep_for(10ms); + awaitPauseElapsed += 10ms; + } + if (awaitPauseElapsed >= 5s) { + PCSX::g_system->quit(3); + break; + } + PCSX::g_system->resume(); + resumeSystem = false; + } } std::this_thread::sleep_for(10ms); elapsed += 10ms; @@ -238,19 +255,18 @@ void invalidTestLoop(const char* type) { PCSX::g_system->quit(1); } if (thread.joinable()) { - std::cout << "Joining invoker thread" << std::endl; thread.join(); } if (result.has_value()) { FAIL() << result.value(); } - EXPECT_EQ(exitCode.load(), 0); + ASSERT_EQ(exitCode.load(), 0); } -// TEST(CPU, InterpreterInvalid) { -// EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-interpreter")); -// } -// -// TEST(CPU, DynarecInvalid) { -// EXPECT_NO_FATAL_FAILURE(invalidTestLoop("-dynarec")); -// } +TEST(MSAN, InterpreterInvalid) { + ASSERT_NO_FATAL_FAILURE(execInvalidTest("-interpreter")); +} + +TEST(MSAN, DynarecInvalid) { + ASSERT_NO_FATAL_FAILURE(execInvalidTest("-dynarec")); +} From db7c89c05bcefc14752c6dcbf737a1d279d58e83 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:56:15 +1000 Subject: [PATCH 65/68] Fixed test guard in msan_valid.c Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-valid/msan_valid.c | 28 ++++++++++++++++---------- tests/pcsxrunner/msan.cc | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index 0dacb5fa1..d6f615299 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -26,6 +26,12 @@ SOFTWARE. #include #include "common/hardware/pcsxhw.h" +#if pcsx_tests +#define cester_maybe_test cester_skip_test +#else +#define cester_maybe_test cester_test +#endif + #undef unix #define CESTER_NO_SIGNAL #define CESTER_NO_TIME @@ -34,7 +40,7 @@ SOFTWARE. #include "exotic/cester.h" // clang-format off -#if PCSX_TESTS + CESTER_BEFORE_EACH(test_instance, testname, testindex, test_instance->arg = pcsx_msanAlloc(sizeof(uint32_t)); ) @@ -50,10 +56,10 @@ CESTER_BEFORE_ALL(test_instance, CESTER_AFTER_ALL(test_instance, pcsx_resetMsan(); ) -#endif + // clang-format on -CESTER_TEST(msan_32bit_sw_lw, test_instance, +CESTER_MAYBE_TEST(msan_32bit_sw_lw, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; register volatile uint32_t result = 0; @@ -77,7 +83,7 @@ CESTER_TEST(msan_32bit_sw_lw, test_instance, // SWL -> LWL -CESTER_TEST(msan_8bit_swl_8bit_lwl, test_instance, +CESTER_MAYBE_TEST(msan_8bit_swl_8bit_lwl, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper byte 0x11 @@ -90,7 +96,7 @@ CESTER_TEST(msan_8bit_swl_8bit_lwl, test_instance, LWX(l, 0, 0x11BBCCDD); ) -CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, test_instance, +CESTER_MAYBE_TEST(msan_16bit_swl_8_to_16bit_lwl, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper 2 bytes 0x1122 @@ -104,7 +110,7 @@ CESTER_TEST(msan_16bit_swl_8_to_16bit_lwl, test_instance, LWX(l, 1, 0x1122CCDD); ) -CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, test_instance, +CESTER_MAYBE_TEST(msan_24bit_swl_8_to_24bit_lwl, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store upper 3 bytes 0x112233 @@ -119,7 +125,7 @@ CESTER_TEST(msan_24bit_swl_8_to_24bit_lwl, test_instance, LWX(l, 2, 0x112233DD); ) -CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, test_instance, +CESTER_MAYBE_TEST(msan_32bit_swl_8_to_32bit_lwl, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store all 4 bytes 0x11223344 @@ -138,7 +144,7 @@ CESTER_TEST(msan_32bit_swl_8_to_32bit_lwl, test_instance, // SWR -> LWR -CESTER_TEST(msan_8bit_swr_8bit_lwr, test_instance, +CESTER_MAYBE_TEST(msan_8bit_swr_8bit_lwr, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower byte 0x44 @@ -151,7 +157,7 @@ CESTER_TEST(msan_8bit_swr_8bit_lwr, test_instance, LWX(r, 3, 0xAABBCC44); ) -CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, test_instance, +CESTER_MAYBE_TEST(msan_16bit_swr_8_to_16_bit_lwr, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 2 bytes 0x3344 @@ -165,7 +171,7 @@ CESTER_TEST(msan_16bit_swr_8_to_16_bit_lwr, test_instance, LWX(r, 2, 0xAABB3344); ) -CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, test_instance, +CESTER_MAYBE_TEST(msan_24bit_swr_8_to_24_bit_lwr, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 3 bytes 0x223344 @@ -180,7 +186,7 @@ CESTER_TEST(msan_24bit_swr_8_to_24_bit_lwr, test_instance, LWX(r, 1, 0xAA223344); ) -CESTER_TEST(msan_32bit_swr_8_to_32_bit_lwr, test_instance, +CESTER_MAYBE_TEST(msan_32bit_swr_8_to_32_bit_lwr, test_instance, register uint32_t* mem_32_bit = (uint32_t*) test_instance->arg; register uint32_t value = 0x11223344; // Store lower 3 bytes 0x11223344 diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 6a57f3927..7067afb52 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -244,8 +244,8 @@ void execInvalidTest(const char* type) { PCSX::g_system->quit(3); break; } - PCSX::g_system->resume(); resumeSystem = false; + PCSX::g_system->resume(); } } std::this_thread::sleep_for(10ms); From 16fdb972a7488c5c7bb93949962460b1a82d5dd2 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Sun, 28 Jun 2026 22:59:41 +1000 Subject: [PATCH 66/68] Fixed test guard in msan_valid.c Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/mips/tests/msan-valid/msan_valid.c | 6 +++--- tests/pcsxrunner/msan.cc | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/mips/tests/msan-valid/msan_valid.c b/src/mips/tests/msan-valid/msan_valid.c index d6f615299..59618b2b7 100644 --- a/src/mips/tests/msan-valid/msan_valid.c +++ b/src/mips/tests/msan-valid/msan_valid.c @@ -26,10 +26,10 @@ SOFTWARE. #include #include "common/hardware/pcsxhw.h" -#if pcsx_tests -#define cester_maybe_test cester_skip_test +#if PCSX_TESTS +#define CESTER_MAYBE_TEST CESTER_SKIP_TEST #else -#define cester_maybe_test cester_test +#define CESTER_MAYBE_TEST CESTER_TEST #endif #undef unix diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index 7067afb52..de815c041 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -17,11 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ -#include #include -#include #include -#include #include #include #include @@ -51,9 +48,9 @@ void execValidTest(const char* type) { exitCode.store(invoker.invoke()); }); while (invoker.isInStartup()); - PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); std::atomic_bool shouldFail = false; std::atomic_bool msanTriggered = false; + PCSX::EventBus::Listener listener(PCSX::g_system->m_eventBus); listener.listen([&](const PCSX::Events::LogMessage& event) { if (isMsanLog(event)) { msanTriggered = true; From e62bf66fb11d15dd101be75a08be9f1a77a246d0 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:26:51 +1000 Subject: [PATCH 67/68] Ported masking fixes to x86 and interpreted CPU Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 26 ++++----- src/core/DynaRec_x64/instructions.cc | 77 +++++++++++++++++---------- src/core/psxinterpreter.cc | 8 +-- tests/pcsxrunner/msan.cc | 1 + 4 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index 827dc5311..cde42ded2 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -529,9 +529,9 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, w3, ~3); // Force align it - gen.And(arg2, w3, 3); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 gen.Sub(arg2, 3, arg2); gen.Lsr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -552,9 +552,9 @@ void DynaRecCPU::recLWL(uint32_t code) { } } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, w3, ~3); // Force align it - gen.And(arg2, w3, 3); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 gen.Sub(arg2, 3, arg2); gen.Lsr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -621,9 +621,9 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, w3, ~3); // Force align it - gen.And(arg2, w3, 3); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 gen.Lsl(arg2, 0b1111, arg2); gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -644,9 +644,9 @@ void DynaRecCPU::recLWR(uint32_t code) { } } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.And(arg1, w3, ~3); // Force align it - gen.And(arg2, w3, 3); // Mask in arg2 + gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.And(arg1, w3, ~3); // Force align it + gen.And(arg2, w3, 3); // Mask in arg2 gen.Lsl(arg2, 0b1111, arg2); gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in eax @@ -1550,7 +1550,7 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 + gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 gen.And(arg3, 0b1111); call(write32MaskedWrapper); } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 74fcf7bc9..022411f1b 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -799,17 +799,19 @@ void DynaRecCPU::recLHU(uint32_t code) { recompileLoad<16, false>(code); } void DynaRecCPU::recLW(uint32_t code) { recompileLoad<32, true>(code); } void DynaRecCPU::recLWL(uint32_t code) { - const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; gen.mov(arg1, address & ~3); // Aligned address in arg1 + gen.mov(arg2, address & 3); } else { - allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it + allocateReg(_Rs_); // Allocate address reg + gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx, ~3); // Force align it + gen.and_(arg2, edx, 3); } - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.sub(arg2, 3, arg2); + gen.shr(arg2, 0b1111, arg2); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address return; } @@ -824,6 +826,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; + const uint32_t msanReadMask = 0b1111 >> (3 - (address & 3)); const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg1, alignedAddress); // Address in arg1 @@ -840,6 +843,7 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWL_MASK[address & 3]; const auto shift = LWL_SHIFT[address & 3]; + const uint32_t msanReadMask = 0b1111 >> (3 - (address & 3)); gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, msanReadMask); // Mask in arg2 @@ -854,9 +858,11 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx, ~3); // Force align it + gen.mov(arg2, edx, 3); // Mask in arg2 + gen.sub(arg2, 3, arg2); + gen.shr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -875,9 +881,11 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx ~3); // Force align it + gen.mov(arg2, edx, 3); // Mask in arg2 + gen.sub(arg2, 3, arg2); + gen.shr(arg2, 0b1111, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -897,17 +905,19 @@ void DynaRecCPU::recLWL(uint32_t code) { } void DynaRecCPU::recLWR(uint32_t code) { - const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; gen.mov(arg1, address & ~3); // Aligned address in arg1 + gen.mov(arg2, address & 3); } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it + gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx, ~3); // Force align it + gen.and_(arg2, edx, 3); } - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.shr(arg2, 0b1111, arg2); + gen.and_(arg2, 0b1111); // Mask in arg2 call(read32MaskedWrapper); // Read from the aligned address return; } @@ -922,6 +932,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; + const uint32_t msanReadMask = (0b1111 << (address & 3)) & 0b1111; const uint32_t previousValue = m_gprs[_Rt_].val; gen.mov(arg1, alignedAddress); // Address in arg1 @@ -938,6 +949,7 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = LWR_MASK[address & 3]; const auto shift = LWR_SHIFT[address & 3]; + const uint32_t msanReadMask = (0b1111 << (address & 3)) & 0b1111; gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, msanReadMask); // Mask in arg2 @@ -952,9 +964,11 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx, ~3); // Force align it + gen.and_(arg2, edx, 3); // Mask in arg2 + gen.shl(arg2, 0b1111, edx); + gen.and_(arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -973,9 +987,11 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(arg1, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, ~3); // Force align it - gen.mov(arg2, msanReadMask); // Mask in arg2 + gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.and_(arg1, edx, ~3); // Force align it + gen.and_(arg2, edx 3); // Mask in arg2 + gen.shl(arg2, 0b1111, edx); + gen.and_(arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1147,14 +1163,13 @@ void DynaRecCPU::recSWL(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0xFFFFFF0000000018, 0xFFFF000000000010, 0xFF00000000000008, 0}; - // TODO: Port Aarch64 changes using address & 3 instead of _Imm_ to here. - const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWL_MASK[address & 3]; const auto shift = SWL_SHIFT[address & 3]; + const uint32_t msanWriteMask = 0b1111 >> (3 - (address & 3)); gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, 0x0); // Mask in arg2 @@ -1171,6 +1186,7 @@ void DynaRecCPU::recSWL(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWL_MASK[address & 3]; const auto shift = SWL_SHIFT[address & 3]; + const uint32_t msanWriteMask = 0b1111 >> (3 - (address & 3)); gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, 0x0); // Mask in arg2 @@ -1218,7 +1234,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msanWriteMask); // Mask in arg3 + gen.sub(arg3, 3, edx); + gen.shr(arg3, 0b1111, arg3); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1254,7 +1271,8 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msanWriteMask); // Mask in arg3 + gen.sub(arg3, 3, edx); + gen.shr(arg3, 0b1111, arg3); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1263,13 +1281,13 @@ void DynaRecCPU::recSWR(uint32_t code) { // The mask to be applied to $rt (top 32 bits) and the shift to be applied to the read memory value (low 32 bits) // Depending on the low 3 bits of the unaligned address static const uint64_t MASKS_AND_SHIFTS[4] = {0, 0x000000FF00000008, 0x0000FFFF00000010, 0x00FFFFFF00000018}; - const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; if (m_gprs[_Rs_].isConst() && m_gprs[_Rt_].isConst()) { // Both previous register value and address are constant const uint32_t address = m_gprs[_Rs_].val + _Imm_; const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWR_MASK[address & 3]; const auto shift = SWR_SHIFT[address & 3]; + const uint32_t msanWriteMask = (0b1111 << (address & 3)) & 0b1111; gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, 0x0); // Mask in arg2 @@ -1286,6 +1304,7 @@ void DynaRecCPU::recSWR(uint32_t code) { const uint32_t alignedAddress = address & ~3; const uint32_t mask = SWR_MASK[address & 3]; const auto shift = SWR_SHIFT[address & 3]; + const uint32_t msanWriteMask = (0b1111 << (address & 3)) & 0b1111; gen.mov(arg1, alignedAddress); // Address in arg1 gen.mov(arg2, 0x0); // Mask in arg2 @@ -1333,7 +1352,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msanWriteMask); // Mask in arg3 + gen.shl(arg3, 0b1111, edx); + gen.and_(arg3, 0b1111); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1369,7 +1389,8 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.mov(arg3, msanWriteMask); // Mask in arg3 + gen.shl(arg3, 0b1111, edx); + gen.and_(arg3, 0b1111); // Mask in arg3 call(write32MaskedWrapper); } } diff --git a/src/core/psxinterpreter.cc b/src/core/psxinterpreter.cc index 9dd85ac25..1fb33078d 100644 --- a/src/core/psxinterpreter.cc +++ b/src/core/psxinterpreter.cc @@ -936,7 +936,7 @@ void InterpretedCPU::psxLW(uint32_t code) { void InterpretedCPU::psxLWL(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; - const uint32_t msanReadMask = 0b1111 >> (3 - _Imm_); + const uint32_t msanReadMask = 0b1111 >> (3 - shift); uint32_t mem = PCSX::g_emulator->m_mem->read32( addr & ~3, PCSX::Memory::ReadType::Data, @@ -959,7 +959,7 @@ void InterpretedCPU::psxLWL(uint32_t code) { void InterpretedCPU::psxLWR(uint32_t code) { uint32_t addr = _oB_; uint32_t shift = addr & 3; - const uint32_t msanReadMask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanReadMask = (0b1111 << shift) & 0b1111; uint32_t mem = PCSX::g_emulator->m_mem->read32( addr & ~3, PCSX::Memory::ReadType::Data, @@ -1015,7 +1015,7 @@ void InterpretedCPU::psxSWL(uint32_t code) { PCSX::Memory::ReadType::Data, 0x0 ); - const uint32_t msanWriteMask = 0b1111 >> (3 - _Imm_); + const uint32_t msanWriteMask = 0b1111 >> (3 - shift); PCSX::g_emulator->m_mem->write32( addr, (_u32(_rRt_) >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift]), @@ -1040,7 +1040,7 @@ void InterpretedCPU::psxSWR(uint32_t code) { PCSX::Memory::ReadType::Data, 0x0 ); - const uint32_t msanWriteMask = (0b1111 << _Imm_) & 0b1111; + const uint32_t msanWriteMask = (0b1111 << shift) & 0b1111; PCSX::g_emulator->m_mem->write32( addr, (_u32(_rRt_) << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift]), diff --git a/tests/pcsxrunner/msan.cc b/tests/pcsxrunner/msan.cc index de815c041..680a8f391 100644 --- a/tests/pcsxrunner/msan.cc +++ b/tests/pcsxrunner/msan.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "fmt/printf.h" #include "gtest/gtest.h" #include "main/main.h" From 21957d4cf2bfe23568a672a4f4df54ba799e7078 Mon Sep 17 00:00:00 2001 From: EngineersBox <35655145+EngineersBox@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:12:39 +1000 Subject: [PATCH 68/68] Fixed BAD_COMBINATION with shl/shr instructions not using CL register for shift operand Signed-off-by: EngineersBox <35655145+EngineersBox@users.noreply.github.com> --- src/core/DynaRec_aa64/instructions.cc | 40 ++++++---- src/core/DynaRec_x64/instructions.cc | 111 ++++++++++++++++---------- 2 files changed, 95 insertions(+), 56 deletions(-) diff --git a/src/core/DynaRec_aa64/instructions.cc b/src/core/DynaRec_aa64/instructions.cc index cde42ded2..954e2e4c6 100644 --- a/src/core/DynaRec_aa64/instructions.cc +++ b/src/core/DynaRec_aa64/instructions.cc @@ -532,8 +532,10 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, w3, ~3); // Force align it gen.And(arg2, w3, 3); // Mask in arg2 - gen.Sub(arg2, 3, arg2); - gen.Lsr(arg2, 0b1111, arg2); + gen.Mov(w5, 3); + gen.Sub(arg2, w5, arg2); + gen.Mov(w5, 0b1111); + gen.Lsr(arg2, w5, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -555,8 +557,10 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, w3, ~3); // Force align it gen.And(arg2, w3, 3); // Mask in arg2 - gen.Sub(arg2, 3, arg2); - gen.Lsr(arg2, 0b1111, arg2); + gen.Mov(w5, 3); + gen.Sub(arg2, w5, arg2); + gen.Mov(w5, 0b1111); + gen.Lsr(arg2, w5, arg2); call(read32MaskedWrapper); // Read from the aligned address, result in w0 if (_Rt_) { @@ -624,7 +628,8 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, w3, ~3); // Force align it gen.And(arg2, w3, 3); // Mask in arg2 - gen.Lsl(arg2, 0b1111, arg2); + gen.Mov(w5, 0b1111); + gen.Lsl(arg2, w5, arg2); gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in w0 @@ -647,7 +652,8 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.moveAndAdd(x3, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 gen.And(arg1, w3, ~3); // Force align it gen.And(arg2, w3, 3); // Mask in arg2 - gen.Lsl(arg2, 0b1111, arg2); + gen.Mov(w5, 0b1111); + gen.Lsl(arg2, w5, arg2); gen.And(arg2, arg2, 0b1111); call(read32MaskedWrapper); // Read from the aligned address, result in eax @@ -1430,8 +1436,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Sub(arg3, 3, w2); // Mask in arg3 - gen.Lsr(arg3, 0b1111, arg3); + gen.Mov(w5, 3); + gen.Sub(arg3, w5, w2); // Mask in arg3 + gen.Mov(w5, 0b1111); + gen.Lsr(arg3, w5, arg3); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1455,8 +1463,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // arg2 = value to write to memory gen.Mov(arg1, w5); - gen.Sub(arg3, 3, w2); // Mask in arg3 - gen.Lsr(arg3, 0b1111, arg3); + gen.Mov(w5, 3); + gen.Sub(arg3, w5, w2); // Mask in arg3 + gen.Mov(w5, 0b1111); + gen.Lsr(arg3, w5, arg3); call(write32MaskedWrapper); } } @@ -1524,8 +1534,9 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); gen.Mov(arg1, w5); - gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 - gen.And(arg3, 0b1111); + gen.Mov(arg3, 0b1111); + gen.Lsl(arg3, arg3, w2); // Mask in arg3 + gen.And(arg3, arg3, 0b1111); call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1550,8 +1561,9 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.And(x0, x0, Operand(x3, LSR, 32)); // Mask read value gen.Orr(arg2, arg2, w0); // Or with read value gen.Mov(arg1, w5); - gen.Lsl(arg3, 0b1111, w2); // Mask in arg3 - gen.And(arg3, 0b1111); + gen.Mov(arg3, 0b1111); + gen.Lsl(arg3, arg3, w2); // Mask in arg3 + gen.And(arg3, arg3, 0b1111); call(write32MaskedWrapper); } } diff --git a/src/core/DynaRec_x64/instructions.cc b/src/core/DynaRec_x64/instructions.cc index 022411f1b..169bd0d4c 100644 --- a/src/core/DynaRec_x64/instructions.cc +++ b/src/core/DynaRec_x64/instructions.cc @@ -802,16 +802,20 @@ void DynaRecCPU::recLWL(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg1, address & ~3); // Aligned address in arg1 - gen.mov(arg2, address & 3); + gen.mov(eax, address & ~3); // Aligned address in arg1 + gen.mov(ebx, address & 3); } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx, ~3); // Force align it - gen.and_(arg2, edx, 3); - } - gen.sub(arg2, 3, arg2); - gen.shr(arg2, 0b1111, arg2); // Mask in arg2 + gen.moveAndAdd(eax, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(ebx, eax); + gen.and_(eax, ~3); // Force align it + gen.and_(ebx, 3); + } + gen.mov(ecx, 3); + gen.sub(ecx, ebx); + gen.mov(arg2, 0b1111); + gen.shr(arg2, cl); // Mask in arg2 + gen.mov(arg1, eax); call(read32MaskedWrapper); // Read from the aligned address return; } @@ -858,11 +862,15 @@ void DynaRecCPU::recLWL(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx, ~3); // Force align it - gen.mov(arg2, edx, 3); // Mask in arg2 - gen.sub(arg2, 3, arg2); - gen.shr(arg2, 0b1111, arg2); + gen.moveAndAdd(eax, m_gprs[_Rs_].allocatedReg, _Imm_); + gen.mov(ebx, eax); + gen.and_(ebx, 3); + gen.mov(ecx, 3); + gen.sub(ecx, ebx); + gen.mov(arg2, 0b1111); + gen.shr(arg2, cl); // Mask in arg2 + gen.mov(arg1, eax); // Address in arg1 + gen.and_(arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -881,11 +889,15 @@ void DynaRecCPU::recLWL(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx ~3); // Force align it - gen.mov(arg2, edx, 3); // Mask in arg2 - gen.sub(arg2, 3, arg2); - gen.shr(arg2, 0b1111, arg2); + gen.moveAndAdd(eax, m_gprs[_Rs_].allocatedReg, _Imm_); + gen.mov(ebx, eax); + gen.and_(ebx, 3); + gen.mov(ecx, 3); + gen.sub(ecx, ebx); + gen.mov(arg2, 0b1111); + gen.shr(arg2, cl); // Mask in arg2 + gen.mov(arg1, eax); // Address in arg1 + gen.and_(arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -908,16 +920,19 @@ void DynaRecCPU::recLWR(uint32_t code) { if (_Rt_ == 0) { // If $rt == 0, just execute the read in case it has side-effects, then return if (m_gprs[_Rs_].isConst()) { const uint32_t address = m_gprs[_Rs_].val + _Imm_; - gen.mov(arg1, address & ~3); // Aligned address in arg1 - gen.mov(arg2, address & 3); + gen.mov(eax, address & ~3); // Aligned address in arg1 + gen.mov(ecx, address & 3); } else { allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(edx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx, ~3); // Force align it - gen.and_(arg2, edx, 3); + gen.moveAndAdd(eax, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(ecx, eax); + gen.and_(eax, ~3); // Force align it + gen.and_(ecx, 3); } - gen.shr(arg2, 0b1111, arg2); + gen.mov(arg2, 0b1111); + gen.shr(arg2, al); gen.and_(arg2, 0b1111); // Mask in arg2 + gen.mov(arg1, eax); call(read32MaskedWrapper); // Read from the aligned address return; } @@ -964,11 +979,14 @@ void DynaRecCPU::recLWR(uint32_t code) { const uint32_t previousValue = m_gprs[_Rt_].val; allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx, ~3); // Force align it - gen.and_(arg2, edx, 3); // Mask in arg2 - gen.shl(arg2, 0b1111, edx); - gen.and_(arg2, 0b1111); + gen.moveAndAdd(ebx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(ecx, ebx); + gen.and_(ecx, 3); + gen.mov(arg2, 0b1111); + gen.shl(arg2, cl); + gen.and_(arg2, 0b1111); // Mask in arg2 + gen.mov(arg1, ebx); + gen.and_(arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -987,11 +1005,14 @@ void DynaRecCPU::recLWR(uint32_t code) { gen.or_(m_gprs[_Rt_].allocatedReg, eax); // Merge with newly read value } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg - gen.moveAndAdd(rdx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 - gen.and_(arg1, edx, ~3); // Force align it - gen.and_(arg2, edx 3); // Mask in arg2 - gen.shl(arg2, 0b1111, edx); - gen.and_(arg2, 0b1111); + gen.moveAndAdd(ebx, m_gprs[_Rs_].allocatedReg, _Imm_); // Address in arg1 + gen.mov(ecx, ebx); + gen.and_(ecx, 3); + gen.mov(arg2, 0b1111); + gen.shl(arg2, cl); + gen.and_(arg2, 0b1111); // Mask in arg2 + gen.mov(arg1, ebx); + gen.and_(arg1, ~3); // Force align it call(read32MaskedWrapper); // Read from the aligned address, result in eax // The call might have flushed $rs, so we need to allocate it again, and also allocate $rt @@ -1234,8 +1255,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.sub(arg3, 3, edx); - gen.shr(arg3, 0b1111, arg3); // Mask in arg3 + // gen.mov(rax, 3); + // gen.sub(rax, edx); + gen.mov(arg3, 0b1111); + // gen.shr(arg3, al); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1271,8 +1294,10 @@ void DynaRecCPU::recSWL(uint32_t code) { gen.mov(arg1, arg4); } - gen.sub(arg3, 3, edx); - gen.shr(arg3, 0b1111, arg3); // Mask in arg3 + // gen.mov(rax, 3); + // gen.sub(rax, edx); + gen.mov(arg3, 0b1111); + // gen.shr(arg3, al); // Mask in arg3 call(write32MaskedWrapper); } } @@ -1352,8 +1377,9 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.shl(arg3, 0b1111, edx); - gen.and_(arg3, 0b1111); // Mask in arg3 + gen.mov(arg3, 0b1111); + // gen.shl(arg3, dl); + // gen.and_(arg3, 0b1111); // Mask in arg3 call(write32MaskedWrapper); } else { // Nothing is constant allocateReg(_Rs_); // Allocate address reg @@ -1389,8 +1415,9 @@ void DynaRecCPU::recSWR(uint32_t code) { gen.mov(arg1, arg4); } - gen.shl(arg3, 0b1111, edx); - gen.and_(arg3, 0b1111); // Mask in arg3 + gen.mov(arg3, 0b1111); + // gen.shl(arg3, cl); + // gen.and_(arg3, 0b1111); // Mask in arg3 call(write32MaskedWrapper); } }