# This is GNU Make rules file
# You need Hitech-C compiler and GNU sh-utils installed.
# vim: set syntax=make foldmethod=marker:
.PHONY: all clean distclean dist TAGS 
.SUFFIXES:
.SUFFIXES: .c .as .s .obj .lib .dll
.LIBPATTERNS: %.lib
.DELETE_ON_ERROR:

srcdir = $(CURDIR)

# where HiTech C compiler installed
ifndef HTC_Z80
$(error environment variable HTC_Z80 is not defined!)
endif

SHELL = /bin/sh
MAKESHELL = sh

HTC_Z80_BIN ?= $(HTZ_Z80)\bin
HTC_Z80_HLP ?= $(HTC_Z80)\hlp
HTC_Z80_INC ?= $(HTC_Z80)\inc
HTC_Z80_LIB ?= $(HTC_Z80)\lib
TEMP ?= \tmp
TMDIR ?= $(TEMP)

CC = $(HTC_Z80_BIN)\zc  
CFLAGS =
AS = $(HTC_Z80_BIN)\zas 
ASFLAGS = -E1
LD = $(HTC_Z80_BIN)\hlink
LDFLAGS =
AR = $(HTC_Z80_BIN)\libr
ARFLAGS = r

ifdef COMSPEC # dos
CPP = $(HTC_Z80_BIN)\cpp
GNUCPP = cpp -traditional
else # unix
CPP = $(CC) -E
GNUCPP = $(CPP)
endif
CPPFLAGS = 

CO = co
COFLAGS = 

RM = rm -f


# HiTech C compiler parts
P1 = $(HTC_Z80_BIN)\p1
CGEN = $(HTC_Z80_BIN)\cgen
OPT = $(HTC_Z80_BIN)\optim
DUMP = $(HTC_Z80_BIN)\dump
OBJTOHEX = $(HTC_Z80_BIN)\objtohex 

# don't change anything if you are not understood that you are doing!
CPP_OPT = -SP2,2,2,2,2,2,2 -S1,2,2,4,4,4 -DHI_TECH_C -Dz80 -E1
P1_OPT = -QPi,port -QDi,code -QQ,fast -QM,nmi -E1 -S -D 
CGEN_OPT =

# passing parameters to HiTech compiler over the pipe
# XXX: Do I need 2>&1 ?  see old makefiles
# pipe = $(SHELL) -c "echo $(quote)$(2)$(quote)" | $(1)
pipe = echo '$(2)' | $(1)

ld = $(call pipe,$(LD),$(1))
ar = $(call pipe,$(AR),$(1))
as = $(AS) $(1)
cpp = $(call pipe,$(CPP),$(1))
objtohex = $(OBJTOHEX) $(1)
# cc = $(call pipe,$(CC),$(1))  # NOTE: CC is really not exist, but emulated

# {{{ CC emulation code follows...

# I'd like GNU make & m4

cpp_defs = $(CPP_OPT) $(if \
    $(filter -CPM -Bc,$(1)), -D_HOSTED -DCPM -DSMALL_MODEL, $(if \
    $(filter -Bs,$(1)), -DSMALL_MODEL, $(if \
    $(filter -Bl,$(1)), -DLARGE_MODEL )))

tmpf = $(TMPDIR)\htc$$$$

define c2cpp # flags .c .o temp.*
$(call cpp, $(call cpp_defs,$(1)) $(filter -W% -I% -D% -U%, $(1)) \
    $(filter -I%,$(filter -I-,$(1))) \
    $(if $(filter -I-,$(1)),,-I$(HTC_Z80_INC)) \ ### FIXME: !!!!!
    $(2) $(3))
endef
### FIXME: !!!! include !
.error
define c2as
( $(call c2cpp,$(1),$(2),$(tmpf).__0,$(4)) && \
$(call pipe,$(P1), $(P1_OPT) \
	$(patsubst -ASMLIST,-S,$(patsubst -g%,-D,\
		$(filter -W% -g% -ASMLIST,$(1)))) \
	$(tmpf).__0 $(tmpf).__1 $(tmpf).__2) && \
$(CGEN) $(CGEN_OPT) \
	$(patsubst -g%,-D%,$(patsubst -P8,-MPORT8,\
	$(patsubst -Bl,-MLGR_CODE,$(patsubst -Z180,-MZ180,\
		$(filter -W% -Zg% -Z180 -Bl -P8 -g%,$(1)))))) \
	$(tmpf).__1 $(if $(filter -O,$(1)),$(tmpf).__0,$(3)) && \
$(if $(filter -O,$(1)),$(OPT) $(4).__0 $(3),:) && \
$(RM) $(tmpf).__[012] ) || (a=$?; $(RM) $(tmpf).__[012] $(3))
endef
define s2obj
$(call as, $(ASFLAGS) \
	$(patsubst -cr%,-C%,$(patsubst -ASMLIST,-L,$(patsubst -O,-J,\
		$(filter -O -x -ASMLIST -cr%, $(1))))) \
	-o$(3) $(2))
