Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ jobs:
- atari
- vic20
- coco
- h89-cpm
- h89-hdos

container:
image: fozztexx/defoogi:1.4.2
image: fozztexx/defoogi:1.4.8

steps:
- uses: actions/checkout@v4
Expand Down
45 changes: 44 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ PLATFORMS += apple2
PLATFORMS += atari
PLATFORMS += c64
PLATFORMS += coco
PLATFORMS += h89-cpm
PLATFORMS += h89-hdos
PLATFORMS += msx
#PLATFORMS += rc2014
PLATFORMS += vic20
Expand All @@ -27,6 +29,12 @@ SRC_DIRS = src src/%PLATFORM%
# - undefined, no fujinet-lib will be used
FUJINET_LIB = https://github.com/FozzTexx/fujinet-lib-experimental.git

# The H89's bus is only in fujinet-lib itself, which has no H89 release
# archive yet, so both H89 builds take it from git.
ifneq ($(filter h89-cpm h89-hdos,$(PLATFORM)),)
FUJINET_LIB = https://github.com/FujiNetWIFI/fujinet-lib.git
endif

# Some platforms don鈥檛 use FUJINET_LIB; set this to allow builds to continue
# even if the library isn鈥檛 present.
FUJINET_LIB_OPTIONAL = 1
Expand All @@ -37,7 +45,20 @@ PLATFORM_COMBOS = \
c64+=commodore \
atarixe+=atari \
msxrom+=msx \
msxdos+=msx
msxdos+=msx \
h89-cpm+=h89 \
h89-hdos+=h89

# H89: no stdio or file handles, since src/h89/con.c talks to the console
# itself. The command line stays on: the URL can come from it. -m leaves the
# map beside the program.
H89_LDFLAGS = -m \
-pragma-define:CRT_ENABLE_STDIO=0 \
-pragma-define:CLIB_FOPEN_MAX=0 \
-pragma-define:CLIB_OPEN_MAX=0 \
-pragma-define:CLIB_EXIT_STACK_SIZE=0
LDFLAGS_EXTRA_H89-CPM = $(H89_LDFLAGS)
LDFLAGS_EXTRA_H89-HDOS = $(H89_LDFLAGS)

include hirestxt-mod-lib.mk
include mekkogx/toplevel-rules.mk
Expand All @@ -49,3 +70,25 @@ include mekkogx/toplevel-rules.mk
# apple2/disk: apple2/custom-step1 apple2/custom-step2

EXTRA_C_DEPS_COCO = .get_hirestxt_lib

# H89: the program on a 100K H17 data disk.
PRODUCT_UPPER = $(shell echo $(PRODUCT) | tr '[:lower:]' '[:upper:]')
H89_APPMAKE ?= z88dk-appmake

# HDOS: appmake +hdos wants the bare program (the .abs less its 8-byte
# header: FF 00, load, length, entry) and the load address. It names the
# image after the input file and the file on it after -o, hence the cd.
h89-hdos/r2r-post::
tail -c +9 $(EXECUTABLE) > $(R2R_PD)/$(PRODUCT).bin
cd $(R2R_PD) && $(H89_APPMAKE) +hdos -b $(PRODUCT).bin \
--org $$(od -An -tu2 -j2 -N2 $(notdir $(EXECUTABLE)) | tr -d ' ') -o $(PRODUCT_UPPER)
rm -f $(R2R_PD)/$(PRODUCT).bin
@echo "$(R2R_PD)/$(PRODUCT).h8d: MOUNT SY1: then SY1:$(PRODUCT_UPPER) [URL]"

# CP/M: the hz89 format (H17, 40 tracks, 3 boot tracks), then mark the empty
# directory entries appmake leaves zeroed as erased (support/h89).
h89-cpm/r2r-post::
cd $(R2R_PD) && $(H89_APPMAKE) +cpmdisk -f hz89 --container raw --no-com-file \
-b $(notdir $(EXECUTABLE)) -o $(PRODUCT).h8d
python3 -I support/h89/cpm-dir-fixup.py $(R2R_PD)/$(PRODUCT).h8d
@echo "$(R2R_PD)/$(PRODUCT).h8d: B:$(PRODUCT_UPPER) [URL]"
12 changes: 6 additions & 6 deletions hirestxt-mod-lib.mk
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
HIRESTXT_LIB_VERSION :=
CURRENT_TARGET = coco

HIRESTXT_LIB = _cache
HIRESTXT_CACHE = _cache

