rexglue-skate3/resources/templates/test/ppc_config_h.inja
2026-06-06 15:13:35 +01:00

188 lines
6.5 KiB
Text

// Generated by rexglue recompile-tests
#pragma once
#ifndef REX_CONFIG_H_INCLUDED
#define REX_CONFIG_H_INCLUDED
#include <rex/ppc.h>
#include <rex/image_info.h>
#include <rex/logging.h>
#include <rex/chrono/clock.h>
#include <rex/perf/counter.h>
#include <rex/thread/mutex.h>
#include <rex/system/mmio_handler.h>
#include <atomic>
#include <cassert>
#include <csetjmp>
#include <cstdlib>
#include <stdexcept>
#include <string_view>
#include <unordered_map>
// Test code base address (matches -Ttext=0x82010000 linker option)
#define REX_IMAGE_BASE 0x{{ image_base }}ull
#define REX_IMAGE_SIZE 0x{{ image_size }}ull
#define REX_CODE_BASE 0x{{ code_base }}ull
#define REX_CODE_SIZE 0x{{ code_size }}ull
#define REX_THUNK_RESERVE_SIZE 0x10000ull
#endif // REX_CONFIG_H_INCLUDED
// Prologue macro (codegen init header defines this with Tracy/alignment hints;
// tests just need a no-op)
#ifndef REX_FUNC_PROLOGUE
#if defined(__clang__)
#define REX_FUNC_PROLOGUE() __builtin_assume(((size_t)base & 0x1F) == 0)
#elif defined(__GNUC__)
#define REX_FUNC_PROLOGUE() \
do { \
if (((size_t)base & 0x1F) != 0) \
__builtin_unreachable(); \
} while (0)
#else
#define REX_FUNC_PROLOGUE() ((void)0)
#endif
#endif
// Function definition macros (same as codegen init header)
#ifndef DECLARE_REX_FUNC
#define DECLARE_REX_FUNC(name) \
REX_EXTERN(name); \
REX_EXTERN(__imp__##name)
#endif
#ifndef DEFINE_REX_FUNC
#if defined(__APPLE__)
#define DEFINE_REX_FUNC(name) \
REX_EXTERN(__imp__##name); \
REX_WEAK_FUNC(name) { \
__imp__##name(ctx, base); \
} \
REX_EXTERN(__imp__##name)
#else
#define DEFINE_REX_FUNC(name) \
__attribute__((alias("__imp__" #name))) REX_WEAK_FUNC(name); \
REX_EXTERN(__imp__##name)
#endif
#endif
#if REX_PLATFORM_WIN32
#define REX_PHYS_HOST_OFFSET(addr) (((u32)(addr) >= 0xE0000000u) ? 0x1000u : 0u)
#else
#define REX_PHYS_HOST_OFFSET(addr) 0u
#endif
#define REX_RAW_ADDR(x) (base + (u32)(x) + REX_PHYS_HOST_OFFSET(x))
#define REX_LOAD_U8(x) (*(volatile u8*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)))
#define REX_LOAD_U16(x) __builtin_bswap16(*(volatile u16*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)))
#define REX_LOAD_U32(x) __builtin_bswap32(*(volatile u32*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)))
#define REX_LOAD_U64(x) __builtin_bswap64(*(volatile u64*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)))
#define REX_LOAD_STRING(x, len) \
std::string_view(reinterpret_cast<const char*>(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)), (len))
#define REX_STORE_U8(x, y) (*(volatile u8*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = (y))
#define REX_STORE_U16(x, y) (*(volatile u16*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap16(y))
#define REX_STORE_U32(x, y) (*(volatile u32*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap32(y))
#define REX_STORE_U64(x, y) (*(volatile u64*)(base + (u32)(x) + REX_PHYS_HOST_OFFSET(x)) = __builtin_bswap64(y))
#define REX_MEMORY_SIZE 0x100000000ull
//=============================================================================
// Function Dispatch
//=============================================================================
#define REX_CALL_FUNC(x) x(ctx, base)
{% include "codegen/_indirect_call.inja" %}
//=============================================================================
// Flush Mode
//=============================================================================
#define REX_SET_FLUSH_MODE(enable) \
do { if (enable) ctx.fpscr.enableFlushMode(); else ctx.fpscr.disableFlushMode(); } while (0)
//=============================================================================
// Misc
//=============================================================================
#define REX_UNIMPLEMENTED(addr, opcode) \
throw std::runtime_error("Unimplemented PPC instruction: " opcode)
#define REX_QUERY_TIMEBASE() rex::chrono::Clock::QueryGuestTickCount()
//=============================================================================
// Setjmp/Longjmp
//=============================================================================
inline std::unordered_map<u32, jmp_buf>& get_jmp_buf_map() {
static thread_local std::unordered_map<u32, jmp_buf> map;
return map;
}
#define ppc_setjmp(guest_buf_addr) (setjmp(::get_jmp_buf_map()[(guest_buf_addr)]))
[[noreturn]] inline void ppc_longjmp(u32 guest_buf_addr, int val) {
auto& map = get_jmp_buf_map();
auto it = map.find(guest_buf_addr);
if (it != map.end()) {
longjmp(it->second, val);
}
std::abort();
}
//=============================================================================
// Trap Handling
//=============================================================================
inline void ppc_trap(PPCContext& ctx, u8* base, u16 trap_type) {
switch (trap_type) {
case 20:
case 26: {
auto str = REX_LOAD_STRING(ctx.r3.u32, ctx.r4.u16);
REXCPU_DEBUG("(service trap) {}", str);
break;
}
case 0:
case 22:
REXCPU_WARN("tw/td trap hit (type {})", trap_type);
break;
case 25:
break;
default:
REXCPU_WARN("Unknown trap type {}", trap_type);
break;
}
}
//=============================================================================
// Interrupt Locks
//=============================================================================
inline std::atomic<i32>& ppc_global_lock_count_() {
static std::atomic<i32> count{0};
return count;
}
#define REX_CHECK_GLOBAL_LOCK() \
([&]() -> u64 { \
auto lock_ = rex::thread::global_critical_region::AcquireDirect(); \
return ppc_global_lock_count_().load() ? 0 : 0x8000; \
}())
#define REX_ENTER_GLOBAL_LOCK() \
do { \
rex::thread::global_critical_region::mutex().lock(); \
ppc_global_lock_count_().fetch_add(1); \
} while (0)
#define REX_LEAVE_GLOBAL_LOCK() \
do { \
auto old_count_ = ppc_global_lock_count_().fetch_sub(1); \
assert(old_count_ >= 1 && "LeaveGlobalLock called without matching EnterGlobalLock"); \
rex::thread::global_critical_region::mutex().unlock(); \
} while (0)