addrspace: mask the shift count taken from the tagged pointer (#2574)

readConst, writeConst, readt and writet shift the 32-bit guest address by
the whole tagged memInfo_ptr value (pointer | shift). Shifting a u32 by 32
or more is undefined, and GCC 16 targeting 32-bit ARM uses that: once the
pointer is known to be non-null (iirf > 31) it drops the offset, so
read32/write16 compile to a plain load/store at the region base. Every
memory access then hits offset 0, the BIOS trampoline copied to 8C0000E0
is lost and the console never boots (black screen, no sound).

Mask the shift with HANDLER_MAX, which is what x86 does in hardware and
what the code intends. No change in behaviour where the old code worked.

Co-authored-by: hitenmandalia <[email protected]>
[ci skip]
This commit is contained in:
jutler authored and GitHub committed 2026-10-21 12:07:14 +02:00
1 parent 38a3d83c77
commit 128e6ce29c
1 file changed
+20 -8
+20 -8
View File
@@ -61,8 +61,11 @@ void *readConst(u32 addr, bool& ismem, u32 sz)
else
{
ismem = true;
addr <<= iirf;
addr >>= iirf;
// Only the low bits of iirf hold the mask shift. Shifting a 32-bit value by the
// whole tagged pointer is undefined, and GCC 16 on 32-bit ARM drops the
// address entirely (every access hits offset 0).
addr <<= (iirf & HANDLER_MAX);
addr >>= (iirf & HANDLER_MAX);
return &(((u8 *)ptr)[addr]);
}
@@ -94,8 +97,11 @@ void *writeConst(u32 addr, bool& ismem, u32 sz)
else
{
ismem = true;
addr <<= iirf;
addr >>= iirf;
// Only the low bits of iirf hold the mask shift. Shifting a 32-bit value by the
// whole tagged pointer is undefined, and GCC 16 on 32-bit ARM drops the
// address entirely (every access hits offset 0).
addr <<= (iirf & HANDLER_MAX);
addr >>= (iirf & HANDLER_MAX);
return &(((u8 *)ptr)[addr]);
}
@@ -112,8 +118,11 @@ T DYNACALL readt(u32 addr)
if (likely(ptr != nullptr))
{
addr <<= iirf;
addr >>= iirf;
// Only the low bits of iirf hold the mask shift. Shifting a 32-bit value by the
// whole tagged pointer is undefined, and GCC 16 on 32-bit ARM drops the
// address entirely (every access hits offset 0).
addr <<= (iirf & HANDLER_MAX);
addr >>= (iirf & HANDLER_MAX);
return *(T *)&((u8 *)ptr)[addr];
}
@@ -156,8 +165,11 @@ void DYNACALL writet(u32 addr, T data)
if (likely(ptr != nullptr))
{
addr <<= iirf;
addr >>= iirf;
// Only the low bits of iirf hold the mask shift. Shifting a 32-bit value by the
// whole tagged pointer is undefined, and GCC 16 on 32-bit ARM drops the
// address entirely (every access hits offset 0).
addr <<= (iirf & HANDLER_MAX);
addr >>= (iirf & HANDLER_MAX);
*(T *)&((u8 *)ptr)[addr] = data;
}