Files
Andreas T Jonsson 4d5413503b refactor: cleanup phase 1 -- bug fixes and dead code removal
Bug fixes:
- pic: honor auto-EOI mode (ICW4 bit 1); ISR bit now only set when
  auto-EOI is disabled (previously set unconditionally)
- spec: correct port 0xB0-in polarity (0x00 = hard disk, 0xFF = floppy)
  to match both the Odin and C implementations
- debug: gate cga_regs/cga_vram handlers on the video adapter actually
  being CGA (returned garbage when EGA/HGC was active)

Dead code removal:
- unused public APIs: vxt_cpu_flush_prefetch, vxt_bus_set_pic,
  vxt_uart_registers, vxt_uart_set_error, vxt_disk_bootstrap
- write-only CPU instruction-stream fields + MAX_INSTRUCTION_SIZE
- unused macros VXT_CPU_USE_PREFETCH, MBOX_TOTAL_SIZE
- debug server: g_active_server, single_step, last_segments, SEG_TAG_*
- UART iir field, always-false ega_config stub, empty destroy stubs
  (joystick, UART, mouse), exec_wait/exec_esc folded into exec_nop
- frontend: BIOS_ROM_BASE/VXTX_ROM_BASE, mixer HOSTFS_DURATION/
  HOSTFS_VOLUME/seek_volume, unreachable clamps, ini_parse return value
- misc dead stores/clamps (hostfs, hgc), dangling comment (cpu.c)

Makefile: drop 12 redundant pattern rules, replace dead check-circle
test with a real compiler check, drop duplicated distclean line,
complete .PHONY, collapse flag lists, develop honors LIBRETRO_OUT.

STATUS.md: correct stale module test counts (110 -> 119).
2026-07-18 21:31:08 +02:00

303 lines
12 KiB
Makefile

