├── .gitignore ├── Makefile ├── README.md ├── graphics ├── metr.grit └── metr.png └── source └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.log 3 | *.sav 4 | *.gba 5 | *.lnk 6 | *.elf 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/gba_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # the LIBGBA path is defined in gba_rules, but we have to define LIBTONC ourselves 13 | #--------------------------------------------------------------------------------- 14 | LIBTONC := $(DEVKITPRO)/libtonc 15 | 16 | #--------------------------------------------------------------------------------- 17 | # TARGET is the name of the output 18 | # BUILD is the directory where object files & intermediate files will be placed 19 | # SOURCES is a list of directories containing source code 20 | # INCLUDES is a list of directories containing extra header files 21 | # DATA is a list of directories containing binary data 22 | # GRAPHICS is a list of directories containing files to be processed by grit 23 | # 24 | # All directories are specified relative to the project directory where 25 | # the makefile is found 26 | # 27 | #--------------------------------------------------------------------------------- 28 | TARGET := $(notdir $(CURDIR)) 29 | BUILD := build 30 | SOURCES := source 31 | INCLUDES := include 32 | DATA := 33 | MUSIC := 34 | GRAPHICS := graphics 35 | 36 | #--------------------------------------------------------------------------------- 37 | # options for code generation 38 | #--------------------------------------------------------------------------------- 39 | ARCH := -mthumb -mthumb-interwork 40 | 41 | CFLAGS := -g -Wall -O2\ 42 | -mcpu=arm7tdmi -mtune=arm7tdmi\ 43 | $(ARCH) 44 | 45 | CFLAGS += $(INCLUDE) 46 | 47 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 48 | 49 | ASFLAGS := -g $(ARCH) 50 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) 51 | 52 | #--------------------------------------------------------------------------------- 53 | # any extra libraries we wish to link with the project 54 | #--------------------------------------------------------------------------------- 55 | LIBS := -lmm -ltonc 56 | 57 | 58 | #--------------------------------------------------------------------------------- 59 | # list of directories containing libraries, this must be the top level containing 60 | # include and lib. 61 | # the LIBGBA path should remain in this list if you want to use maxmod 62 | #--------------------------------------------------------------------------------- 63 | LIBDIRS := $(LIBGBA) $(LIBTONC) 64 | 65 | #--------------------------------------------------------------------------------- 66 | # no real need to edit anything past this point unless you need to add additional 67 | # rules for different file extensions 68 | #--------------------------------------------------------------------------------- 69 | 70 | 71 | ifneq ($(BUILD),$(notdir $(CURDIR))) 72 | #--------------------------------------------------------------------------------- 73 | 74 | export OUTPUT := $(CURDIR)/$(TARGET) 75 | 76 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 77 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 78 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 79 | 80 | export DEPSDIR := $(CURDIR)/$(BUILD) 81 | 82 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 83 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 84 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 85 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 86 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 87 | 88 | ifneq ($(strip $(MUSIC)),) 89 | export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir)) 90 | BINFILES += soundbank.bin 91 | endif 92 | 93 | #--------------------------------------------------------------------------------- 94 | # use CXX for linking C++ projects, CC for standard C 95 | #--------------------------------------------------------------------------------- 96 | ifeq ($(strip $(CPPFILES)),) 97 | #--------------------------------------------------------------------------------- 98 | export LD := $(CC) 99 | #--------------------------------------------------------------------------------- 100 | else 101 | #--------------------------------------------------------------------------------- 102 | export LD := $(CXX) 103 | #--------------------------------------------------------------------------------- 104 | endif 105 | #--------------------------------------------------------------------------------- 106 | 107 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 108 | 109 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 110 | 111 | export OFILES_GRAPHICS := $(PNGFILES:.png=.o) 112 | 113 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) $(OFILES_GRAPHICS) 114 | 115 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(PNGFILES:.png=.h) 116 | 117 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 118 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 119 | -I$(CURDIR)/$(BUILD) 120 | 121 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 122 | 123 | .PHONY: $(BUILD) clean 124 | 125 | #--------------------------------------------------------------------------------- 126 | $(BUILD): 127 | @[ -d $@ ] || mkdir -p $@ 128 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 129 | 130 | #--------------------------------------------------------------------------------- 131 | clean: 132 | @echo clean ... 133 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba 134 | 135 | 136 | #--------------------------------------------------------------------------------- 137 | else 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | 143 | $(OUTPUT).gba : $(OUTPUT).elf 144 | 145 | $(OUTPUT).elf : $(OFILES) 146 | 147 | $(OFILES_SOURCES) : $(HFILES) 148 | 149 | #--------------------------------------------------------------------------------- 150 | # The bin2o rule should be copied and modified 151 | # for each extension used in the data directories 152 | #--------------------------------------------------------------------------------- 153 | 154 | #--------------------------------------------------------------------------------- 155 | # rule to build soundbank from music files 156 | #--------------------------------------------------------------------------------- 157 | soundbank.bin soundbank.h : $(AUDIOFILES) 158 | #--------------------------------------------------------------------------------- 159 | @mmutil $^ -osoundbank.bin -hsoundbank.h 160 | 161 | #--------------------------------------------------------------------------------- 162 | # This rule links in binary data with the .bin extension 163 | #--------------------------------------------------------------------------------- 164 | %.bin.o %_bin.h : %.bin 165 | #--------------------------------------------------------------------------------- 166 | @echo $(notdir $<) 167 | @$(bin2o) 168 | 169 | #--------------------------------------------------------------------------------- 170 | # This rule creates assembly source files using grit 171 | # grit takes an image file and a .grit describing how the file is to be processed 172 | # add additional rules like this for each image extension 173 | # you use in the graphics folders 174 | #--------------------------------------------------------------------------------- 175 | %.s %.h: %.png %.grit 176 | #--------------------------------------------------------------------------------- 177 | @echo "grit $(notdir $<)" 178 | @grit $< -fts -o$* 179 | 180 | # make likes to delete intermediate files. This prevents it from deleting the 181 | # files generated by grit after building the GBA ROM. 182 | .SECONDARY: 183 | 184 | -include $(DEPSDIR)/*.d 185 | #--------------------------------------------------------------------------------------- 186 | endif 187 | #--------------------------------------------------------------------------------------- 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Quickstart 2 | 3 | > _"I want to make GBA homebrew, where do I begin?"_ 4 | 5 | 6 | 1) [Install devkitARM](https://devkitpro.org/wiki/Getting_Started) for your system. 7 | 8 | 2) [Read Tonc](https://www.coranac.com/tonc/text/) to learn all about GBA programming! 9 | * You can skip _"Setting up a development environment"_ because we did that in step 1. 10 | 11 | 3) Clone this repo and run `make` in your terminal. 12 | * If you used the graphical installer on Windows for step 1, you should use the _MSYS2_ terminal for this step. 13 | 14 | 15 | ## About this repo 16 | 17 | This is a minimal GBA example project using libtonc instead of libgba. 18 | 19 | The commit history exists to document the process of converting a _libgba_ example to a _libtonc_ example. 20 | 21 | ### Why? 22 | 23 | devkitPro provides two general-purpose libraries: _libgba_ and _libtonc_. Of the two, I recommend _libtonc_, because it has better documentation and more features (it's part of the accompanying material to the excellent Tonc tutorial). 24 | 25 | The trouble here is that all the GBA examples are using _libgba_, and converting the _libgba_ template to use _libtonc_ is non-trivial for those who are just getting started. -------------------------------------------------------------------------------- /graphics/metr.grit: -------------------------------------------------------------------------------- 1 | # 2 | # Metroid : 4bpp, not compressed. Only use the first 64 columns. 3 | # 4 | -gB4 -ar64 5 | -------------------------------------------------------------------------------- /graphics/metr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbadev-org/libtonc-template/90f957279a53fdcf253984b0a3e347e7068ba3ac/graphics/metr.png -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This file is autogenerated from the file in the graphics folder 4 | #include "metr.h" 5 | 6 | OBJ_ATTR obj_buffer[128]; 7 | OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer; 8 | 9 | void load_sprite(void) 10 | { 11 | // Load tiles and palette of sprite into video and palete RAM. Note that 12 | // regular memcpy() won't work. To write to VRAM, it is required to write in 13 | // units of 16 or 32 bytes. memcpy() doesn't guarantee the size of the 14 | // writes, but mempcy32() always writes in chunks of 32 bytes. 15 | memcpy32(&tile_mem[4][0], metrTiles, metrTilesLen / 4); 16 | memcpy32(pal_obj_mem, metrPal, metrPalLen / 4); 17 | 18 | oam_init(obj_buffer, 128); 19 | 20 | OBJ_ATTR *metr = &obj_buffer[0]; 21 | obj_set_attr(metr, 22 | ATTR0_SQUARE, // Square, regular sprite 23 | ATTR1_SIZE_64, // 64x64 pixels, 24 | ATTR2_PALBANK(0) | 0); // palette index 0, tile index 0 25 | 26 | // Set position 27 | obj_set_pos(metr, 176, 96); 28 | 29 | oam_copy(oam_mem, obj_buffer, 1); // Update first OAM object 30 | } 31 | 32 | int main(void) 33 | { 34 | irq_init(NULL); 35 | irq_enable(II_VBLANK); 36 | 37 | tte_init_chr4c_default(0, BG_CBB(0) | BG_SBB(31)); 38 | tte_set_pos(92, 68); 39 | tte_write("Hello World!"); 40 | 41 | load_sprite(); 42 | 43 | REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_OBJ | DCNT_OBJ_1D; 44 | 45 | while (1) { 46 | VBlankIntrWait(); 47 | } 48 | } 49 | --------------------------------------------------------------------------------