├── Makefile ├── README.md ├── build.mk └── src ├── hello.bin └── main.c /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(and $(strip $(DEVKITPRO)),$(strip $(DEVKITARM))),) 2 | $(error Make sure DEVKITPRO and DEVKITARM are correctly set in your environment.) 3 | endif 4 | 5 | # Name of your ROM 6 | # 7 | # Add _mb to the end to build a multiboot ROM. 8 | PROJECT := gba-template 9 | 10 | # Uncomment this if you're building a library 11 | # 12 | # BUILD_LIB := yes 13 | 14 | # Options for gbafix (optional) 15 | # 16 | # Title: 12 characters 17 | # Game code: 4 characters 18 | # Maker code: 2 characters 19 | # Version: 1 character 20 | ROM_TITLE := 21 | ROM_GAMECODE := 22 | ROM_MAKERCODE := 23 | ROM_VERSION := 24 | 25 | # 26 | # Files 27 | # 28 | # All options support glob patterns like `src/*.c`. 29 | # 30 | 31 | # Binary files to process with bin2s 32 | BINARY_FILES := src/hello.bin 33 | 34 | # Audio files to process with mmutil 35 | AUDIO_FILES := 36 | 37 | # Graphics files to process with grit 38 | # 39 | # Every file requires an accompanying .grit file, 40 | # so gfx/test.png needs gfx/test.grit 41 | GRAPHICS := 42 | 43 | # Source files to compile 44 | SOURCES := src/main.c 45 | 46 | # Include directories 47 | INCLUDES := 48 | 49 | # 50 | # Dependencies 51 | # 52 | 53 | # Library directories, with /include and /lib 54 | LIBDIRS := $(DEVKITPRO)/libgba $(DEVKITPRO)/libtonc 55 | 56 | # Libraries to link 57 | LIBS := tonc 58 | 59 | # 60 | # Directories 61 | # 62 | 63 | # All build output goes here 64 | BUILDDIR := build 65 | 66 | # 67 | # Build Options 68 | # 69 | 70 | # Compiler flags (all languages) 71 | ALLFLAGS := -Wall -Wextra -g3 -gdwarf-4 -O2 \ 72 | -ffunction-sections -fdata-sections \ 73 | -masm-syntax-unified \ 74 | -D_DEFAULT_SOURCE 75 | 76 | # C compiler flags 77 | CFLAGS := -std=c99 78 | 79 | # C++ compiler flags 80 | CXXFLAGS := -std=c++20 -fno-rtti -fno-exceptions 81 | 82 | # Assembler flags (as passed to GCC) 83 | ASFLAGS := 84 | 85 | # Linker flags (as passed to GCC) 86 | LDFLAGS := -mthumb \ 87 | $(if $(filter %_mb,$(PROJECT)),-specs=gba_mb.specs,-specs=gba.specs) 88 | 89 | # Uncomment this if you want to use Link Time Optimization 90 | # 91 | # USE_LTO := yes 92 | 93 | include build.mk 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GBA Makefile Template 2 | 3 | This repository provides a solid template Makefile for GBA development with 4 | devkitARM, supporting C, C++, and Assembly. 5 | 6 | Note that this template requires GNU Make 4.3. Most systems should have it, 7 | however LTS Linux distros like Debian 10 or Ubuntu 20.04 might not. 8 | Check with `make --version`. 9 | 10 | ## Usage 11 | 12 | - Modify the options inside `Makefile` to suit your needs, and run `make`. 13 | - To get verbose output, run `make V=1` or `make VERBOSE=1`. 14 | - To run a clean build, run `make -B` 15 | 16 | build.mk contains all of the build code, and you usually don't need to touch it 17 | at all. 18 | 19 | ## Features 20 | 21 | - Soundbank creation with mmutil 22 | - Graphics processing with grit 23 | - Binary conversion with bin2s 24 | - Use of relative and symlinked file paths (e.g. `../etc/hi.c`) 25 | - Creating a multiboot build by adding `_mb` suffix to project name 26 | - `*.iwram.*`, `*.arm.*`, and `*.thumb.*` filenames override ARM/THUMB code 27 | - Building with LTO 28 | - Building a static library 29 | - Specifying title, game code, etc. for gbafix 30 | - Running ROM in mGBA (by default) via `make run`. Custom runner via `make RUNNER=... run` 31 | - Automatic `.gitignore` creation in build directory 32 | 33 | ## License 34 | 35 | This repository is licensed under the [CC0 1.0 Universal License][CC0]. 36 | 37 | [CC0]: https://creativecommons.org/publicdomain/zero/1.0/ 38 | -------------------------------------------------------------------------------- /build.mk: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | 3 | # 4 | # Functions 5 | # 6 | 7 | # String equality 8 | eq = $(if $(and $(findstring x$(1),x$(2)),$(findstring x$(2),x$(1))),y,) 9 | 10 | # Map relative directory names into build tree 11 | pathmap = $(1)$(subst $(subst ,, ),/,$(foreach component,$(subst /, ,$(2)),$(if $(call eq,$(component),..),__,$(component))))$(3) 12 | 13 | glob = $(foreach pat,$(1),$(wildcard $(pat))) 14 | 15 | # Object and dependency filename functions 16 | obj = $(call pathmap,$(BUILDDIR)/obj/,$(1),.o) 17 | dep = $(call pathmap,$(BUILDDIR)/dep/,$(1),.d) 18 | gen_s = $(call pathmap,$(BUILDDIR)/gen_src/,$(1),$(2)) 19 | gen_i = $(call pathmap,$(BUILDDIR)/gen_inc/,$(1),$(2)) 20 | gen_b = $(call pathmap,$(BUILDDIR)/gen_bin/,$(1),$(2)) 21 | 22 | # Language detection by filenames (taken from GCC docs) 23 | is-c = $(if $(filter %.c %.i %.h,$(1)),y,) 24 | is-cxx = $(if $(filter %.ii %.cc %.cp %.cxx %.cpp %.CPP %.c++ %.C \ 25 | %.hh %.H %.hp %.hxx %.hpp %.HPP %.h++ %.tcc,$(1)),y,) 26 | is-asm = $(if $(filter %.s %.S %.sx,$(1)),y,) 27 | 28 | # devkitPRO-style architecture flag overrides 29 | arch = $(or \ 30 | $(if $(filter %.arm %.iwram,$(basename $(1))),-marm,),\ 31 | $(if $(filter %.thumb,$(basename $(1))),-mthumb,)) 32 | 33 | # Language-specific compiler flag list generation 34 | flags = $(ALLFLAGS) $(or \ 35 | $(if $(call is-c,$(1)),$(CFLAGS),),\ 36 | $(if $(call is-cxx,$(1)),$(CXXFLAGS),),\ 37 | $(if $(call is-asm,$(1)),$(ASFLAGS),)) $(call arch,$(1)) 38 | 39 | # 40 | # Internal Variables 41 | # 42 | 43 | # Verbosity 44 | SILENT := $(if $(VERBOSE)$(V),,@) 45 | 46 | # Toolchain prefix 47 | TOOLCHAIN := $(DEVKITARM)/bin/arm-none-eabi 48 | 49 | # Tools 50 | CC := $(TOOLCHAIN)-gcc 51 | CXX := $(TOOLCHAIN)-g++ 52 | AR := $(TOOLCHAIN)-ar 53 | OBJCOPY := $(TOOLCHAIN)-objcopy 54 | LD := $(if $(call is-cxx,$(SOURCES)),$(CXX),$(CC)) 55 | BIN2S := $(DEVKITPRO)/tools/bin/bin2s 56 | GBAFIX := $(DEVKITPRO)/tools/bin/gbafix 57 | MMUTIL := $(DEVKITPRO)/tools/bin/mmutil 58 | GRIT := $(DEVKITPRO)/tools/bin/grit 59 | RUNNER := mgba-qt 60 | 61 | # Primary build artifacts 62 | ELFFILE := $(BUILDDIR)/$(PROJECT).elf 63 | ROMFILE := $(BUILDDIR)/$(PROJECT).gba 64 | MAPFILE := $(BUILDDIR)/$(PROJECT).map 65 | LIBFILE := $(BUILDDIR)/lib$(PROJECT).a 66 | 67 | # Default compiler flags 68 | ALLFLAGS += \ 69 | -mthumb \ 70 | -mcpu=arm7tdmi \ 71 | -mabi=aapcs \ 72 | -mfloat-abi=soft \ 73 | $(LIBDIRS:%=-I%/include) \ 74 | $(INCLUDES:%=-iquote %) \ 75 | $(if $(USE_LTO),-flto,-fno-lto) \ 76 | 77 | # Default linker flags 78 | LDFLAGS += \ 79 | -Wl,-n \ 80 | -Wl,--gc-sections \ 81 | -Wl,-Map,$(MAPFILE) \ 82 | $(LIBDIRS:%=-L%/lib) \ 83 | $(LIBS:%=-l%) \ 84 | $(if $(USE_LTO),-flto,-fno-lto) \ 85 | 86 | # Gbafix flags 87 | GFFLAGS := \ 88 | $(if $(strip $(ROM_TITLE)),'-t$(strip $(ROM_TITLE))',) \ 89 | $(if $(strip $(ROM_GAMECODE)),'-c$(strip $(ROM_GAMECODE))',) \ 90 | $(if $(strip $(ROM_MAKERCODE)),'-m$(strip $(ROM_MAKERCODE))',) \ 91 | $(if $(strip $(ROM_VERSION)),'-r$(strip $(ROM_VERSION))',) 92 | 93 | SOURCES := $(call glob,$(SOURCES)) 94 | 95 | BINARY := $(call glob,$(BINARY_FILES)) 96 | 97 | AUDIO := $(call glob,$(AUDIO_FILES)) 98 | 99 | GRAPHICS:= $(filter-out %.grit,$(call glob,$(GRAPHICS))) 100 | 101 | GEN_B := $(if $(strip $(AUDIO)),$(call gen_b,soundbank.bin,)) 102 | 103 | BINARY += $(GEN_B) 104 | 105 | GEN_S := $(foreach bin,$(BINARY),$(call gen_s,$(bin),.S)) \ 106 | $(foreach gfx,$(GRAPHICS),$(call gen_s,$(gfx),.s)) 107 | 108 | SOURCES += $(GEN_S) 109 | 110 | GEN_I := $(foreach bin,$(BINARY),$(call gen_i,$(bin),.h)) \ 111 | $(foreach gfx,$(GRAPHICS),$(call gen_i,$(gfx),.h)) \ 112 | $(if $(strip $(AUDIO)),$(call gen_i,soundbank.h,),) 113 | 114 | ALLFLAGS += $(foreach dir,$(dir $(GEN_I)),-iquote $(dir)) 115 | 116 | # Build artifacts 117 | OBJECTS := $(foreach source,$(SOURCES),$(call obj,$(source))) 118 | DEPENDS := $(foreach source,$(SOURCES),$(call dep,$(source))) 119 | DIRS := $(dir $(BUILDDIR) $(OBJECTS) $(DEPENDS) $(GEN_S) $(GEN_I) $(GEN_B)) 120 | 121 | # 122 | # Targets 123 | # 124 | 125 | ifeq ($(strip $(BUILD_LIB)),) 126 | $(ROMFILE): $(ELFFILE) 127 | $(ELFFILE): $(OBJECTS) 128 | else 129 | $(LIBFILE): $(OBJECTS) 130 | endif 131 | 132 | $(OBJECTS): | dirs $(GEN_I) 133 | 134 | define bin2s = 135 | $(call gen_s,$(1),.S) $(call gen_i,$(1),.h) &: $(1) 136 | @echo "bin2s $$<" 137 | $$(SILENT)$$(BIN2S) -a 2 -H $(call gen_i,$(1),.h) $$< > $(call gen_s,$(1),.S) 138 | endef 139 | 140 | define grit = 141 | $(call gen_s,$(1),.s) $(call gen_i,$(1),.h) &: $(1) $(1).grit 142 | @echo "grit $$<" 143 | $$(SILENT)$$(GRIT) -o$(call gen_s,$(1),.s) -ff$(basename $(1)).grit $$< 144 | $$(SILENT)mv $(call gen_s,$(1),.h) $(call gen_i,$(1),.h) 145 | endef 146 | 147 | define compile = 148 | $(call obj,$(1)): $(1) 149 | endef 150 | 151 | # Process binary files 152 | $(foreach binary,$(BINARY),$(eval $(call bin2s,$(binary)))) 153 | 154 | # Process graphics files 155 | $(foreach graphic,$(GRAPHICS),$(eval $(call grit,$(graphic)))) 156 | 157 | # Compile sources 158 | $(foreach source,$(SOURCES),$(eval $(call compile,$(source)))) 159 | 160 | # 161 | # Rules 162 | # 163 | 164 | $(BUILDDIR)/gen_bin/soundbank.bin $(BUILDDIR)/gen_inc/soundbank.h &: $(AUDIO) 165 | @echo "mmutil $@" 166 | $(SILENT)$(MMUTIL) $^ -o$(BUILDDIR)/gen_bin/soundbank.bin -h$(BUILDDIR)/gen_inc/soundbank.h 167 | 168 | %.o: 169 | @echo "compile $<" 170 | $(SILENT)$(CC) -c -o $@ $(call flags,$<) -MMD -MP -MF $(call dep,$<) $< 171 | 172 | %.a: 173 | @echo "library $@" 174 | $(SILENT)rm -f $@ 175 | $(SILENT)$(AR) rcs $@ $^ 176 | 177 | %.elf: 178 | @echo "link $@" 179 | $(SILENT)$(LD) -o $@ $^ $(LDFLAGS) 180 | 181 | %.gba: 182 | @echo "rom $@" 183 | $(SILENT)$(OBJCOPY) -O binary $< $@ 184 | $(SILENT)$(GBAFIX) $@ $(GFFLAGS) >&- 185 | 186 | dirs: 187 | $(SILENT)mkdir -p $(DIRS) 188 | $(SILENT)echo '*' > $(BUILDDIR)/.gitignore 189 | 190 | clean: 191 | @echo "clean $(BUILDDIR)" 192 | $(SILENT)rm -rf $(BUILDDIR) 193 | 194 | run: $(ELFFILE) 195 | @echo "run $(ELFFILE)" 196 | $(SILENT)$(RUNNER) $< 197 | 198 | .PHONY: dirs clean run 199 | 200 | -include $(DEPENDS) 201 | -------------------------------------------------------------------------------- /src/hello.bin: -------------------------------------------------------------------------------- 1 | Hello, world! -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hello.bin.h" 3 | 4 | int main(void) 5 | { 6 | REG_DISPCNT = DCNT_MODE0 | DCNT_BG0; 7 | 8 | tte_init_chr4c_default(0, BG_CBB(0) | BG_SBB(31)); 9 | tte_set_pos(92, 68); 10 | tte_write((char*)hello_bin); 11 | 12 | irq_init(NULL); 13 | irq_enable(II_VBLANK); 14 | 15 | while (1) 16 | { 17 | VBlankIntrWait(); 18 | } 19 | } 20 | --------------------------------------------------------------------------------