# SDCC Makefile Template written by Leo. C.  < erbl259-lmu /at/ yahoo.de >
# 
# based on
#     WinAVR Makefile Template written by Eric B. Weddington, Jörg Wunsch, et al.
#
# Released to the Public Domain
#
#
#----------------------------------------------------------------------------
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make filename.asm = Just compile filename.c into the assembler code only.
#
#
# To rebuild project do "make clean" then "make all".
#
# TODO: 
# - help target
# - 'make clean' löscht z.Zt. zu wenig Dateien. Immerhin besser als
#   andersrum. ;)
# - Variantenunterstützung (make debug/release)
# - Schalter --fno-omit-frame-pointer abhängig von der SDCC-Version
#   setzen. Der Bug, für den der Schalter ein work araound ist, ist
#   ab SVN Revision #9096 gefixed.
# - ...
#----------------------------------------------------------------------------

# Set the port to use (It may work with oher ports, but this is not tested)
#     z80    Generate code for the Zilog Z80 family of processors.
#     z180   Generate code for the Zilog Z180 family of processors.
#     r2k    Generate code for the Rabbit 2000 / Rabbit 3000 family of processors.
#     r3ka   Generate code for the Rabbit 3000A family of processors.
#     gbz80  Generate code for the LR35902 GameBoy Z80 processor.
PORT 	:= z80

# Target file name (without extension).
TARGETNAME := testproject

# List C source files here. (C dependencies are automatically generated.)
CSRCS	:= hello.c

# List Assembler source files here.
ASRCS	:=

# List any extra directories to look for include files here.
#     Each directory must be seperated by a space.
#     For a directory that has spaces, enclose it in quotes.
INCDIRS := 

# List your custom c startup file (crt0.s) here
#     Leave blank or comment out if the system provided 
#     startup file should be used.
STARTUP := 

# EPROM base address
CODE_LOC := 0x0100
# RAM base address. Leave empty, if the data segment immediatly follows code segment.
DATA_LOC := 

# Object files directory and oject files suffix
#     To put object files in current directory, use a dot (.), do NOT make
#     this an empty or blank macro!
#OBJDIR := build-$(sdcc_version)-$(PORT)
#OBJDIR := .
OBJDIR := build
OEXT := .rel

# Target files directory
#     To put bin and hex files in current directory, use a dot (.), do NOT make
#     this an empty or blank macro!
TARGETDIR := $(OBJDIR)

# Place -D or -U options here for C sources
#CDEFS = -DDEBUG

# List any extra directories to look for libraries here.
#     Each directory must be seperated by a space.
#     For a directory that has spaces, enclose it in quotes.
EXTRALIBDIRS	:=

# List any extra libraries here.
EXTRALIBS	:= 

# Path to sdcc binaries
PREFIX := /usr/local/bin/

# ---------------------------------------------------------------
# Define compiler options

# Compiler flag to set the C Standard level.
#     c89    = C89 standard (slightly incomplete)
#     sdcc89 = C89 standard with SDCC extensions (default)
#     c99    = C99 standard (incomplete)
#     sdcc99 = C99 standard with SDCC extensions
#     c11    = C11 standard (very incomplete)
CSTANDARD = sdcc99

#Debug options
#     TODO:
# --fverbose-asm

# Optimization options
#     --nogcse              Disable the GCSE optimisation
#     --nolabelopt          Disable label optimisation
#     --noinvariant         Disable optimisation of invariants
#     --noinduction         Disable loop variable induction
#     --nojtbound           Don't generate boundary check for jump tables
#     --noloopreverse       Disable the loop reverse optimisation
#     --no-peep             Disable the peephole assembly file optimisation
#     --no-reg-params       On some ports, disable passing some parameters in registers
#     --peep-asm            Enable peephole optimization on inline assembly
#     --peep-return         Enable peephole optimization for return instructions
#     --no-peep-return      Disable peephole optimization for return instructions
#     --peep-file           <file> use this extra peephole file
#     --opt-code-speed      Optimize for code speed rather than size
#     --opt-code-size       Optimize for code size rather than speed
#     --max-allocs-per-node  Maximum number of register assignments considered at each node of the tree decomposition
#     --nolospre            Disable lospre
#     --lospre-unsafe-read  Allow unsafe reads in lospre
OPTOPT += --opt-code-size 
#OPTOPT += --max-allocs-per-node 100000

