Files
PS2Recomp/ps2xIOP/tests/iop_import_version_tests.cpp
Ranieri 75d729ce40 Feature/iop emulator (#244)
* refactor: from guest  threads to EE scheduler

* feat: bad wip mpeg fix for code veronica

* feat: cheap copy from host
feat: small perf o vsync tick

* feat: added EE clock Hz
fix: fix MPEG out of sync with new EE refactor

* fix: fix lotr tests

* fix: fix cri dtx loading
fix: fix wrong mmi instruction translation
fix: fix thread info params
feat: added EE  timers decoder and consumer
feat: split SFI and IOP memory to prevent collision and overrides

* feat: revert wrong changes

* refactor: change GS architecture

* feat: IOP emulator
refactor: codegen to catch callbacks on mips code
feat: added a lot of entries or IOP emulator

* feat: analyzer resolve the complete constant-producing sequence with five-instruction backward scan stopped at LUI and therefore

* feat: remove recompiled version of GetRomName
refactor: split IOP emulator logic
feat: added more HLE IOP modules
feat: added ps2_path

* eat: enhance ELF parser with improved callable entry detection and control flow analysis

* feat: update memory hint handling and enhance entry point discovery logic

* feat: add SET_GPR_ZE32 macro for zero-extending loads with unsigned semantics

* refactor: Refactor PS2 IOP Host Adapter and Memory Management
feat: Added PS2Vfs for virtual file system operations, including file opening, reading, writing, and path resolution.
feat: Improve VIF1 data processing to handle GIF image packets more efficiently.

* feat: added a lot of tests

* fix: fix texture caching
feat: wip multi version on dbcman

* feat: remove LLE IOPs
2026-09-19 21:31:44 -03:00

171 lines
7.5 KiB
C++

#include "iop_compat_test_support.h"
#include "emulator/core/iop_cpu.h"
#include "emulator/core/iop_memory.h"
#include "emulator/imports/iop_imports.h"
#include "emulator/imports/iop_loadcore.h"
namespace
{
using namespace iop_test;
using namespace ps2x::iop::detail;
void addExport(IopMemory &memory, IopImportRegistry &imports, uint32_t address,
uint16_t version, uint32_t target, uint32_t count = 4u)
{
require(memory.zeroRam(address, 128u), "export table does not fit");
memory.write32(address, 0x41C00000u);
memory.write16(address + 8u, version);
constexpr char name[8] = "tstlib";
require(memory.writeRam(address + 12u, name, sizeof(name)), "export name does not fit");
for (uint32_t i = 0u; i < count; ++i)
memory.write32(address + 20u + 4u * i, target);
require(imports.registerExportTable(address), "export registration failed");
}
void importTable(IopMemory &memory, uint32_t address, uint16_t version)
{
require(memory.zeroRam(address, 64u), "import table does not fit");
memory.write32(address, 0x41E00000u);
memory.write16(address + 8u, version);
constexpr char name[8] = "tstlib";
require(memory.writeRam(address + 12u, name, sizeof(name)), "import name does not fit");
memory.write32(address + 20u, 0x03E00008u);
memory.write32(address + 24u, 0x24000003u);
}
void decodeVersion()
{
IopMemory memory;
IopImportRegistry imports(memory);
importTable(memory, 0x1000u, 0x0310u);
const auto call = imports.decode(0x1014u);
require(call && call->library == "tstlib" && call->ordinal == 3u && call->version == 0x0310u,
"decoder dropped the import library version");
const auto alias = imports.decode(0x80001014u);
require(alias && alias->version == 0x0310u, "cached alias lost import version");
}
void majorIsolation()
{
IopMemory memory;
IopImportRegistry imports(memory);
addExport(memory, imports, 0x1000u, 0x0201u, 0x2100u);
addExport(memory, imports, 0x1800u, 0x0101u, 0x3100u);
require(imports.resolve("tstlib", 3u, 0x0101u) == 0x3100u, "linked to wrong library major");
require(imports.resolve("tstlib", 3u, 0x0201u) == 0x2100u, "second major unavailable");
require(imports.resolve("tstlib", 3u, 0x0300u) == 0u, "incompatible major silently linked");
require(imports.findTable("tstlib", 0x0300u) == 0u, "query ignored requested major");
}
void newestMinor()
{
IopMemory memory;
IopImportRegistry imports(memory);
addExport(memory, imports, 0x1000u, 0x0101u, 0x2100u);
addExport(memory, imports, 0x1800u, 0x0104u, 0x3100u);
addExport(memory, imports, 0x1400u, 0x0103u, 0x4100u);
require(imports.resolve("tstlib", 3u, 0x0101u) == 0x3100u, "selected lowest address, not newest minor");
require(imports.resolve("tstlib", 3u, 0x017Fu) == 0x3100u,
"invented a minimum-minor rule absent from LOADCORE linking");
require(imports.releaseExportTable(0x1800u), "unregister failed");
require(imports.resolve("tstlib", 3u, 0x0101u) == 0x4100u, "unregistered library remained selected");
}
void missingOrdinal()
{
IopMemory memory;
IopImportRegistry imports(memory);
addExport(memory, imports, 0x1000u, 0x0101u, 0x2100u, 8u);
addExport(memory, imports, 0x1800u, 0x0102u, 0x3100u, 4u);
require(imports.resolve("tstlib", 7u, 0x0101u) == 0u,
"missing ordinal fell back to a different export table");
require(imports.resolve("missing", 0u, 0x0101u) == 0u, "missing library resolved");
imports.reset();
require(imports.resolve("tstlib", 0u, 0x0101u) == 0u, "registry reset left exports");
}
void queryFunctionArray()
{
IopMemory memory;
IopImportRegistry imports(memory);
IopLoadcore loadcore(memory, imports);
addExport(memory, imports, 0x1000u, 0x0201u, 0x2100u);
addExport(memory, imports, 0x1800u, 0x0101u, 0x3100u);
importTable(memory, 0x800u, 0x0102u);
IopCpuState cpu{};
cpu.gpr[4] = 0x800u;
require(loadcore.dispatchImport(11u, cpu), "QueryLibraryEntryTable unhandled");
require(cpu.gpr[2] == 0x1814u && memory.read32(cpu.gpr[2]) == 0x3100u,
"query returned an export header instead of function array");
memory.write16(0x808u, 0x0300u);
require(loadcore.dispatchImport(11u, cpu) && cpu.gpr[2] == 0u, "query accepted wrong major");
for (uint32_t address : {0u, 0xFFFFFFF8u, IopMemory::RamSize - 4u})
{
cpu.gpr[4] = address;
require(loadcore.dispatchImport(11u, cpu) && cpu.gpr[2] == 0u, "invalid query pointer accepted");
}
}
Irx provider(uint32_t base, uint16_t version, uint32_t result)
{
Irx image(base);
const uint32_t table = base + 0x80u;
const uint32_t importStub = base + 0xC0u + 20u;
image.words(0u, {0x27BDFFE0u, 0xAFBF001Cu,
0x3C040000u | (table >> 16u), 0x34840000u | (table & 0xFFFFu),
0x0C000000u | (importStub >> 2u), 0u,
0x8FBF001Cu, 0x00001021u, 0x27BD0020u, 0x03E00008u, 0u});
image.words(0x60u, {0x03E00008u, 0x24020000u | result});
image.words(0x80u, {0x41C00000u, 0u, version, 0x6C747374u, 0x00006269u,
base, base, base, base + 0x60u, 0u});
image.words(0xC0u, {0x41E00000u, 0u, 0x0101u, 0x64616F6Cu, 0x65726F63u,
0x03E00008u, 0x24000006u, 0u, 0u});
return image;
}
Irx consumer(uint16_t version)
{
constexpr uint32_t base = 0x13000u;
Irx image(base);
image.words(0u, {0x27BDFFF0u, 0xAFBF000Cu,
0x0C000000u | ((base + 0x54u) >> 2u), 0u,
0x8FBF000Cu, 0x27BD0010u, 0x03E00008u, 0u});
image.words(0x40u, {0x41E00000u, 0u, version, 0x6C747374u, 0x00006269u,
0x03E00008u, 0x24000003u, 0u, 0u});
return image;
}
void physicalImportsEndToEnd()
{
Host host;
IopSubsystem iop(host);
auto wrongMajor = provider(0x10000u, 0x0201u, 0x22u);
wrongMajor.install(host);
require(iop.loadModuleBuffer(0x1000u).startResult == 0, "provider 2 failed");
auto oldMinor = provider(0x11000u, 0x0101u, 0x11u);
oldMinor.install(host);
require(iop.loadModuleBuffer(0x1000u).startResult == 0, "provider 1 failed");
auto newMinor = provider(0x12000u, 0x0103u, 0x13u);
newMinor.install(host);
require(iop.loadModuleBuffer(0x1000u).startResult == 0, "provider 1.3 failed");
auto client = consumer(0x0101u);
client.install(host);
const auto result = iop.loadModuleBuffer(0x1000u);
require(result.moduleId > 0 && result.startResult == 0x13, "R3000A called wrong export version");
}
}
int main()
{
const Test tests[] = {
{"Import decoder preserves library ABI version", decodeVersion},
{"Different major versions cannot cross-link", majorIsolation},
{"Newest registered minor wins within the requested major", newestMinor},
{"Ordinal lookup stays in the selected table", missingOrdinal},
{"LOADCORE query returns function array and honors major", queryFunctionArray},
{"Physical IRX consumer links correct version end to end", physicalImportsEndToEnd},
};
return run(tests);
}