I've got DevKitPro working on Game Boy Advance, but I've run into a few problems. The biggest one I see is that my code is assembled at 0x00000000 instead of the normal 0x08000000 for ROM cartridges. My understanding is that C compilers don't use an .org directive to create the code at a specified memory location; rather the linker is supposed to take care of all that for me. But it seems to be placing the code at the "wrong" address. The game will run correctly, but I imagine this is because it's running on an emulator and the emulator doesn't care that it's located somewhere it shouldn't be. How do I get the code to be "assembled" at 0x08000000?
I'm new to the concept of makefiles, compilers, linkers, etc. so I probably have everything set up very poorly. I'll show my makefile, batch script that runs the makefile, and the C code that's being compiled. I've also included the objdump in case that's relevant.
C code:
// LIBGBA HEADERS
#include <gba_console.h>
#include <gba_video.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <stdio.h>
#include <stdlib.h>
// GAME-SPECIFIC INCLUDES
#include "M:\SrcGBA\PaintBoyAdvance\include\bitmap.h"
#include "M:\SrcGBA\PaintBoyAdvance\include\bitmap.c" //BITMAP SCREEN FUNCTIONS
//---------------------------------------------------------------------------------
// Program entry point
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
// the vblank interrupt must be enabled for VBlankIntrWait() to work
// since the default dispatcher handles the bios flags no vblank handler
// is required
irqInit();
irqEnable(IRQ_VBLANK);
// consoleDemoInit();
REG_DISPCNT = 0x1403;
while (1) {
VBlankIntrWait();
}
}
Batch script:
@echo off
set path=C:\devkitPro\;%path%
cd M:\SrcGBA\PaintBoyAdvance
make
if not "%errorlevel%"=="0" goto Abandon
C:\devkitPro\devkitARM\bin\arm-none-eabi-objdump -h M:\SrcGBA\PaintBoyAdvance\build\paintboyadvance.o
C:\Users\puppy\Documents\VisualBoyAdvance\visualboyadvance-m.exe M:\SrcGBA\PaintBoyAdvance\PaintBoyAdvance.gba
:Abandon
exit
Makefile:
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/gba_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary data
# GRAPHICS is a list of directories containing files to be processed by grit
#
# All directories are specified relative to the project directory where
# the makefile is found
#
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
INCLUDES := include
DATA := data
MUSIC :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
SPECS := -specs=gba.specs
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi\
-ffreestanding \
$(ARCH)
CFLAGS := $(INCLUDE)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(INCLUDE) -Wl,-Map,$(notdir [email protected])
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lmm -lgba
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBGBA)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
ifneq ($(strip $(MUSIC)),)
export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir))
BINFILES += soundbank.bin
endif
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).gba : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SOURCES) : $(HFILES)
#---------------------------------------------------------------------------------
# The bin2o rule should be copied and modified
# for each extension used in the data directories
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
# rule to build soundbank from music files
#---------------------------------------------------------------------------------
soundbank.bin soundbank.h : $(AUDIOFILES)
#---------------------------------------------------------------------------------
@mmutil $^ -osoundbank.bin -hsoundbank.h
#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
Output of object dump:
M:\SrcGBA\PaintBoyAdvance\build\paintboyadvance.o: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000268 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 0000029c 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 0000029c 2**0
ALLOC
3 .comment 00000024 00000000 00000000 0000029c 2**0
CONTENTS, READONLY
4 .ARM.attributes 0000002a 00000000 00000000 000002c0 2**0
CONTENTS, READONLY
EDIT: As requested here is the output of the batch file:
\SrcGBA\PaintBoyAdvance\compile.bat paintboyadvance.c M:\SrcGBA\PaintBoyAdvance\source nopause
Process started >>>
paintboyadvance.c
linking cartridge
built ... PaintBoyAdvance.gba
ROM fixed!
M:\SrcGBA\PaintBoyAdvance\build\paintboyadvance.o: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000280 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 000002b4 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 000002b4 2**0
ALLOC
3 .comment 00000024 00000000 00000000 000002b4 2**0
CONTENTS, READONLY
4 .ARM.attributes 0000002a 00000000 00000000 000002d8 2**0
CONTENTS, READONLY
The key is to use a linker script. I will just post a complete working example.
startup.s
notmain.c
memmap
rommap
build
Now vba lets you run multiboot files as well as gba files
One uses EWRAM the other the GAME ROM.
You do not have to use both a MEMORY and SECTIONS in the linker script, can hard code addresses in the sections but I recommend memory being there as well. I recommend against using (rwx) stuff, YMMV.
The name rom does not matter - you could call it pickle and it will be fine. It is just a name that you use to connect a memory range definition to one or more sections. .text* inside is all the stuff in the object(s) that start with .text (.text is the instructions). The *() outside that means all objects that are fed through the linker you could specify specific objects for each entry to have finer control (beware the linker script language is not as nice and perfect as you expect, sometimes calling out objects does not work the way you would think).
Then the .text up front is so the output binary uses that name, you could call that .baseball and it would work just fine just tools that you feed that file would not know what to do so just using .text is a good idea.
You add more things like this
and it will process those in order in that the .text stuff goes in the output first then .rodata then .bss and last .data. Also note that since no files or objects are called out here the command line drives the order of things as well.
we need the entry point code to be first so that file goes first on the command line (easier than complicating it with made up section names, etc).
Note as soon as you build and before you try commit something to a flash
Look at the disassembly:
If you swap the files on the command line
game over, that is going to just crash. certainly won't run like you hope/expect (you might get lucky sometimes, but in general this is a fail).
I left room in the bootstrap for logo data and such to get it to work as a real rom for mgba or a real gba with the right cartridge (some have a menu and you do not have to have the cartridge header right). At the multiboot level you do not need to have all of that space in there just start with the bootstrap.
You could get by with
-Ttext=0x08000000as a quick and dirty hack, but with gnu ld that still uses a default linker script that is built into the toolchain you are using, and you are just tweaking that section type. And you start to get into strange things if you push that with more complicated stuff. So you can see how trivial it is to use a linker script, they do not have to be complicated. So many people tend to grossly over complicate, and to be honest the gnu linker script language can be painful as it does not always work in a sane and consistent manner. I prefer to do most of the work elsewhere and not in the linker script.