# Special options for the z80 port:
#      --callee-saves-bc     Force a called function to always save BC
#      --portmode=           Determine PORT I/O mode (z80/z180)
#      --asm=                Define assembler name (rgbds/asxxxx/isas/z80asm)
#      --codeseg             <name> use this name for the code segment
#      --constseg            <name> use this name for the const segment
#      --no-std-crt0         For the z80/gbz80 do not link default crt0.rel
#      --reserve-regs-iy     Do not use IY (incompatible with --fomit-frame-pointer)
#      --oldralloc           Use old register allocator
#      --fno-omit-frame-pointer  Do not omit frame pointer
ZOPT = --fno-omit-frame-pointer

# C compiler options
#      --less-pedantic       Disable some of the more pedantic warnings
#      --disable-warning     <nnnn> Disable specific warning
#      --Werror              Treat the warnings as errors
#      --debug               Enable debugging symbol output
#      --cyclomatic          Display complexity of compiled functions
# Internal debugging options:
#      --dump-ast            Dump front-end AST before generating i-code
#      --dump-i-code         Dump the i-code structure at all stages
#      --dump-graphs         Dump graphs (control-flow, conflict, etc)
#      --i-code-in-asm       Include i-code as comments in the asm file
#      --fverbose-asm        Include code generator comments in the asm output
#
# Code generation options:
#      --fomit-frame-pointer  Leave out the frame pointer.
#      --all-callee-saves    callee will always save registers used
#      --no-c-code-in-asm    don't include c-code as comments in the asm file
#      --no-peep-comments    don't include peephole optimizer comments

CFLAGS := -m$(PORT)
CFLAGS	+= --std-$(CSTANDARD)
CFLAGS	+= $(patsubst %,-I%,$(INCDIRS))
CFLAGS	+= $(CDEFS)
CFLAGS	+= $(OPTOPT) $(ZOPT)

# ---------------------------------------------------------------
# Assembler options (sdasxxxx):
#      -x -d -q		Hex (default), Octal or Decimal listing
#      -g 		Undefined symbols made global
#      -a 		All user symbols made global
#      -b 		Display .define substitutions in listing
#      -bb		and display without .define substitutions
#      -c 		Disable instruction cycle count in listing
#      -j 		Enable NoICE Debug Symbols
#      -y 		Enable SDCC  Debug Symbols
#      -l 		Create list   output (out)file[.lst]
#      -o 		Create object output (out)file[.rel]
#      -s 		Create symbol output (out)file[.sym]
#      -p 		Disable listing pagination
#      -u 		Disable .list/.nlist processing
#      -w 		Wide listing format for symbol table
#      -z 		Disable case sensitivity for symbols
#      -f 		Flag relocatable references by  `   in listing file
#      -ff		Flag relocatable references by mode in listing file
AFLAGS	= -plosff

# ---------------------------------------------------------------
# Linker options (sdcc compiler driver):
#  -l                        Include the given library in the link
#  -L                        Add the next field to the library search path
#      --lib-path            <path> use this path to search for libraries
#      --out-fmt-ihx         Output in Intel hex format
#      --out-fmt-s19         Output in S19 hex format
#      --code-loc            <nnnn> Code Segment Location
#      --code-size           <nnnn> Code Segment size
#      --stack-loc           <nnnn> Stack pointer initial value
#      --data-loc            <nnnn> Direct data start location
#      --no-optsdcc-in-asm   Do not emit .optsdcc in asm

