TARGET              := monitor
BUILDDIR            := build

METAL_C_SRCFILES    := $(wildcard *.c)
METAL_ASM_SRCFILES  := boot.asm
METAL_RELFILES      := $(METAL_ASM_SRCFILES:%.asm=$(BUILDDIR)/metal/%.rel) \
                       $(METAL_C_SRCFILES:%.c=$(BUILDDIR)/metal/%.rel)

METAL_CC            := sdcc
METAL_AS            := sdasz80
METAL_MAKEBIN       := makebin
METAL_FLAGS         := -mz80 --no-std-crt0 --code-loc 0xC080 --code-size 0x0F80 --data-loc 0xF000
METAL_CPPFLAGS      := -I src -I $(BUILDDIR)/metal/generated --disable-warning 283
METAL_BIN_SIZE      := 32768
METAL_RAM_SIZE      := 16384
METAL_IMAGE_SIZE    := 65536
RESIDENT_BASE       := 0xC000
RESIDENT_ROM_OFFSET := 128

TINYBASIC_BASE      := 0x1000
TINYBASIC_ROM_OFFSET := 4096
TINYBASIC_SRC       := apps/tinybasic.asm
TINYBASIC_BUILD     := $(BUILDDIR)/tinybasic

.DEFAULT_GOAL       := metal

.PHONY: all metal clean

all: metal

metal: $(BUILDDIR)/metal/$(TARGET).ihx $(BUILDDIR)/metal/$(TARGET).bin $(BUILDDIR)/metal/$(TARGET).prog.bin
	@stored=$$(wc -c < $(BUILDDIR)/metal/$(TARGET).prog.bin); \
	awk -v rom_capacity=$(METAL_BIN_SIZE) -v stored=$$stored -v ram_capacity=$(METAL_RAM_SIZE) '\
		$$1 ~ /^_(CODE|HOME|GSINIT|GSFINAL|INITIALIZER)$$/ || $$1 ~ /^_ITABLE/ { code += $$5 } \
		$$1 ~ /^_(DATA|INITIALIZED|BSEG|BSS|HEAP)$$/ { data += $$5 } \
		END { \
			printf "\nMemory usage:\n"; \
			printf "  ROM storage:  %5u / %5u bytes (%5.1f%%)\n", stored, rom_capacity, 100 * stored / rom_capacity; \
			printf "  Resident BIOS+monitor: %5u bytes (%5u code/vector + %5u static data)\n", code + data, code, data; \
		}' $(BUILDDIR)/metal/$(TARGET).map
	@printf "  Tiny BASIC:   %5u ROM bytes at 1000\n" $$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin)

$(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES)
	@mkdir -p $(@D)
	$(METAL_CC) $(METAL_FLAGS) -o $@ $(METAL_RELFILES)

$(BUILDDIR)/metal/$(TARGET).flat.bin: $(BUILDDIR)/metal/$(TARGET).ihx
	$(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@

$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(TINYBASIC_BUILD)/tinybasic.bin
	@payload_end=$$(awk '$$1 == "DEF" && $$2 == "resident_image_end" { print $$3 }' $(BUILDDIR)/metal/$(TARGET).noi); \
	payload_size=$$((payload_end - $(RESIDENT_BASE))); \
	test $$payload_size -gt 0; \
	test $$payload_size -le $$(( $(TINYBASIC_ROM_OFFSET) - $(RESIDENT_ROM_OFFSET) )); \
	dd if=$< of=$@ bs=1 count=$(TINYBASIC_ROM_OFFSET) status=none; \
	dd if=$< of=$@ bs=1 skip=$$(( $(RESIDENT_BASE) )) seek=$(RESIDENT_ROM_OFFSET) count=$$payload_size conv=notrunc status=none; \
	dd if=$(TINYBASIC_BUILD)/tinybasic.bin of=$@ bs=1 seek=$(TINYBASIC_ROM_OFFSET) conv=notrunc status=none

$(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(BUILDDIR)/metal/$(TARGET).prog.bin
	dd if=$< of=$@ bs=$(METAL_BIN_SIZE) count=1 status=none
	dd if=$(BUILDDIR)/metal/$(TARGET).prog.bin of=$@ conv=notrunc status=none

$(BUILDDIR)/metal/%.rel: %.c
	@mkdir -p $(@D)
	$(METAL_CC) $(METAL_FLAGS) $(METAL_CPPFLAGS) -o $@ -c $<

$(BUILDDIR)/metal/%.rel: %.asm
	@mkdir -p $(@D)
	$(METAL_AS) -Iinclude -I$(BUILDDIR)/metal/generated -plosgff -o $@ $<

$(TINYBASIC_BUILD)/tinybasic.rel: $(TINYBASIC_SRC)
	@mkdir -p $(@D)
	$(METAL_AS) -Iinclude -plosgff -o $@ $<

$(TINYBASIC_BUILD)/tinybasic.ihx: $(TINYBASIC_BUILD)/tinybasic.rel
	sdldz80 -n -i -m -j $(basename $@) $<

$(TINYBASIC_BUILD)/tinybasic.flat.bin: $(TINYBASIC_BUILD)/tinybasic.ihx
	$(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@

$(TINYBASIC_BUILD)/tinybasic.bin: $(TINYBASIC_BUILD)/tinybasic.flat.bin
	@tiny_size=$$(awk '$$1 == "TINYBASIC0" { print $$5 + 0 }' $(TINYBASIC_BUILD)/tinybasic.map); \
	test $$tiny_size -gt 0; \
	dd if=$< of=$@ bs=1 skip=$$(( $(TINYBASIC_BASE) )) count=$$tiny_size status=none

clean:
	rm -rf $(BUILDDIR)

-include $(EMU_DEPFILES)
