# Apple IIgs build rules (llvm-mos w65816: clang + llvm-mc + link816).
#
# toolchains/iigs/clang-build.sh drives compile (clang) / assemble
# (llvm-mc) / link (link816) against the prebuilt w65816 runtime.
# Hand-rolled asm lives in src/iigs/*.s (llvm-mc GAS syntax);
# bank placement is handled by -ffunction-sections + link816
# dead-stripping.
#
# STATUS: the launchable-binary pipeline (multi-segment OMF emission +
# disk packaging + GS/OS launch) is still being brought up. `iigs-lib`
# compile-checks every library + example translation unit, and
# `iigs-clang-smoke` validates the full clang -> llvm-mc -> link816 ->
# MAME path end to end. Per-example binary targets build via
# clang-build.sh; large examples need the multi-segment OMF wiring still
# in progress.

include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk

PLATFORM := iigs
BUILD    := $(REPO_DIR)/build/$(PLATFORM)
BINDIR   := $(BUILD)/bin
OBJDIR   := $(BUILD)/obj

CLANG    := $(LLVM816_ROOT)/tools/llvm-mos-build/bin/clang
LLVM_MC  := $(LLVM816_ROOT)/tools/llvm-mos-build/bin/llvm-mc
AR       := $(LLVM816_ROOT)/tools/llvm-mos-sdk/bin/llvm-ar
CCWRAP   := $(LLVM816_ROOT)/scripts/ccRegallocFallback.sh
RT_INC   := $(LLVM816_ROOT)/runtime/include
CFLAGS   := --target=w65816 -O2 -ffreestanding -ffunction-sections
INCLUDES := -I$(RT_INC) -I$(INCLUDE_DIR) -I$(INCLUDE_DIR)/joey -I$(SRC_CORE) -I$(REPO_DIR)/src/codegen
LIBDIR   := $(BUILD)/lib
LIB      := $(LIBDIR)/libjoey.a