# Linker options (sdldxxx):
#	-n		No echo of commands to stdout
#	-f		Command File input as file[.lnk]
# Relocation
#	-b 		area base address=expression
#	-g		global symbol=expression
# Map format:
#	-m		Map output generated as (out)file[.map]
#	-w		Wide listing format for map file
#	-x		Hexadecimal (default)
#	-d		Decimal
#	-q		Octal
# Output:
#	-i 		Intel Hex as (out)file[.i--]
#	-s   		Motorola S Record as (out)file[.s--]
#	-j		NoICE Debug output as (out)file[.noi]
#	-y		SDCDB Debug output as (out)file[.cdb]
# List:
#	-u		Update listing file(s) with link data as file(s)[.rst]
#
# Libraries:
#	-k		Library path specification, one per -k
#	-l 		Library file specification, one per -l
# End:
#	-e   		or null line terminates input	
LDFLAGS	= -nmwxj


# ---------------------------------------------------------------
# Define programs and commands.

SHELL	= sh
CC	= $(PREFIX)sdcc
AS	= $(PREFIX)sdasz80
LD	= $(PREFIX)sdldz80
# sdcc comes with sdobjcopy, but that doesn't work for me
OBJCOPY	= objcopy
HEX2BIN	= $(OBJCOPY) --input-target=ihex --output-target=binary --gap-fill 0xff
RM	= rm -f
RMDIR	= rm -rf
CP	= cp
SED	= sed
AWK	= awk

# ---------------------------------------------------------------

# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
endif

ifeq ($(strip $(TARGETNAME)),)
 $(error 'TARGETNAME is empty!')
endif
ifeq ($(strip $(CSRCS) $(ASRCS)),)
 $(error 'No source files specified!')
endif

sdcc_version  := $(shell $(CC) --version | awk '/SDCC/ {print $$4}')
sdcc_revision := $(shell $(CC) --version | awk '/SDCC/ {print substr($$5,2)}')

TARGET = $(TARGETDIR)/$(TARGETNAME)

LIBDIRS += $(EXTRALIBDIRS)
LIBDIRS += $(shell $(CC) --print-search-dirs $(CFLAGS) | \
	$(AWK) '/^[a-zA-Z]+:/ { islibdir = $$1 ~ /^libdir:/ } ; \
	       !/^[a-zA-Z]+:/ { if (islibdir) print }')

LDLIBS += $(EXTRALIBS)
LDLIBS += $(PORT)

ifeq ($(strip $(STARTUP)),)
  CRT0 = $(firstword $(wildcard $(addsuffix /crt0.rel,$(LIBDIRS))))
else
  STARTUPOBJ = $(OBJDIR)/$(notdir $(basename $(STARTUP)))$(OEXT)
  CRT0 = $(STARTUPOBJ)
endif

ASRCS += $(STARTUP)

# Define all source files.
SOURCES = $(CSRCS) $(ASRCS)

# Define all object files.
OBJS = $(addprefix $(OBJDIR)/,$(addsuffix $(OEXT),$(basename $(filter-out $(STARTUP),$(SOURCES)))))

# Name and location of linker script
LDSCRIPT = $(OBJDIR)/$(TARGETNAME).lnk

CFLAGS := $(strip $(CFLAGS))

# ---------------------------------------------------------------
# Autodependency generation 
#  see Paul D. Smith <psmith@gnu.org>, http://make.paulandlesley.org/autodep.html

define makedepend =
 #$(CC) -MM $(CFLAGS) -o .dep/$(*F).d $<
 (echo -n '$(OBJDIR)/' && $(CC) -MM $(CFLAGS) $< ) >.dep/$(*F).d
 $(CP) .dep/$(*F).d .dep/$(*F).P
 $(SED) -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
     -e '/^$$/ d' -e 's/$$/ :/' < .dep/$(*F).d >> .dep/$(*F).P
 $(RM) .dep/$(*F).d
endef

# Generate linker script (not used)
define makelinkscript =
echo $(LDFLAGS) >$@
echo '-i $(TARGET).ihx' >>$@
echo '-b _CODE = $(CODE_LOC)' >>$@
echo '-b _DATA = $(DATA_LOC)' >>$@
for i in $(LIBDIRS);do echo "-k $$i" >>$@; done
for i in $(LDLIBS);do echo "-l $$i" >>$@; done
for i in $(CRT0) $(OBJS);do echo "$$i" >>$@; done
echo '-e' >>$@
endef

BINSIZE = stat -c %s $(TARGET).bin

#-b _CODE=$(CODE_LOC) \

