mirror of
https://github.com/jessicanataliagta/PSPRecomp
synced 2026-09-26 08:41:08 -04:00
otimizações round 10
otimizações round 10
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -29,6 +30,7 @@ public:
|
||||
static constexpr std::uint32_t kPhysicalBase = 0x08000000u;
|
||||
|
||||
explicit GuestMemory(std::uint32_t size_bytes = 32u * 1024u * 1024u);
|
||||
~GuestMemory();
|
||||
|
||||
// The AOT fast paths below index cached region pointers, so an instance may
|
||||
// not be relocated after construction. Runtime owns exactly one by value
|
||||
@@ -67,21 +69,29 @@ public:
|
||||
class AotFastView {
|
||||
public:
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint8_t aot_load8(std::uint32_t address) const {
|
||||
if (fastmem_base_ != nullptr) return *fastmem_pointer(fastmem_base_, address);
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
if (offset <= ram_limit8_) return ram_data_[offset];
|
||||
return owner_->aot_load8_slow(address);
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint16_t aot_load16(std::uint32_t address) const {
|
||||
if (fastmem_base_ != nullptr) return GuestMemory::read_le16(fastmem_pointer(fastmem_base_, address));
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
if (offset <= ram_limit16_) return GuestMemory::read_le16(ram_data_ + offset);
|
||||
return owner_->aot_load16_slow(address);
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_load32(std::uint32_t address) const {
|
||||
if (fastmem_base_ != nullptr) return GuestMemory::read_le32(fastmem_pointer(fastmem_base_, address));
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
if (offset <= ram_limit32_) return GuestMemory::read_le32(ram_data_ + offset);
|
||||
return owner_->aot_load32_slow(address);
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_store8(std::uint32_t address, std::uint8_t value) const {
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) { *fastmem_pointer(fastmem_base_, address) = value; return; }
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) { *fastmem_pointer(fastmem_base_, address) = value; return; }
|
||||
#endif
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (offset <= ram_limit8_) {
|
||||
@@ -94,6 +104,11 @@ public:
|
||||
owner_->aot_store8_slow(address, value);
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_store16(std::uint32_t address, std::uint16_t value) const {
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) { GuestMemory::write_le16(fastmem_pointer(fastmem_base_, address), value); return; }
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) { GuestMemory::write_le16(fastmem_pointer(fastmem_base_, address), value); return; }
|
||||
#endif
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (offset <= ram_limit16_) {
|
||||
@@ -106,6 +121,11 @@ public:
|
||||
owner_->aot_store16_slow(address, value);
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_store32(std::uint32_t address, std::uint32_t value) const {
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) { GuestMemory::write_le32(fastmem_pointer(fastmem_base_, address), value); return; }
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) { GuestMemory::write_le32(fastmem_pointer(fastmem_base_, address), value); return; }
|
||||
#endif
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (offset <= ram_limit32_) {
|
||||
@@ -131,6 +151,15 @@ public:
|
||||
std::uint32_t address, std::uint32_t (&values)[N]) const {
|
||||
static_assert(N != 0u);
|
||||
constexpr std::uint32_t kTail = static_cast<std::uint32_t>((N - 1u) * 4u);
|
||||
if (fastmem_base_ != nullptr) {
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
std::memcpy(values, fastmem_pointer(fastmem_base_, address), N * sizeof(std::uint32_t));
|
||||
} else {
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
values[i] = GuestMemory::read_le32(fastmem_pointer(fastmem_base_, address + static_cast<std::uint32_t>(i * 4u)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
if (offset > ram_limit32_ || kTail > (ram_limit32_ - offset))
|
||||
return false;
|
||||
@@ -156,6 +185,19 @@ public:
|
||||
std::uint32_t address, const std::uint32_t (&values)[N]) const {
|
||||
static_assert(N != 0u);
|
||||
constexpr std::uint32_t kTail = static_cast<std::uint32_t>((N - 1u) * 4u);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) {
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) {
|
||||
#endif
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
std::memcpy(fastmem_pointer(fastmem_base_, address), values, N * sizeof(std::uint32_t));
|
||||
} else {
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
GuestMemory::write_le32(fastmem_pointer(fastmem_base_, address + static_cast<std::uint32_t>(i * 4u)), values[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
const bool direct = offset <= ram_limit32_ && kTail <= (ram_limit32_ - offset);
|
||||
@@ -192,6 +234,30 @@ public:
|
||||
PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_append32(
|
||||
std::uint32_t cursor_address, std::uint32_t value,
|
||||
std::uint32_t *old_pointer = nullptr) const {
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) {
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) {
|
||||
#endif
|
||||
const std::uint32_t pointer =
|
||||
GuestMemory::read_le32(fastmem_pointer(fastmem_base_, cursor_address));
|
||||
// Preserve the alias-aware overlap rule from the baseline: two
|
||||
// numerically different MIPS aliases may refer to the same four
|
||||
// physical RAM bytes.
|
||||
const std::uint32_t cursor_offset = ram_offset_of_fast(cursor_address);
|
||||
const std::uint32_t target_offset = ram_offset_of_fast(pointer);
|
||||
const bool target_direct = cursor_offset <= ram_limit32_ &&
|
||||
target_offset <= ram_limit32_;
|
||||
const bool disjoint = target_direct &&
|
||||
(target_offset + 3u < cursor_offset || cursor_offset + 3u < target_offset);
|
||||
if (disjoint) {
|
||||
GuestMemory::write_le32(fastmem_pointer(fastmem_base_, pointer), value);
|
||||
const std::uint32_t next = pointer + 4u;
|
||||
GuestMemory::write_le32(fastmem_pointer(fastmem_base_, cursor_address), next);
|
||||
if (old_pointer != nullptr) *old_pointer = pointer;
|
||||
return next;
|
||||
}
|
||||
}
|
||||
const std::uint32_t cursor_offset = ram_offset_of_fast(cursor_address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
const bool cursor_direct = cursor_offset <= ram_limit32_;
|
||||
@@ -222,6 +288,16 @@ public:
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_advance32(
|
||||
std::uint32_t cursor_address) const {
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (fastmem_base_ != nullptr) {
|
||||
#else
|
||||
if (fastmem_base_ != nullptr && !write_watch_enabled_) {
|
||||
#endif
|
||||
const std::uint32_t next =
|
||||
GuestMemory::read_le32(fastmem_pointer(fastmem_base_, cursor_address)) + 4u;
|
||||
GuestMemory::write_le32(fastmem_pointer(fastmem_base_, cursor_address), next);
|
||||
return next;
|
||||
}
|
||||
const std::uint32_t cursor_offset = ram_offset_of_fast(cursor_address);
|
||||
#if defined(PSPRECOMP_AOT_ASSUME_NO_WRITE_WATCH)
|
||||
if (cursor_offset <= ram_limit32_) {
|
||||
@@ -271,25 +347,36 @@ public:
|
||||
friend class GuestMemory;
|
||||
AotFastView(GuestMemory *owner, std::uint8_t *ram_data,
|
||||
std::uint32_t limit8, std::uint32_t limit16,
|
||||
std::uint32_t limit32, bool write_watch) noexcept
|
||||
std::uint32_t limit32, bool write_watch,
|
||||
std::uint8_t *fastmem_base) noexcept
|
||||
: owner_(owner), ram_data_(ram_data), ram_limit8_(limit8),
|
||||
ram_limit16_(limit16), ram_limit32_(limit32),
|
||||
write_watch_enabled_(write_watch) {}
|
||||
write_watch_enabled_(write_watch), fastmem_base_(fastmem_base) {}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static constexpr std::uint32_t ram_offset_of_fast(
|
||||
std::uint32_t address) noexcept {
|
||||
return (address & 0x1FFFFFFFu) - GuestMemory::kPhysicalBase;
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static std::uint8_t *fastmem_pointer(
|
||||
std::uint8_t *base, std::uint32_t address) noexcept {
|
||||
return reinterpret_cast<std::uint8_t *>(
|
||||
reinterpret_cast<std::uintptr_t>(base) + static_cast<std::uintptr_t>(address));
|
||||
}
|
||||
GuestMemory *owner_{};
|
||||
std::uint8_t *ram_data_{};
|
||||
std::uint32_t ram_limit8_{};
|
||||
std::uint32_t ram_limit16_{};
|
||||
std::uint32_t ram_limit32_{};
|
||||
bool write_watch_enabled_{};
|
||||
std::uint8_t *fastmem_base_{};
|
||||
public:
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH bool direct_fastmem_enabled() const noexcept {
|
||||
return fastmem_base_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH AotFastView aot_fast_view() noexcept {
|
||||
return AotFastView(this, ram_data_, ram_limit8_, ram_limit16_, ram_limit32_,
|
||||
write_watch_enabled_);
|
||||
write_watch_enabled_, direct_fastmem_base_);
|
||||
}
|
||||
|
||||
// Fast paths used only by statically generated AOT code. They retain
|
||||
@@ -391,8 +478,12 @@ public:
|
||||
void zero(std::uint32_t address, std::size_t length);
|
||||
|
||||
[[nodiscard]] std::string read_c_string(std::uint32_t address, std::size_t max_length = 256u) const;
|
||||
[[nodiscard]] const std::vector<std::uint8_t> &bytes() const noexcept;
|
||||
[[nodiscard]] const std::vector<std::uint8_t> &vram_bytes() const noexcept;
|
||||
[[nodiscard]] std::span<const std::uint8_t> bytes() const noexcept;
|
||||
[[nodiscard]] std::span<const std::uint8_t> vram_bytes() const noexcept;
|
||||
[[nodiscard]] bool direct_fastmem_enabled() const noexcept { return direct_fastmem_base_ != nullptr; }
|
||||
[[nodiscard]] std::uintptr_t direct_fastmem_base_address() const noexcept {
|
||||
return reinterpret_cast<std::uintptr_t>(direct_fastmem_base_);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Region { Vram, Ram };
|
||||
@@ -404,8 +495,8 @@ private:
|
||||
[[nodiscard]] ResolvedAddress resolve(std::uint32_t address, std::size_t length) const;
|
||||
[[nodiscard]] bool is_vram_window(std::uint32_t canonical_address) const noexcept;
|
||||
[[nodiscard]] std::size_t vram_offset(std::uint32_t canonical_address) const noexcept;
|
||||
[[nodiscard]] const std::vector<std::uint8_t> ®ion_bytes(Region region) const noexcept;
|
||||
[[nodiscard]] std::vector<std::uint8_t> ®ion_bytes(Region region) noexcept;
|
||||
[[nodiscard]] std::span<const std::uint8_t> region_bytes(Region region) const noexcept;
|
||||
[[nodiscard]] std::span<std::uint8_t> region_bytes(Region region) noexcept;
|
||||
|
||||
// Canonicalize and rebase in one step. An address below kPhysicalBase --
|
||||
// EDRAM included -- wraps to a value far above any RAM size, so a single
|
||||
@@ -450,9 +541,11 @@ private:
|
||||
void aot_store16_slow(std::uint32_t address, std::uint16_t value);
|
||||
void aot_store32_slow(std::uint32_t address, std::uint32_t value);
|
||||
|
||||
std::vector<std::uint8_t> vram_;
|
||||
std::vector<std::uint8_t> bytes_;
|
||||
// Cached view of bytes_ for the inline fast paths. Neither region is ever
|
||||
std::vector<std::uint8_t> fallback_vram_;
|
||||
std::vector<std::uint8_t> fallback_ram_;
|
||||
std::uint32_t ram_size_{};
|
||||
std::uint8_t *vram_data_{};
|
||||
// Cached view of main RAM for the inline fast paths. The fallback vectors
|
||||
// resized after construction, so these stay valid for the object's life.
|
||||
//
|
||||
// Deliberately not __restrict. It was tried on the theory that aliasing
|
||||
@@ -462,6 +555,18 @@ private:
|
||||
// sound here -- this pointer aliases bytes_ below, which other members of
|
||||
// this class access directly.
|
||||
std::uint8_t *ram_data_{};
|
||||
// V7 architectural fastmem. On 64-bit Windows the same RAM/VRAM sections
|
||||
// are mapped at their PSP virtual aliases inside a sparse 4 GiB arena.
|
||||
// Generated AOT can then load/store at fastmem_base + guest_address, which
|
||||
// provides a sparse direct-address fast-memory model for generated AOT code.
|
||||
std::uint8_t *direct_fastmem_base_{};
|
||||
static constexpr std::size_t kFastmemMaxViews = 40u;
|
||||
std::array<void *, kFastmemMaxViews> fastmem_views_{};
|
||||
std::size_t fastmem_view_count_{};
|
||||
void *fastmem_ram_mapping_{};
|
||||
void *fastmem_vram_mapping_{};
|
||||
[[nodiscard]] bool initialize_direct_fastmem(std::uint32_t size_bytes) noexcept;
|
||||
void shutdown_direct_fastmem() noexcept;
|
||||
std::uint32_t ram_limit8_{};
|
||||
std::uint32_t ram_limit16_{};
|
||||
std::uint32_t ram_limit32_{};
|
||||
|
||||
@@ -177,6 +177,17 @@ int main(int argc, char **argv) {
|
||||
|
||||
psprecomp::Elf32Image elf = psprecomp::Elf32Image::from_file(executable);
|
||||
psprecomp::Runtime runtime(32u * 1024u * 1024u);
|
||||
{
|
||||
std::ostringstream fastmem_line;
|
||||
fastmem_line << "aot direct fastmem enabled="
|
||||
<< (runtime.memory().direct_fastmem_enabled() ? 1 : 0);
|
||||
if (runtime.memory().direct_fastmem_enabled())
|
||||
fastmem_line << " base=0x" << std::hex
|
||||
<< runtime.memory().direct_fastmem_base_address() << std::dec;
|
||||
else
|
||||
fastmem_line << " fallback=checked-memory";
|
||||
vcs::runtime_log_line(fastmem_line.str());
|
||||
}
|
||||
// The heavy GUESTHOT sampler is opt-in. The rolling PERF telemetry stays on,
|
||||
// but normal gameplay does not pay a census/timestamp branch per cross-unit edge.
|
||||
psprecomp::set_guest_hotspot_profile(configuration.diagnostics.guest_hotspot_profile, 8u);
|
||||
|
||||
@@ -1413,7 +1413,7 @@ void dump_ram_if_requested(const psprecomp::GuestMemory &memory) {
|
||||
std::filesystem::create_directories(config.directory);
|
||||
std::ostringstream stem;
|
||||
stem << "ram_vblank_" << std::setw(6) << std::setfill('0') << display_vblank_index;
|
||||
const auto write_bytes = [&](const std::filesystem::path &path, const std::vector<std::uint8_t> &bytes) {
|
||||
const auto write_bytes = [&](const std::filesystem::path &path, std::span<const std::uint8_t> bytes) {
|
||||
std::ofstream output(path, std::ios::binary | std::ios::trunc);
|
||||
if (!output) throw std::runtime_error("Unable to create RAM diagnostic dump: " + path.string());
|
||||
output.write(reinterpret_cast<const char *>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||
@@ -2379,7 +2379,7 @@ SavedataDisplayMetadata read_savedata_metadata_file(const std::filesystem::path
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// Imported PSP/PPSSPP savedata directories may already contain a standard
|
||||
// Imported PSP savedata directories may already contain a standard
|
||||
// PARAM.SFO. Read the three user-facing strings directly so pre-existing saves
|
||||
// can show their title/mission metadata without first being re-saved by
|
||||
// VCSNative. This is deliberately a tiny bounded PSF reader, not a general SFO
|
||||
@@ -7651,9 +7651,11 @@ void install_profile(psprecomp::Runtime &runtime, std::uint32_t user_arena_start
|
||||
total_fallback += c.fallbacks;
|
||||
total_sample_ns += c.sampled_ns;
|
||||
total_sample_entries += c.sampled_entries;
|
||||
const std::uint64_t estimated_us = (c.sampled_ns * 256u) / 1000u;
|
||||
tier2_line << ' ' << tier2_cluster_name(id) << "_e=" << c.entries
|
||||
<< ' ' << tier2_cluster_name(id) << "_x="
|
||||
<< (c.fused_tail_edges + c.fused_calls);
|
||||
<< (c.fused_tail_edges + c.fused_calls)
|
||||
<< ' ' << tier2_cluster_name(id) << "_est_us=" << estimated_us;
|
||||
}
|
||||
tier2_line << " total_entries=" << total_entries
|
||||
<< " fused_tail=" << total_tail
|
||||
|
||||
@@ -65,7 +65,7 @@ void runtime_log_initialize(const VcsConfiguration &configuration) {
|
||||
return;
|
||||
}
|
||||
s.file << "VCSNative runtime log\n";
|
||||
s.file << "stage=perf-v6-entity-leaf-inline-crashfix1-2026-08-17\n";
|
||||
s.file << "stage=perf-v7-arch-fastmem-2026-08-17\n";
|
||||
s.file << "config=" << configuration.source_path.string() << '\n';
|
||||
s.file << "started=" << timestamp_now() << '\n';
|
||||
s.file << "perf_telemetry=" << (configuration.diagnostics.perf_telemetry ? 1 : 0)
|
||||
@@ -80,7 +80,8 @@ void runtime_log_initialize(const VcsConfiguration &configuration) {
|
||||
<< " unwind_fix=1 reentry_guard=1 dataflow=1 vfpu_block32=133 mem_runs=35 mem_words=287"
|
||||
<< " append32=51 advance32=89 simd_mat4=4 simd_matvec=19"
|
||||
<< " gpr_shadow_clusters=4 gpr_shadow_regs=24 gpr_shadow_occurrences=3461 geometry_shadow=0"
|
||||
<< " perf_layer=6 entity_leaf_inline=1 entity_leaf_scheduler_accounting=1 entity_leaf_resume_pc_fix=1"
|
||||
<< " perf_layer=7 arch_fastmem=1 aot_direct_fastmem_default=1"
|
||||
<< " entity_leaf_inline=1 entity_leaf_scheduler_accounting=1 entity_leaf_resume_pc_fix=1"
|
||||
<< " ge_async_default=0 parallel_vertex_decode_default=0"
|
||||
<< " v5_vfpu_fast_quarantined=1 native_vfpu_088b1780_v4=1"
|
||||
<< " dx12_execute_indirect_default=0 indirect_buffer_mb=4"
|
||||
|
||||
@@ -101,6 +101,7 @@ set "PERF_V5_SYNC_RECOVERY_STAMP=%BUILD%\.vcs_perf_v5_sync_recovery_20260817"
|
||||
set "PERF_V5_STABLE_RECOVERY2_STAMP=%BUILD%\.vcs_perf_v5_stable_recovery2_20260817"
|
||||
set "PERF_V6_ENTITY_LEAF_STAMP=%BUILD%\.vcs_perf_v6_entity_leaf_inline_20260817"
|
||||
set "PERF_V6_ENTITY_LEAF_FIX1_STAMP=%BUILD%\.vcs_perf_v6_entity_leaf_inline_crashfix1_20260817"
|
||||
set "PERF_V7_ARCH_FASTMEM_STAMP=%BUILD%\.vcs_perf_v7_arch_fastmem_20260817"
|
||||
|
||||
echo ================================================================
|
||||
echo VCS - NINJA PERFORMANCE INCREMENTAL BUILD
|
||||
@@ -112,7 +113,7 @@ echo CMake: %CMAKE_EXE%
|
||||
echo Ninja: %NINJA_EXE%
|
||||
echo Ninja workers: %JOBS%
|
||||
echo cl.exe /MP: OFF ^(Ninja owns compile parallelism^)
|
||||
echo Generated AOT: O3, cold /Ob0, measured hot /Ob3; V6 Entity leaf-inline CRASHFIX1 over V4-stable Tier2; risky async/decode quarantined
|
||||
echo Generated AOT: O3, cold /Ob0, measured hot /Ob3; V7 architectural direct-fastmem over V6 CRASHFIX1; risky async/decode quarantined
|
||||
echo Host/core LTCG: ON
|
||||
echo AVX2/fast paths: ON
|
||||
echo ================================================================
|
||||
@@ -121,7 +122,7 @@ echo [0b/7] Reapplying BOOTFIX-safe Tier-2 transforms (OPT1 semantic transforms
|
||||
call "%PROFILE%\APPLY_TIER2_EXTREME.bat"
|
||||
if errorlevel 1 goto :FAIL
|
||||
|
||||
echo [0b2/7] Building V6 Entity leaf-inline CRASHFIX1 over gameplay-stable V4 Tier2...
|
||||
echo [0b2/7] Building V7 ARCH FASTMEM over gameplay-stable V6 CRASHFIX1...
|
||||
set "PYTHON3_CMD="
|
||||
py -3 -c "import sys; raise SystemExit(0 if sys.version_info.major == 3 else 1)" >nul 2>&1
|
||||
if not errorlevel 1 set "PYTHON3_CMD=py -3"
|
||||
@@ -223,6 +224,21 @@ if exist "%BUILD%" if not exist "%PERF_V6_ENTITY_LEAF_FIX1_STAMP%" (
|
||||
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
|
||||
)
|
||||
|
||||
if exist "%BUILD%" if not exist "%PERF_V7_ARCH_FASTMEM_STAMP%" (
|
||||
echo.
|
||||
echo [0c-v7fastmem/7] V7 ARCH FASTMEM - one-time AOT memory-model rebuild...
|
||||
rem This is intentionally not a micro hotfix: guest_memory.hpp is inlined into every
|
||||
rem generated unit so all AOT objects must see the direct-fastmem address model.
|
||||
rem Geometry/Boundary keep their exact V4 Tier2 source; they only recompile against
|
||||
rem the new memory view. This one-time rebuild is required for a global CPU change.
|
||||
del /s /q "%BUILD%\*generated_unit_*.obj" >nul 2>&1
|
||||
del /s /q "%BUILD%\*vcs_tier2_cluster*.obj" >nul 2>&1
|
||||
del /s /q "%BUILD%\*guest_memory*.obj" >nul 2>&1
|
||||
del /s /q "%BUILD%\*vcs_profile*.obj" >nul 2>&1
|
||||
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
|
||||
del /s /q "%BUILD%\*main*.obj" >nul 2>&1
|
||||
)
|
||||
|
||||
if exist "%BUILD%" if not exist "%BOOTFIX_STAMP%" (
|
||||
echo.
|
||||
echo [0c/7] BOOTFIX revision changed - invalidating stale .obj/.pch once...
|
||||
@@ -266,6 +282,7 @@ if errorlevel 1 goto :FAIL
|
||||
>"%PERF_V5_STABLE_RECOVERY2_STAMP%" echo VCS PERF V5 STABLE RECOVERY2 2026-08-17
|
||||
>"%PERF_V6_ENTITY_LEAF_STAMP%" echo VCS PERF V6 ENTITY LEAF INLINE 2026-08-17
|
||||
>"%PERF_V6_ENTITY_LEAF_FIX1_STAMP%" echo VCS PERF V6 ENTITY LEAF INLINE CRASHFIX1 2026-08-17
|
||||
>"%PERF_V7_ARCH_FASTMEM_STAMP%" echo VCS PERF V7 ARCH FASTMEM 2026-08-17
|
||||
|
||||
echo.
|
||||
echo [2b/7] Building tests and DX12 probes...
|
||||
|
||||
+250
-59
@@ -7,6 +7,15 @@
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace psprecomp {
|
||||
|
||||
@@ -55,6 +64,14 @@ bool overlaps_watch(std::uint32_t address, std::size_t length) {
|
||||
static_cast<std::uint64_t>(canonical_watch) < first_end;
|
||||
}
|
||||
|
||||
bool environment_enabled_default_on(const char *name) noexcept {
|
||||
const char *value = std::getenv(name);
|
||||
if (value == nullptr || *value == '\0') return true;
|
||||
const std::string_view text(value);
|
||||
return !(text == "0" || text == "off" || text == "OFF" ||
|
||||
text == "false" || text == "FALSE" || text == "no" || text == "NO");
|
||||
}
|
||||
|
||||
void log_write_watch(std::uint32_t address, std::size_t length, const char *operation,
|
||||
std::uint64_t old_value, std::uint64_t new_value) {
|
||||
if (!overlaps_watch(address, length)) return;
|
||||
@@ -69,21 +86,182 @@ void log_write_watch(std::uint32_t address, std::size_t length, const char *oper
|
||||
}
|
||||
}
|
||||
|
||||
bool GuestMemory::initialize_direct_fastmem(std::uint32_t size_bytes) noexcept {
|
||||
direct_fastmem_base_ = nullptr;
|
||||
fastmem_view_count_ = 0u;
|
||||
fastmem_views_.fill(nullptr);
|
||||
fastmem_ram_mapping_ = nullptr;
|
||||
fastmem_vram_mapping_ = nullptr;
|
||||
|
||||
if (!environment_enabled_default_on("PSPRECOMP_AOT_DIRECT_FASTMEM"))
|
||||
return false;
|
||||
|
||||
#if defined(_WIN32) && INTPTR_MAX > INT32_MAX
|
||||
HANDLE ram_mapping = CreateFileMappingW(
|
||||
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0u,
|
||||
static_cast<DWORD>(size_bytes), nullptr);
|
||||
if (ram_mapping == nullptr) return false;
|
||||
|
||||
HANDLE vram_mapping = CreateFileMappingW(
|
||||
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0u,
|
||||
static_cast<DWORD>(kVramSize), nullptr);
|
||||
if (vram_mapping == nullptr) {
|
||||
CloseHandle(ram_mapping);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map the exact alias model used by canonical(address): the top three bits
|
||||
// are ignored, so every 0x20000000 mirror must resolve to the same physical
|
||||
// bytes. VRAM additionally has four 2 MiB mirrors inside its 8 MiB window.
|
||||
// No 4 GiB reservation is needed; only the 40 live sparse views consume VA.
|
||||
const auto clear_attempt = [&]() noexcept {
|
||||
for (std::size_t i = 0u; i < fastmem_view_count_; ++i) {
|
||||
if (fastmem_views_[i] != nullptr) UnmapViewOfFile(fastmem_views_[i]);
|
||||
fastmem_views_[i] = nullptr;
|
||||
}
|
||||
fastmem_view_count_ = 0u;
|
||||
};
|
||||
|
||||
const auto map_exact = [&](HANDLE mapping, std::uintptr_t host_address,
|
||||
std::size_t bytes) noexcept -> bool {
|
||||
void *const requested = reinterpret_cast<void *>(host_address);
|
||||
void *const view = MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0u, 0u,
|
||||
bytes, requested);
|
||||
if (view != requested) {
|
||||
if (view != nullptr) UnmapViewOfFile(view);
|
||||
return false;
|
||||
}
|
||||
if (fastmem_view_count_ >= fastmem_views_.size()) {
|
||||
UnmapViewOfFile(view);
|
||||
return false;
|
||||
}
|
||||
fastmem_views_[fastmem_view_count_++] = view;
|
||||
return true;
|
||||
};
|
||||
|
||||
// High, 64 KiB-aligned bases keep the sparse PSP 4 GiB window away from
|
||||
// ordinary executable/heap allocations. Try several independent 1 TiB
|
||||
// slots so ASLR or another mapping cannot make fastmem boot-critical.
|
||||
constexpr std::uintptr_t kFirstCandidate = UINT64_C(0x0000040000000000);
|
||||
constexpr std::uintptr_t kCandidateStep = UINT64_C(0x0000010000000000);
|
||||
constexpr std::size_t kCandidateCount = 24u;
|
||||
|
||||
bool mapped = false;
|
||||
for (std::size_t attempt = 0u; attempt < kCandidateCount && !mapped; ++attempt) {
|
||||
clear_attempt();
|
||||
const std::uintptr_t base = kFirstCandidate + kCandidateStep * attempt;
|
||||
bool ok = true;
|
||||
|
||||
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
|
||||
const std::uint32_t guest = kPhysicalBase + alias * 0x20000000u;
|
||||
ok = map_exact(ram_mapping, base + guest, size_bytes);
|
||||
}
|
||||
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
|
||||
for (std::uint32_t mirror = 0u; mirror < kVramMirrorCount && ok; ++mirror) {
|
||||
const std::uint32_t guest = kVramPhysicalBase + mirror * kVramSize +
|
||||
alias * 0x20000000u;
|
||||
ok = map_exact(vram_mapping, base + guest, kVramSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
// Verify that the OS really gave us coherent aliases before any
|
||||
// guest data is loaded. This turns a broken/partial mapping into a
|
||||
// clean fallback rather than latent guest-memory corruption.
|
||||
auto *const probe_base = reinterpret_cast<std::uint8_t *>(base);
|
||||
const std::uint32_t ram_probe_offset = size_bytes - 1u;
|
||||
probe_base[kPhysicalBase + ram_probe_offset] = 0x5Au;
|
||||
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
|
||||
const std::uint32_t guest = kPhysicalBase + alias * 0x20000000u;
|
||||
ok = probe_base[guest + ram_probe_offset] == 0x5Au;
|
||||
}
|
||||
probe_base[kPhysicalBase + ram_probe_offset] = 0u;
|
||||
|
||||
const std::uint32_t vram_probe_offset = kVramSize - 1u;
|
||||
probe_base[kVramPhysicalBase + vram_probe_offset] = 0xA5u;
|
||||
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
|
||||
for (std::uint32_t mirror = 0u; mirror < kVramMirrorCount && ok; ++mirror) {
|
||||
const std::uint32_t guest = kVramPhysicalBase + mirror * kVramSize +
|
||||
alias * 0x20000000u;
|
||||
ok = probe_base[guest + vram_probe_offset] == 0xA5u;
|
||||
}
|
||||
}
|
||||
probe_base[kVramPhysicalBase + vram_probe_offset] = 0u;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
direct_fastmem_base_ = reinterpret_cast<std::uint8_t *>(base);
|
||||
mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mapped) {
|
||||
clear_attempt();
|
||||
CloseHandle(vram_mapping);
|
||||
CloseHandle(ram_mapping);
|
||||
return false;
|
||||
}
|
||||
|
||||
fastmem_ram_mapping_ = ram_mapping;
|
||||
fastmem_vram_mapping_ = vram_mapping;
|
||||
return true;
|
||||
#else
|
||||
(void)size_bytes;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GuestMemory::shutdown_direct_fastmem() noexcept {
|
||||
#if defined(_WIN32) && INTPTR_MAX > INT32_MAX
|
||||
for (std::size_t i = 0u; i < fastmem_view_count_; ++i) {
|
||||
if (fastmem_views_[i] != nullptr) UnmapViewOfFile(fastmem_views_[i]);
|
||||
fastmem_views_[i] = nullptr;
|
||||
}
|
||||
fastmem_view_count_ = 0u;
|
||||
if (fastmem_vram_mapping_ != nullptr) {
|
||||
CloseHandle(static_cast<HANDLE>(fastmem_vram_mapping_));
|
||||
fastmem_vram_mapping_ = nullptr;
|
||||
}
|
||||
if (fastmem_ram_mapping_ != nullptr) {
|
||||
CloseHandle(static_cast<HANDLE>(fastmem_ram_mapping_));
|
||||
fastmem_ram_mapping_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
direct_fastmem_base_ = nullptr;
|
||||
}
|
||||
|
||||
GuestMemory::GuestMemory(std::uint32_t size_bytes)
|
||||
: vram_(kVramSize, 0u), bytes_(size_bytes, 0u), write_watch_enabled_(std::getenv("PSPRECOMP_WATCH_WRITE") != nullptr) {
|
||||
: ram_size_(size_bytes),
|
||||
write_watch_enabled_(std::getenv("PSPRECOMP_WATCH_WRITE") != nullptr) {
|
||||
if (size_bytes != 32u * 1024u * 1024u && size_bytes != 64u * 1024u * 1024u) {
|
||||
throw Error("PSP RAM size must be 32 MiB or 64 MiB");
|
||||
}
|
||||
// Bind the inline AOT fast paths to main RAM. bytes_ is never resized
|
||||
// afterwards, and the instance is non-copyable, so this stays valid.
|
||||
ram_data_ = bytes_.data();
|
||||
|
||||
if (initialize_direct_fastmem(size_bytes)) {
|
||||
// These two aliases are backed by the same page-file sections as every
|
||||
// other PSP mirror in the fastmem arena. Keeping the ordinary pointers
|
||||
// on those mappings makes HLE/raw_pointer/ELF loading coherent with the
|
||||
// generated AOT direct-address path without a shadow copy.
|
||||
vram_data_ = direct_fastmem_base_ + kVramPhysicalBase;
|
||||
ram_data_ = direct_fastmem_base_ + kPhysicalBase;
|
||||
} else {
|
||||
fallback_vram_.assign(kVramSize, 0u);
|
||||
fallback_ram_.assign(size_bytes, 0u);
|
||||
vram_data_ = fallback_vram_.data();
|
||||
ram_data_ = fallback_ram_.data();
|
||||
}
|
||||
|
||||
ram_limit8_ = size_bytes - 1u;
|
||||
ram_limit16_ = size_bytes - 2u;
|
||||
ram_limit32_ = size_bytes - 4u;
|
||||
}
|
||||
|
||||
std::uint32_t GuestMemory::size() const noexcept { return static_cast<std::uint32_t>(bytes_.size()); }
|
||||
std::uint32_t GuestMemory::vram_size() const noexcept { return static_cast<std::uint32_t>(vram_.size()); }
|
||||
GuestMemory::~GuestMemory() {
|
||||
shutdown_direct_fastmem();
|
||||
}
|
||||
|
||||
std::uint32_t GuestMemory::size() const noexcept { return ram_size_; }
|
||||
std::uint32_t GuestMemory::vram_size() const noexcept { return kVramSize; }
|
||||
|
||||
bool GuestMemory::is_vram_window(std::uint32_t canonical_address) const noexcept {
|
||||
return canonical_address >= kVramPhysicalBase &&
|
||||
@@ -99,7 +277,7 @@ bool GuestMemory::contains(std::uint32_t address, std::size_t length) const noex
|
||||
const std::uint64_t end = static_cast<std::uint64_t>(c) + static_cast<std::uint64_t>(length);
|
||||
if (is_vram_window(c) && end <= static_cast<std::uint64_t>(kVramPhysicalBase) + kVramAddressSpan)
|
||||
return true;
|
||||
if (c >= kPhysicalBase && end <= static_cast<std::uint64_t>(kPhysicalBase) + bytes_.size())
|
||||
if (c >= kPhysicalBase && end <= static_cast<std::uint64_t>(kPhysicalBase) + ram_size_)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -114,11 +292,15 @@ GuestMemory::ResolvedAddress GuestMemory::resolve(std::uint32_t address, std::si
|
||||
return {Region::Ram, static_cast<std::size_t>(c - kPhysicalBase)};
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) const noexcept {
|
||||
return region == Region::Vram ? vram_ : bytes_;
|
||||
std::span<const std::uint8_t> GuestMemory::region_bytes(Region region) const noexcept {
|
||||
return region == Region::Vram
|
||||
? std::span<const std::uint8_t>(vram_data_, kVramSize)
|
||||
: std::span<const std::uint8_t>(ram_data_, ram_size_);
|
||||
}
|
||||
std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) noexcept {
|
||||
return region == Region::Vram ? vram_ : bytes_;
|
||||
std::span<std::uint8_t> GuestMemory::region_bytes(Region region) noexcept {
|
||||
return region == Region::Vram
|
||||
? std::span<std::uint8_t>(vram_data_, kVramSize)
|
||||
: std::span<std::uint8_t>(ram_data_, ram_size_);
|
||||
}
|
||||
|
||||
// The `_slow` bodies below are the original aot_* implementations, reached only
|
||||
@@ -126,9 +308,9 @@ std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) noexcept {
|
||||
// an out-of-range address, a region-crossing width, or an armed write watch.
|
||||
std::uint8_t GuestMemory::aot_load8_slow(std::uint32_t address) const {
|
||||
const std::uint32_t c = canonical(address);
|
||||
if (is_vram_window(c)) return vram_[vram_offset(c)];
|
||||
if (c >= kPhysicalBase && c - kPhysicalBase < bytes_.size())
|
||||
return bytes_[static_cast<std::size_t>(c - kPhysicalBase)];
|
||||
if (is_vram_window(c)) return vram_data_[vram_offset(c)];
|
||||
if (c >= kPhysicalBase && c - kPhysicalBase < ram_size_)
|
||||
return ram_data_[static_cast<std::size_t>(c - kPhysicalBase)];
|
||||
return load8(address);
|
||||
}
|
||||
|
||||
@@ -136,34 +318,37 @@ std::uint16_t GuestMemory::aot_load16_slow(std::uint32_t address) const {
|
||||
const std::uint32_t c = canonical(address);
|
||||
if (is_vram_window(c)) {
|
||||
const std::size_t offset = vram_offset(c);
|
||||
if (offset + 2u <= vram_.size())
|
||||
return static_cast<std::uint16_t>(vram_[offset]) |
|
||||
static_cast<std::uint16_t>(static_cast<std::uint16_t>(vram_[offset + 1u]) << 8u);
|
||||
if (offset + 2u <= static_cast<std::size_t>(kVramSize))
|
||||
return static_cast<std::uint16_t>(vram_data_[offset]) |
|
||||
static_cast<std::uint16_t>(static_cast<std::uint16_t>(vram_data_[offset + 1u]) << 8u);
|
||||
} else if (c >= kPhysicalBase) {
|
||||
const std::size_t offset = static_cast<std::size_t>(c - kPhysicalBase);
|
||||
if (offset + 2u <= bytes_.size())
|
||||
return static_cast<std::uint16_t>(bytes_[offset]) |
|
||||
static_cast<std::uint16_t>(static_cast<std::uint16_t>(bytes_[offset + 1u]) << 8u);
|
||||
if (offset + 2u <= ram_size_)
|
||||
return static_cast<std::uint16_t>(ram_data_[offset]) |
|
||||
static_cast<std::uint16_t>(static_cast<std::uint16_t>(ram_data_[offset + 1u]) << 8u);
|
||||
}
|
||||
return load16(address);
|
||||
}
|
||||
|
||||
std::uint32_t GuestMemory::aot_load32_slow(std::uint32_t address) const {
|
||||
const std::uint32_t c = canonical(address);
|
||||
const std::vector<std::uint8_t> *data = nullptr;
|
||||
const std::uint8_t *data = nullptr;
|
||||
std::size_t data_size = 0u;
|
||||
std::size_t offset = 0u;
|
||||
if (is_vram_window(c)) {
|
||||
data = &vram_;
|
||||
data = vram_data_;
|
||||
data_size = kVramSize;
|
||||
offset = vram_offset(c);
|
||||
} else if (c >= kPhysicalBase) {
|
||||
data = &bytes_;
|
||||
data = ram_data_;
|
||||
data_size = ram_size_;
|
||||
offset = static_cast<std::size_t>(c - kPhysicalBase);
|
||||
}
|
||||
if (data != nullptr && offset + 4u <= data->size()) {
|
||||
return static_cast<std::uint32_t>((*data)[offset]) |
|
||||
(static_cast<std::uint32_t>((*data)[offset + 1u]) << 8u) |
|
||||
(static_cast<std::uint32_t>((*data)[offset + 2u]) << 16u) |
|
||||
(static_cast<std::uint32_t>((*data)[offset + 3u]) << 24u);
|
||||
if (data != nullptr && offset + 4u <= data_size) {
|
||||
return static_cast<std::uint32_t>(data[offset]) |
|
||||
(static_cast<std::uint32_t>(data[offset + 1u]) << 8u) |
|
||||
(static_cast<std::uint32_t>(data[offset + 2u]) << 16u) |
|
||||
(static_cast<std::uint32_t>(data[offset + 3u]) << 24u);
|
||||
}
|
||||
return load32(address);
|
||||
}
|
||||
@@ -182,9 +367,9 @@ std::uint32_t GuestMemory::aot_load_word_right(std::uint32_t address, std::uint3
|
||||
void GuestMemory::aot_store8_slow(std::uint32_t address, std::uint8_t value) {
|
||||
if (write_watch_enabled_) { store8(address, value); return; }
|
||||
const std::uint32_t c = canonical(address);
|
||||
if (is_vram_window(c)) { vram_[vram_offset(c)] = value; return; }
|
||||
if (c >= kPhysicalBase && c - kPhysicalBase < bytes_.size()) {
|
||||
bytes_[static_cast<std::size_t>(c - kPhysicalBase)] = value;
|
||||
if (is_vram_window(c)) { vram_data_[vram_offset(c)] = value; return; }
|
||||
if (c >= kPhysicalBase && c - kPhysicalBase < ram_size_) {
|
||||
ram_data_[static_cast<std::size_t>(c - kPhysicalBase)] = value;
|
||||
return;
|
||||
}
|
||||
store8(address, value);
|
||||
@@ -192,13 +377,14 @@ void GuestMemory::aot_store8_slow(std::uint32_t address, std::uint8_t value) {
|
||||
void GuestMemory::aot_store16_slow(std::uint32_t address, std::uint16_t value) {
|
||||
if (write_watch_enabled_) { store16(address, value); return; }
|
||||
const std::uint32_t c = canonical(address);
|
||||
std::vector<std::uint8_t> *data = nullptr;
|
||||
std::uint8_t *data = nullptr;
|
||||
std::size_t data_size = 0u;
|
||||
std::size_t offset = 0u;
|
||||
if (is_vram_window(c)) { data = &vram_; offset = vram_offset(c); }
|
||||
else if (c >= kPhysicalBase) { data = &bytes_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
|
||||
if (data != nullptr && offset + 2u <= data->size()) {
|
||||
(*data)[offset] = static_cast<std::uint8_t>(value & 0xFFu);
|
||||
(*data)[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
|
||||
if (is_vram_window(c)) { data = vram_data_; data_size = kVramSize; offset = vram_offset(c); }
|
||||
else if (c >= kPhysicalBase) { data = ram_data_; data_size = ram_size_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
|
||||
if (data != nullptr && offset + 2u <= data_size) {
|
||||
data[offset] = static_cast<std::uint8_t>(value & 0xFFu);
|
||||
data[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
|
||||
return;
|
||||
}
|
||||
store16(address, value);
|
||||
@@ -206,15 +392,16 @@ void GuestMemory::aot_store16_slow(std::uint32_t address, std::uint16_t value) {
|
||||
void GuestMemory::aot_store32_slow(std::uint32_t address, std::uint32_t value) {
|
||||
if (write_watch_enabled_) { store32(address, value); return; }
|
||||
const std::uint32_t c = canonical(address);
|
||||
std::vector<std::uint8_t> *data = nullptr;
|
||||
std::uint8_t *data = nullptr;
|
||||
std::size_t data_size = 0u;
|
||||
std::size_t offset = 0u;
|
||||
if (is_vram_window(c)) { data = &vram_; offset = vram_offset(c); }
|
||||
else if (c >= kPhysicalBase) { data = &bytes_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
|
||||
if (data != nullptr && offset + 4u <= data->size()) {
|
||||
(*data)[offset] = static_cast<std::uint8_t>(value & 0xFFu);
|
||||
(*data)[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
|
||||
(*data)[offset + 2u] = static_cast<std::uint8_t>((value >> 16u) & 0xFFu);
|
||||
(*data)[offset + 3u] = static_cast<std::uint8_t>((value >> 24u) & 0xFFu);
|
||||
if (is_vram_window(c)) { data = vram_data_; data_size = kVramSize; offset = vram_offset(c); }
|
||||
else if (c >= kPhysicalBase) { data = ram_data_; data_size = ram_size_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
|
||||
if (data != nullptr && offset + 4u <= data_size) {
|
||||
data[offset] = static_cast<std::uint8_t>(value & 0xFFu);
|
||||
data[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
|
||||
data[offset + 2u] = static_cast<std::uint8_t>((value >> 16u) & 0xFFu);
|
||||
data[offset + 3u] = static_cast<std::uint8_t>((value >> 24u) & 0xFFu);
|
||||
return;
|
||||
}
|
||||
store32(address, value);
|
||||
@@ -259,7 +446,7 @@ void GuestMemory::aot_copy_lz_match(std::uint32_t destination, std::uint32_t sou
|
||||
return;
|
||||
}
|
||||
|
||||
auto &data = region_bytes(destination_resolved.region);
|
||||
auto data = region_bytes(destination_resolved.region);
|
||||
if (destination_resolved.offset + length > data.size() ||
|
||||
source_resolved.offset + length > data.size() ||
|
||||
source_resolved.offset >= destination_resolved.offset) {
|
||||
@@ -276,10 +463,10 @@ void GuestMemory::aot_copy_lz_match(std::uint32_t destination, std::uint32_t sou
|
||||
// the already produced prefix in geometrically growing non-overlapping
|
||||
// chunks. This is equivalent to the guest's forward byte loop, including
|
||||
// distance=1 runs, but completes in O(log(length)) host copies.
|
||||
std::size_t copied = std::min(distance, total);
|
||||
std::size_t copied = (std::min)(distance, total);
|
||||
std::memcpy(data.data() + destination_offset, data.data() + source_offset, copied);
|
||||
while (copied < total) {
|
||||
const std::size_t chunk = std::min(copied, total - copied);
|
||||
const std::size_t chunk = (std::min)(copied, total - copied);
|
||||
std::memcpy(data.data() + destination_offset + copied, data.data() + destination_offset, chunk);
|
||||
copied += chunk;
|
||||
}
|
||||
@@ -296,12 +483,12 @@ const std::uint8_t *GuestMemory::raw_pointer(std::uint32_t address, std::size_t
|
||||
const std::size_t offset = vram_offset(c);
|
||||
// A run that would wrap past the end of the 2 MiB EDRAM image is not
|
||||
// contiguous in host memory even though it is legal in guest space.
|
||||
if (offset + length <= vram_.size()) return vram_.data() + offset;
|
||||
if (offset + length <= static_cast<std::size_t>(kVramSize)) return vram_data_ + offset;
|
||||
return nullptr;
|
||||
}
|
||||
if (c < kPhysicalBase) return nullptr;
|
||||
const std::size_t offset = static_cast<std::size_t>(c - kPhysicalBase);
|
||||
if (offset + length <= bytes_.size()) return bytes_.data() + offset;
|
||||
if (offset + length <= ram_size_) return ram_data_ + offset;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -331,7 +518,7 @@ std::uint32_t GuestMemory::load_word_right(std::uint32_t address, std::uint32_t
|
||||
}
|
||||
void GuestMemory::store8(std::uint32_t address, std::uint8_t value) {
|
||||
const auto r = resolve(address, 1u);
|
||||
auto &data = region_bytes(r.region);
|
||||
auto data = region_bytes(r.region);
|
||||
const std::uint8_t old = data[r.offset];
|
||||
log_write_watch(address, 1u, "store8", old, value);
|
||||
data[r.offset] = value;
|
||||
@@ -381,8 +568,8 @@ void GuestMemory::copy_in(std::uint32_t address, std::span<const std::uint8_t> s
|
||||
while (copied < source.size()) {
|
||||
const std::uint32_t current = address + static_cast<std::uint32_t>(copied);
|
||||
const auto r = resolve(current, 1u);
|
||||
auto &data = region_bytes(r.region);
|
||||
const std::size_t chunk = std::min(source.size() - copied, data.size() - r.offset);
|
||||
auto data = region_bytes(r.region);
|
||||
const std::size_t chunk = (std::min)(source.size() - copied, data.size() - r.offset);
|
||||
std::copy_n(source.begin() + static_cast<std::ptrdiff_t>(copied), chunk,
|
||||
data.begin() + static_cast<std::ptrdiff_t>(r.offset));
|
||||
copied += chunk;
|
||||
@@ -395,8 +582,8 @@ void GuestMemory::copy_out(std::uint32_t address, std::span<std::uint8_t> destin
|
||||
while (copied < destination.size()) {
|
||||
const std::uint32_t current = address + static_cast<std::uint32_t>(copied);
|
||||
const auto r = resolve(current, 1u);
|
||||
const auto &data = region_bytes(r.region);
|
||||
const std::size_t chunk = std::min(destination.size() - copied, data.size() - r.offset);
|
||||
const auto data = region_bytes(r.region);
|
||||
const std::size_t chunk = (std::min)(destination.size() - copied, data.size() - r.offset);
|
||||
std::copy_n(data.begin() + static_cast<std::ptrdiff_t>(r.offset), chunk,
|
||||
destination.begin() + static_cast<std::ptrdiff_t>(copied));
|
||||
copied += chunk;
|
||||
@@ -410,8 +597,8 @@ void GuestMemory::zero(std::uint32_t address, std::size_t length) {
|
||||
while (cleared < length) {
|
||||
const std::uint32_t current = address + static_cast<std::uint32_t>(cleared);
|
||||
const auto r = resolve(current, 1u);
|
||||
auto &data = region_bytes(r.region);
|
||||
const std::size_t chunk = std::min(length - cleared, data.size() - r.offset);
|
||||
auto data = region_bytes(r.region);
|
||||
const std::size_t chunk = (std::min)(length - cleared, data.size() - r.offset);
|
||||
std::fill_n(data.begin() + static_cast<std::ptrdiff_t>(r.offset), chunk, 0u);
|
||||
cleared += chunk;
|
||||
}
|
||||
@@ -426,7 +613,11 @@ std::string GuestMemory::read_c_string(std::uint32_t address, std::size_t max_le
|
||||
}
|
||||
throw Error("Unterminated guest string at " + hex32(address));
|
||||
}
|
||||
const std::vector<std::uint8_t> &GuestMemory::bytes() const noexcept { return bytes_; }
|
||||
const std::vector<std::uint8_t> &GuestMemory::vram_bytes() const noexcept { return vram_; }
|
||||
std::span<const std::uint8_t> GuestMemory::bytes() const noexcept {
|
||||
return {ram_data_, ram_size_};
|
||||
}
|
||||
std::span<const std::uint8_t> GuestMemory::vram_bytes() const noexcept {
|
||||
return {vram_data_, kVramSize};
|
||||
}
|
||||
|
||||
} // namespace psprecomp
|
||||
|
||||
Reference in New Issue
Block a user