PORT_C_SRCS   := $(wildcard $(SRC_DIR)/iigs/*.c)
# Hand-rolled asm, migrated to llvm-mc GAS .s.
PORT_ASM_SRCS := $(wildcard $(SRC_DIR)/iigs/*.s)

# IIgs uses NTPstreamsound for SFX, not the libxmp+overlay combo that
# DOS and ST share, so src/core/audioSfxMix.c is unused here.
CORE_C_SRCS_IIGS := $(filter-out %/audioSfxMix.c, $(CORE_C_SRCS))

# Sprite codegen: the 65816 emitter (spriteEmitIigs.c) now lives in src/iigs/
# and is picked up by PORT_C_SRCS; only the CPU-independent compile dispatch
# remains in src/codegen.
CODEGEN_SRCS := $(REPO_DIR)/src/codegen/spriteCompile.c $(REPO_DIR)/src/codegen/spriteStage.c

LIB_C_SRCS := $(CORE_C_SRCS_IIGS) $(GENERIC_C_SRCS) $(PORT_C_SRCS) $(CODEGEN_SRCS)
LIB_SRCS   := $(LIB_C_SRCS) $(PORT_ASM_SRCS)

# Library header set for archive rebuild triggering (coarse on purpose: any
# header change forces a full lib recompile -- a needless rebuild is safe, a
# missed one is not).
IIGS_LIB_HDRS := $(wildcard $(INCLUDE_DIR)/joey/*.h) $(wildcard $(SRC_CORE)/*.h) $(wildcard $(REPO_DIR)/src/codegen/*.h) $(wildcard $(SRC_DIR)/iigs/*.h) $(wildcard $(REPO_DIR)/src/m68k/*.h)

# Example sources (single-TU unless noted).
PATTERN_SRC := $(EXAMPLES)/pattern/pattern.c
DRAW_SRC    := $(EXAMPLES)/draw/draw.c
KEYS_SRC    := $(EXAMPLES)/keys/keys.c
SERIAL_SRC  := $(EXAMPLES)/serial/serial.c
SERTEST_SRC := $(EXAMPLES)/sertest/sertest.c
SAVE_SRC    := $(EXAMPLES)/save/save.c
JOY_SRC     := $(EXAMPLES)/joy/joy.c
SPRITE_SRC  := $(EXAMPLES)/sprite/sprite.c
UBER_SRC    := $(EXAMPLES)/uber/uber.c
ADV_SRC     := $(EXAMPLES)/adventure/adventure.c
ADV2_SRC    := $(EXAMPLES)/adventure2/adventure2.c
AGI_SRCS    := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
STAXI_SRCS  := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stEngine.c $(EXAMPLES)/spacetaxi/stPassenger.c $(EXAMPLES)/spacetaxi/stHud.c $(EXAMPLES)/spacetaxi/stAudio.c
AUDIO_SRC   := $(EXAMPLES)/audio/audio.c

# NinjaTrackerPlus replayer: Merlin32-assemble ninjatrackerplus.s to a 34KB
# binary, then bake it into a dead-strippable GAS data object (gNtpPlayerBytes
# / _len, which audio_full.c externs). bin-to-s.sh is the clang-era replacement
# for the deleted bin-to-asm.sh. Only the AUDIO binary pulls it in; link816
# dead-strips it everywhere else.
NTP_SRC     := $(REPO_DIR)/toolchains/iigs/ntp/ninjatrackerplus.s
NTP_BIN     := $(BUILD)/audio/ntpplayer.bin
NTP_ASM     := $(BUILD)/audio/ntpdata.s
IIGS_MERLIN := $(REPO_DIR)/toolchains/iigs/merlin32/bin/merlin32

.PHONY: all iigs iigs-lib iigs-clang-smoke iigs-examples iigs-disk iigs-verify iigs-verify-all iigs-verify-save iigs-verify-shrtail clean-iigs clean

# Default: compile-check the library + run the end-to-end smoke test.
all iigs: iigs-lib iigs-clang-smoke

# Per-example launchable binaries. STAXI and AUDIO are INCLUDED: the
# old exclusion comment ("blocked on deeper toolchain features") went
# stale -- STAXI has linked and run since 2026-07-08 (toolchain #77
# fix), and AUDIO's 34KB-rodata blocker was resolved by loading the
# NTP replayer from disk (see the $(BINDIR)/AUDIO rule). Leaving them
# out made `make iigs` silently test 8-day-old STAXI binaries against
# new assets (the 2026-07-20 gear-out/no-flame reports).
iigs-examples: $(BINDIR)/PATTERN $(BINDIR)/DRAW $(BINDIR)/KEYS $(BINDIR)/SERIAL $(BINDIR)/SERTEST $(BINDIR)/SAVE $(BINDIR)/JOY \
               $(BINDIR)/SPRITE $(BINDIR)/UBER $(BINDIR)/ADV $(BINDIR)/ADV2 \
               $(BINDIR)/AGI $(BINDIR)/STAXI $(BINDIR)/AUDIO

# Build the JOEYLIB data disk (build/iigs/bin/joey.2mg) that run-iigs-mame.sh /
# run-iigs.sh mount on flop4. Packs the launchable examples as ProDOS S16 apps
# (an 800KB floppy can't hold every example; see make-iigs-disk.sh).
# NTP_BIN rides along so jlpAudioInit's runtime load of "ntpplayer" finds it
# on the volume -- without it every example logs "audioInit failed" (Phase 1
# IIgs stabilization item c: the audio code is wired; the file was missing).
# Pre-compiled sprite banks for Space Taxi. Unlike the other ports, the IIgs
# asset tree (examples/spacetaxi/generated/iigs) is produced out-of-band, so
# we bake the .spc straight from the committed .spr with the host spritebake
# tool; make-iigs-disk.sh then find(1)s the whole generated/iigs tree, so the
# .spc rides onto the disk automatically once it exists.
STAXI_IIGS_SPR   := $(wildcard $(EXAMPLES)/spacetaxi/generated/iigs/sprites/*.spr)
STAXI_IIGS_SPC   := $(STAXI_IIGS_SPR:.spr=.spc)
SPRITEBAKE_IIGS  := $(REPO_DIR)/build/tools/spritebake-iigs

$(SPRITEBAKE_IIGS):
	$(MAKE) -f $(REPO_DIR)/make/tools.mk spritebake-iigs

$(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spc: $(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spr $(SPRITEBAKE_IIGS)
	$(SPRITEBAKE_IIGS) $< $@

iigs-disk: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC)
	BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" $(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey.2mg

# Headless visual-verification gate: boot GS/OS under MAME, launch the DRAW
# example off joey.2mg, and assert the SHR framebuffer was actually rendered
# (many distinct colors -> pixels/lines/circles/tiles/flood all drew). Pass
# VERIFY_EXAMPLE=name to check a different example.
iigs-verify: iigs-disk
	$(REPO_DIR)/scripts/verify-iigs.sh $(or $(VERIFY_EXAMPLE),draw)

# Boot-verify every launchable example on joey.2mg (per-example thresholds).
iigs-verify-all: iigs-disk
	$(REPO_DIR)/scripts/verify-iigs-all.sh

# Gate the 712 display-state bytes ABOVE the pixel block: the SCB array
# ($E1:9D00-$9DC7) and all 16 palettes ($E1:9E00-$9FFF). Nothing else in the
# tree can see them -- the golden hash covers the STAGE, not $E1, and P9's
# pixel compare stops at $9CFF -- so a wrong palette is invisible to every
# other gate and shows only as wrong colors on a real screen. That blind spot
# is why a phantom "PEI-slam corrupts palettes 1-15" note survived five weeks.
#
# UBER's shrTailUpload / shrTailSlam rows live behind #ifdef UBER_PROBE, which
# no normal build defines, so this target builds its own UBER (never the
# goldened one) into a scratch dir, packs a single-app disk, and benches that.
# -DUBER_FRAMES=1 shrinks the timing window: a -DUBER_PROBE run at the golden
# 16 frames does not fit bench-iigs.sh's MAME cap. The goldened UBER binary,
# joey.2mg and joeylog.txt are all left untouched.
IIGS_PROBE_DIR := $(BUILD)/probe

iigs-verify-shrtail: $(LIB) $(NTP_BIN)
	@mkdir -p $(IIGS_PROBE_DIR)/bin
	$(IIGS_CLANG_BUILD) -DUBER_PROBE -DUBER_FRAMES=1 $(INCLUDES) -o $(IIGS_PROBE_DIR)/bin/UBER $(UBER_SRC) $(LIB)
	BINDIR="$(IIGS_PROBE_DIR)/bin" NTP_BIN="$(NTP_BIN)" JOEY_DISK_EXAMPLES="UBER" \
	    $(REPO_DIR)/scripts/make-iigs-disk.sh $(IIGS_PROBE_DIR)/uberprobe.2mg
	BENCH_DATA_DISK="$(IIGS_PROBE_DIR)/uberprobe.2mg" BENCH_OUT_LOG="$(IIGS_PROBE_DIR)/probelog.txt" \
	    BENCH_MAX_FRAMES="$(or $(PROBE_MAX_FRAMES),45000)" $(REPO_DIR)/scripts/bench-iigs.sh
	@grep -E 'shrParity|shrTailUpload|shrTailSlam' $(IIGS_PROBE_DIR)/probelog.txt || \
	    { echo "iigs-verify-shrtail: FAIL - no probe rows captured" >&2; exit 1; }
	@grep -qE 'shrTail(Upload|Slam) FAIL' $(IIGS_PROBE_DIR)/probelog.txt && \
	    { echo "iigs-verify-shrtail: FAIL - \$$E1 SCB/palette block mismatched the stage" >&2; exit 1; } || true
	@echo "iigs-verify-shrtail: PASS"

# Runtime gate for the save HAL's GS/OS calls (Create + GetDevNumber/DInfo/
# Volume): boots SAVE under MAME and checks the save-OK lamp + disk-free bar.
iigs-verify-save: $(BINDIR)/SAVE $(BINDIR)/DRAW
	$(REPO_DIR)/scripts/verify-iigs-save.sh

$(NTP_BIN): $(NTP_SRC) $(IIGS_MERLIN)
	@mkdir -p $(BUILD)/audio
	@cp $(NTP_SRC) $(BUILD)/audio/ninjatrackerplus.s
	cd $(BUILD)/audio && $(IIGS_MERLIN) . ninjatrackerplus.s
	mv $(BUILD)/audio/ntpplayer $@

$(NTP_ASM): $(NTP_BIN) $(REPO_DIR)/toolchains/iigs/bin-to-s.sh
	$(REPO_DIR)/toolchains/iigs/bin-to-s.sh $(NTP_BIN) $@ gNtpPlayerBytes gNtpPlayerBytes_len

# Compile-check every library and example translation unit with clang,
# and assemble every .s with llvm-mc. This is the canonical "does the
# IIgs source still build" gate while the launchable pipeline is wired.
# The real static library: compile every library TU (through the regalloc
# fallback, matching clang-build.sh) + assemble every .s, then archive them
# into libjoey.a. link816 reads the archive natively and --gc-sections selects
# what each example references, so examples link $(LIB) exactly like the other
# ports. `iigs-lib' is kept as the canonical build/compile-check entry point.
$(LIB): $(LIB_SRCS) $(IIGS_LIB_HDRS)
	@mkdir -p $(OBJDIR) $(LIBDIR)
	@set -e; objs=""; \
	for c in $(LIB_C_SRCS); do \
	    o=$(OBJDIR)/$$(echo $$c | sed 's#[/.]#_#g').o; \
	    echo "  CC  $$c"; \
	    $(CCWRAP) $(CLANG) $(CFLAGS) $(INCLUDES) -c $$c -o $$o; \
	    objs="$$objs $$o"; \
	done; \
	for s in $(PORT_ASM_SRCS); do \
	    o=$(OBJDIR)/$$(basename $$s .s).o; \
	    echo "  AS  $$s"; \
	    $(LLVM_MC) -arch=w65816 -filetype=obj $$s -o $$o; \
	    objs="$$objs $$o"; \
	done; \
	rm -f $@; \
	echo "  AR  $@"; \
	$(AR) rcs $@ $$objs

iigs-lib: $(LIB)

# End-to-end smoke test: clang -> llvm-mc -> link816 -> MAME, validating
# the ported asm computes correctly on emulated hardware.
iigs-clang-smoke:
	$(REPO_DIR)/toolchains/iigs/clang-smoke.sh

# Per-example binaries via clang-build.sh. clang-build.sh mirrors the
# old driver's CLI (-I / -o / -M; -s and -b are accepted no-ops). These
# link single-segment today; examples larger than one bank need the
# multi-segment OMF wiring still in progress.
DEP_DIR := $(BUILD)/dep

$(BINDIR)/PATTERN: $(PATTERN_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/PATTERN.d $(INCLUDES) -o $@ $(PATTERN_SRC) $(LIB)

$(BINDIR)/DRAW: $(DRAW_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/DRAW.d $(INCLUDES) -o $@ $(DRAW_SRC) $(LIB)

$(BINDIR)/KEYS: $(KEYS_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/KEYS.d $(INCLUDES) -o $@ $(KEYS_SRC) $(LIB)

$(BINDIR)/SERIAL: $(SERIAL_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/SERIAL.d $(INCLUDES) -o $@ $(SERIAL_SRC) $(LIB)

$(BINDIR)/SERTEST: $(SERTEST_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/SERTEST.d $(INCLUDES) -o $@ $(SERTEST_SRC) $(LIB)

$(BINDIR)/SAVE: $(SAVE_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/SAVE.d $(INCLUDES) -o $@ $(SAVE_SRC) $(LIB)

$(BINDIR)/JOY: $(JOY_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/JOY.d $(INCLUDES) -o $@ $(JOY_SRC) $(LIB)

$(BINDIR)/SPRITE: $(SPRITE_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/SPRITE.d $(INCLUDES) -o $@ $(SPRITE_SRC) $(LIB)

$(BINDIR)/UBER: $(UBER_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/UBER.d $(INCLUDES) -o $@ $(UBER_SRC) $(LIB)

$(BINDIR)/ADV: $(ADV_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/ADV.d $(INCLUDES) -o $@ $(ADV_SRC) $(LIB)

$(BINDIR)/ADV2: $(ADV2_SRC) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/ADV2.d $(INCLUDES) -o $@ $(ADV2_SRC) $(LIB)

$(BINDIR)/AGI: $(AGI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/AGI.d $(INCLUDES) -I$(EXAMPLES)/agi -o $@ $(AGI_SRCS) $(LIB)

$(BINDIR)/STAXI: $(STAXI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/STAXI.d $(INCLUDES) -I$(EXAMPLES)/spacetaxi -o $@ $(STAXI_SRCS) $(LIB)

# AUDIO loads the NTP replayer from disk at runtime: audio_full.c reads
# ntpplayer.bin via NTP_PATH instead of baking the 34KB replayer as rodata
# (which overflowed bank 0, and clang's IMM16+$BE near form can't reach a far
# rodata segment anyway). The binary no longer links $(NTP_ASM); $(NTP_BIN) is
# still built so it can be shipped on the application disk next to the binary.
$(BINDIR)/AUDIO: $(AUDIO_SRC) $(LIB) $(NTP_BIN) $(IIGS_CLANG_BUILD)
	@mkdir -p $(dir $@) $(DEP_DIR)
	$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/AUDIO.d $(INCLUDES) -o $@ $(AUDIO_SRC) $(LIB)

clean-iigs:
	rm -rf $(BUILD)

# `clean` as an alias for `clean-iigs`. Without it a bare
# `make -f make/iigs.mk clean` FAILS -- and with stderr discarded that
# looks like success while leaving stale objects the next build links.
clean: clean-iigs

-include $(wildcard $(DEP_DIR)/*.d)