endef
define c2obj
$(call c2as,$(1),$(2),$(4).__1,$(4)) && \
$(call s2obj,$(1),$(4).__1,$(3),$(4))
endef
define as2obj
$(if $(filter -P,$(1)),\
	$(call c2cpp,$(1),$(2),$(4).__0,$(4)) && \
	$(call s2obj,$(1),$(4).__0,$(3),$(4)), \
$(call s2obj,$(1),$(2),$(3),$(4)) )
endef

# NOTE: don't leave spaces at end of lines
ifnempty = $(if $(1),$(1),$(2))
o_name = $(call ifnempty,$(patsubst -o%,%,$(filter -o%,$(1))),$(2))

define all2cpp # args
$(foreach cc_arg,\
  $(filter %.c $(if $(filter -P,$(1)),%.as),$(filter-out -%,$(1))),\
      $(call c2cpp,$(1),$(cc_arg),$(call o_name,$(1),),$(cc_arg:%.c=%)) && ) :
endef

define all2s # args
$(foreach cc_arg,$(filter %.c,$(filter-out -%,$(1))),\
    $(call c2as,$(1),$(cc_arg),\
       $(call o_name,$(1),$(cc_arg:%.c=%.as)),\
       $(cc_arg:%.c=%)) && ) :
endef

define all2obj # args
$(foreach cc_arg,$(filter %.c %.as,$(filter-out -%,$(1))),\
  $(if $(filter %.c,$(cc_arg)),\
      $(call c2obj,$(1),$(cc_arg),\
          $(call o_name,$(1),$(cc_arg:%.c=%.obj)),\
          $(cc_arg:%.c=%)),\
  $(if $(filter %.as,$(cc_arg)),\
      $(call as2obj,$(1),$(cc_arg),\
          $(call o_name,$(1),$(cc_arg:%.as=%.obj)),\
          $(cc_arg:%.as=%)))) && ) :
endef

# startup object
rt_obj = rtz$(if $(filter -Z180,$(1)),801,80)$(call \
	ifnempty,$(patsubst -B%,%,$(filter -B%,$(1))),$(if \
	$(filter -CPM,$(1)),c,s)).obj

# C library
c_lib = z$(if $(filter -Z180,$(1)),801,80)$(call \
	ifnempty,$(patsubst -B%,%,$(filter -B%,$(1))),$(if \
	$(filter -CPM,$(1)),c,s))$(if \
	$(filter -Lf,$(1)),f,$(if \
	$(filter -Ll,$(1)),l,c)).lib

comma = ,

# -A%(rom),%(ram),%(ramsize) recognized 
A_addrs = $(subst $(comma), ,$(patsubst -A%,%,$(filter -A%,$(1))))
A_rom = $(firstword $(call A_addrs,$(1)))
A_ram = $(word 2,$(call A_addrs,$(1)))
A_size = $(word 3,$(call A_addrs,$(1)))
A_top = $(shell printf "0%xh" $$(( \
    $(patsubst %h,0x%,$(call A_size,$(1)) + $(call A_ram,$(1))) )))

# NOTE: large model psects are not well defined...
define psects 
$(if $(filter -CPM -Bc,$(1)),\
-pvectors=0$(comma)lowtext$(comma)text$(comma)strings$(comma)const$(comma)im2vecs$(comma)data$(comma)bss \
-pnvram=bss$(comma)heap ,\
$(if $(filter -A%,$(1)),$(if \
$(filter -Bs,$(1)),\
-pvectors=$(call A_rom,$(1))$(comma)lowtext$(comma)text$(comma)strings$(comma)const$(comma)$(if \
$(filter -ROMDATA,$(1)),data$(comma))im2vecs \
-pbaseram=040000h \
-pramstart=$(call A_ram,$(1))$(comma)$(if \
$(filter -ROMDATA,$(1)),bss,data=ramstart/im2vecs$(comma)bss/.)$(comma)stack=$(call A_top,$(1)) \
-pnvram=bss$(comma)heap ,\
$(if $(filter -Bl,$(1)),\
-pvectors=$(call A_rom,$(1))$(comma)lowtext$(comma)text$(comma)strings$(comma)const$(comma)$(if \
$(filter -ROMDATA,$(1)),data$(comma))im2vecs \
-pbaseram=40000h$(comma)basecode=c000h/2000h$(comma)FARCODE=basecode/ \
-pramstart=$(call A_ram,$(1))/baseram$(comma)$(if \
$(filter -ROMDATA,$(1)),bss,data=ramstart/im2vecs$(comma)bss/ramstart)$(comma)stack=$(call A_top,$(1)) \
-SFARCODE=04000h$(comma)1000h -pnvram=bss$(comma)heap ,\
$(error no default psect(s): unknown memory model))),\
$(error no -A<romaddr>,<ramaddr><ramsize> option given)))
endef

