# SplitBit Programs Makefile
# Anachronaut
#
# Builds every SplitBit program into build/, and keeps track of which libraries
# each one includes so that editing a library reassembles whatever depends on it.
#
#   make            Assemble everything.
#   make clean      Throw away build/.
#   make run-hello  Assemble and run one program.

ASM ?= ../Assembler
EMU ?= ../SplitBit
BUILD ?= build

# Libraries are included by bare name, so the assembler is told where to find them.
# CosmOS owns the filesystem library and the service names, so it is a place to look too.
INCLUDES = -I Libraries -I CosmOS/Source

# The programs worth building. Every one lives in a directory that says what kind it is:
# Examples/ is what you read to learn, Loader/ is the standalone loader CosmOS grew out of,
# CosmOS/ is the system. Files in Libraries/ are left out because they have no entry point
# of their own, and the ones in testPrograms/ are covered by 'make test' in the parent
# directory.
PROGRAMS = \
	CosmOS/Source/cosmos.asm \
	Examples/hello.asm \
	Examples/printHello.asm \
	Examples/inputTest.asm \
	Examples/replCalculator.asm \
	Examples/Fibonacci/8bitFibonacci.asm \
	Examples/Fibonacci/16bitFibonacci.asm \
	Examples/Fibonacci/32bitFibonacci.asm \
	Examples/primeSieve/8bitSieve.asm \
	Examples/primeSieve/16bitSegmentedSieve.asm \
	Examples/primeSieve/16bitSegmentedSieveModern.asm \
	Examples/gameOfLife/16x16Life.asm \
	Examples/gameOfLife/16x16LifeModern.asm \
	Loader/loader.asm \
	Loader/loadable.asm

BINARIES = $(PROGRAMS:%.asm=$(BUILD)/%.bin)
DEPENDENCIES = $(BINARIES:.bin=.d)

all: $(BINARIES)

# -M writes out which source files went into the binary, in the form of a make rule.
$(BUILD)/%.bin: %.asm
	@mkdir -p $(@D)
	$(ASM) $(INCLUDES) -M $(@:.bin=.d) -o $@ $<

# Assemble and run a single program, as in 'make run-hello'.
#
# THE NAME IS THE PROGRAM'S, NOT ITS PATH. This used to be a pattern rule against
# $(BUILD)/%.bin, which worked while every program sat at the top of this directory and
# stopped working the moment they were filed into Examples/ - 'make run-hello' had nothing
# to match. Somebody trying the one command the header advertises should not be the way
# that is discovered, so the name is now looked up among the programs instead.
run-%:
	@target=`echo $(BINARIES) | tr ' ' '\n' | grep -E "(^|/)$*\.bin$$" | head -1`; \
	if [ -z "$$target" ]; then \
		echo "There is no program called '$*'. What there is:"; \
		echo $(PROGRAMS) | tr ' ' '\n' | sed 's|.*/||;s|\.asm$$||;s|^|  |'; \
		exit 1; \
	fi; \
	$(MAKE) --no-print-directory "$$target" && $(EMU) "$$target"

# ---- CosmOS ----
#
#   make cosmos       Assemble the system and everything it can load.
#   make cosmos-disk  ... and put the loadable programs on a disk image.
#   make run-cosmos   ... and boot the machine with that disk in the drive.
#
# Programs in Apps/ say where they live with #Base, so the assembler writes them out as
# loadable programs rather than as boot images. They are named .sbx to keep that
# difference visible: a .bin is something the machine boots, a .sbx is something a
# running system loads.

