320 lines
15 KiB
Text
320 lines
15 KiB
Text
// Generated by rexglue
|
|
// DO NOT EDIT - this file is auto-generated
|
|
#pragma once
|
|
|
|
//=============================================================================
|
|
// Project Configuration
|
|
//=============================================================================
|
|
|
|
#define REX_CONFIG_H_INCLUDED
|
|
|
|
{% if config_flags.skip_lr %}#define REX_CONFIG_SKIP_LR
|
|
{% endif %}{% if config_flags.ctr_as_local %}#define REX_CONFIG_CTR_AS_LOCAL
|
|
{% endif %}{% if config_flags.xer_as_local %}#define REX_CONFIG_XER_AS_LOCAL
|
|
{% endif %}{% if config_flags.reserved_as_local %}#define REX_CONFIG_RESERVED_AS_LOCAL
|
|
{% endif %}{% if config_flags.skip_msr %}#define REX_CONFIG_SKIP_MSR
|
|
{% endif %}{% if config_flags.cr_as_local %}#define REX_CONFIG_CR_AS_LOCAL
|
|
{% endif %}{% if config_flags.non_argument_as_local %}#define REX_CONFIG_NON_ARGUMENT_AS_LOCAL
|
|
{% endif %}{% if config_flags.non_volatile_as_local %}#define REX_CONFIG_NON_VOLATILE_AS_LOCAL
|
|
{% endif %}
|
|
#define REX_IMAGE_BASE {{ image_base }}ull
|
|
#define REX_IMAGE_SIZE {{ image_size }}ull
|
|
#define REX_CODE_BASE {{ code_base }}ull
|
|
#define REX_CODE_SIZE {{ code_size }}ull
|
|
#define REX_THUNK_RESERVE_SIZE {{ thunk_reserve_size }}ull
|
|
|
|
#define REXCRT_HEAP {% if rexcrt_heap %}1{% else %}0{% endif %}
|
|
|
|
//=============================================================================
|
|
// SDK Headers
|
|
//=============================================================================
|
|
|
|
#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>
|
|
|
|
//=============================================================================
|
|
// Image Info
|
|
//=============================================================================
|
|
|
|
extern const rex::PPCImageInfo PPCImageConfig;
|
|
extern PPCFuncMapping PPCFuncMappings[];
|
|
|
|
//=============================================================================
|
|
// Function Definition Macros
|
|
//=============================================================================
|
|
|
|
#define DECLARE_REX_FUNC(name) \
|
|
REX_EXTERN(name); \
|
|
REX_EXTERN(__imp__##name)
|
|
|
|
#define DEFINE_REX_FUNC(name) \
|
|
__attribute__((alias("__imp__" #name))) REX_WEAK_FUNC(name); \
|
|
REX_EXTERN(__imp__##name)
|
|
|
|
//=============================================================================
|
|
// Function Prologue
|
|
//=============================================================================
|
|
|
|
#if defined(REXGLUE_PROFILE_GUEST_FUNCTIONS) && defined(REXGLUE_ENABLE_PROFILING)
|
|
#include <tracy/Tracy.hpp>
|
|
#if defined(__clang__)
|
|
#define REX_FUNC_PROLOGUE() \
|
|
__builtin_assume(((size_t)base & 0x1F) == 0); \
|
|
ZoneNamedN(___tracy_guest_zone, __func__, true)
|
|
#elif defined(__GNUC__)
|
|
#define REX_FUNC_PROLOGUE() \
|
|
do { \
|
|
if (((size_t)base & 0x1F) != 0) \
|
|
__builtin_unreachable(); \
|
|
} while (0); \
|
|
ZoneNamedN(___tracy_guest_zone, __func__, true)
|
|
#else
|
|
#define REX_FUNC_PROLOGUE() ZoneNamedN(___tracy_guest_zone, __func__, true)
|
|
#endif
|
|
#else
|
|
#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
|
|
|
|
//=============================================================================
|
|
// Memory Access
|
|
//=============================================================================
|
|
|
|
#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
|
|
|
|
//=============================================================================
|
|
// MMIO
|
|
//=============================================================================
|
|
|
|
#define REX_IS_MMIO_ADDR(addr) ((addr) >= 0x7F000000u && (addr) < 0x80000000u)
|
|
|
|
#define REX_MM_STORE_U8(addr, val) \
|
|
do { \
|
|
u32 _mmio_addr = (addr); \
|
|
if (REX_IS_MMIO_ADDR(_mmio_addr)) { \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckStore(_mmio_addr, \
|
|
static_cast<u32>(val)); \
|
|
} else { \
|
|
*(volatile u8*)(base + _mmio_addr + REX_PHYS_HOST_OFFSET(_mmio_addr)) = (val); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define REX_MM_STORE_U16(addr, val) \
|
|
do { \
|
|
u32 _mmio_addr = (addr); \
|
|
if (REX_IS_MMIO_ADDR(_mmio_addr)) { \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckStore(_mmio_addr, \
|
|
static_cast<u32>(val)); \
|
|
} else { \
|
|
*(volatile u16*)(base + _mmio_addr + REX_PHYS_HOST_OFFSET(_mmio_addr)) = \
|
|
__builtin_bswap16(val); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define REX_MM_STORE_U32(addr, val) \
|
|
do { \
|
|
u32 _mmio_addr = (addr); \
|
|
if (REX_IS_MMIO_ADDR(_mmio_addr)) { \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckStore(_mmio_addr, \
|
|
static_cast<u32>(val)); \
|
|
} else { \
|
|
*(volatile u32*)(base + _mmio_addr + REX_PHYS_HOST_OFFSET(_mmio_addr)) = \
|
|
__builtin_bswap32(val); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define REX_MM_STORE_U64(addr, val) \
|
|
do { \
|
|
u32 _mmio_addr = (addr); \
|
|
if (REX_IS_MMIO_ADDR(_mmio_addr)) { \
|
|
u64 _v64 = static_cast<u64>(val); \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckStore(_mmio_addr, \
|
|
static_cast<u32>(_v64 >> 32)); \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckStore(_mmio_addr + 4, \
|
|
static_cast<u32>(_v64)); \
|
|
} else { \
|
|
*(volatile u64*)(base + _mmio_addr + REX_PHYS_HOST_OFFSET(_mmio_addr)) = \
|
|
__builtin_bswap64(val); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define REX_MM_LOAD_U8(addr) \
|
|
(REX_IS_MMIO_ADDR(addr) ? ({ \
|
|
u32 _v; \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckLoad(addr, &_v); \
|
|
static_cast<u8>(_v); \
|
|
}) \
|
|
: *(volatile u8*)(base + (addr) + REX_PHYS_HOST_OFFSET(addr)))
|
|
|
|
#define REX_MM_LOAD_U16(addr) \
|
|
(REX_IS_MMIO_ADDR(addr) \
|
|
? ({ \
|
|
u32 _v; \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckLoad(addr, &_v); \
|
|
static_cast<u16>(_v); \
|
|
}) \
|
|
: __builtin_bswap16(*(volatile u16*)(base + (addr) + REX_PHYS_HOST_OFFSET(addr))))
|
|
|
|
#define REX_MM_LOAD_U32(addr) \
|
|
(REX_IS_MMIO_ADDR(addr) \
|
|
? ({ \
|
|
u32 _v; \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckLoad(addr, &_v); \
|
|
_v; \
|
|
}) \
|
|
: __builtin_bswap32(*(volatile u32*)(base + (addr) + REX_PHYS_HOST_OFFSET(addr))))
|
|
|
|
#define REX_MM_LOAD_U64(addr) \
|
|
(REX_IS_MMIO_ADDR(addr) \
|
|
? ({ \
|
|
u32 _hi, _lo; \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckLoad(addr, &_hi); \
|
|
rex::runtime::MMIOHandler::global_handler()->CheckLoad((addr) + 4, &_lo); \
|
|
(static_cast<u64>(_hi) << 32) | _lo; \
|
|
}) \
|
|
: __builtin_bswap64(*(volatile u64*)(base + (addr) + REX_PHYS_HOST_OFFSET(addr))))
|
|
|
|
//=============================================================================
|
|
// 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)
|
|
|
|
//=============================================================================
|
|
// Function Declarations
|
|
//=============================================================================
|
|
|
|
{% for fn in functions %}DECLARE_REX_FUNC({{ fn.name }});
|
|
{% endfor %}
|