# Amiga (Bebbo m68k-amigaos-gcc) build rules. include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk $(call requireTool,$(AMIGA_CC),toolchain:amiga) $(call requireTool,$(AMIGA_AS),toolchain:amiga) $(call requireTool,$(AMIGA_PTPLAYER)/ptplayer.asm,toolchain:amiga) PLATFORM := amiga BUILD := $(REPO_DIR)/build/$(PLATFORM) LIBDIR := $(BUILD)/lib BINDIR := $(BUILD)/bin # PTPlayer is installed by ./install.sh (AMIGA_PTPLAYER); we # reference its ptplayer.asm + header from there rather than copying # them into the source tree, so the installer can refresh / version it # independently. -I on $(SRC_DIR)/amiga lets ptplayer.h resolve # from the machine-local shim alongside our code. PTPLAYER_DIR := $(AMIGA_PTPLAYER) CFLAGS := $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer -noixemul -D__OSCOMPAT -I$(SRC_DIR)/amiga -I$(SRC_68K) -I$(PTPLAYER_DIR) -MMD -MP # OSCOMPAT=1 selects PTPlayer's audio.device-friendly variant (uses # CIA-B + audio.device interrupts via the OS rather than taking over # Paula directly), matching the way our HAL cooperates with Intuition. # vasm is only invoked for ptplayer.asm here -- Frank Wille's source # is written in Devpac/vasm syntax and there is no maintained GAS- # syntax port. JoeyLib's own .s files are GAS syntax and assembled by # the bundled m68k-amigaos-as via the gcc driver. # -Fhunk matches Bebbo gcc-amigaos's AmigaOS HUNK object format. ELF # objects from vasm cannot be linked into a HUNK libjoey.a (the ar # "file format not recognized" failure mode is silent at archive time # and surfaces as undefined references at every binary's link step). PTPLAYER_ASFLAGS := -Fhunk -m68000 -quiet -DOSCOMPAT=1 # --allow-multiple-definition lets our user-space tzset stub # (src/amiga/libinit.c) win over libnix's version in # __gmtoffset.o. libnix's tzset dereferences a possibly-NULL # LocaleBase; our no-op skips the deref. LDFLAGS := -noixemul -Wl,--allow-multiple-definition PORT_C_SRCS := $(wildcard $(SRC_DIR)/amiga/*.c) PORT_S_SRCS := $(wildcard $(SRC_DIR)/amiga/*.s) SHARED_S := $(wildcard $(SRC_68K)/*.s) # Amiga uses PTPlayer's mt_playfx for SFX, not the libxmp+overlay # combo that DOS and ST share, so the shared SFX overlay mixer in # src/core/audioSfxMix.c is unused here. Filter it out to keep dead # code out of every Amiga binary. CORE_C_SRCS_AMIGA := $(filter-out %/audioSfxMix.c, $(CORE_C_SRCS)) # Sprite codegen: the Amiga-planar emitter (spriteEmitPlanar68k.c) lives in # src/m68k/ and builds into obj/68k/; only the CPU-independent dispatch stays # in src/codegen. CODEGEN_DIR := $(REPO_DIR)/src/codegen LIB_OBJS := \ $(patsubst $(SRC_CORE)/%.c,$(BUILD)/obj/core/%.o,$(CORE_C_SRCS_AMIGA)) \ $(patsubst $(SRC_DIR)/generic/%.c,$(BUILD)/obj/generic/%.o,$(GENERIC_C_SRCS)) \ $(patsubst $(SRC_DIR)/amiga/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \ $(patsubst $(SRC_DIR)/amiga/%.s,$(BUILD)/obj/port/%.o,$(PORT_S_SRCS)) \ $(patsubst $(SRC_68K)/%.s,$(BUILD)/obj/68k/%.o,$(SHARED_S)) \ $(BUILD)/obj/port/ptplayer.o \ $(BUILD)/obj/68k/spriteEmitPlanar68k.o \ $(BUILD)/obj/codegen/spriteCompile.o \ $(BUILD)/obj/codegen/spriteStage.o LIB := $(LIBDIR)/libjoey.a HELLO_SRC := $(EXAMPLES)/hello/hello.c HELLO_BIN := $(BINDIR)/Hello PATTERN_SRC := $(EXAMPLES)/pattern/pattern.c PATTERN_BIN := $(BINDIR)/Pattern DRAW_SRC := $(EXAMPLES)/draw/draw.c DRAW_BIN := $(BINDIR)/Draw KEYS_SRC := $(EXAMPLES)/keys/keys.c KEYS_BIN := $(BINDIR)/Keys SERIAL_SRC := $(EXAMPLES)/serial/serial.c SERIAL_BIN := $(BINDIR)/Serial SERTEST_SRC := $(EXAMPLES)/sertest/sertest.c SERTEST_BIN := $(BINDIR)/SerTest SAVE_SRC := $(EXAMPLES)/save/save.c SAVE_BIN := $(BINDIR)/Save TILEBANK_SRC := $(EXAMPLES)/tilebank/tilebank.c TILEBANK_BIN := $(BINDIR)/TileBank JOY_SRC := $(EXAMPLES)/joy/joy.c JOY_BIN := $(BINDIR)/Joy SPRITE_SRC := $(EXAMPLES)/sprite/sprite.c SPRITE_BIN := $(BINDIR)/Sprite AUDIO_SRC := $(EXAMPLES)/audio/audio.c AUDIO_BIN := $(BINDIR)/Audio UBER_SRC := $(EXAMPLES)/uber/uber.c UBER_BIN := $(BINDIR)/Uber ADV_SRC := $(EXAMPLES)/adventure/adventure.c ADV_BIN := $(BINDIR)/Adv ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c ADV2_BIN := $(BINDIR)/Adv2 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 AGI_BIN := $(BINDIR)/Agi STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c STAXI_INSTALL_DIR := $(BINDIR)/Taxi STAXI_BIN := $(STAXI_INSTALL_DIR)/Taxi # Space Taxi asset staging: same pattern as the DOS port -- generated # artifacts under examples/spacetaxi/generated (nukeable), the runtime # tree in DATA/ beside the binary. Mirrors the dos.mk wiring exactly. STAXI_GEN_DIR := $(EXAMPLES)/spacetaxi/generated/amiga STAXI_RUN_DIR := $(STAXI_INSTALL_DIR)/DATA # Level data is extracted from the C64 ROM (see dos.mk for the long # explanation): 24 lettered levels A..X from scenes 0..23 plus the # title from scene 24, via examples/spacetaxi/assets/romToLevel.py. STAXI_ROM_LEVEL_GEN := $(foreach n,$(STAXI_LEVEL_NUMS),$(STAXI_GEN_DIR)/levels/level$(n).dat) \ $(STAXI_GEN_DIR)/levels/title.dat STAXI_LEVEL_GEN := $(STAXI_ROM_LEVEL_GEN) STAXI_LEVEL_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXI_LEVEL_GEN)) STAXI_ASSET_DSTS := $(STAXI_LEVEL_RUN) # Pre-compiled sprite banks (.spc): baked from the .spr by the host spritebake # tool for THIS target and staged in DATA/sprites, so the runtime loads # compiled routines with no startup JIT (jlSpriteBankLoadPrecompiled). SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-amiga # The C64-exact cel bank, generated by staxibake (from the shared cel table # stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so # STAXI loads its cels already compiled instead of JIT-compiling them at boot. STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake # Per-level hook-sprite banks: the cels stLevelCelList names for each # level that has hook sprites, baked from its level file and pre-compiled # for this target, so no cel is JIT-compiled mid-ride. Levels without # hook sprites have no bank; the game JIT-compiles at level entry when a # bank is missing. STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21 STAXILEVEL_SPR_GEN := $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN_DIR)/sprites/level$(n).spr) STAXILEVEL_SPC_GEN := $(STAXILEVEL_SPR_GEN:.spr=.spc) STAXILEVEL_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXILEVEL_SPC_GEN)) STAXI_ASSET_DSTS += $(STAXILEVEL_SPC_RUN) # Speech baked to sample bytes per passenger pitch (assets/genSpeechPcm.py): # expanding bits at play time is what keeps "HEY TAXI" from stalling the # ride; the C64 run-length source stays in stSpeechData.c as the input. STAXI_SPEECH_GEN := $(STAXI_GEN_DIR)/speech.bin STAXI_SPEECH_RUN := $(STAXI_RUN_DIR)/speech.bin STAXI_ASSET_DSTS += $(STAXI_SPEECH_RUN) STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc) STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN)) # Added HERE, not up with the other asset destinations: this variable is # defined below that point, and the := there expanded it to nothing, so # the compiled cel bank was never a prerequisite of the port and never # got copied into the build tree. Every port shipped whatever .spc was # staged the day the file was first created, and edits to stCels.c did # not reach the game (found 2026-09-23 when a new cel made the runtime # reject the bank as too short). STAXI_ASSET_DSTS += $(STAXICELS_SPC_RUN) # Game data lives under bin/DATA/, ready to be copied into the # scratch JOEYLIB hard-drive dir staged by joey-run. # audio.c fopens "DATA/test.mod" etc. relative to the boot volume. DATA_DIR := $(BINDIR)/DATA DATA_FILES := $(DATA_DIR)/test.mod $(DATA_DIR)/test.sfx # TileBank's DATA/tilebank: this port's bake of the tile-bank fixture, the # other targets' bakes it must refuse, and the expected pixels (common.mk). TILEBANK_DATA := $(DATA_DIR)/tilebank/own.tbk .PHONY: all amiga clean-amiga clean all amiga: $(LIB) $(HELLO_BIN) $(PATTERN_BIN) $(DRAW_BIN) $(KEYS_BIN) $(SERIAL_BIN) $(SERTEST_BIN) $(SAVE_BIN) $(TILEBANK_BIN) $(JOY_BIN) $(SPRITE_BIN) $(AUDIO_BIN) $(UBER_BIN) $(ADV_BIN) $(ADV2_BIN) $(AGI_BIN) $(STAXI_BIN) $(DATA_FILES) $(TILEBANK_DATA) $(call staxiData,$(STAXI_ASSET_DSTS)) $(BUILD)/obj/core/%.o: $(SRC_CORE)/%.c @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -c $< -o $@ $(BUILD)/obj/generic/%.o: $(SRC_DIR)/generic/%.c @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -c $< -o $@ $(BUILD)/obj/port/%.o: $(SRC_DIR)/amiga/%.c @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -c $< -o $@ # Hand-written 68k assembly: GAS syntax, fed through the gcc driver # so the bundled m68k-amigaos-as (binutils) does the work. $(BUILD)/obj/port/%.o: $(SRC_DIR)/amiga/%.s @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -c $< -o $@ # PTPlayer is third-party Devpac/vasm-syntax assembly with no # maintained GAS port; vasm is invoked only here. $(BUILD)/obj/port/ptplayer.o: $(PTPLAYER_DIR)/ptplayer.asm @mkdir -p $(dir $@) $(AMIGA_AS) $(PTPLAYER_ASFLAGS) $< -o $@ $(BUILD)/obj/codegen/%.o: $(CODEGEN_DIR)/%.c @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -I$(CODEGEN_DIR) -c $< -o $@ $(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -c $< -o $@ # 68k sprite emitters (spriteEmitPlanar68k.c) live in src/m68k/; # -I$(CODEGEN_DIR) lets them resolve spriteEmitter.h. $(BUILD)/obj/68k/%.o: $(SRC_68K)/%.c @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) -I$(CODEGEN_DIR) -c $< -o $@ $(LIB): $(LIB_OBJS) @mkdir -p $(dir $@) $(AMIGA_AR) rcs $@ $^ $(HELLO_BIN): $(HELLO_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(PATTERN_BIN): $(PATTERN_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(DRAW_BIN): $(DRAW_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(KEYS_BIN): $(KEYS_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(SERIAL_BIN): $(SERIAL_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(SERTEST_BIN): $(SERTEST_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(SAVE_BIN): $(SAVE_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(TILEBANK_BIN): $(TILEBANK_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(JOY_BIN): $(JOY_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(SPRITE_BIN): $(SPRITE_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(AUDIO_BIN): $(AUDIO_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(UBER_BIN): $(UBER_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(ADV_BIN): $(ADV_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(ADV2_BIN): $(ADV2_SRC) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS) $(AGI_BIN): $(AGI_SRCS) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $(AGI_SRCS) $(LIB) -o $@ $(LDFLAGS) $(STAXI_BIN): $(STAXI_SRCS) $(LIB) @mkdir -p $(dir $@) $(AMIGA_CC) $(CFLAGS) $(STAXI_SRCS) $(LIB) -o $@ $(LDFLAGS) # Space Taxi asset pipeline. .SECONDARY keeps the generated/ artifacts # from being auto-deleted after staging. .SECONDARY: $(STAXI_LEVEL_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN) $(STAXI_ROM_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN) @mkdir -p $(STAXI_GEN_DIR)/levels python3 $(ROMTOLEVEL_BIN) $(STAXI_RAW_DUMP) $(STAXI_GEN_DIR)/levels $(STAXI_GEN_DIR)/%.spc: $(STAXI_GEN_DIR)/%.spr $(SPRITEBAKE_BIN) @mkdir -p $(dir $@) $(SPRITEBAKE_BIN) $< $@ $(SPRITEBAKE_BIN): FORCE $(MAKE) -f $(REPO_DIR)/make/tools.mk spritebake-amiga $(STAXIBAKE_BIN): FORCE $(MAKE) -f $(REPO_DIR)/make/tools.mk staxibake # The cel bank is generated from the game's cel table (stCels.c). $(STAXICELS_SPR_GEN): $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stC64Data.c @mkdir -p $(dir $@) $(STAXIBAKE_BIN) $@ $(STAXI_RUN_DIR)/levels/%.dat: $(STAXI_GEN_DIR)/levels/%.dat @mkdir -p $(dir $@) cp $< $@ $(STAXI_RUN_DIR)/%.spc: $(STAXI_GEN_DIR)/%.spc @mkdir -p $(dir $@) cp $< $@ $(DATA_DIR)/test.mod: $(REPO_DIR)/assets/test.mod @mkdir -p $(DATA_DIR) cp $< $@ $(DATA_DIR)/test.sfx: $(REPO_DIR)/assets/test.sfx @mkdir -p $(DATA_DIR) cp $< $@ $(TILEBANK_DATA): $(TILEBANK_BAKE_DEPS) python3 $(TILEBANK_STAGE) $(PLATFORM) $(TILEBANK_PNG) $(dir $@) clean-amiga: rm -rf $(BUILD) # `clean` as an alias for `clean-amiga`. Without it a bare # `make -f make/amiga.mk clean` FAILS -- and with stderr discarded that # looks like success while leaving stale objects the next build links. clean: clean-amiga # Pull in per-object header-dependency files generated by gcc -MMD/-MP. # Without this, editing a header (e.g. surfaceInternal.h) doesn't rebuild # the .c files that include it, leaving a frankenstein binary where # different TUs see different struct layouts. -include $(LIB_OBJS:.o=.d) # The per-level cel banks, baked from each level's file. $(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c @mkdir -p $(dir $@) $(STAXIBAKE_BIN) --level $< $@ # Speech bake rules live down here so they never become the default goal. $(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py @mkdir -p $(dir $@) python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@ $(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN) @mkdir -p $(dir $@) cp $< $@