# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License. # See LICENSE.md for license information. # SPDX-License-Identifier: MIT # # DVX build rules: every example as a DVX program (.app). # # A JoeyLib program on DVX runs in a window of the DVX desktop, or on the # whole screen (src/dvx). The library, libjoey.a, is built here with # JoeyLib's own DJGPP (DVX is built with the same gcc 12.2.0) against the SDK # the installer unpacked from a DVX tree's dist/sdk.zip into # toolchains/opt/dvx/sdk (DVX_SDK); the stage and the graphics are the DOS # port's (src/x86, the chunky generics). Each example is then a DVX SDK # project in build/dvx/apps// -- symlinks to its sources and to the # port's entry (src/dvx/dvxApp.c), a generated one-topic help file and # resource list -- built and packaged by the SDK's own sdk.mk (KIND = app, # LIBS = libjoey.a, DATA = its data files) and copied into build/dvx/bin/ # laid out as it goes into DVX: apps///.app, .hlp # and DATA/. The gates and joey-run copy that tree into a DVX installation. # # This file holds the library and programRules, the DVX rules for one # program, which make/programs.mk instantiates for every program (a project # reaches both through make/project.mk). JoeyLib's own examples are at the # end. PLATFORM := dvx include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk $(call requireTool,$(DOS_CC),toolchain:dos) $(call requireTool,$(DVX_SDK)/sdk.mk,toolchain:dvx) APPSDIR := $(PROGRAM_BUILD)/apps PORT_DIR := $(SRC_DIR)/dvx # sdk.mk's code generation, so the library matches the programs it links # into; -fomit-frame-pointer as on DOS. DVX_ARCH := -march=i486 -mtune=i586 -fomit-frame-pointer # The SDK's header folders (sdk.mk's list: all but the BASIC and socket ones), # for the port's own files only -- the core never sees a DVX header. DVX_SDK_INCS := $(addprefix -I,$(filter-out %/basic %/net,$(patsubst %/,%,$(wildcard $(DVX_SDK)/include/*/)))) # A program's exit() and atexit() are the port's (src/dvx/program.c): the # handlers run, and JoeyLib shuts down, when the program ends rather than # when DVX does. Applied to the library and the programs, never to the port. PROGRAM_RENAMES := -Dexit=jlDvxPortExit -Datexit=jlDvxPortAtExit CFLAGS := $(COMMON_CFLAGS) $(DVX_ARCH) -DJOEYLIB_PLATFORM_DVX -I$(SRC_X86) -I$(SRC_CG) -MMD -MP LIB_CFLAGS := $(CFLAGS) $(PROGRAM_RENAMES) PORT_CFLAGS := $(CFLAGS) -I$(PORT_DIR) $(DVX_SDK_INCS) # The flags every program adds to sdk.mk's (which bring the SDK headers and # -Werror), with the include folders every port gives its programs: the # program's main becomes jlGameMain, which the port calls. APP_CFLAGS := -DJOEYLIB_PLATFORM_DVX -Dmain=jlGameMain $(PROGRAM_RENAMES) -fomit-frame-pointer -I$(INCLUDE_DIR) -I$(SRC_CORE) -I$(PORT_DIR) # dvxApp.c is compiled into each program (it names it), not the library. PORT_ENTRY := $(PORT_DIR)/dvxApp.c PORT_C_SRCS := $(filter-out $(PORT_ENTRY),$(wildcard $(PORT_DIR)/*.c)) X86_C_SRCS := $(wildcard $(SRC_X86)/*.c) X86_S_SRCS := $(wildcard $(SRC_X86)/*.s) LIB_OBJS := \ $(patsubst $(SRC_CORE)/%.c,$(BUILD)/obj/core/%.o,$(CORE_C_SRCS_NO_MIX)) \ $(patsubst $(SRC_DIR)/generic/%.c,$(BUILD)/obj/generic/%.o,$(GENERIC_C_SRCS)) \ $(patsubst $(PORT_DIR)/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \ $(patsubst $(SRC_X86)/%.c,$(BUILD)/obj/x86/%.o,$(X86_C_SRCS)) \ $(patsubst $(SRC_X86)/%.s,$(BUILD)/obj/x86/%.o,$(X86_S_SRCS)) \ $(BUILD)/obj/codegen/spriteCompile.o \ $(BUILD)/obj/codegen/spriteStage.o LIB := $(LIBDIR)/libjoey.a $(BUILD)/obj/core/%.o: $(SRC_CORE)/%.c @mkdir -p $(dir $@) $(DOS_CC) $(LIB_CFLAGS) -c $< -o $@ $(BUILD)/obj/generic/%.o: $(SRC_DIR)/generic/%.c @mkdir -p $(dir $@) $(DOS_CC) $(LIB_CFLAGS) -c $< -o $@ $(BUILD)/obj/x86/%.o: $(SRC_X86)/%.c @mkdir -p $(dir $@) $(DOS_CC) $(LIB_CFLAGS) -c $< -o $@ $(BUILD)/obj/x86/%.o: $(SRC_X86)/%.s @mkdir -p $(dir $@) $(DOS_CC) -c $< -o $@ $(BUILD)/obj/codegen/%.o: $(SRC_CG)/%.c @mkdir -p $(dir $@) $(DOS_CC) $(LIB_CFLAGS) -c $< -o $@ $(BUILD)/obj/port/%.o: $(PORT_DIR)/%.c @mkdir -p $(dir $@) $(DOS_CC) $(PORT_CFLAGS) -c $< -o $@ $(LIB): $(LIB_OBJS) @mkdir -p $(dir $@) rm -f $@ $(DOS_AR) rcs $@ $^ # Pull in per-object header-dependency files generated by gcc -MMD/-MP. -include $(LIB_OBJS:.o=.d) # ---- programs ----------------------------------------------------------------- # A program is a folder laid out as it installs in DVX, apps/// # under bin/: .app, .hlp and its DATA/. The name is the id in lower # case (DVX runs on DOS: an 8.3 name). DATA is staged apart, in # build/dvx/data//DATA, for the SDK to package. # DVX_VENDOR the vendor folder: whoever makes the program, 1 to 8 # letters, digits or _ (DVX names its package vendor.name) # DVX_AUTHOR the resource list's author # DVX_KIND what the help and resource texts call a program: " is a # <kind> program" (default JoeyLib) # The vendor and the author are the project's own: it must set them. DVX_KIND := JoeyLib PROGRAM_EXT := .app PROGRAM_DATA_APART := yes # What every program's help says after "<title> is a <kind> program.". DVX_HELP_WINDOW := It runs in its own window at a virtual 70 frames a second; while the window is active every key goes to it except Alt+Tab (switch programs), Ctrl+Esc (Task Manager) and Alt+F4 (close it). Game > Exit also closes it. DVX_HELP_SCREEN := Game > Full Screen, or Alt+Enter, puts it on the whole screen at 320x200 in 256 colours, and back in its window. It starts the way it was left. Alt+Tab or Ctrl+Esc brings the desktop back while it runs on; coming back to it gives it the whole screen again. programNameRule = $(call lc,$(1)) programDir = $(or $(call programVar,$(1),BINDIR),$(BINDIR))/apps/$(DVX_VENDOR)/$(call programName,$(1)) programTarget = app-$(call programName,$(1)) programTitle = $(or $(call programVar,$(1),TITLE),$(call capFirst,$(call lc,$(1)))) # One program, $(1) its id, as the phony app-<name>: (re)write its SDK project # in build/dvx/apps/<name>/ -- the Makefile, symlinks to its sources and the # port's entry, the help source, the resource list, and its icon -- then let # sdk.mk build and package it, and copy the package into bin/ (or # ID_BINDIR). sdk.mk rebuilds only what changed; the project files are # rewritten only when their contents change, so an unchanged program is not # relinked. Its CFLAGS are added to the program's flags. Its sources are # linked by absolute path, so a project may name them relative to its own # folder. programRules = $(call programDvxRules,$(1),$(call programName,$(1))) # $(1) the id, $(2) the name. Everything that may hold a comma (the title, # the help line) is looked up here rather than passed in, and every line # written out is shellQuote'd, so a title or help line may hold quotes. define programDvxRules $(call requireSet,DVX_VENDOR,the project's own vendor folder$(comma) which its programs install in: 1 to 8 letters$(comma) digits or _) $(call requireSet,DVX_AUTHOR,the author DVX names for the project's programs) .PHONY: app-$(2) app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(1),DATA) $(APPSDIR)/$(2)/icon32.bmp @mkdir -p $(APPSDIR)/$(2) @for src in $(abspath $(call programVar,$(1),SRCS)) $(PORT_ENTRY); do ln -sfn $$$$src $(APPSDIR)/$(2)/$$$$(basename $$$$src); done @printf '%s\n' \ $(call shellQuote,# Generated by make/dvx.mk: the DVX SDK project of the $(DVX_KIND) $(2).) \ 'SDK_DIR = $(DVX_SDK)' \ 'KIND = app' \ 'NAME = $(2)' \ 'VENDOR = $(DVX_VENDOR)' \ 'LIBS = $(LIB)' \ 'DATA = $(if $(call programVar,$(1),DATA),$(call programDataDir,$(1)))' \ $(call shellQuote,CFLAGS = $(APP_CFLAGS) $(call programVar,$(1),CFLAGS) -I$(dir $(abspath $(firstword $(call programVar,$(1),SRCS)))) -DDVX_PORT_APP_NAME=$(call shellQuote,$(call cQuote,$(call programTitle,$(1))))) \ 'include $$$$(SDK_DIR)/sdk.mk' | $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/Makefile @printf '%s\n' \ '.topic $(2).overview' \ $(call shellQuote,.title $(call programTitle,$(1))) \ $(call shellQuote,.toc 0 $(call programTitle,$(1))) \ '.default' \ $(call shellQuote,.index $(call programTitle,$(1))) \ '' \ $(call shellQuote,.h1 $(call programTitle,$(1))) \ '' \ $(call shellQuote,$(call programVar,$(1),HELP)) \ '' \ $(call shellQuote,$(call programTitle,$(1)) is a $(DVX_KIND) program. $(DVX_HELP_WINDOW)) \ '' \ $(call shellQuote,$(DVX_HELP_SCREEN)) \ | $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/$(2).dhs @printf '%s\n' \ 'icon32 icon icon32.bmp' \ $(call shellQuote,name text "$(call programTitle,$(1))") \ $(call shellQuote,author text "$(DVX_AUTHOR)") \ $(call shellQuote,description text "$(DVX_KIND): $(call programTitle,$(1))") \ | $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/$(2).res $$(MAKE) -C $(APPSDIR)/$(2) DJGPP_PREFIX=$(DOS_PREFIX) package @mkdir -p $(or $(call programVar,$(1),BINDIR),$(BINDIR)) rm -rf $(call programDir,$(1)) cp -R $(APPSDIR)/$(2)/pkg/apps $(or $(call programVar,$(1),BINDIR),$(BINDIR))/ # Its icon, the one Program Manager shows: icon32 among its resources, a # 32x32 BMP on DVX's grey, which sdk.mk takes from the project's folder. $(APPSDIR)/$(2)/icon32.bmp: $(call programIcon,$(1)) $(ICON_BAKE) @mkdir -p $$(dir $$@) $(ICON_BAKE) bmp $$< $$@ endef # ---- JoeyLib's own build: its examples ------------------------------------------ ifndef JOEY_PROJECT include $(REPO_DIR)/make/examples.mk include $(REPO_DIR)/make/programs.mk # UBER's probe build is part of `all` (scripts/verify-dvx-display.sh). .DEFAULT_GOAL := all .PHONY: all dvx dvx-lib dvx-probe clean-dvx clean all dvx: dvx-lib programs dvx-probe dvx-lib: $(LIB) dvx-probe: $(call programTarget,UBERPROB) clean-dvx: rm -rf $(BUILD) # `clean` as an alias for `clean-dvx`. Without it a bare # `make -f make/dvx.mk clean` FAILS -- and with stderr discarded that # looks like success while leaving stale objects the next build links. clean: clean-dvx endif