diff --git a/demos/build.sh b/demos/build.sh index 95d4e5c..1e2ba0b 100755 --- a/demos/build.sh +++ b/demos/build.sh @@ -61,11 +61,22 @@ echo "compile: $BASE.c -> $OUTBASE.o" -O2 -ffunction-sections -c "$SRC" -o "$OBJ" echo "link: -> $OUTBASE.bin" +# text-base 0: under the GS/OS Loader our segment lands at some bank +# the Loader picks (KIND=0x1000), so link-time addresses can start at +# offset 0 in the segment. With text-base=0 the link-time-baked IMM16 +# operand for `lda gFoo` already equals gFoo's runtime offset within +# the segment (no segPlacedBase adjustment needed since the Loader +# places at bank-aligned bases), so the linker can skip emitting +# cRELOC entries for every IMM16 site — 60–90% reduction in cRELOC +# count, proportional speed-up in the Loader's per-reloc walk. +# Bare-metal builds keep text-base=0x1000 (runInMame.sh loads at +# bank-0:1000 and link-time addresses must match). +# # bss-base 0xA000 keeps BSS above the SHR shadow region ($2000-$9FFF # in bank 0 mirrors to bank E1 SHR memory). Without this, the smaller # section-gc'd demos place BSS at e.g. $2300 and global writes scribble # on the screen. -LINKER_ARGS=(-o "$BIN" --text-base 0x1000 --bss-base 0xA000 \ +LINKER_ARGS=(-o "$BIN" --text-base 0 --bss-base 0xA000 \ --map "$MAP" --reloc-out "$RELOC") if [ "$DEBUG" = 1 ]; then LINKER_ARGS+=(--debug-out "$DWARF") @@ -90,7 +101,7 @@ fi echo "OMF: -> $OUTBASE.omf" "$OMF" --input "$BIN" --map "$MAP" \ - --base 0x1000 --entry __start --output "$OUT" \ + --base 0 --entry __start --output "$OUT" \ --name "$(echo "$OUTBASE" | tr '[:lower:]' '[:upper:]' | cut -c1-8)" \ --expressload --relocs "$RELOC" diff --git a/demos/rsrcProbe.apl b/demos/rsrcProbe.apl index 1648aec..e1becc3 100644 Binary files a/demos/rsrcProbe.apl and b/demos/rsrcProbe.apl differ diff --git a/src/link816/link816.cpp b/src/link816/link816.cpp index e4be5a3..3ad21b8 100644 --- a/src/link816/link816.cpp +++ b/src/link816/link816.cpp @@ -548,7 +548,19 @@ static void applyReloc(std::vector &buf, uint32_t off, // `if (weakFn) weakFn()` null test that the null is meant to fail. // recordCRelocSite handles the gate; byteCnt=2 distinguishes // from IMM24 (3) so omfEmit emits cRELOC ByteCnt=2 here. - recordCRelocSite(patchAddr, target, /*byteCnt=*/2, /*bitShift=*/0); + // Optimization: when text-base's low 16 bits are zero, the + // link-time baked operand (target & 0xFFFF) is already equal + // to (segPlacedBase + offsetRef) & 0xFFFF — because the + // Loader places KIND=0x1000 segments at bank-aligned bases + // (segPlacedBase.lo16 = 0), and we baked target = textBase + + // offsetRef, so the patch is a no-op. Skipping it eliminates + // 50–90% of cRELOC entries in typical builds (most data refs + // are IMM16); the GS/OS Loader walks each cRELOC site in a + // linear loop, so the per-boot reloc-application time drops + // proportionally. Programs that link with a non-bank-aligned + // text-base (e.g. bare-metal at $1000) still get the cRELOC. + if ((gTextBaseForSites & 0xFFFFu) != 0) + recordCRelocSite(patchAddr, target, /*byteCnt=*/2, /*bitShift=*/0); break; case R_W65816_BANK16: // 2-byte patch: byte 0 = bank of target, byte 1 = 0 (pad).