DISKTOOL ?= ../SplitDisk
COSMOS = $(BUILD)/CosmOS/Source/cosmos.bin
APPS = $(patsubst CosmOS/Apps/%.asm,$(BUILD)/CosmOS/Apps/%.sbx,$(wildcard CosmOS/Apps/*.asm))
COSMOS_DISK = $(BUILD)/cosmos.img
DEPENDENCIES += $(APPS:.sbx=.d)

$(BUILD)/CosmOS/Apps/%.sbx: CosmOS/Apps/%.asm
	@mkdir -p $(@D)
	$(ASM) $(INCLUDES) -M $(@:.sbx=.d) -o $@ $<

# The assembler that runs on the machine. It is not in Apps/ because it is not one file:
# it has a directory of its own, the way the C assembler does. Its own pieces are found
# beside it without being told, since an include is looked for next to the file that asked
# for it before anywhere else; only services.asm needs the include path.
NATIVE_ASM = $(BUILD)/CosmOS/Assembler/Asm.sbx
DEPENDENCIES += $(NATIVE_ASM:.sbx=.d)

$(NATIVE_ASM): CosmOS/Assembler/Asm.asm
	@mkdir -p $(@D)
	$(ASM) $(INCLUDES) -M $(@:.sbx=.d) -o $@ $<

cosmos: $(COSMOS) $(APPS) $(NATIVE_ASM)

# Made from scratch every time, so that what is on it is what is in Apps/ now and not
# also whatever used to be.
#
# TWENTY FOUR DIRECTORY BLOCKS, WHICH IS ONE HUNDRED AND NINETY TWO NAMES. It was eight,
# and that is sixty four, of which thirty nine were already spoken for. The two ceilings a
# disk has were nowhere near each other: at the average file on here, twenty six blocks,
# sixty four names run out with the disk forty one per cent full. Names were going to be
# gone long before space was.
#
# A directory block is 256 bytes and holds eight entries, so the difference costs sixteen
# blocks of four thousand and ninety six - three tenths of one per cent - to buy a hundred
# and twenty eight more names. The superblock has carried this number per disk since the
# format was written, so nothing but this line knows what it is.
$(COSMOS_DISK): $(APPS) $(NATIVE_ASM) testPrograms/stringKeyword.asm \
                $(wildcard CosmOS/Apps/*.asm) \
                $(wildcard CosmOS/Source/*.asm) $(wildcard CosmOS/Assembler/*.asm)
	@mkdir -p $(@D)
	rm -f $@
	$(DISKTOOL) format $@ 4096 24
	@# THREE DIRECTORIES, WHICH IS WHAT A CLEAN INSTALL LOOKS LIKE: what you run, what you
	@# assemble, and what those include. It was thirty nine files in one list with
	@# cosmos.asm sitting between fileStream.asm and sbfs.asm.
	@#
	@# The split is by ROLE rather than by which directory the host keeps them in. /Source
	@# holds the things you name to the assembler and /Lib the things they pull in, which is
	@# a distinction the host makes with -I and the machine now makes with a search path of
	@# its own: an include is looked for beside you and then in /Lib. Without that, every
	@# source that calls a service would have to sit in the same directory as services.asm
	@# and there would be nothing to organise.
	$(DISKTOOL) mkdir $@ /Apps
	$(DISKTOOL) mkdir $@ /Source
	$(DISKTOOL) mkdir $@ /Lib
	@# What you run. /Apps is the second place the shell looks when a word it does not know
	@# turns out to be a program, so anything in here starts by name from anywhere.
	@for app in $(APPS); do \
		$(DISKTOOL) put $@ $$app /Apps/`basename $$app` >/dev/null || exit 1; done
	$(DISKTOOL) put $@ $(NATIVE_ASM) /Apps/Asm.sbx
	@# What you assemble. All of it, because an assembler with nothing to assemble is a
	@# demonstration of nothing:
	@#
	@#   > cd /Source
	@#   /Source> Asm cosmos.asm      the system it is running on
	@#   /Source> Asm Asm.asm         and the thing that built it
	@#
	@# Both come out byte for byte what the host tool makes from the same source.
	@#
	@# Keys.asm brings a vector of its own, so assembling it exercises the version two
	@# header and the Vector Segment: the loader installs its handler, the console
	@# interrupts into it, and the shell takes the vector back at exit.
	@#
	@# strings.asm is the odd one out on purpose. It has no #Include and no #Base, so it
	@# comes out as a boot image rather than a loadable program, and the difference between
	@# the two is visible on one disk.
	$(DISKTOOL) put $@ CosmOS/Source/cosmos.asm /Source/cosmos.asm
	$(DISKTOOL) put $@ CosmOS/Assembler/Asm.asm /Source/Asm.asm
	$(DISKTOOL) put $@ CosmOS/Assembler/readTest.asm /Source/readTest.asm
	$(DISKTOOL) put $@ CosmOS/Assembler/tokenTest.asm /Source/tokenTest.asm
	$(DISKTOOL) put $@ CosmOS/Apps/hello.asm /Source/hello.asm
	$(DISKTOOL) put $@ CosmOS/Apps/Say.asm /Source/Say.asm
	$(DISKTOOL) put $@ CosmOS/Apps/Keys.asm /Source/Keys.asm
	$(DISKTOOL) put $@ testPrograms/stringKeyword.asm /Source/strings.asm
	@# And what those include. Everything here is named by an #Include somewhere and by
	@# nothing else, which is exactly what makes it a library rather than a source.
	@for f in CosmOS/Source/console.asm CosmOS/Source/fileStream.asm \
	          CosmOS/Source/sbfs.asm CosmOS/Source/services.asm CosmOS/Source/text.asm \
	          CosmOS/Assembler/classify.asm CosmOS/Assembler/labels.asm \
	          CosmOS/Assembler/numbers.asm CosmOS/Assembler/scratch.asm \
	          CosmOS/Assembler/source.asm CosmOS/Assembler/table.asm \
	          CosmOS/Assembler/token.asm CosmOS/Assembler/vectors.asm; do \
		$(DISKTOOL) put $@ $$f /Lib/`basename $$f` >/dev/null || exit 1; done

# The system as well as the disk. Building only the image leaves whatever cosmos.bin was
# there before, or none at all, and then the disk is booted with a system that does not
# match the programs on it.
cosmos-disk: $(COSMOS) $(COSMOS_DISK)

run-cosmos: $(COSMOS) $(COSMOS_DISK)
	$(EMU) --disk $(COSMOS_DISK) $(COSMOS)

clean:
	rm -rf $(BUILD)

# Pull in the dependency rules written by -M above, so that touching a library
# reassembles every program that includes it.
-include $(DEPENDENCIES)

.PHONY: all clean cosmos cosmos-disk run-cosmos
