forked from Ports/goldeneye-pc-port
Adds native arm64 macOS support on top of #88's Intel macOS layer. The one structural change arm64 needs is an address model. The port realises the N64's 32-bit address space *as host addresses* — DRAM at 0x70000000, cart at 0x10000000, plus ~1700 linker-absolute symbols — which is only sound while host addresses fit in 32 bits. Native arm64 cannot map below 4 GiB (__PAGEZERO; a shrunken pagezero is SIGKILLed at exec; PIE is mandatory; MAP_32BIT fails). So the whole space is relocated to a fixed host base, PORT_ADDR_BASE (16 TiB), with portN64ToHost / portHostToN64 / PORT_N64PTR as the conversion chokepoints. PORT_ADDR_BASE is 0 on every other platform, so all of this is an identity there. That relocation then surfaces a class of latent 64-bit bugs that are invisible at base 0 — any place the decomp round-trips a pointer through a 32-bit integer. The src/ changes here are that class: pointer-width/layout only, no behaviour change, N64 branch textually unchanged, each documented in docs/dev/findings.md (D298/M2). Also: Mach-O absolute-symbol generation at the same base, #pragma weak -> symbol equates, __x86_64__ -> PLATFORM_64BIT, Darwin crash handler/ucontext, shm_open DRAM, the AppKit main-thread guard, and the sidecar generation that was the real boot blocker. Verified: 21/21 solo levels boot, render and run crash-free.
106 lines
4.2 KiB
C
106 lines
4.2 KiB
C
#ifndef PORT_ADDR_H
|
|
#define PORT_ADDR_H
|
|
|
|
/*
|
|
* The N64 address-space window (M1 of the macOS port).
|
|
*
|
|
* The port realises the N64's 32-bit address space as a range of *host*
|
|
* addresses, because the game stores addresses in 32-bit fields and 32-bit
|
|
* arithmetic and expects `(u32)ptr` to round-trip:
|
|
*
|
|
* 0x00000000..0x0fffffff segmented DL addresses (never host memory)
|
|
* 0x10000000..0x1fffffff cart image + sidecars (CART_BASE)
|
|
* 0x40000000..0x6fffffff image-relative encoding (see below)
|
|
* 0x70000000..0x707fffff DRAM V1 ("virtual", s32-safe; all RAM symbols)
|
|
* 0x80000000..0x807fffff DRAM V2 (KSEG0 mirror of V1)
|
|
* 0xa0000000..0xbfffffff game-thread stacks
|
|
*
|
|
* On Windows/Linux that range starts at host address 0, so `(u32)ptr ==
|
|
* offset` and the pointer IS the N64 address. Native arm64 macOS cannot map
|
|
* below 4 GiB (__PAGEZONE; see docs/dev/MACOS-ARM64-PLAN.md §1), so there the
|
|
* whole range is shifted up by PORT_ADDR_BASE. Every game-visible 32-bit value
|
|
* is then IDENTICAL to the Windows build; only the u32->host-pointer direction
|
|
* adds the base. PORT_ADDR_BASE is 0 everywhere but macOS, so those builds are
|
|
* unchanged.
|
|
*
|
|
* Image-relative encoding: pointers into the executable image cannot be
|
|
* truncated into the window (the image is not inside it, and on arm64 it is
|
|
* PIE with a per-run slide). They are encoded as 0x40000000 + (ptr - image
|
|
* base). On Windows the image base is 0x140000000, so this equals the raw
|
|
* `(u32)ptr` truncation the D131 fix relied on; on macOS it makes the round
|
|
* trip explicit. Ported game code must use portN64ToHost/portHostToN64 (or
|
|
* PORT_N64PTR) rather than a bare cast wherever a 32-bit field carries a
|
|
* pointer that is not a plain N64 address.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include "platform.h"
|
|
|
|
#if defined(PLATFORM_MACOS) && defined(PLATFORM_ARM)
|
|
/* 16 TiB: reserved cleanly on Apple Silicon, far above libmalloc's regions and
|
|
* the mmap hint area, and well below fast3d_ptr_ok()'s 128 TiB bound. The
|
|
* committed port/src/*.darwin.s files are generated with the SAME base
|
|
* (scripts/gen_romassets.py --darwin --base 0x100000000000); portAddrInit()
|
|
* checks the two agree at startup. */
|
|
#define PORT_ADDR_BASE 0x100000000000ULL
|
|
#else
|
|
#define PORT_ADDR_BASE 0x0ULL
|
|
#endif
|
|
|
|
/* Size of the N64 address space. */
|
|
#define PORT_ADDR_WINDOW (1ULL << 32)
|
|
|
|
/* Bound on the executable image's size, for classifying a host pointer as
|
|
* image-relative. The binary is a few MB; 256 MiB is comfortably generous. */
|
|
#define PORT_IMAGE_MAX (256u << 20)
|
|
|
|
/* Set by portAddrInit(): the executable's load address, and whether the image
|
|
* lies outside the window (so a truncated pointer needs the 0x40000000
|
|
* encoding). False on Linux, true on Windows and macOS. */
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
extern uintptr_t g_portImageBase;
|
|
extern int g_portUseImageRel;
|
|
|
|
/* Reserve the window (macOS only) and record the image base. Must run before
|
|
* any of the port's fixed mappings and before large allocations. */
|
|
void portAddrInit(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
/* u32 N64/physical address -> live host pointer. */
|
|
static inline void *portN64ToHost(uint32_t a)
|
|
{
|
|
if (g_portUseImageRel && a >= 0x40000000u && a < 0x70000000u) {
|
|
return (void *)(g_portImageBase + (uintptr_t)(a - 0x40000000u));
|
|
}
|
|
return (void *)((uintptr_t)PORT_ADDR_BASE + (uintptr_t)a);
|
|
}
|
|
|
|
/* Host pointer -> u32, the inverse of portN64ToHost for in-window and image
|
|
* pointers. Out-of-range pointers fall back to raw truncation (the pre-M1
|
|
* behaviour); they should not be stored in 32-bit fields. */
|
|
static inline uint32_t portHostToN64(const void *p)
|
|
{
|
|
uintptr_t v = (uintptr_t)p;
|
|
if (v - (uintptr_t)PORT_ADDR_BASE < (uintptr_t)PORT_ADDR_WINDOW) {
|
|
return (uint32_t)(v - (uintptr_t)PORT_ADDR_BASE);
|
|
}
|
|
if (g_portUseImageRel && v >= g_portImageBase
|
|
&& v - g_portImageBase < (uintptr_t)PORT_IMAGE_MAX) {
|
|
return (uint32_t)(0x40000000u + (v - g_portImageBase));
|
|
}
|
|
return (uint32_t)v;
|
|
}
|
|
|
|
/* Cast-site helper for game code (the D3x ABI/layout class): reads a 32-bit
|
|
* address out of a field/expression and yields a typed pointer. Identity on
|
|
* Windows/Linux (PORT_ADDR_BASE == 0). */
|
|
#define PORT_N64PTR(T, expr) ((T *)portN64ToHost((uint32_t)(expr)))
|
|
|
|
#endif /* PORT_ADDR_H */
|