mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
feat: invert codegen hight to low convertion (#131)
* feat: invert codegen hight to low convertion feat: added copy and GetEntryAddress feat: handle truncated DMAC * feat: always use address on analyzer now * feat: correct pick syscalls ID * feat: added deci2Call * feat: added wip dbcmain IOP * feat: added InitTLB feat: added err logs on thread for debug sus crash * fix: fix SetupHeap for strange cases * feat: fix incorrect SetupHeap test(it use a wrong idea on how heap allocate memory) * feat: added memalign and memalign_r feat: added GetOsdConfigParam2 and SetOsdConfigParam2 but idk if was a good idea * feat: added more memory stuff * feat: back to library functions
This commit is contained in:
@@ -29,7 +29,7 @@ namespace ps2recomp
|
||||
const std::vector<std::string> stdLibFuncs = {
|
||||
"printf", "sprintf", "snprintf", "fprintf", "vprintf", "vfprintf", "vsprintf", "vsnprintf",
|
||||
"puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf",
|
||||
"sprint", "sbprintf",
|
||||
"sprint", "sbprintf", "__mcmp", "__sbprintf", "__sprint", "__sprint_r",
|
||||
"malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign",
|
||||
"memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero",
|
||||
"strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr",
|
||||
|
||||
@@ -72,12 +72,6 @@ namespace ps2recomp
|
||||
|
||||
auto makeSelector = [&](const std::string &name, uint32_t start) -> std::string
|
||||
{
|
||||
auto it = functionNameCounts.find(name);
|
||||
if (it == functionNameCounts.end() || it->second <= 1)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
std::stringstream selector;
|
||||
selector << name << "@0x"
|
||||
<< std::hex << std::uppercase << std::setw(8) << std::setfill('0')
|
||||
|
||||
@@ -2527,8 +2527,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
inst.rd,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
}
|
||||
case VU0_S2_VMOVE:
|
||||
@@ -2678,7 +2678,7 @@ namespace ps2recomp
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
uint8_t field = inst.function & 0x3;
|
||||
std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field);
|
||||
return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VSUB_Field(const Instruction &inst)
|
||||
@@ -2689,7 +2689,7 @@ namespace ps2recomp
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
uint8_t field = inst.function & 0x3;
|
||||
std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field);
|
||||
return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VMUL_Field(const Instruction &inst)
|
||||
@@ -2700,7 +2700,7 @@ namespace ps2recomp
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
uint8_t field = inst.function & 0x3;
|
||||
std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field);
|
||||
return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VADD(const Instruction &inst)
|
||||
@@ -2709,7 +2709,7 @@ namespace ps2recomp
|
||||
uint8_t vfs = inst.rd;
|
||||
uint8_t vft = inst.rt;
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VSUB(const Instruction &inst)
|
||||
@@ -2718,7 +2718,7 @@ namespace ps2recomp
|
||||
uint8_t vfs = inst.rd;
|
||||
uint8_t vft = inst.rt;
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VMUL(const Instruction &inst)
|
||||
@@ -2727,7 +2727,7 @@ namespace ps2recomp
|
||||
uint8_t vfs = inst.rd;
|
||||
uint8_t vft = inst.rt;
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd);
|
||||
return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translatePEXT5(const Instruction &inst)
|
||||
@@ -3080,8 +3080,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
inst.rd,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
}
|
||||
|
||||
@@ -3186,8 +3186,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs, vft, vft, shuffle_pattern,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3207,8 +3207,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs, vft, vft, shuffle_pattern,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3226,8 +3226,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, vft, vft, shuffle_pattern,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3245,8 +3245,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, vft, vft, shuffle_pattern,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3262,8 +3262,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs, vft,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3278,8 +3278,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3294,8 +3294,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3309,8 +3309,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, vft,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3323,8 +3323,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3337,8 +3337,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3351,8 +3351,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3365,8 +3365,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3383,8 +3383,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs, vft,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3399,8 +3399,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3413,8 +3413,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3432,8 +3432,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs, vft,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3447,8 +3447,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, vft,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3463,8 +3463,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3477,8 +3477,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3494,8 +3494,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3510,8 +3510,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vu0_acc = res; }}",
|
||||
vfs,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vfd, vfd);
|
||||
}
|
||||
|
||||
@@ -3713,8 +3713,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, formatFloatLiteral(scale),
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
}
|
||||
|
||||
@@ -3731,8 +3731,8 @@ namespace ps2recomp
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, formatFloatLiteral(scale),
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
}
|
||||
|
||||
@@ -3746,8 +3746,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); "
|
||||
"ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}",
|
||||
vis,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt,
|
||||
vis, vis);
|
||||
}
|
||||
@@ -3763,8 +3763,8 @@ namespace ps2recomp
|
||||
"ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}",
|
||||
vis,
|
||||
inst.rt,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
vis, vis);
|
||||
}
|
||||
|
||||
@@ -3779,8 +3779,8 @@ namespace ps2recomp
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vis, vis,
|
||||
vis,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
}
|
||||
|
||||
@@ -3796,15 +3796,15 @@ namespace ps2recomp
|
||||
vis, vis,
|
||||
vis,
|
||||
inst.rt,
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0);
|
||||
(dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0,
|
||||
(dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VRGET(const Instruction &inst)
|
||||
{
|
||||
uint8_t dest_mask = inst.vectorInfo.vectorField;
|
||||
uint8_t ft_reg = inst.rt;
|
||||
return fmt::format("{{ __m128 res = ctx->vu0_r; __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, ft_reg, ft_reg);
|
||||
return fmt::format("{{ __m128 res = ctx->vu0_r; __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, ft_reg, ft_reg);
|
||||
}
|
||||
|
||||
std::string CodeGenerator::translateVU_VRINIT(const Instruction &inst)
|
||||
|
||||
@@ -53,19 +53,21 @@ public class ExportPS2Functions extends GhidraScript {
|
||||
"iGsPutIMR", "SetVSyncFlag", "SetSyscall", "GsSetVideoMode", "GetOsdConfigParam", "SetOsdConfigParam",
|
||||
"EnableCache", "DisableCache", "GetRomName", "SifLoadElfPart", "sceSifLoadElf", "sceSifLoadElfPart",
|
||||
"sceSifLoadModule", "sceSifLoadModuleBuffer", "SetupThread", "EndOfHeap", "GetMemorySize", "Deci2Call",
|
||||
"QueryBootMode", "GetThreadTLS", "RegisterExitHandler", "ret0", "ret1", "reta0",
|
||||
"calloc_r", "free_r", "malloc_r", "malloc_trim_r", "mbtowc_r", "printf_r",
|
||||
"QueryBootMode", "GetThreadTLS", "Copy", "GetEntryAddress", "RegisterExitHandler", "ret0", "ret1", "reta0",
|
||||
"calloc_r", "free_r", "realloc_r", "memalign_r", "malloc_r", "malloc_extend_top", "malloc_trim_r", "mbtowc_r", "printf_r",
|
||||
"__malloc_lock", "__malloc_unlock",
|
||||
"abs", "__ieee754_rem_pio2f", "__kernel_cosf", "__kernel_sinf", "atan", "atan2",
|
||||
"calloc", "ceil", "close", "cos", "exit", "exp",
|
||||
"fabs", "fclose", "fflush", "floor", "fopen", "fprintf",
|
||||
"fread", "free", "fseek", "fstat", "ftell", "fwrite",
|
||||
"getpid", "log", "log10", "lseek", "malloc", "memchr",
|
||||
"getpid", "log", "log10", "lseek", "malloc", "memalign", "memclr", "memchr",
|
||||
"memcmp", "memcpy", "memmove", "memset", "open", "pow",
|
||||
"printf", "puts", "rand", "read", "realloc", "sin",
|
||||
"snprintf", "sprintf", "sqrt", "srand", "stat", "strcasecmp",
|
||||
"strcat", "strchr", "strcmp", "strcpy", "strlen", "strncat",
|
||||
"strncmp", "strncpy", "strrchr", "strstr", "tan", "vfprintf",
|
||||
"vsprintf", "write", "DmaAddr", "builtin_set_imask", "sceCdRI", "sceCdRM",
|
||||
"vsprintf", "write", "__divdi3",
|
||||
"DmaAddr", "builtin_set_imask", "sceCdRI", "sceCdRM",
|
||||
"sceDevVif0Reset", "sceDevVu0Reset", "sceFsDbChk", "sceFsIntrSigSema", "sceFsSemExit", "sceFsSemInit",
|
||||
"sceFsSigSema", "sceIDC", "sceMpegFlush", "sceRpcFreePacket", "sceRpcGetFPacket", "sceRpcGetFPacket2",
|
||||
"sceSDC", "sceSifCmdIntrHdlr", "sceVu0ecossin", "mcCallMessageTypeSe", "mcCheckReadStartConfigFile", "mcCheckReadStartSaveFile",
|
||||
@@ -141,7 +143,7 @@ public class ExportPS2Functions extends GhidraScript {
|
||||
"sceVu0InterVectorXYZ", "sceVu0InversMatrix", "sceVu0LightColorMatrix", "sceVu0MulMatrix", "sceVu0MulVector", "sceVu0NormalLightMatrix",
|
||||
"sceVu0Normalize", "sceVu0OuterProduct", "sceVu0RotMatrix", "sceVu0RotMatrixX", "sceVu0RotMatrixY", "sceVu0RotMatrixZ",
|
||||
"sceVu0RotTransPers", "sceVu0RotTransPersN", "sceVu0ScaleVector", "sceVu0ScaleVectorXYZ", "sceVu0SubVector", "sceVu0TransMatrix",
|
||||
"sceVu0TransposeMatrix", "sceVu0UnitMatrix", "sceVu0ViewScreenMatrix", "sceWrite"
|
||||
"sceVu0TransposeMatrix", "sceVu0UnitMatrix", "sceVu0ViewScreenMatrix", "sceWrite", "InitTLB"
|
||||
));
|
||||
|
||||
private static final Set<String> PS2_API_PREFIXES = new HashSet<>(Arrays.asList(
|
||||
@@ -158,9 +160,9 @@ public class ExportPS2Functions extends GhidraScript {
|
||||
|
||||
private static final Set<String> KNOWN_STDLIB_NAMES = new HashSet<>(Arrays.asList(
|
||||
"printf", "sprintf", "snprintf", "fprintf", "vprintf", "vfprintf", "vsprintf", "vsnprintf",
|
||||
"puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf",
|
||||
"sprint", "sbprintf",
|
||||
"malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign",
|
||||
"puts", "putchar", "getchar", "gets", "fgets",
|
||||
"__mcmp", "__sbprintf", "__sprint", "__sprint_r",
|
||||
"malloc", "free", "calloc", "realloc", "memalign", "memclr", "aligned_alloc", "posix_memalign",
|
||||
"memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero",
|
||||
"strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr",
|
||||
"strchr", "strrchr", "strdup", "strtok", "strtok_r", "strerror",
|
||||
|
||||
@@ -170,6 +170,7 @@ add_library(ps2_runtime STATIC
|
||||
src/lib/ps2_gs_rasterizer.cpp
|
||||
src/lib/ps2_iop.cpp
|
||||
src/lib/ps2_iop_audio.cpp
|
||||
src/lib/ps2_iop_dbcman.cpp
|
||||
src/lib/ps2_memory.cpp
|
||||
src/lib/ps2_pad.cpp
|
||||
src/lib/ps2_runtime.cpp
|
||||
|
||||
@@ -116,6 +116,8 @@
|
||||
\
|
||||
X(GetOsdConfigParam) \
|
||||
X(SetOsdConfigParam) \
|
||||
X(GetOsdConfigParam2) \
|
||||
X(SetOsdConfigParam2) \
|
||||
X(EnableCache) \
|
||||
X(DisableCache) \
|
||||
X(GetRomName) \
|
||||
@@ -128,9 +130,12 @@
|
||||
X(SetupThread) \
|
||||
X(EndOfHeap) \
|
||||
X(GetMemorySize) \
|
||||
X(InitTLB) \
|
||||
X(Deci2Call) \
|
||||
X(QueryBootMode) \
|
||||
X(GetThreadTLS) \
|
||||
X(Copy) \
|
||||
X(GetEntryAddress) \
|
||||
X(RegisterExitHandler)
|
||||
|
||||
// Stubs
|
||||
@@ -141,10 +146,15 @@
|
||||
X(reta0) \
|
||||
X(calloc_r) \
|
||||
X(free_r) \
|
||||
X(realloc_r) \
|
||||
X(memalign_r) \
|
||||
X(malloc_r) \
|
||||
X(malloc_extend_top) \
|
||||
X(malloc_trim_r) \
|
||||
X(mbtowc_r) \
|
||||
X(printf_r) \
|
||||
X(__malloc_lock) \
|
||||
X(__malloc_unlock) \
|
||||
X(abs) \
|
||||
X(__ieee754_rem_pio2f) \
|
||||
X(__kernel_cosf) \
|
||||
@@ -174,6 +184,8 @@
|
||||
X(log10) \
|
||||
X(lseek) \
|
||||
X(malloc) \
|
||||
X(memalign) \
|
||||
X(memclr) \
|
||||
X(memchr) \
|
||||
X(memcmp) \
|
||||
X(memcpy) \
|
||||
@@ -207,6 +219,7 @@
|
||||
X(vfprintf) \
|
||||
X(vsprintf) \
|
||||
X(write) \
|
||||
X(__divdi3) \
|
||||
/* PS2 native */ \
|
||||
X(DmaAddr) \
|
||||
X(builtin_set_imask) \
|
||||
|
||||
@@ -511,6 +511,7 @@ public:
|
||||
void guestFree(uint32_t guestAddr);
|
||||
uint32_t guestHeapBase() const;
|
||||
uint32_t guestHeapEnd() const;
|
||||
uint32_t guestHeapLimit() const;
|
||||
uint32_t reserveAsyncCallbackStack(uint32_t size, uint32_t alignment = 16u);
|
||||
void dispatchLoop(uint8_t *rdram, R5900Context *ctx);
|
||||
bool shouldPreemptGuestExecution();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class PS2Runtime;
|
||||
|
||||
namespace ps2_iop_dbcman
|
||||
{
|
||||
bool handleDbcManRpc(uint8_t *rdram,
|
||||
uint32_t sid, uint32_t rpcNum,
|
||||
uint32_t sendBufAddr, uint32_t sendSize,
|
||||
uint32_t recvBufAddr, uint32_t recvSize,
|
||||
uint32_t &resultPtr);
|
||||
}
|
||||
@@ -39,6 +39,22 @@ namespace ps2_stubs
|
||||
}
|
||||
}
|
||||
|
||||
void realloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t oldGuestAddr = getRegU32(ctx, 5); // $a1
|
||||
const uint32_t newSize = getRegU32(ctx, 6); // $a2
|
||||
const uint32_t guestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u;
|
||||
setReturnU32(ctx, guestAddr);
|
||||
}
|
||||
|
||||
void memalign_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t alignment = getRegU32(ctx, 5); // $a1
|
||||
const uint32_t size = getRegU32(ctx, 6); // $a2
|
||||
const uint32_t guestAddr = runtime ? runtime->guestMalloc(size, alignment) : 0u;
|
||||
setReturnU32(ctx, guestAddr);
|
||||
}
|
||||
|
||||
void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t size = getRegU32(ctx, 5); // $a1
|
||||
@@ -46,11 +62,31 @@ namespace ps2_stubs
|
||||
setReturnU32(ctx, guestAddr);
|
||||
}
|
||||
|
||||
void malloc_extend_top(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
// newlib allocator internal. The runtime owns guest heap growth.
|
||||
setReturnU32(ctx, 0u);
|
||||
}
|
||||
|
||||
void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void __malloc_lock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)rdram;
|
||||
(void)ctx;
|
||||
(void)runtime;
|
||||
}
|
||||
|
||||
void __malloc_unlock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)rdram;
|
||||
(void)ctx;
|
||||
(void)runtime;
|
||||
}
|
||||
|
||||
void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t wcAddr = getRegU32(ctx, 5); // $a1
|
||||
|
||||
@@ -9,8 +9,13 @@ namespace ps2_stubs
|
||||
void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void realloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memalign_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void malloc_extend_top(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void __malloc_lock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void __malloc_unlock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace ps2_stubs
|
||||
}
|
||||
return (offset < PS2_RAM_SIZE) ? (PS2_RAM_SIZE - offset) : 0u;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -53,6 +54,14 @@ namespace ps2_stubs
|
||||
setReturnU32(ctx, guestAddr);
|
||||
}
|
||||
|
||||
void memalign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t alignment = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t size = getRegU32(ctx, 5); // $a1
|
||||
const uint32_t guestAddr = runtime ? runtime->guestMalloc(size, alignment) : 0u;
|
||||
setReturnU32(ctx, guestAddr);
|
||||
}
|
||||
|
||||
void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t guestAddr = getRegU32(ctx, 4); // $a0
|
||||
@@ -158,6 +167,42 @@ namespace ps2_stubs
|
||||
ctx->r[2] = ctx->r[4];
|
||||
}
|
||||
|
||||
void memclr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t destAddr = getRegU32(ctx, 4); // $a0
|
||||
uint32_t size = getRegU32(ctx, 5); // $a1
|
||||
size = sanitizeMemTransferSize(size, "memclr");
|
||||
|
||||
uint32_t written = 0u;
|
||||
uint32_t curDst = destAddr;
|
||||
while (written < size)
|
||||
{
|
||||
uint8_t *hostDest = getMemPtr(rdram, curDst);
|
||||
if (!hostDest)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t chunk = size - written;
|
||||
chunk = std::min(chunk, guestContiguousBytes(curDst));
|
||||
if (chunk == 0u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
::memset(hostDest, 0, chunk);
|
||||
written += chunk;
|
||||
curDst += chunk;
|
||||
}
|
||||
|
||||
if (written != 0u)
|
||||
{
|
||||
ps2TraceGuestRangeWrite(rdram, destAddr, written, "memclr", ctx);
|
||||
}
|
||||
|
||||
ctx->r[2] = ctx->r[4];
|
||||
}
|
||||
|
||||
void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t destAddr = getRegU32(ctx, 4); // $a0
|
||||
@@ -1129,4 +1174,22 @@ namespace ps2_stubs
|
||||
|
||||
setReturnS32(ctx, ret);
|
||||
}
|
||||
|
||||
void __divdi3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const int64_t num = GPR_S64(ctx, 4);
|
||||
const int64_t den = GPR_S64(ctx, 5);
|
||||
if (den == 0)
|
||||
{
|
||||
setReturnU64(ctx, 0u);
|
||||
return;
|
||||
}
|
||||
if (num == std::numeric_limits<int64_t>::min() && den == -1)
|
||||
{
|
||||
setReturnU64(ctx, static_cast<uint64_t>(num));
|
||||
return;
|
||||
}
|
||||
setReturnU64(ctx, static_cast<uint64_t>(num / den));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
namespace ps2_stubs
|
||||
{
|
||||
void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memalign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memclr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
@@ -57,4 +59,5 @@ namespace ps2_stubs
|
||||
void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void __divdi3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
#include "System.h"
|
||||
#include "Common.h"
|
||||
#include "ps2_runtime.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Deci2Session
|
||||
{
|
||||
uint16_t protocol = 0;
|
||||
uint32_t opt = 0;
|
||||
uint32_t handler = 0;
|
||||
uint32_t userArea = 0;
|
||||
bool locked = false;
|
||||
};
|
||||
|
||||
std::mutex g_deci2Mutex;
|
||||
std::unordered_map<int32_t, Deci2Session> g_deci2Sessions;
|
||||
int32_t g_nextDeci2Socket = 1;
|
||||
std::atomic<uint32_t> g_deci2LogCount{0};
|
||||
|
||||
static bool readDeci2U32(uint8_t *rdram, uint32_t guestAddr, uint32_t &value)
|
||||
{
|
||||
value = 0;
|
||||
|
||||
const uint8_t *ptr = getConstMemPtr(rdram, guestAddr);
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(&value, ptr, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void readDeci2Args(uint8_t *rdram, uint32_t argsAddr, uint32_t args[4])
|
||||
{
|
||||
for (uint32_t argIndex = 0; argIndex < 4; argIndex++)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
readDeci2U32(rdram, argsAddr + (argIndex * sizeof(uint32_t)), value);
|
||||
args[argIndex] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string sanitizeDeci2Text(std::string text)
|
||||
{
|
||||
for (char &ch : text)
|
||||
{
|
||||
const unsigned char value = static_cast<unsigned char>(ch);
|
||||
if (ch == '\r')
|
||||
{
|
||||
ch = '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch != '\n' && ch != '\t' && !std::isprint(value))
|
||||
{
|
||||
ch = '.';
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
static void logDeci2Text(const char *prefix, const std::string &text)
|
||||
{
|
||||
constexpr uint32_t kMaxDeci2TextLogs = 256u;
|
||||
const uint32_t logIndex = g_deci2LogCount.fetch_add(1u, std::memory_order_relaxed);
|
||||
if (logIndex >= kMaxDeci2TextLogs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string safeText = sanitizeDeci2Text(text);
|
||||
std::cerr << prefix << safeText;
|
||||
if (safeText.empty() || safeText.back() != '\n')
|
||||
{
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t allocateDeci2Socket(uint16_t protocol, uint32_t opt, uint32_t handler, uint32_t userArea)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_deci2Mutex);
|
||||
|
||||
if (g_nextDeci2Socket <= 0)
|
||||
{
|
||||
g_nextDeci2Socket = 1;
|
||||
}
|
||||
|
||||
const int32_t socket = g_nextDeci2Socket;
|
||||
Deci2Session session;
|
||||
session.protocol = protocol;
|
||||
session.opt = opt;
|
||||
session.handler = handler;
|
||||
session.userArea = userArea;
|
||||
session.locked = false;
|
||||
|
||||
g_deci2Sessions[socket] = session;
|
||||
return socket;
|
||||
}
|
||||
|
||||
static bool updateDeci2LockState(int32_t socket, bool locked)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_deci2Mutex);
|
||||
|
||||
auto sessionIt = g_deci2Sessions.find(socket);
|
||||
if (sessionIt == g_deci2Sessions.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionIt->second.locked = locked;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool closeDeci2Socket(int32_t socket)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_deci2Mutex);
|
||||
return g_deci2Sessions.erase(socket) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ps2_syscalls
|
||||
{
|
||||
void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
#if defined(_DEBUG) || defined(RUNTIME_DECI2CALL)
|
||||
const int32_t code = static_cast<int32_t>(getRegU32(ctx, 4));
|
||||
const uint32_t argsAddr = getRegU32(ctx, 5);
|
||||
|
||||
uint32_t args[4] = {};
|
||||
readDeci2Args(rdram, argsAddr, args);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 1: // sceDeci2Open(protocol, opt, handler)
|
||||
{
|
||||
const uint16_t protocol = static_cast<uint16_t>(args[0] & 0xFFFFu);
|
||||
const uint32_t opt = args[1];
|
||||
const uint32_t handler = args[2];
|
||||
const uint32_t userArea = args[3];
|
||||
|
||||
const int32_t socket = allocateDeci2Socket(protocol, opt, handler, userArea);
|
||||
setReturnS32(ctx, socket);
|
||||
return;
|
||||
}
|
||||
case 2: // sceDeci2Close(socket)
|
||||
{
|
||||
const int32_t socket = static_cast<int32_t>(args[0]);
|
||||
setReturnS32(ctx, closeDeci2Socket(socket) ? KE_OK : KE_ERROR);
|
||||
return;
|
||||
}
|
||||
// WE dont need to do thouses
|
||||
case 3: // sceDeci2ReqSend(socket, dest)
|
||||
case 4: // sceDeci2Poll(socket)
|
||||
case -7: // sceDeci2ExReqSend(socket, dest)
|
||||
{
|
||||
setReturnS32(ctx, KE_OK);
|
||||
return;
|
||||
}
|
||||
case -5: // sceDeci2ExRecv(socket, buf, len)
|
||||
{
|
||||
// No host debugger channel is attached, so report "no bytes available".
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
case -6: // sceDeci2ExSend(socket, buf, len)
|
||||
{
|
||||
const uint32_t bufferAddr = args[1];
|
||||
const uint32_t length = args[2] & 0xFFFFu;
|
||||
|
||||
if (bufferAddr != 0u && length != 0u)
|
||||
{
|
||||
const uint32_t clampedLength = std::min<uint32_t>(length, 512u);
|
||||
std::string text;
|
||||
text.reserve(clampedLength);
|
||||
for (uint32_t byteIndex = 0; byteIndex < clampedLength; byteIndex++)
|
||||
{
|
||||
text.push_back(static_cast<char>(rdram[(bufferAddr + byteIndex) & PS2_RAM_MASK]));
|
||||
}
|
||||
logDeci2Text("[Deci2Call:send] ", text);
|
||||
}
|
||||
|
||||
// Treat send as successful and return the amount accepted.
|
||||
setReturnS32(ctx, static_cast<int32_t>(length));
|
||||
return;
|
||||
}
|
||||
case -8: // sceDeci2ExLock(socket)
|
||||
{
|
||||
const int32_t socket = static_cast<int32_t>(args[0]);
|
||||
setReturnS32(ctx, updateDeci2LockState(socket, true) ? KE_OK : KE_ERROR);
|
||||
return;
|
||||
}
|
||||
case -9: // sceDeci2ExUnLock(socket)
|
||||
{
|
||||
const int32_t socket = static_cast<int32_t>(args[0]);
|
||||
setReturnS32(ctx, updateDeci2LockState(socket, false) ? KE_OK : KE_ERROR);
|
||||
return;
|
||||
}
|
||||
case 16: // kputs(char *s)
|
||||
{
|
||||
constexpr size_t kMaxDeci2KputsBytes = 4096u;
|
||||
const uint32_t textAddr = args[0];
|
||||
const std::string text = readGuestCStringBounded(rdram, textAddr, kMaxDeci2KputsBytes);
|
||||
|
||||
logDeci2Text("[Deci2Call:kputs] ", text);
|
||||
setReturnS32(ctx, static_cast<int32_t>(text.size()));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
static std::atomic<uint32_t> s_unknownDeci2Logs{0u};
|
||||
constexpr uint32_t kMaxUnknownDeci2Logs = 64u;
|
||||
const uint32_t logIndex = s_unknownDeci2Logs.fetch_add(1u, std::memory_order_relaxed);
|
||||
if (logIndex < kMaxUnknownDeci2Logs)
|
||||
{
|
||||
std::cerr << "[Deci2Call:unknown]"
|
||||
<< " code=" << code
|
||||
<< " args=0x" << std::hex << argsAddr
|
||||
<< " a0=0x" << args[0]
|
||||
<< " a1=0x" << args[1]
|
||||
<< " a2=0x" << args[2]
|
||||
<< " a3=0x" << args[3]
|
||||
<< " pc=0x" << ctx->pc
|
||||
<< std::dec << std::endl;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, KE_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -223,10 +223,10 @@ namespace ps2_syscalls
|
||||
iReferEventFlagStatus(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x5A:
|
||||
QueryBootMode(rdram, ctx, runtime);
|
||||
Copy(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x5B:
|
||||
GetThreadTLS(rdram, ctx, runtime);
|
||||
GetEntryAddress(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x5C:
|
||||
case static_cast<uint32_t>(-0x5C):
|
||||
@@ -253,6 +253,12 @@ namespace ps2_syscalls
|
||||
case 0x64:
|
||||
FlushCache(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x6E:
|
||||
SetOsdConfigParam2(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x6F:
|
||||
GetOsdConfigParam2(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x70:
|
||||
GsGetIMR(rdram, ctx, runtime);
|
||||
return true;
|
||||
@@ -283,6 +289,16 @@ namespace ps2_syscalls
|
||||
case static_cast<uint32_t>(-0x78):
|
||||
ps2_stubs::sceSifSetDChain(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x7F:
|
||||
GetMemorySize(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x82:
|
||||
InitTLB(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x7C:
|
||||
case static_cast<uint32_t>(-0x7C):
|
||||
Deci2Call(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x83:
|
||||
FindAddress(rdram, ctx, runtime);
|
||||
return true;
|
||||
|
||||
@@ -593,6 +593,20 @@ static uint32_t packOsdConfig(uint32_t spdifMode, uint32_t screenType, uint32_t
|
||||
return raw;
|
||||
}
|
||||
|
||||
static uint32_t packOsdConfig2(uint32_t format, uint32_t daylightSaving, uint32_t timeFormat,
|
||||
uint32_t dateFormat, uint32_t version, uint32_t language)
|
||||
{
|
||||
const uint32_t flags =
|
||||
((daylightSaving & 0x1u) << 4) |
|
||||
((timeFormat & 0x1u) << 5) |
|
||||
((dateFormat & 0x3u) << 6);
|
||||
|
||||
return (format & 0xFFu) |
|
||||
((flags & 0xFFu) << 8) |
|
||||
((version & 0xFFu) << 16) |
|
||||
((language & 0xFFu) << 24);
|
||||
}
|
||||
|
||||
static int decodeTimezoneOffset(uint32_t raw)
|
||||
{
|
||||
int tz = static_cast<int>((raw >> 21) & 0x7FF);
|
||||
@@ -627,6 +641,42 @@ static uint32_t sanitizeOsdConfigRaw(uint32_t raw)
|
||||
return packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz);
|
||||
}
|
||||
|
||||
static uint32_t sanitizeOsdConfig2Raw(uint32_t raw)
|
||||
{
|
||||
const uint32_t format = raw & 0xFFu;
|
||||
const uint32_t flags = (raw >> 8) & 0xFFu;
|
||||
const uint32_t daylightSaving = (flags >> 4) & 0x1u;
|
||||
const uint32_t timeFormat = (flags >> 5) & 0x1u;
|
||||
uint32_t dateFormat = (flags >> 6) & 0x3u;
|
||||
if (dateFormat > 2u)
|
||||
dateFormat = 0u;
|
||||
|
||||
uint32_t version = (raw >> 16) & 0xFFu;
|
||||
if (version > 2u)
|
||||
version = 1u;
|
||||
|
||||
const uint32_t language = (raw >> 24) & 0xFFu;
|
||||
return packOsdConfig2(format, daylightSaving, timeFormat, dateFormat, version, language);
|
||||
}
|
||||
|
||||
static uint32_t syncOsdConfigRawVersionLanguage(uint32_t raw, uint32_t version, uint32_t language)
|
||||
{
|
||||
raw &= ~((0x7u << 13) | (0x1Fu << 16));
|
||||
raw |= (version & 0x7u) << 13;
|
||||
raw |= (language & 0x1Fu) << 16;
|
||||
return sanitizeOsdConfigRaw(raw);
|
||||
}
|
||||
|
||||
static uint32_t makeReadableOsdConfig2RawLocked()
|
||||
{
|
||||
const uint32_t version = (g_osd_config_raw >> 13) & 0x7u;
|
||||
const uint32_t language = (g_osd_config_raw >> 16) & 0x1Fu;
|
||||
uint32_t raw = g_osd_config2_raw & 0x0000FFFFu;
|
||||
raw |= (version & 0xFFu) << 16;
|
||||
raw |= (language & 0xFFu) << 24;
|
||||
return sanitizeOsdConfig2Raw(raw);
|
||||
}
|
||||
|
||||
static void ensureOsdConfigInitialized()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_osd_mutex);
|
||||
@@ -642,6 +692,7 @@ static void ensureOsdConfigInitialized()
|
||||
uint32_t version = 1; // OSD2
|
||||
uint32_t language = 1; // English
|
||||
g_osd_config_raw = packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz);
|
||||
g_osd_config2_raw = packOsdConfig2(0, 0, 0, 0, version, language);
|
||||
g_osd_config_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -523,6 +523,7 @@ inline uint32_t g_tls_index = 0;
|
||||
inline std::mutex g_osd_mutex;
|
||||
inline bool g_osd_config_initialized = false;
|
||||
inline uint32_t g_osd_config_raw = 0;
|
||||
inline uint32_t g_osd_config2_raw = 0;
|
||||
|
||||
inline std::mutex g_ps2_path_mutex;
|
||||
inline bool g_ps2_paths_initialized = false;
|
||||
|
||||
@@ -126,12 +126,121 @@ namespace ps2_syscalls
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_osd_mutex);
|
||||
g_osd_config_raw = raw;
|
||||
g_osd_config2_raw = makeReadableOsdConfig2RawLocked();
|
||||
g_osd_config_initialized = true;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)runtime;
|
||||
const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param*
|
||||
const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4
|
||||
|
||||
ensureOsdConfigInitialized();
|
||||
|
||||
if (size == 0u)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t raw = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_osd_mutex);
|
||||
raw = makeReadableOsdConfig2RawLocked();
|
||||
}
|
||||
|
||||
const uint32_t copyBytes = std::min<uint32_t>(size, 4u);
|
||||
uint8_t rawBytes[4] = {
|
||||
static_cast<uint8_t>(raw & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 8) & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 16) & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 24) & 0xFFu),
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < copyBytes; ++i)
|
||||
{
|
||||
const uint8_t *src = getConstMemPtr(rdram, paramAddr + i);
|
||||
if (!src)
|
||||
{
|
||||
std::cerr << "PS2 SetOsdConfigParam2 error: Invalid parameter address: 0x"
|
||||
<< std::hex << (paramAddr + i) << std::dec << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
rawBytes[i] = *src;
|
||||
}
|
||||
|
||||
raw = static_cast<uint32_t>(rawBytes[0]) |
|
||||
(static_cast<uint32_t>(rawBytes[1]) << 8) |
|
||||
(static_cast<uint32_t>(rawBytes[2]) << 16) |
|
||||
(static_cast<uint32_t>(rawBytes[3]) << 24);
|
||||
raw = sanitizeOsdConfig2Raw(raw);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_osd_mutex);
|
||||
g_osd_config2_raw = raw;
|
||||
|
||||
uint32_t version = (g_osd_config_raw >> 13) & 0x7u;
|
||||
uint32_t language = (g_osd_config_raw >> 16) & 0x1Fu;
|
||||
if (copyBytes >= 3u)
|
||||
version = (raw >> 16) & 0xFFu;
|
||||
if (copyBytes >= 4u)
|
||||
language = (raw >> 24) & 0xFFu;
|
||||
g_osd_config_raw = syncOsdConfigRawVersionLanguage(g_osd_config_raw, version, language);
|
||||
g_osd_config_initialized = true;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)runtime;
|
||||
const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param*
|
||||
const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4
|
||||
|
||||
ensureOsdConfigInitialized();
|
||||
|
||||
if (size == 0u)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t raw = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_osd_mutex);
|
||||
raw = makeReadableOsdConfig2RawLocked();
|
||||
}
|
||||
|
||||
const uint8_t rawBytes[4] = {
|
||||
static_cast<uint8_t>(raw & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 8) & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 16) & 0xFFu),
|
||||
static_cast<uint8_t>((raw >> 24) & 0xFFu),
|
||||
};
|
||||
const uint32_t copyBytes = std::min<uint32_t>(size, 4u);
|
||||
|
||||
for (uint32_t i = 0; i < copyBytes; ++i)
|
||||
{
|
||||
uint8_t *dst = getMemPtr(rdram, paramAddr + i);
|
||||
if (!dst)
|
||||
{
|
||||
std::cerr << "PS2 GetOsdConfigParam2 error: Invalid parameter address: 0x"
|
||||
<< std::hex << (paramAddr + i) << std::dec << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
*dst = rawBytes[i];
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t bufAddr = getRegU32(ctx, 4); // $a0
|
||||
@@ -244,7 +353,7 @@ namespace ps2_syscalls
|
||||
uint32_t syscallId = encodedSyscallId;
|
||||
if (syscallId == 0u)
|
||||
{
|
||||
syscallId = (v0 != 0u) ? v0 : v1;
|
||||
syscallId = v1;
|
||||
}
|
||||
|
||||
std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc
|
||||
@@ -573,18 +682,38 @@ namespace ps2_syscalls
|
||||
// 0x3D SetupHeap: returns heap base/start pointer
|
||||
void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t heapBase = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t heapBaseRaw = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size)
|
||||
|
||||
const uint32_t heapBase = (heapBaseRaw + 0xFu) & ~0xFu;
|
||||
|
||||
// Silent Hill and other games often pass -1 (0xFFFFFFFF) to mean "rest of RAM".
|
||||
static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u;
|
||||
uint32_t heapLimit = kDefaultGuestHeapEnd;
|
||||
|
||||
if (heapSize != 0u && heapSize != 0xFFFFFFFFu)
|
||||
{
|
||||
const uint64_t candidate = static_cast<uint64_t>(heapBase) + static_cast<uint64_t>(heapSize);
|
||||
heapLimit = static_cast<uint32_t>(std::min<uint64_t>(candidate, kDefaultGuestHeapEnd));
|
||||
}
|
||||
|
||||
if (heapLimit <= heapBase)
|
||||
{
|
||||
heapLimit = kDefaultGuestHeapEnd;
|
||||
}
|
||||
|
||||
if (runtime)
|
||||
{
|
||||
uint32_t heapLimit = PS2_RAM_SIZE;
|
||||
if (heapSize != 0u && heapBase < PS2_RAM_SIZE)
|
||||
{
|
||||
const uint64_t candidateLimit = static_cast<uint64_t>(heapBase) + static_cast<uint64_t>(heapSize);
|
||||
heapLimit = static_cast<uint32_t>(std::min<uint64_t>(candidateLimit, PS2_RAM_SIZE));
|
||||
}
|
||||
runtime->configureGuestHeap(heapBase, heapLimit);
|
||||
|
||||
std::cerr << "[SetupHeap]"
|
||||
<< " base=0x" << std::hex << heapBaseRaw
|
||||
<< " alignedBase=0x" << heapBase
|
||||
<< " size=0x" << heapSize
|
||||
<< " runtimeBase=0x" << runtime->guestHeapBase()
|
||||
<< " runtimeEnd=0x" << runtime->guestHeapEnd()
|
||||
<< std::dec << std::endl;
|
||||
|
||||
setReturnU32(ctx, runtime->guestHeapBase());
|
||||
return;
|
||||
}
|
||||
@@ -595,13 +724,15 @@ namespace ps2_syscalls
|
||||
// 0x3E EndOfHeap: commonly returns current heap end; keep it stable for now.
|
||||
void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
if (runtime)
|
||||
{
|
||||
setReturnU32(ctx, runtime->guestHeapEnd());
|
||||
return;
|
||||
}
|
||||
(void)rdram;
|
||||
|
||||
setReturnU32(ctx, getRegU32(ctx, 4));
|
||||
static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u;
|
||||
|
||||
const uint32_t ret = runtime
|
||||
? runtime->guestHeapLimit()
|
||||
: kDefaultGuestHeapEnd;
|
||||
|
||||
setReturnU32(ctx, ret);
|
||||
}
|
||||
|
||||
void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -611,6 +742,30 @@ namespace ps2_syscalls
|
||||
setReturnU32(ctx, PS2_RAM_SIZE);
|
||||
}
|
||||
|
||||
void InitTLB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)rdram;
|
||||
// TODO I`m 99% sure we dont need this we could just return ok and should be fine.
|
||||
auto &memory = runtime->memory();
|
||||
|
||||
const uint32_t entryCount = static_cast<uint32_t>(memory.tlbEntryCount());
|
||||
for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex)
|
||||
{
|
||||
memory.tlbWrite(entryIndex, 0u, 0u, 0u, false);
|
||||
}
|
||||
|
||||
// Reset basic COP0 TLB bookkeeping to sane post-init values.
|
||||
ctx->cop0_index = 0u;
|
||||
ctx->cop0_random = entryCount > 0u ? (entryCount - 1u) : 0u;
|
||||
ctx->cop0_entrylo0 = 0u;
|
||||
ctx->cop0_entrylo1 = 0u;
|
||||
ctx->cop0_context = 0u;
|
||||
ctx->cop0_pagemask = 0u;
|
||||
ctx->cop0_entryhi = 0u;
|
||||
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
static inline uint32_t normalizeKernelAlias(uint32_t addr)
|
||||
{
|
||||
if (addr >= 0x80000000u && addr < 0xC0000000u)
|
||||
@@ -897,14 +1052,7 @@ namespace ps2_syscalls
|
||||
setReturnU32(ctx, resultAddr);
|
||||
}
|
||||
|
||||
void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)rdram;
|
||||
(void)runtime;
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
// 0x5A QueryBootMode (stub): return 0 for now
|
||||
// QueryBootMode (stub): return 0 for now
|
||||
void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t mode = getRegU32(ctx, 4);
|
||||
@@ -919,7 +1067,7 @@ namespace ps2_syscalls
|
||||
setReturnU32(ctx, addr);
|
||||
}
|
||||
|
||||
// 0x5B GetThreadTLS (stub): return 0
|
||||
// GetThreadTLS (stub): return 0
|
||||
void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
auto info = ensureCurrentThreadInfo(ctx);
|
||||
@@ -937,6 +1085,37 @@ namespace ps2_syscalls
|
||||
setReturnU32(ctx, info->tlsBase);
|
||||
}
|
||||
|
||||
void Copy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t dest = getRegU32(ctx, 4);
|
||||
const uint32_t src = getRegU32(ctx, 5);
|
||||
const uint32_t size = getRegU32(ctx, 6);
|
||||
|
||||
if (rdram && size > 0)
|
||||
{
|
||||
uint8_t *destPtr = getMemPtr(rdram, dest);
|
||||
const uint8_t *srcPtr = getConstMemPtr(rdram, src);
|
||||
if (destPtr && srcPtr)
|
||||
{
|
||||
std::memcpy(destPtr, srcPtr, size);
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void GetEntryAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t syscallNum = getRegU32(ctx, 4);
|
||||
|
||||
const uint32_t entryAddr = kGuestSyscallTableGuestBase + (syscallNum * 4u);
|
||||
uint32_t handler = 0;
|
||||
if (const uint8_t *ptr = getConstMemPtr(rdram, entryAddr))
|
||||
{
|
||||
std::memcpy(&handler, ptr, sizeof(handler));
|
||||
}
|
||||
setReturnU32(ctx, handler);
|
||||
}
|
||||
|
||||
// 0x74 RegisterExitHandler (stub): return 0
|
||||
void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace ps2_syscalls
|
||||
void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
@@ -27,9 +29,12 @@ namespace ps2_syscalls
|
||||
void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void InitTLB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void Copy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void GetEntryAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
}
|
||||
|
||||
@@ -520,6 +520,9 @@ namespace ps2_syscalls
|
||||
|
||||
void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
RUNTIME_LOG("[ExitThread] Game requested thread exit! PC=0x" << std::hex << ctx->pc
|
||||
<< " RA=0x" << getRegU32(ctx, 31) << std::dec << " tid=" << g_currentThreadId << std::endl);
|
||||
|
||||
runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime);
|
||||
auto info = ensureCurrentThreadInfo(ctx);
|
||||
if (info)
|
||||
@@ -541,6 +544,9 @@ namespace ps2_syscalls
|
||||
void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
int tid = g_currentThreadId;
|
||||
RUNTIME_LOG("[ExitDeleteThread] Game requested thread exit & delete! PC=0x" << std::hex << ctx->pc
|
||||
<< " RA=0x" << getRegU32(ctx, 31) << std::dec << " tid=" << tid << std::endl);
|
||||
|
||||
runExitHandlersForThread(tid, rdram, ctx, runtime);
|
||||
auto info = ensureCurrentThreadInfo(ctx);
|
||||
if (info)
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#include "runtime/ps2_iop.h"
|
||||
#include "runtime/ps2_iop_audio.h"
|
||||
#include "runtime/ps2_iop_dbcman.h"
|
||||
#include "runtime/ps2_memory.h"
|
||||
#include "ps2_runtime.h"
|
||||
#include "Kernel/Syscalls/RPC.h"
|
||||
|
||||
// ps2_iop.cpp
|
||||
|
||||
namespace
|
||||
{
|
||||
}
|
||||
ps2_iop::ps2_iop()
|
||||
{
|
||||
reset();
|
||||
@@ -43,6 +49,15 @@ bool ps2_iop::handleRPC(PS2Runtime *runtime,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ps2_iop_dbcman::handleDbcManRpc(m_rdram,
|
||||
sid, rpcNum,
|
||||
sendBufAddr, sendSize,
|
||||
recvBufAddr, recvSize,
|
||||
resultPtr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sid == IOP_SID_LIBSD)
|
||||
{
|
||||
const uint8_t *sendPtr = sendBufAddr ? getConstMemPtr(m_rdram, sendBufAddr) : nullptr;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "runtime/ps2_iop_dbcman.h"
|
||||
#include "runtime/ps2_memory.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t IOP_SID_DBCMAN = 0x80001300u;
|
||||
constexpr uint32_t DBCMAN_RPC_CHECK_VERSION = 0x80001363u;
|
||||
|
||||
static bool writeIopU32(uint8_t *rdram, uint32_t addr, uint32_t value)
|
||||
{
|
||||
uint8_t *ptr = getMemPtr(rdram, addr);
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(ptr, &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ps2_iop_dbcman
|
||||
{
|
||||
bool handleDbcManRpc(uint8_t *rdram,
|
||||
uint32_t sid,
|
||||
uint32_t rpcNum,
|
||||
uint32_t sendBufAddr,
|
||||
uint32_t sendSize,
|
||||
uint32_t recvBufAddr,
|
||||
uint32_t recvSize,
|
||||
uint32_t &resultPtr)
|
||||
{
|
||||
if (sid != IOP_SID_DBCMAN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recvBufAddr == 0u || recvSize == 0u)
|
||||
{
|
||||
resultPtr = recvBufAddr;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (rpcNum)
|
||||
{
|
||||
case DBCMAN_RPC_CHECK_VERSION:
|
||||
{
|
||||
// TODO move this to compile flag
|
||||
// libdbc expects dbcman.irx version 3.20
|
||||
constexpr uint32_t DBCMAN_VERSION = 0x0320;
|
||||
|
||||
writeIopU32(rdram, recvBufAddr + 0x00u, DBCMAN_VERSION);
|
||||
writeIopU32(rdram, recvBufAddr + 0x04u, DBCMAN_VERSION);
|
||||
writeIopU32(rdram, recvBufAddr + 0x08u, DBCMAN_VERSION);
|
||||
writeIopU32(rdram, recvBufAddr + 0x0Cu, DBCMAN_VERSION);
|
||||
|
||||
resultPtr = recvBufAddr;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
static uint32_t dbcLogCount = 0;
|
||||
if (dbcLogCount < 32)
|
||||
{
|
||||
std::cerr << "[DBCMAN:stub]"
|
||||
<< " sid=0x" << std::hex << sid
|
||||
<< " rpc=0x" << rpcNum
|
||||
<< " send=0x" << sendBufAddr
|
||||
<< " sendSize=0x" << sendSize
|
||||
<< " recv=0x" << recvBufAddr
|
||||
<< " recvSize=0x" << recvSize
|
||||
<< std::dec << std::endl;
|
||||
++dbcLogCount;
|
||||
}
|
||||
|
||||
resultPtr = recvBufAddr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -882,13 +882,19 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value)
|
||||
base2 = m_rdram;
|
||||
maxSz2 = PS2_RAM_SIZE;
|
||||
}
|
||||
if (src >= maxSz2)
|
||||
return;
|
||||
if (src + bytes > maxSz2)
|
||||
bytes = maxSz2 - src;
|
||||
if (bytes == 0)
|
||||
return;
|
||||
chainBuf.insert(chainBuf.end(), base2 + src, base2 + src + bytes);
|
||||
while (bytes > 0)
|
||||
{
|
||||
if (src >= maxSz2)
|
||||
src = 0;
|
||||
uint32_t chunk = bytes;
|
||||
if (src + chunk > maxSz2)
|
||||
chunk = maxSz2 - src;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
chainBuf.insert(chainBuf.end(), base2 + src, base2 + src + chunk);
|
||||
bytes -= chunk;
|
||||
src += chunk;
|
||||
}
|
||||
};
|
||||
|
||||
auto appendCompactVif1TagData = [&](uint32_t localTagAddr, uint32_t qwCount)
|
||||
@@ -1114,22 +1120,40 @@ void PS2Memory::processPendingTransfers()
|
||||
}
|
||||
if (p.fromScratchpad)
|
||||
{
|
||||
if (srcPhys + sizeBytes <= PS2_SCRATCHPAD_SIZE && sizeBytes >= 16)
|
||||
uint32_t bytesLeft = sizeBytes;
|
||||
while (bytesLeft >= 16)
|
||||
{
|
||||
if (srcPhys >= PS2_SCRATCHPAD_SIZE)
|
||||
srcPhys = 0;
|
||||
uint32_t chunk = bytesLeft;
|
||||
if (srcPhys + chunk > PS2_SCRATCHPAD_SIZE)
|
||||
chunk = PS2_SCRATCHPAD_SIZE - srcPhys;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
submitGifPacket(GifPathId::Path3, m_scratchpad + srcPhys, sizeBytes, false);
|
||||
submitGifPacket(GifPathId::Path3, m_scratchpad + srcPhys, chunk, false);
|
||||
bytesLeft -= chunk;
|
||||
srcPhys += chunk;
|
||||
}
|
||||
}
|
||||
else if (srcPhys < PS2_RAM_SIZE)
|
||||
else
|
||||
{
|
||||
if (static_cast<uint64_t>(srcPhys) + sizeBytes > PS2_RAM_SIZE)
|
||||
sizeBytes = PS2_RAM_SIZE - srcPhys;
|
||||
if (sizeBytes >= 16)
|
||||
uint32_t bytesLeft = sizeBytes;
|
||||
while (bytesLeft >= 16)
|
||||
{
|
||||
if (srcPhys >= PS2_RAM_SIZE)
|
||||
srcPhys = 0;
|
||||
uint32_t chunk = bytesLeft;
|
||||
if (srcPhys + chunk > PS2_RAM_SIZE)
|
||||
chunk = PS2_RAM_SIZE - srcPhys;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
submitGifPacket(GifPathId::Path3, m_rdram + srcPhys, sizeBytes, false);
|
||||
submitGifPacket(GifPathId::Path3, m_rdram + srcPhys, chunk, false);
|
||||
bytesLeft -= chunk;
|
||||
srcPhys += chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1158,15 +1182,37 @@ void PS2Memory::processPendingTransfers()
|
||||
}
|
||||
if (p.fromScratchpad)
|
||||
{
|
||||
if (srcPhys + sizeBytes <= PS2_SCRATCHPAD_SIZE && sizeBytes > 0u)
|
||||
processVIF1Data(m_scratchpad + srcPhys, sizeBytes);
|
||||
uint32_t bytesLeft = sizeBytes;
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
if (srcPhys >= PS2_SCRATCHPAD_SIZE)
|
||||
srcPhys = 0;
|
||||
uint32_t chunk = bytesLeft;
|
||||
if (srcPhys + chunk > PS2_SCRATCHPAD_SIZE)
|
||||
chunk = PS2_SCRATCHPAD_SIZE - srcPhys;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
processVIF1Data(m_scratchpad + srcPhys, chunk);
|
||||
bytesLeft -= chunk;
|
||||
srcPhys += chunk;
|
||||
}
|
||||
}
|
||||
else if (srcPhys < PS2_RAM_SIZE)
|
||||
else
|
||||
{
|
||||
if (srcPhys + sizeBytes > PS2_RAM_SIZE)
|
||||
sizeBytes = PS2_RAM_SIZE - srcPhys;
|
||||
if (sizeBytes > 0)
|
||||
processVIF1Data(srcPhys, sizeBytes);
|
||||
uint32_t bytesLeft = sizeBytes;
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
if (srcPhys >= PS2_RAM_SIZE)
|
||||
srcPhys = 0;
|
||||
uint32_t chunk = bytesLeft;
|
||||
if (srcPhys + chunk > PS2_RAM_SIZE)
|
||||
chunk = PS2_RAM_SIZE - srcPhys;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
processVIF1Data(srcPhys, chunk);
|
||||
bytesLeft -= chunk;
|
||||
srcPhys += chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,15 +1308,24 @@ void PS2Memory::processGIFPacket(uint32_t srcPhysAddr, uint32_t qwCount)
|
||||
return;
|
||||
const uint64_t bytes64 = static_cast<uint64_t>(qwCount) * 16ull;
|
||||
uint32_t sizeBytes = (bytes64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast<uint32_t>(bytes64);
|
||||
if (srcPhysAddr >= PS2_RAM_SIZE)
|
||||
return;
|
||||
if (static_cast<uint64_t>(srcPhysAddr) + static_cast<uint64_t>(sizeBytes) > static_cast<uint64_t>(PS2_RAM_SIZE))
|
||||
sizeBytes = PS2_RAM_SIZE - srcPhysAddr;
|
||||
if (sizeBytes < 16)
|
||||
return;
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
submitGifPacket(GifPathId::Path3, m_rdram + srcPhysAddr, sizeBytes);
|
||||
uint32_t bytesLeft = sizeBytes;
|
||||
while (bytesLeft >= 16)
|
||||
{
|
||||
if (srcPhysAddr >= PS2_RAM_SIZE)
|
||||
srcPhysAddr = 0;
|
||||
uint32_t chunk = bytesLeft;
|
||||
if (srcPhysAddr + chunk > PS2_RAM_SIZE)
|
||||
chunk = PS2_RAM_SIZE - srcPhysAddr;
|
||||
if (chunk == 0)
|
||||
break;
|
||||
|
||||
m_seenGifCopy = true;
|
||||
m_gifCopyCount.fetch_add(1, std::memory_order_relaxed);
|
||||
submitGifPacket(GifPathId::Path3, m_rdram + srcPhysAddr, chunk);
|
||||
|
||||
bytesLeft -= chunk;
|
||||
srcPhysAddr += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
void PS2Memory::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
|
||||
@@ -1163,23 +1163,11 @@ void PS2Runtime::handleSyscall(uint8_t *rdram, R5900Context *ctx, uint32_t encod
|
||||
"This breaks the atomic basic block model and is structurally unsupported by the emulator.");
|
||||
}
|
||||
|
||||
// Try immediate first
|
||||
if (encodedSyscallId != 0 && ps2_syscalls::dispatchNumericSyscall(encodedSyscallId, rdram, ctx, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const uint32_t syscallId = (encodedSyscallId != 0u)
|
||||
? encodedSyscallId
|
||||
: getRegU32(ctx, 3); // $v1 / $3 is the EE kernel syscall number
|
||||
|
||||
// Try $v1 (standard)
|
||||
const uint32_t syscallFromV1 = getRegU32(ctx, 3); // $v1
|
||||
if (ps2_syscalls::dispatchNumericSyscall(syscallFromV1, rdram, ctx, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Try $v0 (negative syscalls)
|
||||
const uint32_t syscallFromV0 = getRegU32(ctx, 2); // $v0 (some ABIs)
|
||||
if (syscallFromV0 != syscallFromV1 &&
|
||||
ps2_syscalls::dispatchNumericSyscall(syscallFromV0, rdram, ctx, this))
|
||||
if (ps2_syscalls::dispatchNumericSyscall(syscallId, rdram, ctx, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1671,6 +1659,12 @@ uint32_t PS2Runtime::guestHeapEnd() const
|
||||
return m_guestHeapConfigured ? m_guestHeapEnd : m_guestHeapSuggestedBase;
|
||||
}
|
||||
|
||||
uint32_t PS2Runtime::guestHeapLimit() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_guestHeapMutex);
|
||||
return m_guestHeapConfigured ? m_guestHeapLimit : m_guestHeapSuggestedBase;
|
||||
}
|
||||
|
||||
uint32_t PS2Runtime::reserveAsyncCallbackStack(uint32_t size, uint32_t alignment)
|
||||
{
|
||||
if (size == 0u)
|
||||
|
||||
@@ -44,6 +44,8 @@ void register_elf_analyzer_tests()
|
||||
"AddIntcHandler2 kernel wrapper should be classified as library/runtime");
|
||||
t.IsTrue(analyzer.isLibrarySymbolNameForHeuristics("SetGsCrt"),
|
||||
"SetGsCrt kernel wrapper should be classified as library/runtime");
|
||||
t.IsTrue(analyzer.isLibrarySymbolNameForHeuristics("__sbprintf"),
|
||||
"optional stdio internals should be classified as library functions");
|
||||
|
||||
t.IsFalse(analyzer.isLibrarySymbolNameForHeuristics("bhEne13_Brain"),
|
||||
"named game function should not be classified as library");
|
||||
@@ -62,6 +64,18 @@ void register_elf_analyzer_tests()
|
||||
"double-underscore libm helpers should be active stubs only when the runtime knows them");
|
||||
t.IsTrue(FunctionClassifier::hasRuntimeHandler("__kernel_cosf"),
|
||||
"runtime-known libm kernel helpers should resolve exactly");
|
||||
t.IsTrue(FunctionClassifier::hasRuntimeHandler("_realloc_r"),
|
||||
"reentrant allocator aliases should resolve to runtime stubs");
|
||||
t.IsTrue(FunctionClassifier::hasRuntimeHandler("__malloc_lock"),
|
||||
"newlib malloc locks should resolve to runtime stubs");
|
||||
t.IsTrue(FunctionClassifier::hasRuntimeHandler("memclr"),
|
||||
"libdma memclr should resolve to a runtime stub");
|
||||
t.IsTrue(FunctionClassifier::hasRuntimeHandler("__divdi3"),
|
||||
"libgcc 64-bit division should resolve to a runtime stub");
|
||||
t.IsFalse(FunctionClassifier::hasRuntimeHandler("__sbprintf"),
|
||||
"optional stdio internals should not resolve as automatic runtime stubs");
|
||||
t.IsFalse(FunctionClassifier::hasRuntimeHandler("__sprint"),
|
||||
"optional stdio internals should be TOML opt-in only");
|
||||
t.IsFalse(FunctionClassifier::hasRuntimeHandler("scePP1_Kick"),
|
||||
"SDK functions without runtime handlers should not be active stubs"); });
|
||||
|
||||
|
||||
@@ -55,6 +55,52 @@ namespace
|
||||
std::memcpy(rdram + tagAddr, &tagLo, sizeof(tagLo));
|
||||
}
|
||||
|
||||
void writeU64(uint8_t *rdram, uint32_t addr, uint64_t value)
|
||||
{
|
||||
std::memcpy(rdram + addr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
uint64_t makeGifTag(uint16_t nloop, uint8_t flg, uint8_t nreg, bool eop);
|
||||
|
||||
uint64_t makeBitbltbuf(uint32_t dbp, uint32_t dbw, uint32_t dpsm)
|
||||
{
|
||||
return (static_cast<uint64_t>(dbp & 0x3FFFu) << 32) |
|
||||
(static_cast<uint64_t>(dbw & 0x3Fu) << 48) |
|
||||
(static_cast<uint64_t>(dpsm & 0x3Fu) << 56);
|
||||
}
|
||||
|
||||
uint32_t writeGifAd(uint8_t *rdram, uint32_t addr, uint64_t value, uint64_t reg)
|
||||
{
|
||||
writeU64(rdram, addr + 0u, value);
|
||||
writeU64(rdram, addr + 8u, reg);
|
||||
return addr + 16u;
|
||||
}
|
||||
|
||||
uint32_t writeTextureUploadSetup(uint8_t *rdram, uint32_t addr, uint32_t dbp, uint32_t dpsm)
|
||||
{
|
||||
writeDmaTag(rdram, addr, makeDmaTag(5u, 1u, 0u, false)); // CNT: setup tag + four A+D writes.
|
||||
addr += 16u;
|
||||
writeU64(rdram, addr + 0u, makeGifTag(4u, GIF_FMT_PACKED, 1u, false));
|
||||
writeU64(rdram, addr + 8u, 0x0Eull); // GIF PACKED A+D descriptor.
|
||||
addr += 16u;
|
||||
addr = writeGifAd(rdram, addr, makeBitbltbuf(dbp, 1u, dpsm), GS_REG_BITBLTBUF);
|
||||
addr = writeGifAd(rdram, addr, 0ull, GS_REG_TRXPOS);
|
||||
addr = writeGifAd(rdram, addr, (16ull << 0) | (16ull << 32), GS_REG_TRXREG);
|
||||
addr = writeGifAd(rdram, addr, 0ull, GS_REG_TRXDIR);
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint32_t writeTextureImageRef(uint8_t *rdram, uint32_t addr, uint32_t qwc, uint32_t dataAddr)
|
||||
{
|
||||
writeDmaTag(rdram, addr, makeDmaTag(1u, 1u, 0u, false)); // CNT: GIF IMAGE tag.
|
||||
addr += 16u;
|
||||
writeU64(rdram, addr + 0u, makeGifTag(static_cast<uint16_t>(qwc), GIF_FMT_IMAGE, 0u, false));
|
||||
writeU64(rdram, addr + 8u, 0ull);
|
||||
addr += 16u;
|
||||
writeDmaTag(rdram, addr, makeDmaTag(static_cast<uint16_t>(qwc), 3u, dataAddr, false)); // REF: image payload.
|
||||
return addr + 16u;
|
||||
}
|
||||
|
||||
uint64_t makeGifTag(uint16_t nloop, uint8_t flg, uint8_t nreg, bool eop = true)
|
||||
{
|
||||
uint64_t tag = static_cast<uint64_t>(nloop & 0x7FFFu);
|
||||
@@ -861,6 +907,112 @@ void register_ps2_memory_tests()
|
||||
t.IsTrue(contentOk, "scratchpad alias chain payload should match scratchpad bytes");
|
||||
});
|
||||
|
||||
tc.Run("GIF DMA chain REF keeps CT32 image data after paletted upload", [](TestCase &t)
|
||||
{
|
||||
PS2Memory mem;
|
||||
t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed");
|
||||
|
||||
GS gs;
|
||||
gs.init(mem.getGSVRAM(), static_cast<uint32_t>(PS2_GS_VRAM_SIZE), &mem.gs());
|
||||
std::vector<uint8_t> capturedGifPacket;
|
||||
mem.setGifPacketCallback([&](const uint8_t *data, uint32_t sizeBytes)
|
||||
{
|
||||
capturedGifPacket.assign(data, data + sizeBytes);
|
||||
gs.processGIFPacket(data, sizeBytes);
|
||||
});
|
||||
|
||||
constexpr uint32_t kGifCh = 0x1000A000u;
|
||||
constexpr uint32_t kChain = 0x00028000u;
|
||||
constexpr uint32_t kT4Data = 0x00029000u;
|
||||
constexpr uint32_t kCt32Data = 0x0002A000u;
|
||||
constexpr uint32_t kT4Dbp = 0u;
|
||||
constexpr uint32_t kCt32Dbp = 32u;
|
||||
constexpr uint32_t kWidth = 16u;
|
||||
constexpr uint32_t kHeight = 16u;
|
||||
constexpr uint32_t kT4Bytes = kWidth * kHeight / 2u;
|
||||
constexpr uint32_t kCt32Bytes = kWidth * kHeight * 4u;
|
||||
|
||||
uint8_t *rdram = mem.getRDRAM();
|
||||
uint32_t pixel = 0u;
|
||||
for (uint32_t i = 0; i < kT4Bytes; ++i)
|
||||
{
|
||||
const uint8_t lo = static_cast<uint8_t>(pixel & 0xFu);
|
||||
const uint8_t hi = static_cast<uint8_t>((pixel + 1u) & 0xFu);
|
||||
rdram[kT4Data + i] = static_cast<uint8_t>(lo | (hi << 4));
|
||||
pixel += 2u;
|
||||
if (pixel > 0xEu)
|
||||
{
|
||||
pixel -= 0xEu;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t color = 0u;
|
||||
for (uint32_t i = 0; i < kCt32Bytes; i += 4u)
|
||||
{
|
||||
rdram[kCt32Data + i + 0u] = static_cast<uint8_t>((color >> 0) & 0xFFu);
|
||||
rdram[kCt32Data + i + 1u] = static_cast<uint8_t>((color >> 8) & 0xFFu);
|
||||
rdram[kCt32Data + i + 2u] = static_cast<uint8_t>((color >> 16) & 0xFFu);
|
||||
rdram[kCt32Data + i + 3u] = 0x80u;
|
||||
color += 0xF1u;
|
||||
if (color >= 0xFFFFFFu)
|
||||
{
|
||||
color = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t chain = kChain;
|
||||
chain = writeTextureUploadSetup(rdram, chain, kT4Dbp, GS_PSM_T4HL);
|
||||
chain = writeTextureImageRef(rdram, chain, kT4Bytes / 16u, kT4Data);
|
||||
chain = writeTextureUploadSetup(rdram, chain, kCt32Dbp, GS_PSM_CT32);
|
||||
chain = writeTextureImageRef(rdram, chain, kCt32Bytes / 16u, kCt32Data);
|
||||
writeDmaTag(rdram, chain, makeDmaTag(0u, 7u, 0u, false)); // END.
|
||||
|
||||
t.IsTrue(mem.writeIORegister(kGifCh + 0x30u, kChain), "write GIF TADR should succeed");
|
||||
t.IsTrue(mem.writeIORegister(kGifCh + 0x00u, 0x104u), "write GIF CHCR STR|CHAIN should succeed");
|
||||
|
||||
mem.processPendingTransfers();
|
||||
|
||||
constexpr uint32_t kCt32PayloadOffset = 5u * 16u + 16u + kT4Bytes + 5u * 16u + 16u;
|
||||
t.IsTrue(capturedGifPacket.size() >= kCt32PayloadOffset + kCt32Bytes,
|
||||
"flattened GIF chain should contain the full CT32 REF payload");
|
||||
bool ct32PayloadOk = capturedGifPacket.size() >= kCt32PayloadOffset + kCt32Bytes;
|
||||
for (uint32_t i = 0; i < kCt32Bytes && ct32PayloadOk; ++i)
|
||||
{
|
||||
if (capturedGifPacket[kCt32PayloadOffset + i] != rdram[kCt32Data + i])
|
||||
{
|
||||
ct32PayloadOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
t.IsTrue(ct32PayloadOk, "flattened GIF chain should preserve CT32 REF bytes after the T4 REF payload");
|
||||
|
||||
const uint32_t row1Off = GSPSMCT32::addrPSMCT32(kCt32Dbp, 1u, 0u, 1u);
|
||||
uint32_t actualRow1X0 = 0u;
|
||||
uint32_t expectedRow1X0 = 0u;
|
||||
std::memcpy(&actualRow1X0, mem.getGSVRAM() + row1Off, sizeof(actualRow1X0));
|
||||
std::memcpy(&expectedRow1X0, rdram + kCt32Data + kWidth * 4u, sizeof(expectedRow1X0));
|
||||
t.Equals(actualRow1X0, expectedRow1X0, "CT32 row 1 must come from CT32 data, not the previous T4 REF payload");
|
||||
|
||||
bool ct32Ok = true;
|
||||
for (uint32_t y = 0; y < kHeight && ct32Ok; ++y)
|
||||
{
|
||||
for (uint32_t x = 0; x < kWidth && ct32Ok; ++x)
|
||||
{
|
||||
const uint32_t dstOff = GSPSMCT32::addrPSMCT32(kCt32Dbp, 1u, x, y);
|
||||
const uint32_t srcOff = kCt32Data + ((y * kWidth + x) * 4u);
|
||||
for (uint32_t c = 0; c < 4u; ++c)
|
||||
{
|
||||
if (mem.getGSVRAM()[dstOff + c] != rdram[srcOff + c])
|
||||
{
|
||||
ct32Ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
t.IsTrue(ct32Ok, "all CT32 pixels should survive a preceding paletted upload in the same GIF DMA chain");
|
||||
});
|
||||
|
||||
tc.Run("VIF1 DMA DIRECT forwards payload to GIF callback and clears channel", [](TestCase &t)
|
||||
{
|
||||
PS2Memory mem;
|
||||
|
||||
@@ -864,6 +864,30 @@ void register_ps2_recompiler_tests()
|
||||
"isceSifSetDma should resolve as a stub name");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("isceSifSetDChain"), std::string_view{"isceSifSetDChain"},
|
||||
"isceSifSetDChain should resolve as a stub name");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("memalign"), std::string_view{"memalign"},
|
||||
"memalign should resolve as a stub name");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("_memalign_r"), std::string_view{"memalign_r"},
|
||||
"_memalign_r should resolve to the memalign_r stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("_realloc_r"), std::string_view{"realloc_r"},
|
||||
"_realloc_r should resolve to the realloc_r stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("malloc_extend_top"), std::string_view{"malloc_extend_top"},
|
||||
"malloc_extend_top should resolve as an allocator compatibility stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__malloc_lock"), std::string_view{"__malloc_lock"},
|
||||
"__malloc_lock should resolve as an allocator compatibility stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__malloc_unlock"), std::string_view{"__malloc_unlock"},
|
||||
"__malloc_unlock should resolve as an allocator compatibility stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("memclr"), std::string_view{"memclr"},
|
||||
"memclr should resolve as a runtime stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__divdi3"), std::string_view{"__divdi3"},
|
||||
"__divdi3 should resolve as a runtime stub");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__mcmp"), std::string_view{},
|
||||
"__mcmp should be left for recompilation");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__sprint"), std::string_view{},
|
||||
"__sprint should be left for recompilation");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__sprint_r"), std::string_view{},
|
||||
"__sprint_r should be left for recompilation");
|
||||
t.Equals(ps2_runtime_calls::resolveStubName("__sbprintf"), std::string_view{},
|
||||
"__sbprintf should be left for recompilation");
|
||||
});
|
||||
|
||||
tc.Run("respect max length for .cpp filenames", [](TestCase& t) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "ps2_runtime.h"
|
||||
#include "ps2_runtime_macros.h"
|
||||
#include "ps2_syscalls.h"
|
||||
#include "ps2_stubs.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
@@ -558,7 +559,7 @@ void register_ps2_runtime_kernel_tests()
|
||||
t.Equals(getRegS32(env.ctx, 2), KE_OK, "DeleteThread should clean up the waiter thread");
|
||||
});
|
||||
|
||||
tc.Run("setup heap and allocator primitives track end-of-heap", [](TestCase &t)
|
||||
tc.Run("setup heap configures limits and EndOfHeap reports the limit", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
|
||||
@@ -569,17 +570,13 @@ void register_ps2_runtime_kernel_tests()
|
||||
t.Equals(heapBase, 0x00180010u, "SetupHeap should return configured base");
|
||||
|
||||
t.IsTrue(callSyscall(0x3Eu, env.rdram.data(), &env.ctx, &env.runtime), "EndOfHeap syscall should dispatch");
|
||||
const uint32_t heapEndBefore = static_cast<uint32_t>(getRegS32(env.ctx, 2));
|
||||
t.Equals(heapEndBefore, heapBase, "EndOfHeap should start at heap base before allocation");
|
||||
const uint32_t heapLimit = static_cast<uint32_t>(getRegS32(env.ctx, 2));
|
||||
t.Equals(heapLimit, 0x00181010u, "EndOfHeap should report the upper limit of the configured heap");
|
||||
|
||||
const uint32_t alignedAlloc = env.runtime.guestMalloc(0x20u, 64u);
|
||||
t.IsTrue(alignedAlloc != 0u, "guestMalloc should allocate inside configured heap");
|
||||
t.Equals(alignedAlloc & 0x3Fu, 0u, "guestMalloc should honor 64-byte alignment");
|
||||
|
||||
t.IsTrue(callSyscall(0x3Eu, env.rdram.data(), &env.ctx, &env.runtime), "EndOfHeap syscall should dispatch");
|
||||
const uint32_t heapEndAfter = static_cast<uint32_t>(getRegS32(env.ctx, 2));
|
||||
t.IsTrue(heapEndAfter >= alignedAlloc + 0x20u, "EndOfHeap should advance after allocation");
|
||||
|
||||
env.runtime.guestFree(alignedAlloc);
|
||||
|
||||
const uint32_t a = env.runtime.guestMalloc(0x100u, 16u);
|
||||
@@ -595,6 +592,84 @@ void register_ps2_runtime_kernel_tests()
|
||||
t.Equals(reused, heapBase, "guestFree should make the head block reusable");
|
||||
});
|
||||
|
||||
tc.Run("memalign stubs allocate aligned guest memory", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
|
||||
env.runtime.configureGuestHeap(0x00180010u, 0x00182010u);
|
||||
|
||||
setRegU32(env.ctx, 4, 128u);
|
||||
setRegU32(env.ctx, 5, 0x40u);
|
||||
ps2_stubs::memalign(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
const uint32_t direct = ::getRegU32(&env.ctx, 2);
|
||||
t.IsTrue(direct != 0u, "memalign should return a guest address");
|
||||
t.Equals(direct & 0x7Fu, 0u, "memalign should honor 128-byte alignment");
|
||||
|
||||
setRegU32(env.ctx, 5, 64u);
|
||||
setRegU32(env.ctx, 6, 0x40u);
|
||||
ps2_stubs::memalign_r(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
const uint32_t reent = ::getRegU32(&env.ctx, 2);
|
||||
t.IsTrue(reent != 0u, "_memalign_r should return a guest address");
|
||||
t.Equals(reent & 0x3Fu, 0u, "_memalign_r should honor 64-byte alignment");
|
||||
t.IsTrue(reent != direct, "_memalign_r should allocate a distinct block");
|
||||
});
|
||||
|
||||
tc.Run("allocator compatibility stubs use the runtime guest heap", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
|
||||
env.runtime.configureGuestHeap(0x00180010u, 0x00183010u);
|
||||
|
||||
setRegU32(env.ctx, 5, 0x20u);
|
||||
ps2_stubs::malloc_r(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
const uint32_t initial = ::getRegU32(&env.ctx, 2);
|
||||
t.IsTrue(initial != 0u, "_malloc_r should allocate guest memory");
|
||||
|
||||
writeGuestU32(env.rdram.data(), initial, 0xAABBCCDDu);
|
||||
|
||||
setRegU32(env.ctx, 5, initial);
|
||||
setRegU32(env.ctx, 6, 0x80u);
|
||||
ps2_stubs::realloc_r(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
const uint32_t grown = ::getRegU32(&env.ctx, 2);
|
||||
t.IsTrue(grown != 0u, "_realloc_r should return a guest block");
|
||||
t.Equals(readGuestU32(env.rdram.data(), grown), 0xAABBCCDDu,
|
||||
"_realloc_r should preserve existing guest bytes");
|
||||
|
||||
setRegU32(env.ctx, 5, grown);
|
||||
ps2_stubs::free_r(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
|
||||
setRegU32(env.ctx, 5, 0x100u);
|
||||
ps2_stubs::malloc_extend_top(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
t.Equals(::getRegU32(&env.ctx, 2), 0u,
|
||||
"malloc_extend_top should be a safe runtime-owned heap no-op");
|
||||
|
||||
ps2_stubs::__malloc_lock(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
ps2_stubs::__malloc_unlock(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
});
|
||||
|
||||
tc.Run("libc helper stubs cover memclr and libgcc div", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
|
||||
constexpr uint32_t kBuf = 0x5000u;
|
||||
std::memset(env.rdram.data() + kBuf, 0xCD, 16u);
|
||||
setRegU32(env.ctx, 4, kBuf);
|
||||
setRegU32(env.ctx, 5, 12u);
|
||||
ps2_stubs::memclr(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
for (uint32_t i = 0; i < 12u; ++i)
|
||||
{
|
||||
t.Equals(env.rdram[kBuf + i], static_cast<uint8_t>(0),
|
||||
"memclr should zero the requested byte range");
|
||||
}
|
||||
t.Equals(env.rdram[kBuf + 12u], static_cast<uint8_t>(0xCD),
|
||||
"memclr should not write past the requested byte range");
|
||||
|
||||
SET_GPR_S64(&env.ctx, 4, -9);
|
||||
SET_GPR_S64(&env.ctx, 5, 2);
|
||||
ps2_stubs::__divdi3(env.rdram.data(), &env.ctx, &env.runtime);
|
||||
t.Equals(getRegS32(env.ctx, 2), -4, "__divdi3 should divide signed 64-bit values");
|
||||
});
|
||||
|
||||
tc.Run("ReleaseAlarm aliases CancelAlarm and cache toggles succeed", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
@@ -667,6 +742,56 @@ void register_ps2_runtime_kernel_tests()
|
||||
t.Equals(setupSp & 0xFu, 0u, "SetupThread should always return a 16-byte aligned stack pointer");
|
||||
});
|
||||
|
||||
tc.Run("OSD config2 syscalls round-trip extended config", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
constexpr uint32_t kConfig2Addr = 0x00005000u;
|
||||
constexpr uint32_t kConfig2OutAddr = 0x00005010u;
|
||||
constexpr uint32_t kConfig1OutAddr = 0x00005020u;
|
||||
constexpr uint32_t kInitialConfig1 =
|
||||
(1u << 0) | // SPDIF disabled
|
||||
(1u << 4) | // non-Japanese language flag
|
||||
(1u << 13) | // OSD2
|
||||
(1u << 16); // English
|
||||
constexpr uint32_t kConfig2Raw =
|
||||
0xABu | // format
|
||||
(0xB0u << 8) | // daylightSaving=1, timeFormat=1, dateFormat=2
|
||||
(2u << 16) | // extended OSD version
|
||||
(10u << 24); // traditional Chinese
|
||||
|
||||
writeGuestU32(env.rdram.data(), K_PARAM_ADDR, kInitialConfig1);
|
||||
setRegU32(env.ctx, 4, K_PARAM_ADDR);
|
||||
t.IsTrue(callSyscall(0x4Au, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"SetOsdConfigParam syscall should dispatch");
|
||||
t.Equals(getRegS32(env.ctx, 2), KE_OK, "SetOsdConfigParam should seed base OSD state");
|
||||
|
||||
writeGuestU32(env.rdram.data(), kConfig2Addr, kConfig2Raw);
|
||||
setRegU32(env.ctx, 4, kConfig2Addr);
|
||||
setRegU32(env.ctx, 5, 4u);
|
||||
setRegU32(env.ctx, 6, 0u);
|
||||
t.IsTrue(callSyscall(0x6Eu, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"SetOsdConfigParam2 syscall should dispatch");
|
||||
t.Equals(getRegS32(env.ctx, 2), KE_OK, "SetOsdConfigParam2 should succeed");
|
||||
|
||||
writeGuestU32(env.rdram.data(), kConfig2OutAddr, 0xFFFFFFFFu);
|
||||
setRegU32(env.ctx, 4, kConfig2OutAddr);
|
||||
setRegU32(env.ctx, 5, 4u);
|
||||
setRegU32(env.ctx, 6, 0u);
|
||||
t.IsTrue(callSyscall(0x6Fu, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"GetOsdConfigParam2 syscall should dispatch");
|
||||
t.Equals(getRegS32(env.ctx, 2), KE_OK, "GetOsdConfigParam2 should succeed");
|
||||
const uint32_t readConfig2 = readGuestU32(env.rdram.data(), kConfig2OutAddr);
|
||||
t.Equals(readConfig2, kConfig2Raw, "GetOsdConfigParam2 should round-trip the sanitized Config2Param bytes");
|
||||
t.Equals((readConfig2 >> 12) & 1u, 1u, "Config2 daylightSaving should live at bit 12 for libosd callers");
|
||||
|
||||
setRegU32(env.ctx, 4, kConfig1OutAddr);
|
||||
t.IsTrue(callSyscall(0x4Bu, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"GetOsdConfigParam syscall should dispatch after Config2 update");
|
||||
const uint32_t readConfig1 = readGuestU32(env.rdram.data(), kConfig1OutAddr);
|
||||
t.Equals((readConfig1 >> 13) & 0x7u, 2u, "SetOsdConfigParam2 should sync ConfigParam.version");
|
||||
t.Equals((readConfig1 >> 16) & 0x1Fu, 10u, "SetOsdConfigParam2 should sync ConfigParam.language");
|
||||
});
|
||||
|
||||
tc.Run("numeric syscall 0x83 finds matching table entry", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
@@ -965,5 +1090,59 @@ void register_ps2_runtime_kernel_tests()
|
||||
|
||||
notifyRuntimeStop();
|
||||
});
|
||||
|
||||
tc.Run("Copy syscall (0x5A) performs a memory copy", [](TestCase &t)
|
||||
{
|
||||
TestEnv env;
|
||||
constexpr uint32_t kDestAddr = 0x00005000u;
|
||||
constexpr uint32_t kSrcAddr = 0x00006000u;
|
||||
constexpr uint32_t kSize = 16u;
|
||||
constexpr uint32_t kValues[] = {
|
||||
0x11223344u,
|
||||
0x55667788u,
|
||||
0x99AABBCCu,
|
||||
0xDDEEFF00u
|
||||
};
|
||||
|
||||
writeGuestWords(env.rdram.data(), kSrcAddr, kValues, std::size(kValues));
|
||||
|
||||
setRegU32(env.ctx, 4, kDestAddr);
|
||||
setRegU32(env.ctx, 5, kSrcAddr);
|
||||
setRegU32(env.ctx, 6, kSize);
|
||||
|
||||
t.IsTrue(callSyscall(0x5Au, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"Copy syscall should dispatch");
|
||||
|
||||
for (size_t i = 0; i < std::size(kValues); ++i)
|
||||
{
|
||||
uint32_t destVal = readGuestU32(env.rdram.data(), kDestAddr + static_cast<uint32_t>(i * sizeof(uint32_t)));
|
||||
t.Equals(destVal, kValues[i], "Copy should correctly transfer bytes");
|
||||
}
|
||||
});
|
||||
|
||||
tc.Run("GetEntryAddress syscall (0x5B) returns handler from guest table", [](TestCase &t)
|
||||
{
|
||||
notifyRuntimeStop();
|
||||
TestEnv env;
|
||||
initializeGuestKernelState(env.rdram.data());
|
||||
|
||||
constexpr uint32_t kGuestSyscallTableGuestBase = 0x80011F80u;
|
||||
constexpr uint32_t kSyscallIndex = 0x5Au;
|
||||
constexpr uint32_t kExpectedHandler = 0x00383548u;
|
||||
constexpr uint32_t kEntryPhysAddr = (kGuestSyscallTableGuestBase + (kSyscallIndex * 4u)) & 0x1FFFFFFFu;
|
||||
|
||||
writeGuestU32(env.rdram.data(), kEntryPhysAddr, kExpectedHandler);
|
||||
|
||||
setRegU32(env.ctx, 4, kSyscallIndex);
|
||||
|
||||
t.IsTrue(callSyscall(0x5Bu, env.rdram.data(), &env.ctx, &env.runtime),
|
||||
"GetEntryAddress syscall should dispatch");
|
||||
|
||||
t.Equals(static_cast<uint32_t>(getRegS32(env.ctx, 2)),
|
||||
kExpectedHandler,
|
||||
"GetEntryAddress should read and return the handler address from the table");
|
||||
|
||||
notifyRuntimeStop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user