# A directory that already holds libhirestxt.a (e.g. a local hirestxt-mod
# build) is used as is; anything else gets the release downloaded into it.
ifneq ($(wildcard $(HIRESTXT_LIB)/libhirestxt.a),)
ifneq ($(wildcard $(HIRESTXT_CACHE)/libhirestxt.a),)
HIRESTXT_LIB_LOCAL = 1
HIRESTXT_LIB_VERSION_DIR = $(HIRESTXT_LIB)
HIRESTXT_LIB_VERSION_DIR = $(HIRESTXT_CACHE)
else
# Blank version means the latest release.
ifeq ($(strip $(HIRESTXT_LIB_VERSION)),)
override HIRESTXT_LIB_VERSION := $(shell curl -s https://api.github.com/repos/RichStephens/hirestxt-mod/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
endif
HIRESTXT_LIB_VERSION_DIR = $(HIRESTXT_LIB)/hirestxt-$(HIRESTXT_LIB_VERSION)-$(CURRENT_TARGET)
HIRESTXT_LIB_VERSION_DIR = $(HIRESTXT_CACHE)/hirestxt-$(HIRESTXT_LIB_VERSION)-$(CURRENT_TARGET)
endif
HIRESTXT_LIB_PATH = $(HIRESTXT_LIB_VERSION_DIR)
HIRESTXT_LIB_DOWNLOAD_URL = https://github.com/RichStephens/hirestxt-mod/releases/download/$(HIRESTXT_LIB_VERSION)/hirestxt-mod-bin-$(HIRESTXT_LIB_VERSION).tar.gz
HIRESTXT_LIB_DOWNLOAD_FILE = $(HIRESTXT_LIB)/hirestxt-mod-bin-$(HIRESTXT_LIB_VERSION).tar.gz
HIRESTXT_LIB_DOWNLOAD_FILE = $(HIRESTXT_CACHE)/hirestxt-mod-bin-$(HIRESTXT_LIB_VERSION).tar.gz
HIRESTXT_LIB_BASENAME := $(notdir $(HIRESTXT_LIB_PATH))
HIRESTXT_LIB_SYMLINK := libhirestxt.a

Expand All @@ -38,7 +38,7 @@ ifndef HIRESTXT_LIB_LOCAL
exit 1; \
fi; \
echo "Downloading hirestxt-mod for $(CURRENT_TARGET) version $(HIRESTXT_LIB_VERSION) from $(HIRESTXT_LIB_DOWNLOAD_URL)"; \
mkdir -p $(HIRESTXT_LIB); \
mkdir -p $(HIRESTXT_CACHE); \
mkdir -p $(HIRESTXT_LIB_VERSION_DIR); \
curl -sL $(HIRESTXT_LIB_DOWNLOAD_URL) -o $(HIRESTXT_LIB_DOWNLOAD_FILE); \
echo "Unzipping to $(HIRESTXT_LIB_VERSION_DIR)"; \
Expand Down
53 changes: 52 additions & 1 deletion mekkogx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ build, and the top-level Makefile defines *what* to build.
The **project鈥檚 Makefile** is the only file you should need to edit
when starting a new project. It contains:

- Your project鈥檚 `PRODUCT` name.
- Your project鈥檚 `PRODUCT` name, or `PRODUCTS` if several binaries
share one disk image.
- The `PLATFORMS` you want to build for.
- The `SRC_DIRS` containing your source code.
- Optional project-specific rules or custom targets.
Expand Down Expand Up @@ -157,6 +158,56 @@ With `SRC_DIRS = src src/%PLATFORM%`, building for `c64` would expand `%PLATFORM
- `src/commodore`
- `src/eightbit`

### Multiple binaries on one disk (`PRODUCTS`)

`PRODUCT` names a single binary, and everything found in `SRC_DIRS` is
compiled into it. To build several binaries and put them all onto one
disk image, list them in `PRODUCTS` and name the shared image with
`PRODUCTS_DISK_NAME`:

```
PRODUCTS = hello world
PRODUCTS_DISK_NAME = games
```

Each product is built by its own pass with its own object directory, so
products can be compiled with different settings. Any variable prefixed
with a product name is appended to the matching variable while that
product is being built:

```
SRC_DIRS = src src/%PLATFORM%

hello_SRC_DIRS = src/hello
hello_CFLAGS = -DGREETING=1

world_SRC_DIRS = src/world
world_CFLAGS = -DGREETING=2
```

Notes:

* Products are built in the order listed, so a library can be declared
ahead of the binaries that link against it.
* A product ending in `.lib` is built as a library and is *not* copied
onto the disk image.
* A single-product disk boots straight into that product. A
multi-product disk boots a selector instead, so any of the products
can be chosen: the ProDOS selector (Bitsy Bye) on Apple II, and the
MyPicoDos menu on Atari. Other platforms load programs by name and
are unaffected.
* On Apple II `BASIC.SYSTEM` is left off a multi-product disk. While it
is present ProDOS hands control to BASIC when a program quits;
without it control returns to the selector, so the next product can
be picked straight away. Set `DISK_BASIC_SYSTEM = BASIC.SYSTEM` to
put it back.
* Platforms that have no disk image simply build every binary into
`r2r/<platform>/`.
* If a product needs a completely different set of sources rather than
extra ones, leave the shared `SRC_DIRS` unset and give every product
its own.
* Projects that use `PRODUCT` on its own are unaffected.

### The `r2r` "Ready 2 Run" Target

The `r2r` target is the **default build output** for a platform. It
Expand Down
104 changes: 96 additions & 8 deletions mekkogx/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,37 @@ PLATFORM := $(basename $(notdir $(lastword $(PLATFORM_MK))))
PLATFORM_UC := $(shell echo "$(PLATFORM)" | tr '[:lower:]' '[:upper:]')
$(info Building for PLATFORM=$(PLATFORM))

include $(CURDIR)/Makefile
MEKKO_CONFIG ?= Makefile
$(info MEKKO_CONFIG=$(MEKKO_CONFIG))
include $(CURDIR)/$(MEKKO_CONFIG)

# Define GIT_VERSION to be used in macro define to CFLAGS, includes
# tag if available, short commit hash, appends '*' if changes haven't
# been commited
GIT_VERSION := $(shell git rev-parse --short HEAD)$(shell git status --porcelain | grep -q '^[ MADRCU]' && echo '*')

# A pass with MEKKO_PRODUCT set builds that one product, without it the
# pass builds the disk from all of PRODUCTS
ifeq ($(strip $(MEKKO_PRODUCT)),)
MEKKO_MULTI := $(if $(strip $(PRODUCTS)),1,)
else
PRODUCT := $(MEKKO_PRODUCT)
endif

# <product>_CFLAGS is appended to CFLAGS, and so on for any variable
ifneq ($(MEKKO_MULTI),1)
ifneq ($(strip $(PRODUCT)),)
$(foreach v,$(filter $(PRODUCT)_%,$(.VARIABLES)), \
$(eval $(patsubst $(PRODUCT)_%,%,$(v)) += $(value $(v))))
endif
endif

IS_LIBRARY := $(if $(filter %.lib,$(PRODUCT)),1,0)
ifeq ($(IS_LIBRARY),1)
ifeq ($(MEKKO_MULTI),1)
PRODUCT := $(PRODUCTS_DISK_NAME)
PRODUCT_BASE = $(PRODUCT)
BUILD_DISK = $(DISK)
else ifeq ($(IS_LIBRARY),1)
PRODUCT_BASE = $(basename $(PRODUCT))
BUILD_LIB = $(LIBRARY)
else
Expand All @@ -57,16 +79,34 @@ PC_DEFAULT = $(PC_$(TOOLCHAIN_UC))
endif

R2R_PD := $(R2R_DIR)/$(PLATFORM)
OBJ_DIR := $(BUILD_DIR)/$(PLATFORM)
OBJ_DIR := $(BUILD_DIR)/$(PRODUCT)/$(PLATFORM)
CACHE_PLATFORM := $(CACHE_DIR)/$(PLATFORM)
MKDIR_P ?= mkdir -p

# Lets the disk pass name executables it did not build itself
EXECUTABLE ?= $(R2R_PD)/$(PRODUCT_BASE)$(EXEC_SUFFIX)

# Library products are built but not copied onto the disk
ifeq ($(MEKKO_MULTI),1)
DISK_EXECUTABLES ?= $(foreach p,$(filter-out %.lib,$(PRODUCTS)),$(R2R_PD)/$(p)$(EXEC_SUFFIX))
else
DISK_EXECUTABLES ?= $(BUILD_EXEC)
endif

DISK_BOOT_EXEC ?= $(firstword $(DISK_EXECUTABLES))

# Expand PLATFORM_COMBOS entries into a lookup form
# c64+=commodore,eightbit -> c64 commodore eightbit
# PLATFORM_COMBOS is a flat list of entries like "dragon+=coco"
# $1 = the platform to expand
get_combos = $(foreach e,$(PLATFORM_COMBOS),\
$(if $(filter $1+=%, $(e)), $(lastword $(subst +=, ,$(e)))))
comma := ,
define get_combos
$(foreach e,$(PLATFORM_COMBOS), \
$(if $(filter $1+=%,$(e)), \
$(subst $(comma), ,$(lastword $(subst +=, ,$(e)))) \
) \
)
endef

# Expands patterns with %PLATFORM% to the platform + its combos
expand_platform_pattern = \
Expand Down Expand Up @@ -146,15 +186,35 @@ vpath %.s $(SRC_DIRS_EXPANDED)
vpath %.asm $(SRC_DIRS_EXPANDED)
vpath %.pas $(SRC_DIRS_EXPANDED)

.PHONY: clean debug r2r $(PLATFORM)/r2r disk $(PLATFORM)/disk
.PHONY: clean debug r2r $(PLATFORM)/r2r disk $(PLATFORM)/disk $(PLATFORM)/release

clean::
rm -rf $(OBJ_DIR) $(CACHE_PLATFORM) $(R2R_PD)
rm -rf $(OBJ_DIR) $(CACHE_PLATFORM) $(R2R_PD) $(RELEASE_DIR)

debug::
echo 'What should debug target do?'
exit 1

.PHONY: product products
product: $(BUILD_EXEC) $(BUILD_LIB)

# Built in the order PRODUCTS lists them so a library can come first
products:
@for p in $(PRODUCTS); do \
$(MAKE) -f $(PLATFORM_MK) MEKKO_CONFIG=$(MEKKO_CONFIG) MEKKO_PRODUCT=$$p product \
|| exit 1; \
done

ifeq ($(MEKKO_MULTI),1)
r2r:: products
ifneq ($(strip $(BUILD_DISK)),)
$(BUILD_DISK): products
endif

clean::
rm -rf $(foreach p,$(PRODUCTS),$(BUILD_DIR)/$(p)/$(PLATFORM))
endif

# These targets allow adding platform-specific steps from the top-level Makefile.
# Examples:
# coco/r2r:: coco/custom-step1
Expand All @@ -164,6 +224,7 @@ debug::
# The double-colon form appends without overwriting existing deps.
r2r:: $(PLATFORM)/r2r
disk:: $(PLATFORM)/disk
release:: $(PLATFORM)/release

# Fallback rule so every <platform>/disk-post target exists.
# Does nothing by default (@:).
Expand All @@ -185,6 +246,21 @@ $(PLATFORM)/executable-post::
$(PLATFORM)/library-post::
@:

RELEASE_INCLUDES ?= $(foreach dir,$(INCLUDE_DIRS),$(wildcard $(dir)/*.h $(dir)/*.inc))
RELEASE_INCLUDES += $(wildcard Changelog.md)
RELEASE_VERSION ?= $(shell git describe --tags --abbrev=0 2>/dev/null || echo dev)
PRODUCT_DASH = $(subst .,-,$(PRODUCT))
VERSION_CLEAN = $(patsubst v%,%,$(RELEASE_VERSION))
RELEASE_ZIP = $(RELEASE_DIR)/$(PRODUCT_DASH)-$(PLATFORM)-$(VERSION_CLEAN).zip
RELEASE_LIB = $(RELEASE_DIR)/$(PRODUCT_BASE)-$(PLATFORM)-$(VERSION_CLEAN).lib

$(PLATFORM)/release: $(RELEASE_ZIP)
$(RELEASE_ZIP):: $(LIBRARY)
$(MKDIR_P) $(RELEASE_DIR)
cp $(BUILD_LIB) $(RELEASE_LIB)
zip -j $@ $(RELEASE_INCLUDES) $(RELEASE_LIB)
rm $(RELEASE_LIB)

# include autodeps
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
Expand All @@ -206,9 +282,21 @@ else
$(FUJINET_LIB) | tr '\n' '|')))
ifeq ($(strip $(FUJINET_LIB_LDLIB)),)
ifeq ($(FUJINET_LIB_OPTIONAL),)
$(error fujinet-lib not available)
$(error fujinet-lib not available for $(PLATFORM))
else
$(info fujinet-lib not available, but skipping because FUJINET_LIB_SKIP_MISSING is set)
endif
endif
endif # FUJINET_LIB

HIRESTXT_LIB ?= __UNDEFINED__
ifeq ($(HIRESTXT_LIB),__UNDEFINED__)
$(info HIRESTXT_LIB not defined)
else
ifneq ($(filter coco dragon,$(PLATFORM)),)
$(eval $(subst |,$(_newline),$(shell PLATFORM=$(PLATFORM) CACHE_DIR=$(CACHE_DIR) \
$(MWD)/hirestxtlib.py $(HIRESTXT_LIB) | tr '\n' '|')))
else
$(info HIRESTXT_LIB ignored for PLATFORM=$(PLATFORM) (coco/dragon only))
endif
endif # HIRESTXT_LIB
Loading
Loading