# VirtualXT -- Makefile
#
# Core library, utils, modules, and frontend compile freestanding (no libc).
# The frontend depends only on libretro function pointers.
# Tests are hosted (use libc for stdio, malloc, etc.).
#
# Targets:
# develop (default) -- libretro core with debug server + test runners
# release -- libretro core without debug server
# object -- release-mode libretro object for packaging/web
# rpi -- cross-compile baremetal Raspberry Pi kernel
# web -- standalone Emscripten web frontend
# test -- run all tests (test-modules + test-cpu)
# testdata -- download 8088/V20 MOO CPU test data
# bios-data -- regenerate C headers from BIOS ROMs and disk image
# samples -- regenerate audio sample header
# vxtbios/vxtx/egabios-- assemble BIOS ROMs (requires nasm)
# run -- launch develop core in RetroArch
# clean/distclean -- remove build artifacts (+ test data)
CC ?= gcc
AR ?= ar
NASM ?= nasm
PYTHON ?= python3
BUILD_DATE := $(shell date +%m/%d/%Y)
VXT_VERSION := $(shell . tools/getversion/export.sh)
CFLAGS := -std=c11 -Wall -Wextra -Wpedantic -Isrc
LDFLAGS ?=
LIBRETRO_OUT ?= virtualxt_libretro.so
LIBRETRO_LINK_MODE ?= -shared
LIBRETRO_OBJECT_OUT ?= virtualxt_libretro.o
CIRCLEHOME ?= ../circle
# Cross-compiler prefix for Raspberry Pi.
# Read the toolchain prefix from Circle's Config.mk (set by ./configure).
# Falls back to the standard baremetal prefix if Circle is not yet configured.
CROSS_PREFIX ?= $(shell \
if [ -f "$(CIRCLEHOME)/Config.mk" ]; then \
grep '^PREFIX64' "$(CIRCLEHOME)/Config.mk" 2>/dev/null | sed 's/^PREFIX64[[:space:]]*=[[:space:]]*//' | sed 's/[[:space:]]*$$//'; \
fi)
ifeq ($(CROSS_PREFIX),)
CROSS_PREFIX := aarch64-none-elf-
endif
ifdef NO_RTC
CFLAGS += -DNO_RTC
endif
ifdef NO_DEFAULT_DISK
CFLAGS += -DNO_DEFAULT_DISK
endif
ifdef NO_SAMPLES
CFLAGS += -DNO_SAMPLES
endif
ifdef CRT_FILTER_EXTERNAL
CFLAGS += -DCRT_FILTER_EXTERNAL
endif
CORE_CFLAGS := $(CFLAGS) -ffreestanding -fPIC -O2
TEST_CFLAGS := $(CFLAGS) -O2 -DDEBUG_SERVER
FRONT_CFLAGS := $(CORE_CFLAGS) -Itools/libretro -DVXT_VERSION='"$(VXT_VERSION)"'
DEBUG_CFLAGS := $(TEST_CFLAGS) -fPIC
# ── Source lists ─────────────────────────────────────────────────────────
CORE_SRCS := src/utils/utils.c \
src/vxt/peripheral.c src/vxt/cpu.c src/vxt/alu.c \
src/vxt/div.c src/vxt/cpu_remap.c src/vxt/cpu_v20.c \
src/vxt/cpu_cycles.c src/vxt/machine.c \
src/modules/module_table.c \
src/modules/rom/rom.c \
src/modules/disk/disk.c \
src/modules/chipset/chipset.c src/modules/chipset/pic.c \
src/modules/chipset/pit.c src/modules/chipset/dma.c \
src/modules/chipset/ppi.c \
src/modules/cga/cga.c \
src/modules/ega/ega.c \
src/modules/hgc/hgc.c \
src/modules/uart/uart.c \
src/modules/mouse/mouse.c \
src/modules/joystick/joystick.c \
src/modules/hostfs/hostfs.c \
src/modules/ems/ems.c \
$(if $(NO_RTC),,src/modules/rtc/rtc.c)
TEST_SRCS := src/tests/moo.c src/tests/cpu_main.c
MODULE_SRCS := src/tests/module_main.c \
src/tests/test_bus.c src/tests/test_pic.c \
src/tests/test_pit.c src/tests/test_dma.c \
src/tests/test_rom.c src/tests/test_ppi.c \
src/tests/test_ems.c src/tests/test_rtc.c
# ── Object lists ─────────────────────────────────────────────────────────
CORE_OBJS := $(CORE_SRCS:.c=.o)
TEST_OBJS := $(TEST_SRCS:.c=.o)
MODULE_OBJS := $(MODULE_SRCS:.c=.o)
DEBUG_TEST_OBJS := src/tests/test_debug.o
DEBUG_SERVER_OBJS := src/modules/debug/debug_server.o
DEVELOP_FRONT_OBJS := src/frontend/core.develop.o src/frontend/ini.develop.o src/frontend/crt.develop.o src/frontend/audio_mixer.develop.o
RELEASE_FRONT_OBJS := src/frontend/core.release.o src/frontend/ini.release.o src/frontend/crt.release.o src/frontend/audio_mixer.release.o
# When NO_RTC=1, rtc.o is excluded from libvxt.a but test_rtc.c still needs it.
# Compile rtc.o separately for the module-runner link.
ifdef NO_RTC
MODULE_EXTRA_OBJS := src/modules/rtc/rtc.o
else
MODULE_EXTRA_OBJS :=
endif
# ── BIOS data generation ────────────────────────────────────────────────
BIOS_DATA := src/frontend/generated/vxtbios_bin.h \
src/frontend/generated/vxtx_bin.h \
src/frontend/generated/egabios_rom.h \
src/frontend/generated/freedos_hd_img.h
$(BIOS_DATA): bios/vxtbios.bin bios/vxtx.bin bios/egabios.bin boot/freedos_hd.img
$(PYTHON) tools/bios/gen_bios_data.py
# ── Sample data generation ──────────────────────────────────────────────
SAMPLES_DATA := src/frontend/generated/audio_samples.h
$(SAMPLES_DATA): samples/servo_seeks_eq.wav tools/samples/gen_samples.py
$(PYTHON) tools/samples/gen_samples.py
# ── Top-level targets ───────────────────────────────────────────────────
.PHONY: all develop release object web rpi check-circle clean distclean testdata test test-cpu test-cpu-8088 test-cpu-v20 test-modules run bios-data samples vxtbios vxtx egabios
.DEFAULT_GOAL := develop
all: develop
develop: libvxt.a test-runner module-runner $(DEVELOP_FRONT_OBJS) $(DEBUG_SERVER_OBJS)
mkdir -p $(dir $(LIBRETRO_OUT))
$(CC) $(LIBRETRO_LINK_MODE) -o $(LIBRETRO_OUT) $(LDFLAGS) $(DEVELOP_FRONT_OBJS) $(DEBUG_SERVER_OBJS) ./libvxt.a -lpthread
release: libvxt.a $(RELEASE_FRONT_OBJS)
mkdir -p $(dir $(LIBRETRO_OUT))
$(CC) $(LIBRETRO_LINK_MODE) -o $(LIBRETRO_OUT) $(RELEASE_FRONT_OBJS) ./libvxt.a
object: libvxt.a $(RELEASE_FRONT_OBJS)
mkdir -p $(dir $(LIBRETRO_OBJECT_OUT))
$(CC) -r $(LDFLAGS) -o $(LIBRETRO_OBJECT_OUT) $(RELEASE_FRONT_OBJS) ./libvxt.a
# Cross-compile the libretro core object for Raspberry Pi (AArch64 baremetal),
# then build the Circle kernel image that wraps it.
# The baremetal target excludes the default disk image and audio samples to
# reduce binary size; the user is expected to provide a custom machine setup.
rpi: check-circle
rm -f $(CORE_OBJS) $(RELEASE_FRONT_OBJS) libvxt.a $(LIBRETRO_OBJECT_OUT)
$(MAKE) CC=$(CROSS_PREFIX)gcc AR=$(CROSS_PREFIX)ar object NO_DEFAULT_DISK=1 NO_SAMPLES=1 CRT_FILTER_EXTERNAL=1
$(MAKE) -C tools/circle/kernel PREFIX64=$(CROSS_PREFIX)
# Build standalone web frontend with Emscripten.
web:
$(MAKE) -C tools/web
check-circle:
@command -v $(CROSS_PREFIX)gcc >/dev/null 2>&1 || { \
echo "Error: No AArch64 cross-compiler found ($(CROSS_PREFIX)gcc)."; \
echo "Please install aarch64-none-elf-gcc or aarch64-linux-gnu-gcc."; \
exit 1; \
}
@if [ ! -f "$(CIRCLEHOME)/lib/libcircle.a" ]; then \
echo "Error: Circle library not found at $(CIRCLEHOME)/lib/libcircle.a"; \
echo "Please build Circle first:"; \
echo " cd $(CIRCLEHOME) && ./configure -r 4 --multicore && makeall"; \
exit 1; \
fi
bios-data: $(BIOS_DATA)
samples: $(SAMPLES_DATA)
# ── BIOS assembly (manual, requires nasm) ───────────────────────────────
vxtbios: bios/vxtbios.bin
bios/vxtbios.bin: bios/vxtbios.asm
$(NASM) -f bin -o $@ -l bios/vxtbios.lst -dBUILD_DATE="'$(BUILD_DATE)'" $<
$(PYTHON) tools/checksum/update_bios_checksum.py $@
vxtx: bios/vxtx.bin
bios/vxtx.bin: bios/vxtx.asm
$(NASM) -f bin -o $@ -l bios/vxtx.lst $<
$(PYTHON) tools/checksum/update_bios_checksum.py $@
egabios: bios/egabios.bin
bios/egabios.bin: bios/egabios.asm
$(NASM) -f bin -o $@ -l bios/egabios.lst $<
$(PYTHON) tools/checksum/update_bios_checksum.py $@
# ── Libraries and binaries ──────────────────────────────────────────────
libvxt.a: $(CORE_OBJS)
$(AR) rcs $@ $^
test-runner: $(TEST_OBJS) libvxt.a
$(CC) $(TEST_CFLAGS) -o $@ $(TEST_OBJS) ./libvxt.a
module-runner: $(MODULE_OBJS) $(MODULE_EXTRA_OBJS) $(DEBUG_TEST_OBJS) $(DEBUG_SERVER_OBJS) libvxt.a
$(CC) $(TEST_CFLAGS) -o $@ $(MODULE_OBJS) $(MODULE_EXTRA_OBJS) $(DEBUG_TEST_OBJS) $(DEBUG_SERVER_OBJS) ./libvxt.a -lpthread
# ── Compilation rules ───────────────────────────────────────────────────
# Core: freestanding.
src/utils/%.o: src/utils/%.c; $(CC) $(CORE_CFLAGS) -c -o $@ $<
src/vxt/%.o: src/vxt/%.c; $(CC) $(CORE_CFLAGS) -c -o $@ $<
src/modules/%.o: src/modules/%.c; $(CC) $(CORE_CFLAGS) -c -o $@ $<
src/modules/debug/%.o: src/modules/debug/%.c; $(CC) $(DEBUG_CFLAGS) -c -o $@ $<
# Tests: hosted.
src/tests/%.o: src/tests/%.c
$(CC) $(TEST_CFLAGS) -c -o $@ $<
# Frontend: develop (with debug server) and release (without).
src/frontend/%.develop.o: src/frontend/%.c
$(CC) $(FRONT_CFLAGS) -DDEBUG_SERVER -c -o $@ $<
src/frontend/%.release.o: src/frontend/%.c
$(CC) $(FRONT_CFLAGS) -c -o $@ $<
# ── Test targets ────────────────────────────────────────────────────────
test: test-modules test-cpu
test-modules: module-runner
@./module-runner
test-cpu: test-cpu-8088 test-cpu-v20
test-cpu-8088: test-runner
@if [ ! -d src/tests/testdata ] || [ -z "$$(ls src/tests/testdata/*.MOO 2>/dev/null)" ]; then \
echo "No 8088 test data found. Run 'make testdata' first."; exit 1; \
fi; \
failed=0; passed=0; total=0; \
for f in src/tests/testdata/*.MOO; do \
[ -f "$$f" ] || continue; \
mask=213; \
if [ -f src/tests/testdata/masks.txt ]; then \
m=$$(grep "^$$(basename $$f) " src/tests/testdata/masks.txt | awk '{print $$2}'); \
[ -n "$$m" ] && mask=$$m; \
fi; \
total=$$((total + 1)); \
if ./test-runner "$$f" "$$mask"; then \
passed=$$((passed + 1)); \
else \
failed=$$((failed + 1)); \
fi; \
done; \
echo ""; \
echo "=== 8088 Results: $$passed passed, $$failed failed ($$total files) ==="; \
[ $$failed -eq 0 ]
test-cpu-v20: test-runner
@if [ ! -d src/tests/testdata/v20 ] || [ -z "$$(ls src/tests/testdata/v20/*.MOO 2>/dev/null)" ]; then \
echo "No V20 test data found. Run 'make testdata' first."; exit 1; \
fi; \
failed=0; passed=0; total=0; \
for f in src/tests/testdata/v20/*.MOO; do \
[ -f "$$f" ] || continue; \
mask=213; \
if [ -f src/tests/testdata/v20/masks.txt ]; then \
m=$$(grep "^$$(basename $$f) " src/tests/testdata/v20/masks.txt | awk '{print $$2}'); \
[ -n "$$m" ] && mask=$$m; \
fi; \
total=$$((total + 1)); \
if ./test-runner --v20 "$$f" "$$mask"; then \
passed=$$((passed + 1)); \
else \
failed=$$((failed + 1)); \
fi; \
done; \
echo ""; \
echo "=== V20 Results: $$passed passed, $$failed failed ($$total files) ==="; \
[ $$failed -eq 0 ]
testdata:
$(PYTHON) tools/testdata/fetch.py
# ── Run ─────────────────────────────────────────────────────────────────
run: develop
retroarch -v -L virtualxt_libretro.so
# ── Clean ───────────────────────────────────────────────────────────────
clean:
rm -f $(CORE_OBJS) $(TEST_OBJS) $(MODULE_OBJS) $(MODULE_EXTRA_OBJS) \
$(DEBUG_TEST_OBJS) $(DEBUG_SERVER_OBJS) \
$(DEVELOP_FRONT_OBJS) $(RELEASE_FRONT_OBJS) \
libvxt.a test-runner module-runner $(LIBRETRO_OUT) \
$(LIBRETRO_OBJECT_OUT)
-$(MAKE) -C tools/circle/kernel clean
-$(MAKE) -C tools/web clean
distclean: clean
rm -rf src/tests/testdata