joeylib2/make/x68000.mk
2026-09-29 17:23:29 -05:00

316 lines
15 KiB
Makefile

# Sharp X68000 (elf2x68k / m68k-xelf-gcc) build rules.
#
# The port draws planar into the text plane (src/x68000/halPlanar.c), shares
# the Amiga's 68k planar fills, plots and sprite emitter (src/m68k), plays
# chip music on the OPM and samples through the ADPCM channel.
#
# The toolchain is elf2x68k (BSD-3, gcc 13.4.0 / binutils / newlib), installed
# by ./install.sh (X68K_ROOT). m68k-xelf-gcc emits ELF; the driver's
# -Wl,-elf2x68k pass converts it to a Human68k .X.
include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
# The libxmp-lite rules below define a real target before `all`, which would
# otherwise silently become make's default goal (symptom: a bare `make -f
# make/x68000.mk` reports only "libxmplite.a is up to date" and builds nothing).
.DEFAULT_GOAL := all
$(call requireTool,$(X68K_ROOT)/bin/m68k-xelf-gcc,toolchain:x68000)
$(call requireTool,$(LIBXMP_DIR)/include/libxmp-lite/xmp.h,toolchain:x68000)
PLATFORM := x68000
BUILD := $(REPO_DIR)/build/$(PLATFORM)
LIBDIR := $(BUILD)/lib
BINDIR := $(BUILD)/bin
X68K_CC := $(X68K_ROOT)/bin/m68k-xelf-gcc
X68K_AR := $(X68K_ROOT)/bin/m68k-xelf-ar
# The linker is driven by a wrapper script (m68k-elf/bin/ld.x) that shells out
# to `m68k-xelf-ld.bfd` by bare name, so the toolchain's own bin/ MUST be on
# PATH or the link dies with "m68k-xelf-ld.bfd: command not found". Compiling
# and archiving work fine without it, so this only bites at link time.
export PATH := $(X68K_ROOT)/bin:$(PATH)
CFLAGS := $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer \
-DJOEYLIB_PLATFORM_X68000 \
-I$(LIBXMP_DIR)/include \
-I$(SRC_DIR)/x68000 -I$(SRC_68K) -I$(REPO_DIR)/src/codegen -MMD -MP
LDFLAGS := -lm
PORT_C_SRCS := $(wildcard $(SRC_DIR)/x68000/*.c)
# Raw interrupt shims (timer tick, SCB raster, SCC receive): each saves every
# register and returns with RTE, which a C function cannot.
PORT_S_SRCS := $(wildcard $(SRC_DIR)/x68000/*.s)
# Sprite codegen is the shared 68k PLANAR emitter, unchanged. Its
# AMIGA_PLANE_STRIDE is SURFACE_WIDTH/8 = 40, which is this port's surface
# stride too -- the display's 128-byte stride never reaches the emitter, which
# only ever addresses the surface. So sprites COMPILE here rather than
# interpret; spriteEmitStub.o is gone.
CODEGEN_DIR := $(REPO_DIR)/src/codegen
LIB_OBJS := \
$(patsubst $(SRC_CORE)/%.c,$(BUILD)/obj/core/%.o,$(CORE_C_SRCS)) \
$(patsubst $(SRC_DIR)/generic/%.c,$(BUILD)/obj/generic/%.o,$(GENERIC_C_SRCS)) \
$(patsubst $(SRC_DIR)/x68000/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \
$(patsubst $(SRC_DIR)/x68000/%.s,$(BUILD)/obj/port/%.o,$(PORT_S_SRCS)) \
$(BUILD)/obj/68k/planarFill.o \
$(BUILD)/obj/68k/planarPlot.o \
$(BUILD)/obj/68k/spriteEmitPlanar68k.o \
$(BUILD)/obj/codegen/spriteCompile.o \
$(BUILD)/obj/codegen/spriteStage.o
# libxmp-lite: the Protracker decoder shared with the DOS and ST ports. Same
# build recipe as make/atarist.mk -- LIBXMP_CORE_DISABLE_IT keeps it off math.h,
# which this newlib does not usefully provide for a 68000 target.
# LIBXMP_DIR comes from the installer's config.mk.
LIBXMP_SRC := $(filter-out %/win32.c, $(wildcard $(LIBXMP_DIR)/src/*.c) $(wildcard $(LIBXMP_DIR)/src/loaders/*.c))
LIBXMP_OBJDIR := $(BUILD)/obj/libxmp-lite
LIBXMP_OBJS := $(patsubst $(LIBXMP_DIR)/src/%.c,$(LIBXMP_OBJDIR)/%.o,$(LIBXMP_SRC))
LIBXMP_AR := $(LIBDIR)/libxmplite.a
LIBXMP_CFLAGS := -DLIBXMP_CORE_PLAYER -DLIBXMP_CORE_DISABLE_IT -DHAVE_FNMATCH=0 \
-I$(LIBXMP_DIR)/include -I$(LIBXMP_DIR)/include/libxmp-lite \
-I$(LIBXMP_DIR)/src -Wno-error -w
$(LIBXMP_OBJDIR)/%.o: $(LIBXMP_DIR)/src/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(COMMON_CFLAGS) -m68000 -fomit-frame-pointer $(LIBXMP_CFLAGS) -c $< -o $@
$(LIBXMP_AR): $(LIBXMP_OBJS)
@mkdir -p $(dir $@)
$(X68K_AR) rcs $@ $(LIBXMP_OBJS)
LIB := $(LIBDIR)/libjoey.a
# Every example but Space Taxi is its sources plus the library, linked by the
# one rule below. Worth knowing which ones the gates use:
# PATTERN the SCB acceptance vehicle: 8 bands of 25 lines with 8 gradient
# palettes, so a misplaced band boundary is immediately visible.
# UBER the golden-hash vehicle: every public op, a hash per op, which
# tools/diff-uber-hashes compares against the IIgs reference.
# AUDIO both audio paths: JYM1 chip music on the OPM, MOD/SFX on ADPCM.
# KEYS the input and mouse vehicle: key predicates, the typed line
# (jlInputGetChar) and its verification row.
# SERIAL the RS-232C gate's echo program.
# SERMOUSE the serial + mouse gate's program: streams both ways on the
# RS-232C port (SCC channel A) while the mouse (channel B) moves.
# TILEBANK the tile-bank gate's program: this port's .tbk bake loaded both
# ways and pixel-checked, every other target's bake refused.
HELLO_SRC := $(EXAMPLES)/hello/hello.c
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
SERMOUSE_SRC := $(EXAMPLES)/sermouse/sermouse.c
SAVE_SRC := $(EXAMPLES)/save/save.c
TILEBANK_SRC := $(EXAMPLES)/tilebank/tilebank.c
JOY_SRC := $(EXAMPLES)/joy/joy.c
SPRITE_SRC := $(EXAMPLES)/sprite/sprite.c
AUDIO_SRC := $(EXAMPLES)/audio/audio.c
UBER_SRC := $(EXAMPLES)/uber/uber.c
ADV_SRC := $(EXAMPLES)/adventure/adventure.c
ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
AGI_SRC := $(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
EXAMPLES_X := HELLO PATTERN DRAW KEYS SERIAL SERTEST SERMOUSE SAVE TILEBANK JOY SPRITE AUDIO UBER ADV ADV2 AGI
EXAMPLE_BINS := $(foreach e,$(EXAMPLES_X),$(BINDIR)/$(e).X)
# AUDIO loads DATA/test.mod and DATA/test.sfx from beside the program.
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
# Space Taxi ships in its own folder with its DATA, as on the other ports,
# so its megabyte of data never rides onto every other program's floppy.
STAXI_DIR := $(BINDIR)/STAXI
STAXI_BIN := $(STAXI_DIR)/STAXI.X
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
.PHONY: all x68000 x68000-lib x68000-examples x68000-verify-serial x68000-verify-mouse x68000-verify-serial-mouse x68000-verify-tilebank x68000-verify-golden clean-x68000 clean
all x68000: x68000-lib x68000-examples
x68000-lib: $(LIB) $(LIBXMP_AR)
x68000-examples: $(EXAMPLE_BINS) $(STAXI_BIN) $(DATA_FILES) $(TILEBANK_DATA)
$(BUILD)/obj/core/%.o: $(SRC_CORE)/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
$(BUILD)/obj/generic/%.o: $(SRC_DIR)/generic/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
# Port-local asm. Same object dir as the port C objects; make picks between the
# two pattern rules by which prerequisite exists.
$(BUILD)/obj/port/%.o: $(SRC_DIR)/x68000/%.s
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.s
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
$(BUILD)/obj/68k/%.o: $(SRC_68K)/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
$(BUILD)/obj/codegen/%.o: $(CODEGEN_DIR)/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -I$(CODEGEN_DIR) -c $< -o $@
$(BUILD)/obj/port/%.o: $(SRC_DIR)/x68000/%.c
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -c $< -o $@
$(LIB): $(LIB_OBJS)
@mkdir -p $(dir $@)
$(X68K_AR) rcs $@ $(LIB_OBJS)
.SECONDEXPANSION:
$(EXAMPLE_BINS): $(BINDIR)/%.X: $$($$*_SRC) $(LIB) $(LIBXMP_AR)
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) $($*_SRC) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
$(DATA_DIR)/%: $(REPO_DIR)/assets/%
@mkdir -p $(dir $@)
cp $< $@
$(TILEBANK_DATA): $(TILEBANK_BAKE_DEPS)
python3 $(TILEBANK_STAGE) $(PLATFORM) $(TILEBANK_PNG) $(dir $@)
# Space Taxi asset variables (the rules follow the binary below); make
# expands prerequisites when it reads the rule, so these come first.
STAXI_GEN_DIR := $(EXAMPLES)/spacetaxi/generated/x68000
STAXI_RUN_DIR := $(STAXI_DIR)/DATA
STAXI_LEVEL_GEN := $(foreach n,$(STAXI_LEVEL_NUMS),$(STAXI_GEN_DIR)/levels/level$(n).dat) \
$(STAXI_GEN_DIR)/levels/title.dat
STAXI_LEVEL_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXI_LEVEL_GEN))
SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-x68000
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
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))
STAXI_ASSET_DSTS := $(STAXI_LEVEL_RUN) $(STAXICELS_SPC_RUN)
# 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.
# ...except on the X68000, where they do not fit. The Human68k floppy is
# 1232 KB and these eight banks are 787 KB of it (level09 alone is 236 KB,
# level10 175, level17 165), which with the 296 KB binary, the levels and
# the speech put the disk about 300 KB over however small speech gets.
# stRenderLevelCels already handles their absence the right way: it builds
# and compiles the level's cels at ENTRY, behind the LOADING bar, so the
# ride itself still never compiles. That is also what spacetaxi.c's
# codegen-arena comment says the 68k ports are meant to do -- their cels
# are big and their JIT is fast. Set STAXI_CEL_LEVELS to the list above to
# ship them again if the payload ever moves to a hard disk image.
STAXI_CEL_LEVELS :=
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)
$(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
@mkdir -p $(dir $@)
# --single: one polarity, 60 KB instead of 120 KB. The 1232 KB
# Human68k floppy cannot hold the two-polarity file alongside the
# binary and the levels -- it came up 18 KB short -- and the player
# negates the samples itself when the running polarity is odd.
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@ --single
$(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN)
@mkdir -p $(dir $@)
cp $< $@
# Space Taxi. -s (strip) because the image it ships on is a 1232 KB
# Human68k floppy: the unstripped binary is most of the volume on its
# own. The DATA tree next to it (levels + the precompiled cel bank) is
# what joey-run copies onto the data floppy.
$(STAXI_BIN): $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) $(call staxiData,$(STAXI_ASSET_DSTS))
@mkdir -p $(dir $@)
$(X68K_CC) $(CFLAGS) -s -I$(EXAMPLES)/spacetaxi $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
# Space Taxi asset staging, the amiga.mk pattern cut down to what this
# port loads at run time: the 24 levels + title extracted from the C64
# dump, and the cel bank baked by staxibake and pre-compiled for THIS
# target by spritebake-x68000 (the planar 68k emitter under target byte
# 5, so an Amiga or DOS bank is refused by the loader). Tiles and the
# font come from the C64 data in stC64Data.c, so no .tbk is staged.
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXICELS_SPR_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN)
$(STAXI_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
@mkdir -p $(STAXI_GEN_DIR)/levels
python3 $(ROMTOLEVEL_BIN) $(STAXI_RAW_DUMP) $(STAXI_GEN_DIR)/levels
$(SPRITEBAKE_BIN):
$(MAKE) -f $(REPO_DIR)/make/tools.mk spritebake-x68000
$(STAXIBAKE_BIN):
$(MAKE) -f $(REPO_DIR)/make/tools.mk staxibake
$(STAXICELS_SPR_GEN): $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stC64Data.c
@mkdir -p $(dir $@)
$(STAXIBAKE_BIN) $@
$(STAXI_GEN_DIR)/sprites/%.spc: $(STAXI_GEN_DIR)/sprites/%.spr $(SPRITEBAKE_BIN)
$(SPRITEBAKE_BIN) $< $@
$(STAXI_RUN_DIR)/levels/%.dat: $(STAXI_GEN_DIR)/levels/%.dat
@mkdir -p $(dir $@)
cp $< $@
$(STAXI_RUN_DIR)/sprites/%.spc: $(STAXI_GEN_DIR)/sprites/%.spc
@mkdir -p $(dir $@)
cp $< $@
# Both-directions RS-232C gate (~1 min).
x68000-verify-serial: $(BINDIR)/SERIAL.X
$(REPO_DIR)/scripts/verify-x68000-serial.sh
# Mouse gate: injected MAME mouse packets through the IOCS ROM into
# jlMouse* state, read back from KEYS.X's verification scanline.
x68000-verify-mouse: $(BINDIR)/KEYS.X
$(REPO_DIR)/scripts/verify-x68000-mouse.sh
# Serial + mouse together: both share the SCC, so stream both ways on the
# RS-232C port while the mouse moves, and check neither disturbs the other.
x68000-verify-serial-mouse: $(BINDIR)/SERMOUSE.X
$(REPO_DIR)/scripts/verify-x68000-serial-mouse.sh
# Tile-bank gate: this port's own .tbk bake loaded both ways must hash like
# the host gate's, and every other target's bake must be refused (~1 min).
x68000-verify-tilebank: $(BINDIR)/TILEBANK.X $(TILEBANK_DATA)
$(REPO_DIR)/scripts/verify-x68000-tilebank.sh
# Golden-hash gate against the Apple IIgs reference (~70 min: UBER on the
# generic renderer is slow and must reach jlLogFlush before hashes exist).
x68000-verify-golden: $(LIB) $(LIBXMP_AR)
$(REPO_DIR)/scripts/verify-x68000-golden.sh
clean-x68000:
rm -rf $(BUILD)
# `clean` as an alias for `clean-x68000`. Without it a bare
# `make -f make/x68000.mk clean` FAILS -- and with stderr discarded that
# looks like success while leaving stale objects the next build links.
clean: clean-x68000
-include $(shell find $(BUILD) -name '*.d' 2>/dev/null)
# Shorter stem than the %.spr-from-%.png pattern, so it wins for level banks.
$(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c
@mkdir -p $(dir $@)
$(STAXIBAKE_BIN) --level $< $@