# -ROM%/-ACODE=%  -B{c,s,l}/ -CPM/ -r/
# -l%(lib)? -Ll -Lf -L-% -L- -ROMDATA/ 
define obj2obj  # args(obj|lib)
$(call ld, -z \
    $(patsubst -L-%, -%,\
    $(patsubst -H%, -h+%,\
         $(filter -H% -x -M% -L-%,$(1)))) \
    $(if $(filter -CPM -Bc,$(1)),,\
         $(patsubst -ROM%, -ACODE=%,$(filter -ROM%,$(1)))) \
    $(if $(filter -r,$(1)), $(if $(filter -CPM -Bc,$(1)), -U__getargs)) \
    $(call psects) \
    -o$(call o_name,$(1),l.obj) \
    $(if $(filter -L-,$(1)),,$(call rt_obj,$(1))) \
    $(filter-out -%,$(1)) \
    $(if $(filter -L-,$(1)),,$(call c_lib,$(1))))
endef

define obj2all # args(obj|lib)
$(if $(filter -o%.obj,$(1)),\
    $(call obj2obj, $(1)),\
$(if $(filter -o%.lib,$(1)),\
    $(call ar, r $(patsubst -o%,%,$(filter -o%,$(1))) \
        $(filter %.obj,$(filter-out -%,$(1)))),\
$(call obj2obj, -ol.obj $(filter-out -o%,$(1))) && \
$(if $(filter -o%,$(1)),\
    $(if $(filter -o%.hex,$(1)),\
        $(call objtohex, l.obj $(patsubst -o%,%,$(filter -o%,$(1)))),\
    $(if $(filter -o%.com,$(1)),\
        $(call objtohex, -B100 l.obj $(patsubst -o%,%,$(filter -o%,$(1)))),\
    $(error unknown output file type: $(patsubst -o%,%,$(filter -o%,$(1)))))),\
$(if $(filter -CPM -Bc,$(1)),\
    $(call objtohex, -B100 l.obj l.com),\
$(error no output file name given)))))
endef

cc_csuf := $(if $(1),$(error unknown file(s) suffixes $(1)))

# NOTE: only this macro can be called directly
define cc  # args
$(call cc_csuf,$(filter-out %.as %.c %.obj %.lib,$(filter-out -%,$(1)))) \
\
$(if $(filter -S -c,$(1)),\
    $(if $(filter -o%,$(1)),\
        $(error can not specify -o with -S or -c and multiple compilations))) \
\
$(if $(filter -B%,$(1)),\
    $(if $(filter -CPM,$(1)),\
       $(error can not specify -CPM with -B simultaneously))) \
\
$(if $(filter -E,$(1)),$(call all2cpp,$(1)),\
$(if $(filter -S,$(1)),$(call all2s,$(1)),\
$(if $(filter -c,$(1)),$(call all2obj,$(1)),\
    $(call all2obj,$(filter-out -o%,$(1))) && \
    $(call obj2all,$(filter -%,$(1)) \
        $(patsubst %.as,%.obj,$(patsubst %.c,%.obj,\
	    $(filter-out -%,$(1))))))))
endef


# end of CC emulation code }}}


.obj.com:
	$(call cc, $(patsubst %,-L%,$(filter -%,$(LDFLAGS))) -o$@ $^)
.obj.hex:
	$(call cc, $(patsubst %,-L%,$(filter -%,$(LDFLAGS))) -o$@ $^)
.c.obj:
	$(call cc, -c $(CFLAGS) $(CPPFLAGS) -o$@ $<)
.as.obj:
	$(call cc, -c $(CPPFLAGS) $< $@)
.obj.lib:
	$(call ar, $(ARFLAGS) $@ $^)