ld_segs = -b _CODE=$(CODE_LOC)
ifneq ($(strip $(DATA_LOC)),)
ld_segs += -b _DATA=$(DATA_LOC)
endif

# ---------------------------------------------------------------

.PHONY: all
all: sizebefore bin sizeafter

.DELETE_ON_ERROR:

.SUFFIXES:
.SUFFIXES: .c .s .rel


.PHONY: hex ihx bin
hex ihx: $(TARGET).ihx
bin: $(TARGET).bin

%.bin: %.ihx
	$(Q)$(HEX2BIN) $< $@

$(TARGET).ihx: $(CRT0) $(OBJS)
	$(Q)$(LD) $(LDFLAGS) -i $@ \
	$(ld_segs) \
	$(patsubst %,-k %,$(LIBDIRS)) \
	$(patsubst %,-l %,$(LDLIBS)) \
	$^

# generate file with linker commands
#$(TARGET).ihx: $(LDSCRIPT)
#	$(LD) -n -f $<
#
#$(LDSCRIPT):  $(OBJS) $(STARTUPOBJ)
#	@$(makelinkscript)

$(STARTUPOBJ): $(STARTUP)
	$(Q)$(AS) $(AFLAGS) $@ $< 

$(OBJDIR)/%$(OEXT): %.c
	@$(makedepend)
	$(Q)$(CC) $(CFLAGS) -o $@ -c $<

$(OBJDIR)/%.asm: %.c
	$(Q)$(CC) $(CFLAGS) -o $@ -S $<


$(OBJS) : Makefile

# ---------------------------------------------------------------

.PHONY: sizebefore sizeafter
sizebefore:
	@if test -f $(TARGET).bin; then echo -n "Binary size before: "; $(BINSIZE); \
	2>/dev/null; fi

sizeafter:
	@if test -f $(TARGET).bin; then echo -n "Binary size now: "; $(BINSIZE); \
	2>/dev/null; fi

# ---------------------------------------------------------------
# Sanity checks 
# TODO: ...

.PHONY: check
check:


# ---------------------------------------------------------------
# cleaning
#

clean_exts = .asm .lst .rst .sym $(OEXT)
clean_names = $(basename $(OBJS) $(STARTUPOBJ))

clean_list += $(LDSCRIPT)
clean_list += $(foreach name,$(clean_names), $(foreach ext,$(clean_exts),$(name)$(ext)))

#clean_list := $(filter-out $(subst $(OBJDIR)/,,$(addprefix $(OBJDIR)/,$(SOURCES))),$(clean_list))

.PHONY: clean realclean
clean:
	$(RM) $(clean_list)

realclean: clean
	$(RM) $(TARGET).ihx $(TARGET).bin *~ 
	$(RMDIR) .dep

# ---------------------------------------------------------------

# Create object files directory
$(shell mkdir $(OBJDIR) 2>/dev/null)

# Create target files directory
$(shell mkdir $(TARGETDIR) 2>/dev/null)

# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*.P)

# ---------------------------------------------------------------
# makefile debugging

d = $(OBJDIR) $(TARGETDIR)
da = $(abspath $(d))
df = $(filter-out $(CURDIR), $(da))
dr = $(subst $(CURDIR)/,,$(df))

ddd = $(subst $(CURDIR)/,,$(filter-out $(CURDIR), $(abspath $(OBJDIR) $(TARGETDIR))))

.PHONY: printvars
printvars:
	@echo \
	'------------------------------------------------------------------------'
#	@echo '$(.VARIABLES)' | tr ' ' '\012' | sort
	@echo LIBDIRS: $(LIBDIRS)
	@echo CRT0: $(CRT0)
	@echo SOURCES: $(SOURCES)
	@echo OBJS: $(OBJS)
	@echo clean_list: $(clean_list)
	@echo ----------- misc -------------
	@echo dr:  $(dr)
	@echo ddd: $(ddd)
#	echo $(TARGETDIR))
	echo $(wildcard $(TARGETDIR)/*)
	@echo SDCC Version: $(sdcc_version)
	@echo SDCC Revision: $(sdcc_revision)

