180 lines
6.2 KiB
Text
180 lines
6.2 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
|
|
#define DEFINE_REX_FUNC(name) \
|
|
__attribute__((alias("__imp__" #name))) REX_WEAK_FUNC(name); \
|
|
REX_EXTERN(__imp__##name)
|
|
#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)
|
|
|