diff --git a/GodMoDeS.pnproj b/GodMoDeS.pnproj
new file mode 100644
index 0000000..24e0dd8
--- /dev/null
+++ b/GodMoDeS.pnproj
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/GodMoDeS.pnps b/GodMoDeS.pnps
new file mode 100644
index 0000000..a072584
--- /dev/null
+++ b/GodMoDeS.pnps
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..84eee96
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,193 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+.SECONDARY:
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM")
+endif
+
+include $(DEVKITARM)/ds_rules
+
+export VERSION_MAJOR := 1
+export VERSION_MINOR := 1
+export VERSION_PATCH := 0
+
+
+VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
+#---------------------------------------------------------------------------------
+# 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 files embedded using bin2o
+# GRAPHICS is a list of directories containing image files to be converted with grit
+#---------------------------------------------------------------------------------
+TARGET := GodMoDeS
+BUILD := build
+SOURCES := source
+INCLUDES := include source
+DATA := data
+GRAPHICS := gfx
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -mthumb -mthumb-interwork
+
+CFLAGS := -g -Wall -O2 \
+ -ffunction-sections -fdata-sections \
+ -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM9
+CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++11
+
+ASFLAGS := -g $(ARCH)
+LDFLAGS = -specs=ds_arm9.specs -g -Wl,--gc-sections $(ARCH) -Wl,-Map,$(notdir $*.map)
+
+#---------------------------------------------------------------------------------
+# any extra libraries we wish to link with the project (order is important)
+#---------------------------------------------------------------------------------
+LIBS := -lfat -lnds9
+
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(LIBNDS)
+
+#---------------------------------------------------------------------------------
+# 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 TOPDIR := $(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)))
+BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp)))
+PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+BINFILES := load.bin bootstub.bin
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES := $(addsuffix .o,$(BINFILES)) \
+ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+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)
+
+icons := $(wildcard *.bmp)
+
+ifneq (,$(findstring $(TARGET).bmp,$(icons)))
+ export GAME_ICON := $(CURDIR)/$(TARGET).bmp
+else
+ ifneq (,$(findstring icon.bmp,$(icons)))
+ export GAME_ICON := $(CURDIR)/icon.bmp
+ endif
+endif
+
+export GAME_TITLE := $(TARGET)
+
+.PHONY: bootloader bootstub clean arm7/$(TARGET).elf arm9/$(TARGET).elf
+
+all: bootloader bootstub $(TARGET).nds
+
+dist: all
+ @rm -fr hbmenu
+ @mkdir hbmenu
+ @cp $(TARGET).nds hbmenu/BOOT.NDS
+ @cp BootStrap/_BOOT_MP.NDS BootStrap/TTMENU.DAT BootStrap/_DS_MENU.DAT BootStrap/ez5sys.bin BootStrap/akmenu4.nds hbmenu
+ @tar -cvjf $(TARGET)-$(VERSION).tar.bz2 hbmenu testfiles README.html COPYING hbmenu -X exclude.lst
+
+$(TARGET).nds: $(TARGET).arm7 $(TARGET).arm9
+ ndstool -u 00030004 -g HGMA -c $(TARGET).nds -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf\
+ -b icon.bmp "GodMoDeS;Robz8"
+
+$(TARGET).arm7: arm7/$(TARGET).elf
+ cp arm7/$(TARGET).elf $(TARGET).arm7.elf
+
+$(TARGET).arm9: arm9/$(TARGET).elf
+ cp arm9/$(TARGET).elf $(TARGET).arm9.elf
+
+#---------------------------------------------------------------------------------
+arm7/$(TARGET).elf:
+ @$(MAKE) -C arm7
+
+#---------------------------------------------------------------------------------
+arm9/$(TARGET).elf:
+ @$(MAKE) -C arm9
+
+#---------------------------------------------------------------------------------
+#$(BUILD):
+ #@[ -d $@ ] || mkdir -p $@
+ #@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr data
+ @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds
+ @rm -fr $(TARGET).arm7.elf
+ @rm -fr $(TARGET).arm9.elf
+ @$(MAKE) -C bootloader clean
+ @$(MAKE) -C bootstub clean
+ @$(MAKE) -C arm9 clean
+ @$(MAKE) -C arm7 clean
+
+data:
+ @mkdir -p data
+
+bootloader: data
+ @$(MAKE) -C bootloader
+
+bootstub: data
+ @$(MAKE) -C bootstub
+
+#---------------------------------------------------------------------------------
+else
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+#$(OUTPUT).nds : $(OUTPUT).elf
+#$(OUTPUT).elf : $(OFILES)
+
+#---------------------------------------------------------------------------------
+%.bin.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ $(bin2o)
+
+-include $(DEPSDIR)/*.d
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/arm7/Makefile b/arm7/Makefile
new file mode 100644
index 0000000..703f32c
--- /dev/null
+++ b/arm7/Makefile
@@ -0,0 +1,135 @@
+export ARM7_MAJOR := 0
+export ARM7_MINOR := 6
+export ARM7_PATCH := 0
+
+VERSTRING := $(ARM7_MAJOR).$(ARM7_MINOR).$(ARM7_PATCH)
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM)
+endif
+
+include $(DEVKITARM)/ds_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
+#---------------------------------------------------------------------------------
+TARGET := GodMoDeS
+BUILD := build
+SOURCES := source
+INCLUDES := include build
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -march=armv4t -mthumb -mthumb-interwork
+
+CFLAGS := -g -Wall -O2\
+ -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM7
+
+ASFLAGS := -g $(ARCH)
+LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map
+
+
+#---------------------------------------------------------------------------------
+# any extra libraries we wish to link with the project
+#---------------------------------------------------------------------------------
+LIBS := -lnds7
+
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(LIBNDS)
+
+
+#---------------------------------------------------------------------------------
+# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
+
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+
+export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+.PHONY: all $(BUILD) clean
+
+all : $(BUILD)
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile
+
+
+#---------------------------------------------------------------------------------
+dist: all
+#---------------------------------------------------------------------------------
+ @tar --exclude=*CVS* --exclude=.svn -cvjf default_arm7-src-$(VERSTRING).tar.bz2 source Makefile
+ @tar -cvjf default_arm7-$(VERSTRING).tar.bz2 default.elf
+
+#---------------------------------------------------------------------------------
+install: all
+#---------------------------------------------------------------------------------
+ cp $(TARGET).elf $(LIBNDS)
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(TARGET).elf $(TARGET).arm7
+
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+
+$(OUTPUT).elf : $(OFILES) $(LIBNDS)/lib/libnds7.a
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/arm7/ds_arm7_ram.ld b/arm7/ds_arm7_ram.ld
new file mode 100644
index 0000000..52d6a5e
--- /dev/null
+++ b/arm7/ds_arm7_ram.ld
@@ -0,0 +1,176 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+
+MEMORY {
+ rom : ORIGIN = 0x08000000, LENGTH = 32M
+ ram : ORIGIN = 0x2380000, LENGTH = 128K
+ iwram : ORIGIN = 0x037f8000, LENGTH = 96K
+}
+
+__iwram_start = ORIGIN(iwram);
+__iwram_top = ORIGIN(iwram)+ LENGTH(iwram);
+
+__sp_irq = __iwram_top - 0x100;
+__sp_svc = __sp_irq - 0x100;
+__sp_usr = __sp_svc - 0x100;
+
+__irq_flags = 0x04000000 - 8;
+__irq_flagsaux = 0x04000000 - 0x40;
+__irq_vector = 0x04000000 - 4;
+
+SECTIONS
+{
+ .init :
+ {
+ __text_start = . ;
+ KEEP (*(.init))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+ .plt : { *(.plt) } >ram = 0xff
+
+ .text : /* ALIGN (4): */
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+
+ .fini :
+ {
+ KEEP (*(.fini))
+ } >ram =0xff
+
+ __text_end = . ;
+
+ .rodata :
+ {
+ *(.rodata)
+ *all.rodata*(*)
+ *(.roda)
+ *(.rodata.*)
+ *(.gnu.linkonce.r*)
+ SORT(CONSTRUCTORS)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+
+ .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ram
+ __exidx_start = .;
+ .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ram
+ __exidx_end = .;
+
+/* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { KEEP (*(.preinit_array)) } >ram = 0xff
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { KEEP (*(.init_array)) } >ram = 0xff
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { KEEP (*(.fini_array)) } >ram = 0xff
+ PROVIDE (__fini_array_end = .);
+
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of the constructors, so
+ we make sure it is first. Because this is a wildcard, it
+ doesn't matter if the user does not actually link against
+ crtbegin.o; the linker won't look for a file to match a
+ wildcard. The wildcard also means that it doesn't matter which
+ directory crtbegin.o is in. */
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+
+ .eh_frame :
+ {
+ KEEP (*(.eh_frame))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+
+ .gcc_except_table :
+ {
+ *(.gcc_except_table)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >ram = 0xff
+ .jcr : { KEEP (*(.jcr)) } >ram = 0
+ .got : { *(.got.plt) *(.got) } >ram = 0
+
+ .data ALIGN(4) : {
+ __data_start = ABSOLUTE(.);
+ *(.data)
+ *(.data.*)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ . = ALIGN(4);
+ __data_end = ABSOLUTE(.) ;
+ } >ram = 0xff
+
+ .bss ALIGN(4) :
+ {
+ __bss_start = ABSOLUTE(.);
+ __bss_start__ = ABSOLUTE(.);
+ *(.dynbss)
+ *(.gnu.linkonce.b*)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ __bss_end__ = ABSOLUTE(.);
+ __end__ = ABSOLUTE(.);
+ } >ram
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .stack 0x80000 : { _stack = .; *(.stack) }
+ /* These must appear regardless of . */
+}
diff --git a/arm7/source/main.c b/arm7/source/main.c
new file mode 100644
index 0000000..7c8aab2
--- /dev/null
+++ b/arm7/source/main.c
@@ -0,0 +1,112 @@
+/*---------------------------------------------------------------------------------
+
+ default ARM7 core
+
+ Copyright (C) 2005 - 2010
+ Michael Noland (joat)
+ Jason Rogers (dovoto)
+ Dave Murphy (WinterMute)
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+ must not claim that you wrote the original software. If you use
+ this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and
+ must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+---------------------------------------------------------------------------------*/
+#include
+#include
+
+unsigned int * SCFG_EXT=(unsigned int*)0x4004008;
+
+//---------------------------------------------------------------------------------
+void ReturntoDSiMenu() {
+//---------------------------------------------------------------------------------
+ i2cWriteRegister(0x4A, 0x70, 0x01); // Bootflag = Warmboot/SkipHealthSafety
+ i2cWriteRegister(0x4A, 0x11, 0x01); // Reset to DSi Menu
+}
+
+//---------------------------------------------------------------------------------
+void VblankHandler(void) {
+//---------------------------------------------------------------------------------
+ if(fifoCheckValue32(FIFO_USER_02)) {
+ ReturntoDSiMenu();
+ }
+}
+
+//---------------------------------------------------------------------------------
+void VcountHandler() {
+//---------------------------------------------------------------------------------
+ inputGetAndSend();
+}
+
+volatile bool exitflag = false;
+
+//---------------------------------------------------------------------------------
+void powerButtonCB() {
+//---------------------------------------------------------------------------------
+ exitflag = true;
+}
+
+//---------------------------------------------------------------------------------
+int main() {
+//---------------------------------------------------------------------------------
+ nocashMessage("ARM7 main.c main");
+
+ // clear sound registers
+ dmaFillWords(0, (void*)0x04000400, 0x100);
+
+ REG_SOUNDCNT |= SOUND_ENABLE;
+ writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP );
+ powerOn(POWER_SOUND);
+
+ readUserSettings();
+ ledBlink(0);
+
+ irqInit();
+ // Start the RTC tracking IRQ
+ initClockIRQ();
+
+ //touchInit();
+
+ fifoInit();
+
+ SetYtrigger(80);
+
+ installSystemFIFO();
+
+ irqSet(IRQ_VCOUNT, VcountHandler);
+ irqSet(IRQ_VBLANK, VblankHandler);
+
+ irqEnable( IRQ_VBLANK | IRQ_VCOUNT );
+
+ setPowerButtonCB(powerButtonCB);
+
+ fifoSendValue32(FIFO_USER_03, *SCFG_EXT);
+ fifoSendValue32(FIFO_USER_07, *(u16*)(0x4004700));
+ fifoSendValue32(FIFO_USER_06, 1);
+
+ // Keep the ARM7 mostly idle
+ while (!exitflag) {
+ if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) {
+ exitflag = true;
+ }
+ resyncClock();
+ swiWaitForVBlank();
+ }
+ return 0;
+}
+
diff --git a/arm9/Makefile b/arm9/Makefile
new file mode 100644
index 0000000..e0d29e4
--- /dev/null
+++ b/arm9/Makefile
@@ -0,0 +1,154 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM")
+endif
+
+include $(DEVKITARM)/ds_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 files embedded using bin2o
+# GRAPHICS is a list of directories containing image files to be converted with grit
+# MAXMOD_SOUNDBANK contains a directory of music and sound effect files
+#---------------------------------------------------------------------------------
+TARGET := GodMoDeS
+BUILD := build
+SOURCES := source
+INCLUDES := include source
+DATA := ../data
+GRAPHICS := ../gfx
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -mthumb -mthumb-interwork
+
+CFLAGS := -g -Wall -O2\
+ -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM9
+CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++11
+
+ASFLAGS := -g $(ARCH)
+LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
+
+#---------------------------------------------------------------------------------
+# any extra libraries we wish to link with the project (order is important)
+#---------------------------------------------------------------------------------
+LIBS := -lfat -lnds9
+
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(CURDIR) ../ $(LIBNDS)
+
+#---------------------------------------------------------------------------------
+# 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)))
+BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp)))
+PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES := $(addsuffix .o,$(BINFILES)) \
+ $(BMPFILES:.bmp=.o) \
+ $(PNGFILES:.png=.o) \
+ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+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)
+
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+
+.PHONY: $(BUILD) clean
+
+all : $(BUILD)
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds *.bin
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+$(OUTPUT).elf : $(OFILES)
+
+#---------------------------------------------------------------------------------
+%.bin.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ $(bin2o)
+
+#---------------------------------------------------------------------------------
+# This rule creates assembly source files using grit
+# grit takes an image file and a .grit describing how the file is to be processed
+# add additional rules like this for each image extension
+# you use in the graphics folders
+#---------------------------------------------------------------------------------
+%.s %.h : %.bmp %.grit
+#---------------------------------------------------------------------------------
+ grit $< -fts -o$*
+
+
+#---------------------------------------------------------------------------------
+%.s %.h : %.png %.grit
+#---------------------------------------------------------------------------------
+ grit $< -fts -o$*
+
+-include $(DEPSDIR)/*.d
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/arm9/source/date.cpp b/arm9/source/date.cpp
new file mode 100644
index 0000000..840fe18
--- /dev/null
+++ b/arm9/source/date.cpp
@@ -0,0 +1,110 @@
+#include "date.h"
+
+#include
+#include
+#include
+
+#include
+using std::string;
+
+/**
+ * Get the current date as a C string.
+ * @param format Date format.
+ * @param buf Output buffer.
+ * @param size Size of the output buffer.
+ * @return Number of bytes written, excluding the NULL terminator.
+ * @return Current date. (Caller must free() this string.)
+ */
+size_t GetDate(DateFormat format, char *buf, size_t size)
+{
+ time_t Raw;
+ time(&Raw);
+ const struct tm *Time = localtime(&Raw);
+
+ switch (format) {
+ case FORMAT_YDM:
+ return strftime(buf, size, "%Y-%d-%m_%k-%M", Time);
+ case FORMAT_YMD:
+ return strftime(buf, size, "%Y-%m-%d_%k-%M", Time);
+ case FORMAT_DM:
+ return strftime(buf, size, "%d/%m", Time); // Ex: 26/12
+ case FORMAT_MD:
+ return strftime(buf, size, "%m/%d", Time); // Ex: 12/26
+ case FORMAT_M_D:
+ return strftime(buf, size, "%d.%m.", Time); // Ex: 26.12.
+ case FORMAT_MY:
+ return strftime(buf, size, "%m %Y", Time);
+ case FORMAT_M:
+ return strftime(buf, size, "%m", Time);
+ case FORMAT_Y:
+ return strftime(buf, size, "%Y", Time);
+ default:
+ break;
+ }
+
+ // Invalid format.
+ // Should not get here...
+ if (size > 0) {
+ *buf = 0;
+ }
+ return 0;
+}
+
+/**
+ * Get the current time formatted for the top bar.
+ * @return std::string containing the time.
+ */
+string RetTime()
+{
+ time_t Raw;
+ time(&Raw);
+ const struct tm *Time = localtime(&Raw);
+
+ char Tmp[24];
+ strftime(Tmp, sizeof(Tmp), "%k:%M", Time);
+
+ return string(Tmp);
+}
+
+/**
+ * Draw the date using the specified format.
+ * @param Xpos X position.
+ * @param Ypos Y position.
+ * @param size Text size.
+ * @param format Date format.
+ */
+char* DrawDateF(DateFormat format)
+{
+ char date_str[24];
+ GetDate(format, date_str, sizeof(date_str));
+ if (date_str[0] == 0)
+ return "";
+ return date_str;
+}
+
+/**
+ * Draw the date.
+ * Date format depends on language setting.
+ * @param screen Top or Bottom screen.
+ * @param Xpos X position.
+ * @param Ypos Y position.
+ * @param size Text size.
+ */
+char* DrawDate()
+{
+ // Date formats.
+ // - Index: Language ID.
+ // - Value: Date format.
+ static const uint8_t date_fmt[8] = {
+ FORMAT_MD, // Japanese
+ FORMAT_MD, // English
+ FORMAT_DM, // French
+ FORMAT_M_D, // German
+ FORMAT_DM, // Italian
+ FORMAT_DM, // Spanish
+ FORMAT_MD, // Chinese
+ FORMAT_MD, // Korean
+ };
+
+ return DrawDateF((DateFormat)date_fmt[PersonalData->language]);
+}
diff --git a/arm9/source/date.h b/arm9/source/date.h
new file mode 100644
index 0000000..d712d4d
--- /dev/null
+++ b/arm9/source/date.h
@@ -0,0 +1,47 @@
+#ifndef DATE_H
+#define DATE_H
+
+#include
+#include
+#include
+
+typedef enum {
+ FORMAT_YDM = 0,
+ FORMAT_YMD = 1,
+ FORMAT_DM = 2,
+ FORMAT_MD = 3,
+ FORMAT_M_D = 4,
+ FORMAT_MY = 5,
+ FORMAT_M = 6,
+ FORMAT_Y = 7,
+} DateFormat;
+
+/**
+ * Get the current date as a C string.
+ * @param format Date format.
+ * @param buf Output buffer.
+ * @param size Size of the output buffer.
+ * @return Number of bytes written, excluding the NULL terminator.
+ * @return Current date. (Caller must free() this string.)
+ */
+size_t GetDate(DateFormat format, char *buf, size_t size);
+
+/**
+ * Get the current time formatted for the top bar.
+ * @return std::string containing the time.
+ */
+std::string RetTime();
+
+/**
+ * Draw the date using the specified format.
+ * @param format Date format.
+ */
+char* DrawDateF(DateFormat format);
+
+/**
+ * Draw the month and year using the specified color.
+ * Format is selected based on the language setting.
+ */
+char* DrawDate(void);
+
+#endif // DATE_H
diff --git a/arm9/source/fileOperations.cpp b/arm9/source/fileOperations.cpp
new file mode 100644
index 0000000..3a0a56c
--- /dev/null
+++ b/arm9/source/fileOperations.cpp
@@ -0,0 +1,69 @@
+#include
+#include
+
+u32 copyBuf[0x8000];
+
+off_t getFileSize(const char *fileName)
+{
+ FILE* fp = fopen(fileName, "rb");
+ off_t fsize = 0;
+ if (fp) {
+ fseek(fp, 0, SEEK_END);
+ fsize = ftell(fp); // Get source file's size
+ fseek(fp, 0, SEEK_SET);
+ }
+ fclose(fp);
+
+ return fsize;
+}
+
+int fcopy(const char *sourcePath, const char *destinationPath)
+{
+ FILE* sourceFile = fopen(sourcePath, "rb");
+ off_t fsize = 0;
+ if (sourceFile) {
+ fseek(sourceFile, 0, SEEK_END);
+ fsize = ftell(sourceFile); // Get source file's size
+ fseek(sourceFile, 0, SEEK_SET);
+ } else {
+ fclose(sourceFile);
+ return -1;
+ }
+
+ FILE* destinationFile = fopen(destinationPath, "wb");
+ //if (destinationFile) {
+ fseek(destinationFile, 0, SEEK_SET);
+ /*} else {
+ fclose(sourceFile);
+ fclose(destinationFile);
+ return -1;
+ }*/
+
+ off_t offset = 0;
+ int numr;
+ while (1)
+ {
+ /* scanKeys();
+ if (keysHeld() & KEY_A) {
+ // Cancel copying
+ fclose(sourceFile);
+ fclose(destinationFile);
+ return -1;
+ break;
+ } */
+
+ // Copy file to destination path
+ numr = fread(copyBuf, 2, 0x8000, sourceFile);
+ fwrite(copyBuf, 2, numr, destinationFile);
+ offset += 0x8000;
+
+ if (offset > fsize) {
+ fclose(sourceFile);
+ fclose(destinationFile);
+ return 1;
+ break;
+ }
+ }
+
+ return -1;
+}
diff --git a/arm9/source/fileOperations.h b/arm9/source/fileOperations.h
new file mode 100644
index 0000000..ad38836
--- /dev/null
+++ b/arm9/source/fileOperations.h
@@ -0,0 +1,7 @@
+#ifndef FILE_COPY
+#define FILE_COPY
+
+off_t getFileSize(const char *fileName);
+int fcopy(const char *sourcePath, const char *destinationPath);
+
+#endif // FILE_COPY
\ No newline at end of file
diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp
new file mode 100644
index 0000000..92e7ab7
--- /dev/null
+++ b/arm9/source/file_browse.cpp
@@ -0,0 +1,245 @@
+/*-----------------------------------------------------------------
+ Copyright (C) 2005 - 2013
+ Michael "Chishm" Chisholm
+ Dave "WinterMute" Murphy
+ Claudio "sverx"
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+
+#include "file_browse.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "date.h"
+#include "fileOperations.h"
+
+#define SCREEN_COLS 32
+#define ENTRIES_PER_SCREEN 22
+#define ENTRIES_START_ROW 2
+#define ENTRY_PAGE_LENGTH 10
+
+using namespace std;
+
+struct DirEntry {
+ string name;
+ bool isDirectory;
+} ;
+
+bool nameEndsWith (const string& name, const vector extensionList) {
+
+ if (name.size() == 0) return false;
+
+ if (extensionList.size() == 0) return true;
+
+ for (int i = 0; i < (int)extensionList.size(); i++) {
+ const string ext = extensionList.at(i);
+ if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true;
+ }
+ return false;
+}
+
+bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) {
+
+ if (!lhs.isDirectory && rhs.isDirectory) {
+ return false;
+ }
+ if (lhs.isDirectory && !rhs.isDirectory) {
+ return true;
+ }
+ return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
+}
+
+void getDirectoryContents (vector& dirContents, const vector extensionList) {
+ struct stat st;
+
+ dirContents.clear();
+
+ DIR *pdir = opendir (".");
+
+ if (pdir == NULL) {
+ iprintf ("Unable to open the directory.\n");
+ } else {
+
+ while(true) {
+ DirEntry dirEntry;
+
+ struct dirent* pent = readdir(pdir);
+ if(pent == NULL) break;
+
+ stat(pent->d_name, &st);
+ dirEntry.name = pent->d_name;
+ dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false;
+
+ if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) {
+ dirContents.push_back (dirEntry);
+ }
+
+ }
+
+ closedir(pdir);
+ }
+
+ sort(dirContents.begin(), dirContents.end(), dirEntryPredicate);
+}
+
+void getDirectoryContents (vector& dirContents) {
+ vector extensionList;
+ getDirectoryContents (dirContents, extensionList);
+}
+
+void showDirectoryContents (const vector& dirContents, int startRow) {
+ char path[PATH_MAX];
+
+
+ getcwd(path, PATH_MAX);
+
+ // Clear the screen
+ iprintf ("\x1b[2J");
+
+ // Print the path
+ if (strlen(path) < SCREEN_COLS) {
+ iprintf ("%s", path);
+ } else {
+ iprintf ("%s", path + strlen(path) - SCREEN_COLS);
+ }
+
+ // Move to 2nd row
+ iprintf ("\x1b[1;0H");
+ // Print line of dashes
+ iprintf ("--------------------------------");
+
+ // Print directory listing
+ for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) {
+ const DirEntry* entry = &dirContents.at(i + startRow);
+ char entryName[SCREEN_COLS + 1];
+
+ // Set row
+ iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW);
+
+ if (entry->isDirectory) {
+ strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
+ entryName[SCREEN_COLS - 3] = '\0';
+ iprintf (" [%s]", entryName);
+ } else {
+ strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
+ entryName[SCREEN_COLS - 1] = '\0';
+ iprintf (" %s", entryName);
+ }
+ }
+}
+
+string browseForFile (const vector extensionList) {
+ int pressed = 0;
+ int screenOffset = 0;
+ int fileOffset = 0;
+ off_t fileSize = 0;
+ vector dirContents;
+
+ getDirectoryContents (dirContents, extensionList);
+
+ while (true) {
+ consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true);
+ //consoleClear();
+ DirEntry* entry = &dirContents.at(fileOffset);
+ printf (entry->name.c_str());
+ printf ("\n");
+ if (entry->isDirectory) {
+ printf ("(dir)");
+ } else {
+ fileSize = getFileSize(entry->name.c_str());
+ iprintf ("%i Bytes", (int)fileSize);
+ }
+ iprintf ("\x1b[23;0H");
+ printf ("GodMoDeS v0.1.0");
+
+ consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, true, true);
+ //consoleClear();
+ showDirectoryContents (dirContents, screenOffset);
+
+ // Clear old cursors
+ /*for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) {
+ iprintf ("\x1b[%d;0H ", i);
+ }*/
+ // Show cursor
+ iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW);
+
+ //iconTitleUpdate (dirContents.at(fileOffset).isDirectory,dirContents.at(fileOffset).name.c_str());
+
+ // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
+ do {
+ // Move to right side of screen
+ iprintf ("\x1b[0;27H");
+ // Print time
+ printf (RetTime().c_str());
+
+ scanKeys();
+ pressed = keysDownRepeat();
+ swiWaitForVBlank();
+ } while (!pressed);
+
+ if (pressed & KEY_UP) fileOffset -= 1;
+ if (pressed & KEY_DOWN) fileOffset += 1;
+ if (pressed & KEY_LEFT) fileOffset -= ENTRY_PAGE_LENGTH;
+ if (pressed & KEY_RIGHT) fileOffset += ENTRY_PAGE_LENGTH;
+
+ if (fileOffset < 0) fileOffset = dirContents.size() - 1; // Wrap around to bottom of list
+ if (fileOffset > ((int)dirContents.size() - 1)) fileOffset = 0; // Wrap around to top of list
+
+ // Scroll screen if needed
+ if (fileOffset < screenOffset) {
+ screenOffset = fileOffset;
+ showDirectoryContents (dirContents, screenOffset);
+ }
+ if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) {
+ screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1;
+ showDirectoryContents (dirContents, screenOffset);
+ }
+
+ if (pressed & KEY_A) {
+ DirEntry* entry = &dirContents.at(fileOffset);
+ if (entry->isDirectory) {
+ iprintf("Entering directory\n");
+ // Enter selected directory
+ chdir (entry->name.c_str());
+ getDirectoryContents (dirContents, extensionList);
+ screenOffset = 0;
+ fileOffset = 0;
+ showDirectoryContents (dirContents, screenOffset);
+ } else {
+ // Clear the screen
+ iprintf ("\x1b[2J");
+ // Return the chosen file
+ return entry->name;
+ }
+ }
+
+ if (pressed & KEY_B) {
+ // Go up a directory
+ chdir ("..");
+ getDirectoryContents (dirContents, extensionList);
+ screenOffset = 0;
+ fileOffset = 0;
+ showDirectoryContents (dirContents, screenOffset);
+ }
+ }
+}
diff --git a/arm9/source/file_browse.h b/arm9/source/file_browse.h
new file mode 100644
index 0000000..d811c40
--- /dev/null
+++ b/arm9/source/file_browse.h
@@ -0,0 +1,32 @@
+/*-----------------------------------------------------------------
+ Copyright (C) 2005 - 2010
+ Michael "Chishm" Chisholm
+ Dave "WinterMute" Murphy
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+
+#ifndef FILE_BROWSE_H
+#define FILE_BROWSE_H
+
+#include
+#include
+
+std::string browseForFile (const std::vector extensionList);
+
+
+
+#endif //FILE_BROWSE_H
diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp
new file mode 100644
index 0000000..d79d767
--- /dev/null
+++ b/arm9/source/main.cpp
@@ -0,0 +1,145 @@
+/*-----------------------------------------------------------------
+ Copyright (C) 2005 - 2013
+ Michael "Chishm" Chisholm
+ Dave "WinterMute" Murphy
+ Claudio "sverx"
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "nds_loader_arm9.h"
+#include "file_browse.h"
+
+//#include "iconTitle.h"
+
+using namespace std;
+
+//---------------------------------------------------------------------------------
+void stop (void) {
+//---------------------------------------------------------------------------------
+ while (1) {
+ swiWaitForVBlank();
+ }
+}
+
+char filePath[PATH_MAX];
+
+//---------------------------------------------------------------------------------
+int main(int argc, char **argv) {
+//---------------------------------------------------------------------------------
+
+ // overwrite reboot stub identifier
+ extern u64 *fake_heap_end;
+ *fake_heap_end = 0;
+
+ defaultExceptionHandler();
+
+ int pathLen;
+ std::string filename;
+
+ //iconTitleInit();
+
+ // Subscreen as a console
+ videoSetMode(MODE_0_2D);
+ vramSetBankG(VRAM_G_MAIN_BG);
+ videoSetModeSub(MODE_0_2D);
+ vramSetBankH(VRAM_H_SUB_BG);
+ consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, true, true);
+
+ if (!fatInitDefault()) {
+ iprintf ("fatinitDefault failed!\n");
+ stop();
+ }
+
+ keysSetRepeat(25,5);
+
+ vector extensionList;
+ extensionList.push_back(".nds");
+ extensionList.push_back(".firm");
+ //extensionList.push_back(".argv");
+
+ chdir("/nds");
+
+ while(1) {
+
+ filename = browseForFile(extensionList);
+
+ // Construct a command line
+ getcwd (filePath, PATH_MAX);
+ pathLen = strlen (filePath);
+ vector argarray;
+
+ if ( strcasecmp (filename.c_str() + filename.size() - 5, ".argv") == 0) {
+
+ FILE *argfile = fopen(filename.c_str(),"rb");
+ char str[PATH_MAX], *pstr;
+ const char seps[]= "\n\r\t ";
+
+ while( fgets(str, PATH_MAX, argfile) ) {
+ // Find comment and end string there
+ if( (pstr = strchr(str, '#')) )
+ *pstr= '\0';
+
+ // Tokenize arguments
+ pstr= strtok(str, seps);
+
+ while( pstr != NULL ) {
+ argarray.push_back(strdup(pstr));
+ pstr= strtok(NULL, seps);
+ }
+ }
+ fclose(argfile);
+ filename = argarray.at(0);
+ } else {
+ argarray.push_back(strdup(filename.c_str()));
+ }
+
+ if ( strcasecmp (filename.c_str() + filename.size() - 4, ".nds") != 0 || argarray.size() == 0 ) {
+ iprintf("no nds file specified\n");
+ } else {
+ char *name = argarray.at(0);
+ strcpy (filePath + pathLen, name);
+ free(argarray.at(0));
+ argarray.at(0) = filePath;
+ iprintf ("Running %s with %d parameters\n", argarray[0], argarray.size());
+ int err = runNdsFile (argarray[0], argarray.size(), (const char **)&argarray[0]);
+ iprintf ("Start failed. Error %i\n", err);
+
+ }
+
+ while(argarray.size() !=0 ) {
+ free(argarray.at(0));
+ argarray.erase(argarray.begin());
+ }
+
+ while (1) {
+ swiWaitForVBlank();
+ scanKeys();
+ if (!(keysHeld() & KEY_A)) break;
+ }
+
+ }
+
+ return 0;
+}
diff --git a/arm9/source/nds_loader_arm9.c b/arm9/source/nds_loader_arm9.c
new file mode 100644
index 0000000..c5dc700
--- /dev/null
+++ b/arm9/source/nds_loader_arm9.c
@@ -0,0 +1,436 @@
+/*-----------------------------------------------------------------
+ Copyright (C) 2005 - 2010
+ Michael "Chishm" Chisholm
+ Dave "WinterMute" Murphy
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "load_bin.h"
+
+#ifndef _NO_BOOTSTUB_
+#include "bootstub_bin.h"
+#endif
+
+#include "nds_loader_arm9.h"
+#define LCDC_BANK_C (u16*)0x06840000
+#define STORED_FILE_CLUSTER (*(((u32*)LCDC_BANK_C) + 1))
+#define INIT_DISC (*(((u32*)LCDC_BANK_C) + 2))
+#define WANT_TO_PATCH_DLDI (*(((u32*)LCDC_BANK_C) + 3))
+
+
+/*
+ b startUp
+
+storedFileCluster:
+ .word 0x0FFFFFFF @ default BOOT.NDS
+initDisc:
+ .word 0x00000001 @ init the disc by default
+wantToPatchDLDI:
+ .word 0x00000001 @ by default patch the DLDI section of the loaded NDS
+@ Used for passing arguments to the loaded app
+argStart:
+ .word _end - _start
+argSize:
+ .word 0x00000000
+dldiOffset:
+ .word _dldi_start - _start
+dsiSD:
+ .word 0
+dsiMode:
+ .word 0
+*/
+
+#define STORED_FILE_CLUSTER_OFFSET 4
+#define INIT_DISC_OFFSET 8
+#define WANT_TO_PATCH_DLDI_OFFSET 12
+#define ARG_START_OFFSET 16
+#define ARG_SIZE_OFFSET 20
+#define HAVE_DSISD_OFFSET 28
+#define DSIMODE_OFFSET 32
+
+
+typedef signed int addr_t;
+typedef unsigned char data_t;
+
+#define FIX_ALL 0x01
+#define FIX_GLUE 0x02
+#define FIX_GOT 0x04
+#define FIX_BSS 0x08
+
+enum DldiOffsets {
+ DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm"
+ DO_magicToken = 0x00, // 0xBF8DA5ED
+ DO_magicShortString = 0x04, // " Chishm"
+ DO_version = 0x0C,
+ DO_driverSize = 0x0D,
+ DO_fixSections = 0x0E,
+ DO_allocatedSpace = 0x0F,
+
+ DO_friendlyName = 0x10,
+
+ DO_text_start = 0x40, // Data start
+ DO_data_end = 0x44, // Data end
+ DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing
+ DO_glue_end = 0x4C, // Interworking glue end
+ DO_got_start = 0x50, // GOT start -- Needs address fixing
+ DO_got_end = 0x54, // GOT end
+ DO_bss_start = 0x58, // bss start -- Needs setting to zero
+ DO_bss_end = 0x5C, // bss end
+
+ // IO_INTERFACE data
+ DO_ioType = 0x60,
+ DO_features = 0x64,
+ DO_startup = 0x68,
+ DO_isInserted = 0x6C,
+ DO_readSectors = 0x70,
+ DO_writeSectors = 0x74,
+ DO_clearStatus = 0x78,
+ DO_shutdown = 0x7C,
+ DO_code = 0x80
+};
+
+static addr_t readAddr (data_t *mem, addr_t offset) {
+ return ((addr_t*)mem)[offset/sizeof(addr_t)];
+}
+
+static void writeAddr (data_t *mem, addr_t offset, addr_t value) {
+ ((addr_t*)mem)[offset/sizeof(addr_t)] = value;
+}
+
+static void vramcpy (void* dst, const void* src, int len)
+{
+ u16* dst16 = (u16*)dst;
+ u16* src16 = (u16*)src;
+
+ //dmaCopy(src, dst, len);
+
+ for ( ; len > 0; len -= 2) {
+ *dst16++ = *src16++;
+ }
+}
+
+static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) {
+ const int* dataChunk = (const int*) data;
+ int searchChunk = ((const int*)search)[0];
+ addr_t i;
+ addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int));
+
+ for ( i = 0; i < dataChunkEnd; i++) {
+ if (dataChunk[i] == searchChunk) {
+ if ((i*sizeof(int) + searchLen) > dataLen) {
+ return -1;
+ }
+ if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0) {
+ return i*sizeof(int);
+ }
+ }
+ }
+
+ return -1;
+}
+
+// Normal DLDI uses "\xED\xA5\x8D\xBF Chishm"
+// Bootloader string is different to avoid being patched
+static const data_t dldiMagicLoaderString[] = "\xEE\xA5\x8D\xBF Chishm"; // Different to a normal DLDI file
+
+#define DEVICE_TYPE_DLDI 0x49444C44
+
+static bool dldiPatchLoader (data_t *binData, u32 binSize, bool clearBSS)
+{
+ addr_t memOffset; // Offset of DLDI after the file is loaded into memory
+ addr_t patchOffset; // Position of patch destination in the file
+ addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly
+ addr_t ddmemOffset; // Original offset used in the DLDI file
+ addr_t ddmemStart; // Start of range that offsets can be in the DLDI file
+ addr_t ddmemEnd; // End of range that offsets can be in the DLDI file
+ addr_t ddmemSize; // Size of range that offsets can be in the DLDI file
+
+ addr_t addrIter;
+
+ data_t *pDH;
+ data_t *pAH;
+
+ size_t dldiFileSize = 0;
+
+ // Find the DLDI reserved space in the file
+ patchOffset = quickFind (binData, dldiMagicLoaderString, binSize, sizeof(dldiMagicLoaderString));
+
+ if (patchOffset < 0) {
+ // does not have a DLDI section
+ return false;
+ }
+
+ pDH = (data_t*)(io_dldi_data);
+
+ pAH = &(binData[patchOffset]);
+
+ if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI) {
+ // No DLDI patch
+ return false;
+ }
+
+ if (pDH[DO_driverSize] > pAH[DO_allocatedSpace]) {
+ // Not enough space for patch
+ return false;
+ }
+
+ dldiFileSize = 1 << pDH[DO_driverSize];
+
+ memOffset = readAddr (pAH, DO_text_start);
+ if (memOffset == 0) {
+ memOffset = readAddr (pAH, DO_startup) - DO_code;
+ }
+ ddmemOffset = readAddr (pDH, DO_text_start);
+ relocationOffset = memOffset - ddmemOffset;
+
+ ddmemStart = readAddr (pDH, DO_text_start);
+ ddmemSize = (1 << pDH[DO_driverSize]);
+ ddmemEnd = ddmemStart + ddmemSize;
+
+ // Remember how much space is actually reserved
+ pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace];
+ // Copy the DLDI patch into the application
+ vramcpy (pAH, pDH, dldiFileSize);
+
+ // Fix the section pointers in the header
+ writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset);
+ writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset);
+ writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset);
+ writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset);
+ writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset);
+ writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset);
+ writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset);
+ writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset);
+ // Fix the function pointers in the header
+ writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset);
+ writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset);
+ writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset);
+ writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset);
+ writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset);
+ writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset);
+
+ if (pDH[DO_fixSections] & FIX_ALL) {
+ // Search through and fix pointers within the data section of the file
+ for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (pDH[DO_fixSections] & FIX_GLUE) {
+ // Search through and fix pointers within the glue section of the file
+ for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (pDH[DO_fixSections] & FIX_GOT) {
+ // Search through and fix pointers within the Global Offset Table section of the file
+ for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (clearBSS && (pDH[DO_fixSections] & FIX_BSS)) {
+ // Initialise the BSS to 0, only if the disc is being re-inited
+ memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));
+ }
+
+ return true;
+}
+
+int runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv)
+{
+ char* argStart;
+ u16* argData;
+ u16 argTempVal = 0;
+ int argSize;
+ const char* argChar;
+
+ irqDisable(IRQ_ALL);
+
+ // Direct CPU access to VRAM bank C
+ VRAM_C_CR = VRAM_ENABLE | VRAM_C_LCD;
+ // Load the loader/patcher into the correct address
+ vramcpy (LCDC_BANK_C, loader, loaderSize);
+
+ // Set the parameters for the loader
+ // STORED_FILE_CLUSTER = cluster;
+ writeAddr ((data_t*) LCDC_BANK_C, STORED_FILE_CLUSTER_OFFSET, cluster);
+ // INIT_DISC = initDisc;
+ writeAddr ((data_t*) LCDC_BANK_C, INIT_DISC_OFFSET, initDisc);
+
+ writeAddr ((data_t*) LCDC_BANK_C, DSIMODE_OFFSET, isDSiMode());
+ if(argv[0][0]=='s' && argv[0][1]=='d') {
+ dldiPatchNds = false;
+ writeAddr ((data_t*) LCDC_BANK_C, HAVE_DSISD_OFFSET, 1);
+ }
+
+ // WANT_TO_PATCH_DLDI = dldiPatchNds;
+ writeAddr ((data_t*) LCDC_BANK_C, WANT_TO_PATCH_DLDI_OFFSET, dldiPatchNds);
+ // Give arguments to loader
+ argStart = (char*)LCDC_BANK_C + readAddr((data_t*)LCDC_BANK_C, ARG_START_OFFSET);
+ argStart = (char*)(((int)argStart + 3) & ~3); // Align to word
+ argData = (u16*)argStart;
+ argSize = 0;
+
+ for (; argc > 0 && *argv; ++argv, --argc)
+ {
+ for (argChar = *argv; *argChar != 0; ++argChar, ++argSize)
+ {
+ if (argSize & 1)
+ {
+ argTempVal |= (*argChar) << 8;
+ *argData = argTempVal;
+ ++argData;
+ }
+ else
+ {
+ argTempVal = *argChar;
+ }
+ }
+ if (argSize & 1)
+ {
+ *argData = argTempVal;
+ ++argData;
+ }
+ argTempVal = 0;
+ ++argSize;
+ }
+ *argData = argTempVal;
+
+ writeAddr ((data_t*) LCDC_BANK_C, ARG_START_OFFSET, (addr_t)argStart - (addr_t)LCDC_BANK_C);
+ writeAddr ((data_t*) LCDC_BANK_C, ARG_SIZE_OFFSET, argSize);
+
+
+ if(dldiPatchNds) {
+ // Patch the loader with a DLDI for the card
+ if (!dldiPatchLoader ((data_t*)LCDC_BANK_C, loaderSize, initDisc)) {
+ return 3;
+ }
+ }
+
+ irqDisable(IRQ_ALL);
+
+ // Give the VRAM to the ARM7
+ VRAM_C_CR = VRAM_ENABLE | VRAM_C_ARM7_0x06000000;
+ // Reset into a passme loop
+ REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD;
+ *((vu32*)0x02FFFFFC) = 0;
+ *((vu32*)0x02FFFE04) = (u32)0xE59FF018;
+ *((vu32*)0x02FFFE24) = (u32)0x02FFFE04;
+
+ resetARM7(0x06000000);
+
+ swiSoftReset();
+ return true;
+}
+
+int runNdsFile (const char* filename, int argc, const char** argv) {
+ struct stat st;
+ char filePath[PATH_MAX];
+ int pathLen;
+ const char* args[1];
+
+
+ if (stat (filename, &st) < 0) {
+ return 1;
+ }
+
+ if (argc <= 0 || !argv) {
+ // Construct a command line if we weren't supplied with one
+ if (!getcwd (filePath, PATH_MAX)) {
+ return 2;
+ }
+ pathLen = strlen (filePath);
+ strcpy (filePath + pathLen, filename);
+ args[0] = filePath;
+ argv = args;
+ }
+
+ bool havedsiSD = false;
+
+ if(argv[0][0]=='s' && argv[0][1]=='d') havedsiSD = true;
+
+ installBootStub(havedsiSD);
+
+ return runNds (load_bin, load_bin_size, st.st_ino, true, true, argc, argv);
+}
+
+/*
+ b startUp
+
+storedFileCluster:
+ .word 0x0FFFFFFF @ default BOOT.NDS
+initDisc:
+ .word 0x00000001 @ init the disc by default
+wantToPatchDLDI:
+ .word 0x00000001 @ by default patch the DLDI section of the loaded NDS
+@ Used for passing arguments to the loaded app
+argStart:
+ .word _end - _start
+argSize:
+ .word 0x00000000
+dldiOffset:
+ .word _dldi_start - _start
+dsiSD:
+ .word 0
+*/
+bool installBootStub(bool havedsiSD) {
+#ifndef _NO_BOOTSTUB_
+ extern char *fake_heap_end;
+ struct __bootstub *bootstub = (struct __bootstub *)fake_heap_end;
+ u32 *bootloader = (u32*)(fake_heap_end+bootstub_bin_size);
+
+ memcpy(bootstub,bootstub_bin,bootstub_bin_size);
+ memcpy(bootloader,load_bin,load_bin_size);
+ bool ret = false;
+
+ bootloader[8] = isDSiMode();
+ if( havedsiSD) {
+ ret = true;
+ bootloader[3] = 0; // don't dldi patch
+ bootloader[7] = 1; // use internal dsi SD code
+ } else {
+ ret = dldiPatchLoader((data_t*)bootloader, load_bin_size,false);
+ }
+ bootstub->arm9reboot = (VoidFn)(((u32)bootstub->arm9reboot)+fake_heap_end);
+ bootstub->arm7reboot = (VoidFn)(((u32)bootstub->arm7reboot)+fake_heap_end);
+ bootstub->bootsize = load_bin_size;
+
+ DC_FlushAll();
+
+ return ret;
+#else
+ return true;
+#endif
+
+}
+
diff --git a/arm9/source/nds_loader_arm9.h b/arm9/source/nds_loader_arm9.h
new file mode 100644
index 0000000..8d03d77
--- /dev/null
+++ b/arm9/source/nds_loader_arm9.h
@@ -0,0 +1,42 @@
+/*-----------------------------------------------------------------
+ Copyright (C) 2005 - 2010
+ Michael "Chishm" Chisholm
+ Dave "WinterMute" Murphy
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+
+#ifndef NDS_LOADER_ARM9_H
+#define NDS_LOADER_ARM9_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOAD_DEFAULT_NDS 0
+
+int runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv);
+
+int runNdsFile (const char* filename, int argc, const char** argv);
+
+bool installBootStub(bool havedsiSD);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NDS_LOADER_ARM7_H
diff --git a/bootloader/Makefile b/bootloader/Makefile
new file mode 100644
index 0000000..437ac39
--- /dev/null
+++ b/bootloader/Makefile
@@ -0,0 +1,123 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM)
+endif
+
+-include $(DEVKITARM)/ds_rules
+
+#---------------------------------------------------------------------------------
+# 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
+#---------------------------------------------------------------------------------
+TARGET := load
+BUILD ?= build
+SOURCES := source source/patches
+INCLUDES := build
+SPECS := specs
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -mthumb -mthumb-interwork
+
+CFLAGS := -g -Wall -Os\
+ -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
+ -ffast-math \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) $(EXTRA_CFLAGS) -DARM7
+
+ASFLAGS := -g $(ARCH) $(EXTRA_CFLAGS) $(INCLUDE)
+LDFLAGS = -nostartfiles -T $(TOPDIR)/load.ld -g $(ARCH) -Wl,-Map,$(TARGET).map
+
+LIBS := -lnds7
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(LIBNDS)
+
+
+#---------------------------------------------------------------------------------
+# 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 TOPDIR := $(CURDIR)
+export LOADBIN := $(CURDIR)/../data/$(TARGET).bin
+export LOADELF := $(CURDIR)/$(TARGET).elf
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
+
+export CC := $(PREFIX)gcc
+export CXX := $(PREFIX)g++
+export AR := $(PREFIX)ar
+export OBJCOPY := $(PREFIX)objcopy
+
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+
+export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+#---------------------------------------------------------------------------------
+# use CC for linking standard C
+#---------------------------------------------------------------------------------
+export LD := $(CC)
+#---------------------------------------------------------------------------------
+
+.PHONY: $(BUILD) clean
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) *.elf *.bin
+
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+$(LOADBIN) : $(LOADELF)
+ @$(OBJCOPY) -O binary $< $@
+ @echo built ... $(notdir $@)
+
+
+$(LOADELF) : $(OFILES)
+ @echo linking $(notdir $@)
+ @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+
+arm9mpu_reset.o: mpu_reset.bin
+
+mpu_reset.bin: mpu_reset.elf
+ $(OBJCOPY) -O binary $< $@
+
+mpu_reset.elf: $(TOPDIR)/arm9code/mpu_reset.s
+ $(CC) $(ASFLAGS) -Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $< -o $@
+
+-include $(DEPENDS)
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/bootloader/arm9code/mpu_reset.s b/bootloader/arm9code/mpu_reset.s
new file mode 100644
index 0000000..3d499dd
--- /dev/null
+++ b/bootloader/arm9code/mpu_reset.s
@@ -0,0 +1,110 @@
+/*
+ Copyright 2006 - 2015 Dave Murphy (WinterMute)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+
+#include
+
+ .text
+ .align 4
+
+ .arm
+
+ .arch armv5te
+ .cpu arm946e-s
+
+@---------------------------------------------------------------------------------
+ .global _start
+ .type _start STT_FUNC
+@---------------------------------------------------------------------------------
+_start:
+@---------------------------------------------------------------------------------
+ @ Switch off MPU
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #PROTECT_ENABLE
+ mcr p15, 0, r0, c1, c0, 0
+
+
+ adr r12, mpu_initial_data
+ ldmia r12, {r0-r10}
+
+ mcr p15, 0, r0, c2, c0, 0
+ mcr p15, 0, r0, c2, c0, 1
+ mcr p15, 0, r1, c3, c0, 0
+ mcr p15, 0, r2, c5, c0, 2
+ mcr p15, 0, r3, c5, c0, 3
+ mcr p15, 0, r4, c6, c0, 0
+ mcr p15, 0, r5, c6, c1, 0
+ mcr p15, 0, r6, c6, c3, 0
+ mcr p15, 0, r7, c6, c4, 0
+ mcr p15, 0, r8, c6, c6, 0
+ mcr p15, 0, r9, c6, c7, 0
+ mcr p15, 0, r10, c9, c1, 0
+
+ mov r0, #0
+ mcr p15, 0, r0, c6, c2, 0 @ PU Protection Unit Data/Unified Region 2
+ mcr p15, 0, r0, c6, c5, 0 @ PU Protection Unit Data/Unified Region 5
+
+ mrc p15, 0, r0, c9, c1, 0 @ DTCM
+ mov r0, r0, lsr #12 @ base
+ mov r0, r0, lsl #12 @ size
+ add r0, r0, #0x4000 @ dtcm top
+
+ sub r0, r0, #4 @ irq vector
+ mov r1, #0
+ str r1, [r0]
+ sub r0, r0, #4 @ IRQ1 Check Bits
+ str r1, [r0]
+
+ sub r0, r0, #128
+ bic r0, r0, #7
+
+ msr cpsr_c, #0xd3 @ svc mode
+ mov sp, r0
+ sub r0, r0, #128
+ msr cpsr_c, #0xd2 @ irq mode
+ mov sp, r0
+ sub r0, r0, #128
+ msr cpsr_c, #0xdf @ system mode
+ mov sp, r0
+
+ @ enable cache & tcm
+ mrc p15, 0, r0, c1, c0, 0
+ ldr r1,= ITCM_ENABLE | DTCM_ENABLE | ICACHE_ENABLE | DCACHE_ENABLE
+ orr r0,r0,r1
+ mcr p15, 0, r0, c1, c0, 0
+
+ ldr r10, =0x2FFFE04
+ ldr r0, =0xE59FF018
+ str r0, [r10]
+ add r1, r10, #0x20
+ str r10, [r1]
+ bx r10
+
+ .pool
+
+mpu_initial_data:
+ .word 0x00000042 @ p15,0,c2,c0,0..1,r0 ;PU Cachability Bits for Data/Unified+Instruction Protection Region
+ .word 0x00000002 @ p15,0,c3,c0,0,r1 ;PU Write-Bufferability Bits for Data Protection Regions
+ .word 0x15111011 @ p15,0,c5,c0,2,r2 ;PU Extended Access Permission Data/Unified Protection Region
+ .word 0x05100011 @ p15,0,c5,c0,3,r3 ;PU Extended Access Permission Instruction Protection Region
+ .word 0x04000033 @ p15,0,c6,c0,0,r4 ;PU Protection Unit Data/Unified Region 0
+ .word 0x0200002b @ p15,0,c6,c1,0,r5 ;PU Protection Unit Data/Unified Region 1 4MB
+ .word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3
+ .word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4
+ .word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6
+ .word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB
+ .word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size
diff --git a/bootloader/load.ld b/bootloader/load.ld
new file mode 100644
index 0000000..53f3728
--- /dev/null
+++ b/bootloader/load.ld
@@ -0,0 +1,198 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+
+MEMORY {
+
+ vram : ORIGIN = 0x06000000, LENGTH = 128K
+}
+
+__vram_start = ORIGIN(vram);
+__vram_top = ORIGIN(vram)+ LENGTH(vram);
+__sp_irq = __vram_top - 0x60;
+__sp_svc = __sp_irq - 0x100;
+__sp_usr = __sp_svc - 0x100;
+
+__irq_flags = __vram_top - 8;
+__irq_vector = __vram_top - 4;
+
+SECTIONS
+{
+ .init :
+ {
+ __text_start = . ;
+ KEEP (*(.init))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .plt :
+ {
+ *(.plt)
+ } >vram = 0xff
+
+ .text : /* ALIGN (4): */
+ {
+
+ *(.text*)
+ *(.stub)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ *(.glue_7)
+ *(.glue_7t)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .fini :
+ {
+ KEEP (*(.fini))
+ } >vram =0xff
+
+ __text_end = . ;
+
+ .rodata :
+ {
+ *(.rodata)
+ *all.rodata*(*)
+ *(.roda)
+ *(.rodata.*)
+ *(.gnu.linkonce.r*)
+ SORT(CONSTRUCTORS)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >vram
+ __exidx_start = .;
+ .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >vram
+ __exidx_end = .;
+
+/* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { KEEP (*(.preinit_array)) } >vram = 0xff
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { KEEP (*(.init_array)) } >vram = 0xff
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { KEEP (*(.fini_array)) } >vram = 0xff
+ PROVIDE (__fini_array_end = .);
+
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of the constructors, so
+ we make sure it is first. Because this is a wildcard, it
+ doesn't matter if the user does not actually link against
+ crtbegin.o; the linker won't look for a file to match a
+ wildcard. The wildcard also means that it doesn't matter which
+ directory crtbegin.o is in. */
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .eh_frame :
+ {
+ KEEP (*(.eh_frame))
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+
+ .gcc_except_table :
+ {
+ *(.gcc_except_table)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram = 0xff
+ .jcr : { KEEP (*(.jcr)) } >vram = 0
+ .got : { *(.got.plt) *(.got) } >vram = 0
+
+
+ .vram ALIGN(4) :
+ {
+ __vram_start = ABSOLUTE(.) ;
+ *(.vram)
+ *vram.*(.text)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ __vram_end = ABSOLUTE(.) ;
+ } >vram = 0xff
+
+
+ .data ALIGN(4) : {
+ __data_start = ABSOLUTE(.);
+ *(.data)
+ *(.data.*)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ . = ALIGN(4);
+ __data_end = ABSOLUTE(.) ;
+ } >vram = 0xff
+
+
+
+ .bss ALIGN(4) :
+ {
+ __bss_start = ABSOLUTE(.);
+ __bss_start__ = ABSOLUTE(.);
+ *(.dynbss)
+ *(.gnu.linkonce.b*)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4); /* REQUIRED. LD is flaky without it. */
+ } >vram
+
+ __bss_end = . ;
+ __bss_end__ = . ;
+
+ _end = . ;
+ __end__ = . ;
+ PROVIDE (end = _end);
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .stack 0x80000 : { _stack = .; *(.stack) }
+ /* These must appear regardless of . */
+}
diff --git a/bootloader/source/arm7clear.s b/bootloader/source/arm7clear.s
new file mode 100644
index 0000000..52343e3
--- /dev/null
+++ b/bootloader/source/arm7clear.s
@@ -0,0 +1,70 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+ .arm
+ .global arm7clearRAM
+ .type arm7clearRAM STT_FUNC
+arm7clearRAM:
+
+ push {r0-r9}
+ // clear exclusive IWRAM
+ // 0380:0000 to 0380:FFFF, total 64KiB
+ mov r0, #0
+ mov r1, #0
+ mov r2, #0
+ mov r3, #0
+ mov r4, #0
+ mov r5, #0
+ mov r6, #0
+ mov r7, #0
+ mov r8, #0x03800000
+ sub r8, #0x00008000
+ mov r9, #0x03800000
+ orr r9, r9, #0x10000
+clear_IWRAM_loop:
+ stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7}
+ cmp r8, r9
+ blt clear_IWRAM_loop
+
+ // clear most of EWRAM - except after RAM end - 0xc000, which has the bootstub
+ mov r8, #0x02000000
+
+ ldr r9,=0x4004008
+ ldr r9,[r9]
+ ands r9,r9,#0x8000
+ bne dsi_mode
+
+ mov r9, #0x02400000
+ b ds_mode
+dsi_mode:
+ mov r9, #0x03000000
+ds_mode:
+ sub r9, #0x0000c000
+clear_EWRAM_loop:
+ stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7}
+ cmp r8, r9
+ blt clear_EWRAM_loop
+
+ pop {r0-r9}
+
+ bx lr
+
diff --git a/bootloader/source/arm9clear.arm.c b/bootloader/source/arm9clear.arm.c
new file mode 100644
index 0000000..b212da3
--- /dev/null
+++ b/bootloader/source/arm9clear.arm.c
@@ -0,0 +1,112 @@
+#define ARM9
+#undef ARM7
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "boot.h"
+
+/*-------------------------------------------------------------------------
+resetMemory2_ARM9
+Clears the ARM9's DMA channels and resets video memory
+Written by Darkain.
+Modified by Chishm:
+ * Changed MultiNDS specific stuff
+--------------------------------------------------------------------------*/
+void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9 (void)
+{
+ register int i;
+
+ //clear out ARM9 DMA channels
+ for (i=0; i<4; i++) {
+ DMA_CR(i) = 0;
+ DMA_SRC(i) = 0;
+ DMA_DEST(i) = 0;
+ TIMER_CR(i) = 0;
+ TIMER_DATA(i) = 0;
+ }
+
+ VRAM_CR = (VRAM_CR & 0xffff0000) | 0x00008080 ;
+
+ u16 *mainregs = (u16*)0x04000000;
+ u16 *subregs = (u16*)0x04001000;
+
+ for (i=0; i<43; i++) {
+ mainregs[i] = 0;
+ subregs[i] = 0;
+ }
+
+ REG_DISPSTAT = 0;
+
+ VRAM_A_CR = 0;
+ VRAM_B_CR = 0;
+// Don't mess with the ARM7's VRAM
+// VRAM_C_CR = 0;
+ VRAM_D_CR = 0;
+ VRAM_E_CR = 0;
+ VRAM_F_CR = 0;
+ VRAM_G_CR = 0;
+ VRAM_H_CR = 0;
+ VRAM_I_CR = 0;
+ REG_POWERCNT = 0x820F;
+
+ //set shared ram to ARM7
+ WRAM_CR = 0x03;
+
+ // Return to passme loop
+ *((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24
+ *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address
+
+ asm volatile(
+ "\tbx %0\n"
+ : : "r" (0x02FFFE04)
+ );
+ while(1);
+}
+
+void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) clearMasterBright_ARM9 (void)
+{
+ u16 mode = 1 << 14;
+
+ *(u16*)(0x0400006C + (0x1000 * 0)) = 0 + mode;
+ *(u16*)(0x0400006C + (0x1000 * 1)) = 0 + mode;
+
+ // Return to passme loop
+ *((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24
+ *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address
+
+ asm volatile(
+ "\tbx %0\n"
+ : : "r" (0x02FFFE04)
+ );
+ while(1);
+}
+
+/*-------------------------------------------------------------------------
+startBinary_ARM9
+Jumps to the ARM9 NDS binary in sync with the display and ARM7
+Written by Darkain.
+Modified by Chishm:
+ * Removed MultiNDS specific stuff
+--------------------------------------------------------------------------*/
+void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (void)
+{
+ REG_IME=0;
+ REG_EXMEMCNT = 0xE880;
+ // set ARM9 load address to 0 and wait for it to change again
+ ARM9_START_FLAG = 0;
+ while(REG_VCOUNT!=191);
+ while(REG_VCOUNT==191);
+ while ( ARM9_START_FLAG != 1 );
+ VoidFn arm9code = *(VoidFn*)(0x2FFFE24);
+ arm9code();
+ while(1);
+}
+
diff --git a/bootloader/source/arm9mpu_reset.s b/bootloader/source/arm9mpu_reset.s
new file mode 100644
index 0000000..bbd10f0
--- /dev/null
+++ b/bootloader/source/arm9mpu_reset.s
@@ -0,0 +1,6 @@
+ .arm
+ .global mpu_reset, mpu_reset_end
+
+mpu_reset:
+ .incbin "mpu_reset.bin"
+mpu_reset_end:
diff --git a/bootloader/source/bios.s b/bootloader/source/bios.s
new file mode 100644
index 0000000..e98f57c
--- /dev/null
+++ b/bootloader/source/bios.s
@@ -0,0 +1,13 @@
+ .text
+ .align 4
+
+ .thumb
+
+@---------------------------------------------------------------------------------
+ .global swiDelay
+ .thumb_func
+@---------------------------------------------------------------------------------
+swiDelay:
+@---------------------------------------------------------------------------------
+ swi 0x03
+ bx lr
diff --git a/bootloader/source/boot.c b/bootloader/source/boot.c
new file mode 100644
index 0000000..93c69a5
--- /dev/null
+++ b/bootloader/source/boot.c
@@ -0,0 +1,314 @@
+/*-----------------------------------------------------------------
+ boot.c
+
+ BootLoader
+ Loads a file into memory and runs it
+
+ All resetMemory and startBinary functions are based
+ on the MultiNDS loader by Darkain.
+ Original source available at:
+ http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp
+
+License:
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+
+Helpful information:
+ This code runs from VRAM bank C on ARM7
+------------------------------------------------------------------*/
+
+#include
+#include
+#include
+#include
+#include
+#define ARM9
+#undef ARM7
+#include
+#include
+#include
+#undef ARM9
+#define ARM7
+#include
+
+#include "fat.h"
+#include "dldi_patcher.h"
+#include "card.h"
+#include "boot.h"
+
+void arm7clearRAM();
+int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out);
+int sdmmc_sdcard_init();
+void sdmmc_controller_init();
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Important things
+#define TEMP_MEM 0x02FFE000
+#define NDS_HEAD 0x02FFFE00
+#define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4)
+
+
+const char* bootName = "BOOT.NDS";
+
+extern unsigned long _start;
+extern unsigned long storedFileCluster;
+extern unsigned long initDisc;
+extern unsigned long wantToPatchDLDI;
+extern unsigned long argStart;
+extern unsigned long argSize;
+extern unsigned long dsiSD;
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Firmware stuff
+
+#define FW_READ 0x03
+
+void boot_readFirmware (uint32 address, uint8 * buffer, uint32 size) {
+ uint32 index;
+
+ // Read command
+ while (REG_SPICNT & SPI_BUSY);
+ REG_SPICNT = SPI_ENABLE | SPI_CONTINUOUS | SPI_DEVICE_NVRAM;
+ REG_SPIDATA = FW_READ;
+ while (REG_SPICNT & SPI_BUSY);
+
+ // Set the address
+ REG_SPIDATA = (address>>16) & 0xFF;
+ while (REG_SPICNT & SPI_BUSY);
+ REG_SPIDATA = (address>>8) & 0xFF;
+ while (REG_SPICNT & SPI_BUSY);
+ REG_SPIDATA = (address) & 0xFF;
+ while (REG_SPICNT & SPI_BUSY);
+
+ for (index = 0; index < size; index++) {
+ REG_SPIDATA = 0;
+ while (REG_SPICNT & SPI_BUSY);
+ buffer[index] = REG_SPIDATA & 0xFF;
+ }
+ REG_SPICNT = 0;
+}
+
+
+static inline void copyLoop (u32* dest, const u32* src, u32 size) {
+ size = (size +3) & ~3;
+ do {
+ *dest++ = *src++;
+ } while (size -= 4);
+}
+
+//#define resetCpu() __asm volatile("\tswi 0x000000\n");
+
+/*-------------------------------------------------------------------------
+passArgs_ARM7
+Copies the command line arguments to the end of the ARM9 binary,
+then sets a flag in memory for the loaded NDS to use
+--------------------------------------------------------------------------*/
+void passArgs_ARM7 (void) {
+ u32 ARM9_DST = *((u32*)(NDS_HEAD + 0x028));
+ u32 ARM9_LEN = *((u32*)(NDS_HEAD + 0x02C));
+ u32* argSrc;
+ u32* argDst;
+
+ if (!argStart || !argSize) return;
+
+ argSrc = (u32*)(argStart + (int)&_start);
+
+ argDst = (u32*)((ARM9_DST + ARM9_LEN + 3) & ~3); // Word aligned
+
+ copyLoop(argDst, argSrc, argSize);
+
+ __system_argv->argvMagic = ARGV_MAGIC;
+ __system_argv->commandLine = (char*)argDst;
+ __system_argv->length = argSize;
+}
+
+
+
+
+/*-------------------------------------------------------------------------
+resetMemory_ARM7
+Clears all of the NDS's RAM that is visible to the ARM7
+Written by Darkain.
+Modified by Chishm:
+ * Added STMIA clear mem loop
+--------------------------------------------------------------------------*/
+void resetMemory_ARM7 (void)
+{
+ int i;
+ u8 settings1, settings2;
+ u32 settingsOffset = 0;
+
+ REG_IME = 0;
+
+ for (i=0; i<16; i++) {
+ SCHANNEL_CR(i) = 0;
+ SCHANNEL_TIMER(i) = 0;
+ SCHANNEL_SOURCE(i) = 0;
+ SCHANNEL_LENGTH(i) = 0;
+ }
+
+ REG_SOUNDCNT = 0;
+
+ //clear out ARM7 DMA channels and timers
+ for (i=0; i<4; i++) {
+ DMA_CR(i) = 0;
+ DMA_SRC(i) = 0;
+ DMA_DEST(i) = 0;
+ TIMER_CR(i) = 0;
+ TIMER_DATA(i) = 0;
+ }
+
+ arm7clearRAM();
+
+ REG_IE = 0;
+ REG_IF = ~0;
+ (*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version
+ (*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version
+ REG_POWERCNT = 1; //turn off power to stuff
+
+ // Get settings location
+ boot_readFirmware((u32)0x00020, (u8*)&settingsOffset, 0x2);
+ settingsOffset *= 8;
+
+ // Reload DS Firmware settings
+ boot_readFirmware(settingsOffset + 0x070, &settings1, 0x1);
+ boot_readFirmware(settingsOffset + 0x170, &settings2, 0x1);
+
+ if ((settings1 & 0x7F) == ((settings2+1) & 0x7F)) {
+ boot_readFirmware(settingsOffset + 0x000, (u8*)0x02FFFC80, 0x70);
+ } else {
+ boot_readFirmware(settingsOffset + 0x100, (u8*)0x02FFFC80, 0x70);
+ }
+}
+
+
+void loadBinary_ARM7 (u32 fileCluster)
+{
+ u32 ndsHeader[0x170>>2];
+
+ // read NDS header
+ fileRead ((char*)ndsHeader, fileCluster, 0, 0x170);
+ // read ARM9 info from NDS header
+ u32 ARM9_SRC = ndsHeader[0x020>>2];
+ char* ARM9_DST = (char*)ndsHeader[0x028>>2];
+ u32 ARM9_LEN = ndsHeader[0x02C>>2];
+ // read ARM7 info from NDS header
+ u32 ARM7_SRC = ndsHeader[0x030>>2];
+ char* ARM7_DST = (char*)ndsHeader[0x038>>2];
+ u32 ARM7_LEN = ndsHeader[0x03C>>2];
+
+ // Load binaries into memory
+ fileRead(ARM9_DST, fileCluster, ARM9_SRC, ARM9_LEN);
+ fileRead(ARM7_DST, fileCluster, ARM7_SRC, ARM7_LEN);
+
+ // first copy the header to its proper location, excluding
+ // the ARM9 start address, so as not to start it
+ TEMP_ARM9_START_ADDRESS = ndsHeader[0x024>>2]; // Store for later
+ ndsHeader[0x024>>2] = 0;
+ dmaCopyWords(3, (void*)ndsHeader, (void*)NDS_HEAD, 0x170);
+}
+
+/*-------------------------------------------------------------------------
+startBinary_ARM7
+Jumps to the ARM7 NDS binary in sync with the display and ARM9
+Written by Darkain.
+Modified by Chishm:
+ * Removed MultiNDS specific stuff
+--------------------------------------------------------------------------*/
+void startBinary_ARM7 (void) {
+ REG_IME=0;
+ while(REG_VCOUNT!=191);
+ while(REG_VCOUNT==191);
+ // copy NDS ARM9 start address into the header, starting ARM9
+ *((vu32*)0x02FFFE24) = TEMP_ARM9_START_ADDRESS;
+ ARM9_START_FLAG = 1;
+ // Start ARM7
+ VoidFn arm7code = *(VoidFn*)(0x2FFFE34);
+ arm7code();
+}
+
+int sdmmc_sd_readsectors(u32 sector_no, u32 numsectors, void *out);
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Main function
+bool sdmmc_inserted() {
+ return true;
+}
+
+bool sdmmc_startup() {
+ sdmmc_controller_init();
+ return sdmmc_sdcard_init() == 0;
+}
+
+bool sdmmc_readsectors(u32 sector_no, u32 numsectors, void *out) {
+ return sdmmc_sdcard_readsectors(sector_no, numsectors, out) == 0;
+}
+
+int main (void) {
+ if (dsiSD) {
+ _io_dldi.fn_readSectors = sdmmc_readsectors;
+ _io_dldi.fn_isInserted = sdmmc_inserted;
+ _io_dldi.fn_startup = sdmmc_startup;
+ }
+
+ u32 fileCluster = storedFileCluster;
+ // Init card
+ if(!FAT_InitFiles(initDisc))
+ {
+ return -1;
+ }
+ if ((fileCluster < CLUSTER_FIRST) || (fileCluster >= CLUSTER_EOF)) /* Invalid file cluster specified */
+ {
+ fileCluster = getBootFileCluster(bootName);
+ }
+ if (fileCluster == CLUSTER_FREE)
+ {
+ return -1;
+ }
+
+ // ARM9 clears its memory part 2
+ // copy ARM9 function to RAM, and make the ARM9 jump to it
+ copyLoop((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size);
+ (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function
+ // Wait until the ARM9 has completed its task
+ while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM);
+
+ // Get ARM7 to clear RAM
+ resetMemory_ARM7();
+
+ // ARM9 enters a wait loop
+ // copy ARM9 function to RAM, and make the ARM9 jump to it
+ copyLoop((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size);
+ (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function
+
+ // Load the NDS file
+ loadBinary_ARM7(fileCluster);
+
+ // Patch with DLDI if desired
+ if (wantToPatchDLDI) {
+ dldiPatchBinary ((u8*)((u32*)NDS_HEAD)[0x0A], ((u32*)NDS_HEAD)[0x0B]);
+ }
+
+ // Pass command line arguments to loaded program
+ passArgs_ARM7();
+
+ startBinary_ARM7();
+
+ return 0;
+}
+
diff --git a/bootloader/source/boot.h b/bootloader/source/boot.h
new file mode 100644
index 0000000..3bb312e
--- /dev/null
+++ b/bootloader/source/boot.h
@@ -0,0 +1,12 @@
+#ifndef _BOOT_H_
+#define _BOOT_H_
+
+#define resetMemory2_ARM9_size 0x400
+void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9();
+#define clearMasterBright_ARM9_size 0x200
+void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) clearMasterBright_ARM9();
+#define startBinary_ARM9_size 0x100
+void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 ();
+#define ARM9_START_FLAG (*(vu8*)0x02FFFDFB)
+
+#endif // _BOOT_H_
diff --git a/bootloader/source/card.h b/bootloader/source/card.h
new file mode 100644
index 0000000..cd6f6fb
--- /dev/null
+++ b/bootloader/source/card.h
@@ -0,0 +1,45 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+#ifndef CARD_H
+#define CARD_H
+
+#include "disc_io.h"
+#include "io_dldi.h"
+
+static inline bool CARD_StartUp (void) {
+ return _io_dldi.fn_startup();
+}
+
+static inline bool CARD_IsInserted (void) {
+ return _io_dldi.fn_isInserted();
+}
+
+static inline bool CARD_ReadSector (u32 sector, void *buffer) {
+ return _io_dldi.fn_readSectors(sector, 1, buffer);
+}
+
+static inline bool CARD_ReadSectors (u32 sector, int count, void *buffer) {
+ return _io_dldi.fn_readSectors(sector, count, buffer);
+}
+
+#endif // CARD_H
diff --git a/bootloader/source/disc_io.h b/bootloader/source/disc_io.h
new file mode 100644
index 0000000..29033b7
--- /dev/null
+++ b/bootloader/source/disc_io.h
@@ -0,0 +1,82 @@
+/*
+ disc_io.h
+ Interface template for low level disc functions.
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-16 - Chishm
+ * Renamed _CF_USE_DMA to _IO_USE_DMA
+ * Renamed _CF_ALLOW_UNALIGNED to _IO_ALLOW_UNALIGNED
+*/
+
+#ifndef _DISC_IO_H
+#define _DISC_IO_H
+
+#include
+#define BYTES_PER_SECTOR 512
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _IO_USE_DMA
+
+// Allow buffers not alligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+#define _IO_ALLOW_UNALIGNED
+
+#if defined _IO_USE_DMA && defined _IO_ALLOW_UNALIGNED
+ #error "You can't use both DMA and unaligned memory"
+#endif
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u32 numSectors, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u32 numSectors, const void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+struct IO_INTERFACE_STRUCT {
+ unsigned long ioType ;
+ unsigned long features ;
+ FN_MEDIUM_STARTUP fn_startup ;
+ FN_MEDIUM_ISINSERTED fn_isInserted ;
+ FN_MEDIUM_READSECTORS fn_readSectors ;
+ FN_MEDIUM_WRITESECTORS fn_writeSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_clearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_shutdown ;
+} ;
+
+typedef struct IO_INTERFACE_STRUCT IO_INTERFACE ;
+
+#endif // define _DISC_IO_H
diff --git a/bootloader/source/dldi_patcher.c b/bootloader/source/dldi_patcher.c
new file mode 100644
index 0000000..13656f7
--- /dev/null
+++ b/bootloader/source/dldi_patcher.c
@@ -0,0 +1,208 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+#ifndef NO_DLDI
+#include
+#include
+#include "dldi_patcher.h"
+
+#define FIX_ALL 0x01
+#define FIX_GLUE 0x02
+#define FIX_GOT 0x04
+#define FIX_BSS 0x08
+
+enum DldiOffsets {
+ DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm"
+ DO_magicToken = 0x00, // 0xBF8DA5ED
+ DO_magicShortString = 0x04, // " Chishm"
+ DO_version = 0x0C,
+ DO_driverSize = 0x0D,
+ DO_fixSections = 0x0E,
+ DO_allocatedSpace = 0x0F,
+
+ DO_friendlyName = 0x10,
+
+ DO_text_start = 0x40, // Data start
+ DO_data_end = 0x44, // Data end
+ DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing
+ DO_glue_end = 0x4C, // Interworking glue end
+ DO_got_start = 0x50, // GOT start -- Needs address fixing
+ DO_got_end = 0x54, // GOT end
+ DO_bss_start = 0x58, // bss start -- Needs setting to zero
+ DO_bss_end = 0x5C, // bss end
+
+ // IO_INTERFACE data
+ DO_ioType = 0x60,
+ DO_features = 0x64,
+ DO_startup = 0x68,
+ DO_isInserted = 0x6C,
+ DO_readSectors = 0x70,
+ DO_writeSectors = 0x74,
+ DO_clearStatus = 0x78,
+ DO_shutdown = 0x7C,
+ DO_code = 0x80
+};
+
+static addr_t readAddr (data_t *mem, addr_t offset) {
+ return ((addr_t*)mem)[offset/sizeof(addr_t)];
+}
+
+static void writeAddr (data_t *mem, addr_t offset, addr_t value) {
+ ((addr_t*)mem)[offset/sizeof(addr_t)] = value;
+}
+
+
+static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) {
+ const int* dataChunk = (const int*) data;
+ int searchChunk = ((const int*)search)[0];
+ addr_t i;
+ addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int));
+
+ for ( i = 0; i < dataChunkEnd; i++) {
+ if (dataChunk[i] == searchChunk) {
+ if ((i*sizeof(int) + searchLen) > dataLen) {
+ return -1;
+ }
+ if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0) {
+ return i*sizeof(int);
+ }
+ }
+ }
+
+ return -1;
+}
+
+static const data_t dldiMagicString[] = "\xED\xA5\x8D\xBF Chishm"; // Normal DLDI file
+static const data_t dldiMagicLoaderString[] = "\xEE\xA5\x8D\xBF Chishm"; // Different to a normal DLDI file
+#define DEVICE_TYPE_DLDI 0x49444C44
+
+extern const u32 _io_dldi;
+
+bool dldiPatchBinary (data_t *binData, u32 binSize) {
+
+ addr_t memOffset; // Offset of DLDI after the file is loaded into memory
+ addr_t patchOffset; // Position of patch destination in the file
+ addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly
+ addr_t ddmemOffset; // Original offset used in the DLDI file
+ addr_t ddmemStart; // Start of range that offsets can be in the DLDI file
+ addr_t ddmemEnd; // End of range that offsets can be in the DLDI file
+ addr_t ddmemSize; // Size of range that offsets can be in the DLDI file
+ addr_t addrIter;
+
+ data_t *pDH;
+ data_t *pAH;
+
+ size_t dldiFileSize = 0;
+
+ // Find the DLDI reserved space in the file
+ patchOffset = quickFind (binData, dldiMagicString, binSize, sizeof(dldiMagicLoaderString));
+
+ if (patchOffset < 0) {
+ // does not have a DLDI section
+ return false;
+ }
+
+ pDH = (data_t*)(((u32*)(&_io_dldi)) - 24);
+ pAH = &(binData[patchOffset]);
+
+ if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI) {
+ // No DLDI patch
+ return false;
+ }
+
+ if (pDH[DO_driverSize] > pAH[DO_allocatedSpace]) {
+ // Not enough space for patch
+ return false;
+ }
+
+ dldiFileSize = 1 << pDH[DO_driverSize];
+
+ memOffset = readAddr (pAH, DO_text_start);
+ if (memOffset == 0) {
+ memOffset = readAddr (pAH, DO_startup) - DO_code;
+ }
+ ddmemOffset = readAddr (pDH, DO_text_start);
+ relocationOffset = memOffset - ddmemOffset;
+
+ ddmemStart = readAddr (pDH, DO_text_start);
+ ddmemSize = (1 << pDH[DO_driverSize]);
+ ddmemEnd = ddmemStart + ddmemSize;
+
+ // Remember how much space is actually reserved
+ pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace];
+ // Copy the DLDI patch into the application
+ memcpy (pAH, pDH, dldiFileSize);
+
+ // Fix the section pointers in the header
+ writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset);
+ writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset);
+ writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset);
+ writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset);
+ writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset);
+ writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset);
+ writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset);
+ writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset);
+ // Fix the function pointers in the header
+ writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset);
+ writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset);
+ writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset);
+ writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset);
+ writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset);
+ writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset);
+
+ // Put the correct DLDI magic string back into the DLDI header
+ memcpy (pAH, dldiMagicString, sizeof (dldiMagicString));
+
+ if (pDH[DO_fixSections] & FIX_ALL) {
+ // Search through and fix pointers within the data section of the file
+ for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (pDH[DO_fixSections] & FIX_GLUE) {
+ // Search through and fix pointers within the glue section of the file
+ for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (pDH[DO_fixSections] & FIX_GOT) {
+ // Search through and fix pointers within the Global Offset Table section of the file
+ for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) {
+ if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) {
+ writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset);
+ }
+ }
+ }
+
+ if (pDH[DO_fixSections] & FIX_BSS) {
+ // Initialise the BSS to 0
+ memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start));
+ }
+
+ return true;
+}
+#endif
\ No newline at end of file
diff --git a/bootloader/source/dldi_patcher.h b/bootloader/source/dldi_patcher.h
new file mode 100644
index 0000000..f54c531
--- /dev/null
+++ b/bootloader/source/dldi_patcher.h
@@ -0,0 +1,32 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+#ifndef DLDI_PATCHER_H
+#define DLDI_PATCHER_H
+
+#include
+
+typedef signed int addr_t;
+typedef unsigned char data_t;
+bool dldiPatchBinary (data_t *binData, u32 binSize);
+
+#endif // DLDI_PATCHER_H
diff --git a/bootloader/source/fat.c b/bootloader/source/fat.c
new file mode 100644
index 0000000..f405f75
--- /dev/null
+++ b/bootloader/source/fat.c
@@ -0,0 +1,585 @@
+/*-----------------------------------------------------------------
+ fat.c
+
+ NDS MP
+ GBAMP NDS Firmware Hack Version 2.12
+ An NDS aware firmware patch for the GBA Movie Player.
+ By Michael Chisholm (Chishm)
+
+ Filesystem code based on GBAMP_CF.c by Chishm (me).
+
+License:
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+#include "fat.h"
+#include "card.h"
+
+
+//---------------------------------------------------------------
+// FAT constants
+
+#define FILE_LAST 0x00
+#define FILE_FREE 0xE5
+
+#define ATTRIB_ARCH 0x20
+#define ATTRIB_DIR 0x10
+#define ATTRIB_LFN 0x0F
+#define ATTRIB_VOL 0x08
+#define ATTRIB_HID 0x02
+#define ATTRIB_SYS 0x04
+#define ATTRIB_RO 0x01
+
+#define FAT16_ROOT_DIR_CLUSTER 0x00
+
+// File Constants
+#ifndef EOF
+#define EOF -1
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+
+//-----------------------------------------------------------------
+// FAT constants
+#define CLUSTER_EOF_16 0xFFFF
+
+#define ATTRIB_ARCH 0x20
+#define ATTRIB_DIR 0x10
+#define ATTRIB_LFN 0x0F
+#define ATTRIB_VOL 0x08
+#define ATTRIB_HID 0x02
+#define ATTRIB_SYS 0x04
+#define ATTRIB_RO 0x01
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Data Structures
+
+#define __PACKED __attribute__ ((__packed__))
+
+// Boot Sector - must be packed
+typedef struct
+{
+ u8 jmpBoot[3];
+ u8 OEMName[8];
+ // BIOS Parameter Block
+ u16 bytesPerSector;
+ u8 sectorsPerCluster;
+ u16 reservedSectors;
+ u8 numFATs;
+ u16 rootEntries;
+ u16 numSectorsSmall;
+ u8 mediaDesc;
+ u16 sectorsPerFAT;
+ u16 sectorsPerTrk;
+ u16 numHeads;
+ u32 numHiddenSectors;
+ u32 numSectors;
+ union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
+ {
+ struct
+ {
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[448];
+ } fat16;
+ struct
+ {
+ // FAT32 extended block
+ u32 sectorsPerFAT32;
+ u16 extFlags;
+ u16 fsVer;
+ u32 rootClus;
+ u16 fsInfo;
+ u16 bkBootSec;
+ u8 reserved[12];
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[420];
+ } fat32;
+ } extBlock;
+
+ __PACKED u16 bootSig;
+
+} __PACKED BOOT_SEC;
+
+// Directory entry - must be packed
+typedef struct
+{
+ u8 name[8];
+ u8 ext[3];
+ u8 attrib;
+ u8 reserved;
+ u8 cTime_ms;
+ u16 cTime;
+ u16 cDate;
+ u16 aDate;
+ u16 startClusterHigh;
+ u16 mTime;
+ u16 mDate;
+ u16 startCluster;
+ u32 fileSize;
+} __PACKED DIR_ENT;
+
+// File information - no need to pack
+typedef struct
+{
+ u32 firstCluster;
+ u32 length;
+ u32 curPos;
+ u32 curClus; // Current cluster to read from
+ int curSect; // Current sector within cluster
+ int curByte; // Current byte within sector
+ char readBuffer[512]; // Buffer used for unaligned reads
+ u32 appClus; // Cluster to append to
+ int appSect; // Sector within cluster for appending
+ int appByte; // Byte within sector for appending
+ bool read; // Can read from file
+ bool write; // Can write to file
+ bool append;// Can append to file
+ bool inUse; // This file is open
+ u32 dirEntSector; // The sector where the directory entry is stored
+ int dirEntOffset; // The offset within the directory sector
+} FAT_FILE;
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Global Variables
+
+// _VARS_IN_RAM variables are stored in the largest section of WRAM
+// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
+
+// Locations on card
+int discRootDir;
+int discRootDirClus;
+int discFAT;
+int discSecPerFAT;
+int discNumSec;
+int discData;
+int discBytePerSec;
+int discSecPerClus;
+int discBytePerClus;
+
+enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} discFileSystem;
+
+// Global sector buffer to save on stack space
+unsigned char globalBuffer[BYTES_PER_SECTOR];
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+//FAT routines
+
+u32 FAT_ClustToSect (u32 cluster) {
+ return (((cluster-2) * discSecPerClus) + discData);
+}
+
+/*-----------------------------------------------------------------
+FAT_NextCluster
+Internal function - gets the cluster linked from input cluster
+-----------------------------------------------------------------*/
+u32 FAT_NextCluster(u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+
+ switch (discFileSystem)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = discFAT + (((cluster * 3) / 2) / BYTES_PER_SECTOR);
+ offset = ((cluster * 3) / 2) % BYTES_PER_SECTOR;
+ CARD_ReadSector(sector, globalBuffer);
+ nextCluster = ((u8*) globalBuffer)[offset];
+ offset++;
+
+ if (offset >= BYTES_PER_SECTOR) {
+ offset = 0;
+ sector++;
+ }
+
+ CARD_ReadSector(sector, globalBuffer);
+ nextCluster |= (((u8*) globalBuffer)[offset]) << 8;
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = discFAT + ((cluster << 1) / BYTES_PER_SECTOR);
+ offset = cluster % (BYTES_PER_SECTOR >> 1);
+
+ CARD_ReadSector(sector, globalBuffer);
+ // read the nextCluster value
+ nextCluster = ((u16*)globalBuffer)[offset];
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = discFAT + ((cluster << 2) / BYTES_PER_SECTOR);
+ offset = cluster % (BYTES_PER_SECTOR >> 2);
+
+ CARD_ReadSector(sector, globalBuffer);
+ // read the nextCluster value
+ nextCluster = (((u32*)globalBuffer)[offset]) & 0x0FFFFFFF;
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+/*-----------------------------------------------------------------
+ucase
+Returns the uppercase version of the given char
+char IN: a character
+char return OUT: uppercase version of character
+-----------------------------------------------------------------*/
+char ucase (char character)
+{
+ if ((character > 0x60) && (character < 0x7B))
+ character = character - 0x20;
+ return (character);
+}
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (bool initCard)
+{
+ int i;
+ int bootSector;
+ BOOT_SEC* bootSec;
+
+ if (initCard && !CARD_StartUp())
+ {
+ return (false);
+ }
+
+ // Read first sector of card
+ if (!CARD_ReadSector (0, globalBuffer))
+ {
+ return false;
+ }
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
+ {
+ bootSector = 0;
+ }
+ // Check for FAT32
+ else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
+ {
+ bootSector = 0;
+ }
+ else // This is an MBR
+ {
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE)
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
+
+ // Go to first valid partition
+ if ( i != 0x1FE) // Make sure it found a partition
+ {
+ bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ bootSec = (BOOT_SEC*) globalBuffer;
+ CARD_ReadSector (bootSector, bootSec);
+
+ // Store required information about the file system
+ if (bootSec->sectorsPerFAT != 0)
+ {
+ discSecPerFAT = bootSec->sectorsPerFAT;
+ }
+ else
+ {
+ discSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
+ }
+
+ if (bootSec->numSectorsSmall != 0)
+ {
+ discNumSec = bootSec->numSectorsSmall;
+ }
+ else
+ {
+ discNumSec = bootSec->numSectors;
+ }
+
+ discBytePerSec = BYTES_PER_SECTOR; // Sector size is redefined to be 512 bytes
+ discSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTES_PER_SECTOR;
+ discBytePerClus = discBytePerSec * discSecPerClus;
+ discFAT = bootSector + bootSec->reservedSectors;
+
+ discRootDir = discFAT + (bootSec->numFATs * discSecPerFAT);
+ discData = discRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / BYTES_PER_SECTOR);
+
+ if ((discNumSec - discData) / bootSec->sectorsPerCluster < 4085)
+ {
+ discFileSystem = FS_FAT12;
+ }
+ else if ((discNumSec - discData) / bootSec->sectorsPerCluster < 65525)
+ {
+ discFileSystem = FS_FAT16;
+ }
+ else
+ {
+ discFileSystem = FS_FAT32;
+ }
+
+ if (discFileSystem != FS_FAT32)
+ {
+ discRootDirClus = FAT16_ROOT_DIR_CLUSTER;
+ }
+ else // Set up for the FAT32 way
+ {
+ discRootDirClus = bootSec->extBlock.fat32.rootClus;
+ // Check if FAT mirroring is enabled
+ if (!(bootSec->extBlock.fat32.extFlags & 0x80))
+ {
+ // Use the active FAT
+ discFAT = discFAT + ( discSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
+ }
+ }
+
+ return (true);
+}
+
+
+/*-----------------------------------------------------------------
+getBootFileCluster
+-----------------------------------------------------------------*/
+u32 getBootFileCluster (const char* bootName)
+{
+ DIR_ENT dir;
+ int firstSector = 0;
+ bool notFound = false;
+ bool found = false;
+// int maxSectors;
+ u32 wrkDirCluster = discRootDirClus;
+ u32 wrkDirSector = 0;
+ int wrkDirOffset = 0;
+ int nameOffset;
+
+ dir.startCluster = CLUSTER_FREE; // default to no file found
+ dir.startClusterHigh = CLUSTER_FREE;
+
+
+ // Check if fat has been initialised
+ if (discBytePerSec == 0)
+ {
+ return (CLUSTER_FREE);
+ }
+
+ char *ptr = (char*)bootName;
+ while (*ptr != '.') ptr++;
+ int namelen = ptr - bootName;
+
+// maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (discData - discRootDir) : discSecPerClus);
+ // Scan Dir for correct entry
+ firstSector = discRootDir;
+ CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ found = false;
+ notFound = false;
+ wrkDirOffset = -1; // Start at entry zero, Compensating for increment
+ while (!found && !notFound) {
+ wrkDirOffset++;
+ if (wrkDirOffset == BYTES_PER_SECTOR / sizeof (DIR_ENT))
+ {
+ wrkDirOffset = 0;
+ wrkDirSector++;
+ if ((wrkDirSector == discSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ wrkDirSector = 0;
+ wrkDirCluster = FAT_NextCluster(wrkDirCluster);
+ if (wrkDirCluster == CLUSTER_EOF)
+ {
+ notFound = true;
+ }
+ firstSector = FAT_ClustToSect(wrkDirCluster);
+ }
+ else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (discData - discRootDir)))
+ {
+ notFound = true; // Got to end of root dir
+ }
+ CARD_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ }
+ dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
+ found = true;
+ if ((dir.attrib & ATTRIB_DIR) || (dir.attrib & ATTRIB_VOL))
+ {
+ found = false;
+ }
+ if(namelen<8 && dir.name[namelen]!=0x20) found = false;
+ for (nameOffset = 0; nameOffset < namelen && found; nameOffset++)
+ {
+ if (ucase(dir.name[nameOffset]) != bootName[nameOffset])
+ found = false;
+ }
+ for (nameOffset = 0; nameOffset < 3 && found; nameOffset++)
+ {
+ if (ucase(dir.ext[nameOffset]) != bootName[nameOffset+namelen+1])
+ found = false;
+ }
+ if (dir.name[0] == FILE_LAST)
+ {
+ notFound = true;
+ }
+ }
+
+ // If no file is found, return CLUSTER_FREE
+ if (notFound)
+ {
+ return CLUSTER_FREE;
+ }
+
+ return (dir.startCluster | (dir.startClusterHigh << 16));
+}
+
+/*-----------------------------------------------------------------
+fileRead(buffer, cluster, startOffset, length)
+-----------------------------------------------------------------*/
+u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length)
+{
+ int curByte;
+ int curSect;
+
+ int dataPos = 0;
+ int chunks;
+ int beginBytes;
+
+ if (cluster == CLUSTER_FREE || cluster == CLUSTER_EOF)
+ {
+ return 0;
+ }
+
+ // Follow cluster list until desired one is found
+ for (chunks = startOffset / discBytePerClus; chunks > 0; chunks--)
+ {
+ cluster = FAT_NextCluster (cluster);
+ }
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ curSect = (startOffset % discBytePerClus) / BYTES_PER_SECTOR;
+ curByte = startOffset % BYTES_PER_SECTOR;
+
+ // Load sector buffer for new position in file
+ CARD_ReadSector( curSect + FAT_ClustToSect(cluster), globalBuffer);
+ curSect++;
+
+ // Number of bytes needed to read to align with a sector
+ beginBytes = (BYTES_PER_SECTOR < length + curByte ? (BYTES_PER_SECTOR - curByte) : length);
+
+ // Read first part from buffer, to align with sector boundary
+ for (dataPos = 0 ; dataPos < beginBytes; dataPos++)
+ {
+ buffer[dataPos] = globalBuffer[curByte++];
+ }
+
+ // Read in all the 512 byte chunks of the file directly, saving time
+ for ( chunks = ((int)length - beginBytes) / BYTES_PER_SECTOR; chunks > 0;)
+ {
+ int sectorsToRead;
+
+ // Move to the next cluster if necessary
+ if (curSect >= discSecPerClus)
+ {
+ curSect = 0;
+ cluster = FAT_NextCluster (cluster);
+ }
+
+ // Calculate how many sectors to read (read a maximum of discSecPerClus at a time)
+ sectorsToRead = discSecPerClus - curSect;
+ if(chunks < sectorsToRead)
+ sectorsToRead = chunks;
+
+ // Read the sectors
+ CARD_ReadSectors(curSect + FAT_ClustToSect(cluster), sectorsToRead, buffer + dataPos);
+ chunks -= sectorsToRead;
+ curSect += sectorsToRead;
+ dataPos += BYTES_PER_SECTOR * sectorsToRead;
+ }
+
+ // Take care of any bytes left over before end of read
+ if (dataPos < length)
+ {
+
+ // Update the read buffer
+ curByte = 0;
+ if (curSect >= discSecPerClus)
+ {
+ curSect = 0;
+ cluster = FAT_NextCluster (cluster);
+ }
+ CARD_ReadSector( curSect + FAT_ClustToSect( cluster), globalBuffer);
+
+ // Read in last partial chunk
+ for (; dataPos < length; dataPos++)
+ {
+ buffer[dataPos] = globalBuffer[curByte];
+ curByte++;
+ }
+ }
+
+ return dataPos;
+}
diff --git a/bootloader/source/fat.h b/bootloader/source/fat.h
new file mode 100644
index 0000000..d39076b
--- /dev/null
+++ b/bootloader/source/fat.h
@@ -0,0 +1,46 @@
+/*-----------------------------------------------------------------
+ fat.h
+
+ NDS MP
+ GBAMP NDS Firmware Hack Version 2.12
+ An NDS aware firmware patch for the GBA Movie Player.
+ By Michael Chisholm (Chishm)
+
+ Filesystem code based on GBAMP_CF.c by Chishm (me).
+
+License:
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+#ifndef FAT_H
+#define FAT_H
+
+#include
+
+#define CLUSTER_FREE 0x00000000
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FIRST 0x00000002
+
+bool FAT_InitFiles (bool initCard);
+u32 getBootFileCluster (const char* bootName);
+u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length);
+u32 FAT_ClustToSect (u32 cluster);
+
+#endif // FAT_H
diff --git a/bootloader/source/io_dldi.h b/bootloader/source/io_dldi.h
new file mode 100644
index 0000000..e0f4efa
--- /dev/null
+++ b/bootloader/source/io_dldi.h
@@ -0,0 +1,44 @@
+/*
+ io_dldi.h
+
+ Reserved space for post-compilation adding of an extra driver
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 2006-12-22 - Chishm
+ * Original release
+*/
+
+#ifndef IO_DLDI_H
+#define IO_DLDI_H
+
+// 'DLDD'
+#define DEVICE_TYPE_DLDD 0x49444C44
+
+#include "disc_io.h"
+
+// export interface
+extern IO_INTERFACE _io_dldi ;
+
+#endif // define IO_DLDI_H
diff --git a/bootloader/source/io_dldi.s b/bootloader/source/io_dldi.s
new file mode 100644
index 0000000..fdfbde1
--- /dev/null
+++ b/bootloader/source/io_dldi.s
@@ -0,0 +1,124 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+@---------------------------------------------------------------------------------
+ .align 4
+ .arm
+ .global _dldi_start
+ .global _io_dldi
+@---------------------------------------------------------------------------------
+.equ FEATURE_MEDIUM_CANREAD, 0x00000001
+.equ FEATURE_MEDIUM_CANWRITE, 0x00000002
+.equ FEATURE_SLOT_GBA, 0x00000010
+.equ FEATURE_SLOT_NDS, 0x00000020
+
+
+_dldi_start:
+#ifndef NO_DLDI
+
+@---------------------------------------------------------------------------------
+@ Driver patch file standard header -- 16 bytes
+#ifdef STANDARD_DLDI
+ .word 0xBF8DA5ED @ Magic number to identify this region
+#else
+ .word 0xBF8DA5EE @ Magic number to identify this region
+#endif
+ .asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator)
+ .byte 0x01 @ Version number
+ .byte 0x0e @ 16KiB @ Log [base-2] of the size of this driver in bytes.
+ .byte 0x00 @ Sections to fix
+ .byte 0x0e @ 16KiB @ Log [base-2] of the allocated space in bytes.
+
+@---------------------------------------------------------------------------------
+@ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes
+ .align 4
+ .asciz "Loader (No interface)"
+
+@---------------------------------------------------------------------------------
+@ Offsets to important sections within the data -- 32 bytes
+ .align 6
+ .word _dldi_start @ data start
+ .word _dldi_end @ data end
+ .word 0x00000000 @ Interworking glue start -- Needs address fixing
+ .word 0x00000000 @ Interworking glue end
+ .word 0x00000000 @ GOT start -- Needs address fixing
+ .word 0x00000000 @ GOT end
+ .word 0x00000000 @ bss start -- Needs setting to zero
+ .word 0x00000000 @ bss end
+@---------------------------------------------------------------------------------
+@ IO_INTERFACE data -- 32 bytes
+_io_dldi:
+ .ascii "DLDI" @ ioType
+ .word 0x00000000 @ Features
+ .word _DLDI_startup @
+ .word _DLDI_isInserted @
+ .word _DLDI_readSectors @ Function pointers to standard device driver functions
+ .word _DLDI_writeSectors @
+ .word _DLDI_clearStatus @
+ .word _DLDI_shutdown @
+
+
+@---------------------------------------------------------------------------------
+
+_DLDI_startup:
+_DLDI_isInserted:
+_DLDI_readSectors:
+_DLDI_writeSectors:
+_DLDI_clearStatus:
+_DLDI_shutdown:
+ mov r0, #0x00 @ Return false for every function
+ bx lr
+
+
+
+@---------------------------------------------------------------------------------
+ .align
+ .pool
+
+ .space (_dldi_start + 16384) - . @ Fill to 16KiB
+
+_dldi_end:
+ .end
+@---------------------------------------------------------------------------------
+#else
+@---------------------------------------------------------------------------------
+@ IO_INTERFACE data -- 32 bytes
+_io_dldi:
+ .ascii "DLDI" @ ioType
+ .word 0x00000000 @ Features
+ .word _DLDI_startup @
+ .word _DLDI_isInserted @
+ .word _DLDI_readSectors @ Function pointers to standard device driver functions
+ .word _DLDI_writeSectors @
+ .word _DLDI_clearStatus @
+ .word _DLDI_shutdown @
+
+ _DLDI_startup:
+_DLDI_isInserted:
+_DLDI_readSectors:
+_DLDI_writeSectors:
+_DLDI_clearStatus:
+_DLDI_shutdown:
+ mov r0, #0x00 @ Return false for every function
+ bx lr
+
+
+#endif
diff --git a/bootloader/source/load_crt0.s b/bootloader/source/load_crt0.s
new file mode 100644
index 0000000..ea7949b
--- /dev/null
+++ b/bootloader/source/load_crt0.s
@@ -0,0 +1,144 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2005 Michael "Chishm" Chisholm
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ If you use this code, please give due credit and email me about your
+ project at chishm@hotmail.com
+------------------------------------------------------------------*/
+
+@---------------------------------------------------------------------------------
+ .section ".init"
+ .global _start
+ .global storedFileCluster
+ .global initDisc
+ .global wantToPatchDLDI
+ .global argStart
+ .global argSize
+ .global dsiSD
+@---------------------------------------------------------------------------------
+ .align 4
+ .arm
+@---------------------------------------------------------------------------------
+_start:
+@---------------------------------------------------------------------------------
+ b startUp
+
+storedFileCluster:
+ .word 0x0FFFFFFF @ default BOOT.NDS
+initDisc:
+ .word 0x00000001 @ init the disc by default
+wantToPatchDLDI:
+ .word 0x00000001 @ by default patch the DLDI section of the loaded NDS
+@ Used for passing arguments to the loaded app
+argStart:
+ .word _end - _start
+argSize:
+ .word 0x00000000
+dldiOffset:
+ .word _dldi_start - _start
+dsiSD:
+ .word 0
+
+startUp:
+ mov r0, #0x04000000
+ mov r1, #0
+ str r1, [r0,#0x208] @ REG_IME
+ str r1, [r0,#0x210] @ REG_IE
+ str r1, [r0,#0x218] @ REG_AUXIE
+
+ mov r0, #0x12 @ Switch to IRQ Mode
+ msr cpsr, r0
+ ldr sp, =__sp_irq @ Set IRQ stack
+
+ mov r0, #0x13 @ Switch to SVC Mode
+ msr cpsr, r0
+ ldr sp, =__sp_svc @ Set SVC stack
+
+ mov r0, #0x1F @ Switch to System Mode
+ msr cpsr, r0
+ ldr sp, =__sp_usr @ Set user stack
+
+ ldr r0, =__bss_start @ Clear BSS section to 0x00
+ ldr r1, =__bss_end
+ sub r1, r1, r0
+ bl ClearMem
+
+ mov r0, #0 @ int argc
+ mov r1, #0 @ char *argv[]
+ ldr r3, =main
+ bl _blx_r3_stub @ jump to user code
+
+ @ If the user ever returns, restart
+ b _start
+
+@---------------------------------------------------------------------------------
+_blx_r3_stub:
+@---------------------------------------------------------------------------------
+ bx r3
+
+@---------------------------------------------------------------------------------
+@ Clear memory to 0x00 if length != 0
+@ r0 = Start Address
+@ r1 = Length
+@---------------------------------------------------------------------------------
+ClearMem:
+@---------------------------------------------------------------------------------
+ mov r2, #3 @ Round down to nearest word boundary
+ add r1, r1, r2 @ Shouldn't be needed
+ bics r1, r1, r2 @ Clear 2 LSB (and set Z)
+ bxeq lr @ Quit if copy size is 0
+
+ mov r2, #0
+ClrLoop:
+ stmia r0!, {r2}
+ subs r1, r1, #4
+ bne ClrLoop
+ bx lr
+
+@---------------------------------------------------------------------------------
+@ Copy memory if length != 0
+@ r1 = Source Address
+@ r2 = Dest Address
+@ r4 = Dest Address + Length
+@---------------------------------------------------------------------------------
+CopyMemCheck:
+@---------------------------------------------------------------------------------
+ sub r3, r4, r2 @ Is there any data to copy?
+@---------------------------------------------------------------------------------
+@ Copy memory
+@ r1 = Source Address
+@ r2 = Dest Address
+@ r3 = Length
+@---------------------------------------------------------------------------------
+CopyMem:
+@---------------------------------------------------------------------------------
+ mov r0, #3 @ These commands are used in cases where
+ add r3, r3, r0 @ the length is not a multiple of 4,
+ bics r3, r3, r0 @ even though it should be.
+ bxeq lr @ Length is zero, so exit
+CIDLoop:
+ ldmia r1!, {r0}
+ stmia r2!, {r0}
+ subs r3, r3, #4
+ bne CIDLoop
+ bx lr
+
+@---------------------------------------------------------------------------------
+ .align
+ .pool
+ .end
+@---------------------------------------------------------------------------------
diff --git a/bootloader/source/sdmmc.c b/bootloader/source/sdmmc.c
new file mode 100644
index 0000000..13ff139
--- /dev/null
+++ b/bootloader/source/sdmmc.c
@@ -0,0 +1,301 @@
+#ifndef NO_SDMMC
+#include
+#include
+#include
+
+static struct mmcdevice deviceSD;
+
+//---------------------------------------------------------------------------------
+int geterror(struct mmcdevice *ctx) {
+//---------------------------------------------------------------------------------
+ //if(ctx->error == 0x4) return -1;
+ //else return 0;
+ return (ctx->error << 29) >> 31;
+}
+
+
+//---------------------------------------------------------------------------------
+void setTarget(struct mmcdevice *ctx) {
+//---------------------------------------------------------------------------------
+ sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
+ setckl(ctx->clk);
+ if (ctx->SDOPT == 0) {
+ sdmmc_mask16(REG_SDOPT, 0, 0x8000);
+ } else {
+ sdmmc_mask16(REG_SDOPT, 0x8000, 0);
+ }
+
+}
+
+
+//---------------------------------------------------------------------------------
+void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) {
+//---------------------------------------------------------------------------------
+ int i;
+ bool getSDRESP = (cmd << 15) >> 31;
+ uint16_t flags = (cmd << 15) >> 31;
+ const bool readdata = cmd & 0x20000;
+ const bool writedata = cmd & 0x40000;
+
+ if(readdata || writedata)
+ {
+ flags |= TMIO_STAT0_DATAEND;
+ }
+
+ ctx->error = 0;
+ while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
+ sdmmc_write16(REG_SDIRMASK0,0);
+ sdmmc_write16(REG_SDIRMASK1,0);
+ sdmmc_write16(REG_SDSTATUS0,0);
+ sdmmc_write16(REG_SDSTATUS1,0);
+#ifdef DATA32_SUPPORT
+// if(readdata)sdmmc_mask16(REG_DATACTL32, 0x1000, 0x800);
+// if(writedata)sdmmc_mask16(REG_DATACTL32, 0x800, 0x1000);
+// sdmmc_mask16(REG_DATACTL32,0x1800,2);
+#else
+ sdmmc_mask16(REG_DATACTL32,0x1800,0);
+#endif
+ sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
+ sdmmc_write16(REG_SDCMDARG1,args >> 16);
+ sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
+
+ uint32_t size = ctx->size;
+ uint16_t *dataPtr = (uint16_t*)ctx->data;
+ uint32_t *dataPtr32 = (uint32_t*)ctx->data;
+
+ bool useBuf = ( NULL != dataPtr );
+ bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr))));
+
+ uint16_t status0 = 0;
+
+ while(1) {
+ volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1);
+#ifdef DATA32_SUPPORT
+ volatile uint16_t ctl32 = sdmmc_read16(REG_SDDATACTL32);
+ if((ctl32 & 0x100))
+#else
+ if((status1 & TMIO_STAT1_RXRDY))
+#endif
+ {
+ if(readdata) {
+ if(useBuf) {
+ sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
+ if(size > 0x1FF) {
+#ifdef DATA32_SUPPORT
+ if(useBuf32) {
+ for(i = 0; i<0x200; i+=4) {
+ *dataPtr32++ = sdmmc_read32(REG_SDFIFO32);
+ }
+ } else {
+#endif
+ for(i = 0; i<0x200; i+=2) {
+ *dataPtr++ = sdmmc_read16(REG_SDFIFO);
+ }
+#ifdef DATA32_SUPPORT
+ }
+#endif
+ size -= 0x200;
+ }
+ }
+
+ sdmmc_mask16(REG_SDDATACTL32, 0x800, 0);
+ }
+ }
+#ifdef DATA32_SUPPORT
+ if(!(ctl32 & 0x200))
+#else
+ if((status1 & TMIO_STAT1_TXRQ))
+#endif
+ {
+ if(writedata) {
+ if(useBuf) {
+ sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
+ //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
+ if(size > 0x1FF) {
+#ifdef DATA32_SUPPORT
+ for(i = 0; i<0x200; i+=4) {
+ sdmmc_write32(REG_SDFIFO32,*dataPtr32++);
+ }
+#else
+ for(i = 0; i<0x200; i+=2) {
+ sdmmc_write16(REG_SDFIFO,*dataPtr++);
+ }
+#endif
+ size -= 0x200;
+ }
+ }
+
+ sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0);
+ }
+ }
+ if(status1 & TMIO_MASK_GW) {
+ ctx->error |= 4;
+ break;
+ }
+
+ if(!(status1 & TMIO_STAT1_CMD_BUSY)) {
+ status0 = sdmmc_read16(REG_SDSTATUS0);
+ if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) {
+ ctx->error |= 0x1;
+ }
+ if(status0 & TMIO_STAT0_DATAEND) {
+ ctx->error |= 0x2;
+ }
+
+ if((status0 & flags) == flags)
+ break;
+ }
+ }
+ ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
+ ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
+ sdmmc_write16(REG_SDSTATUS0,0);
+ sdmmc_write16(REG_SDSTATUS1,0);
+
+ if(getSDRESP != 0) {
+ ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16);
+ ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16);
+ ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16);
+ ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16);
+ }
+}
+
+
+//---------------------------------------------------------------------------------
+int sdmmc_cardinserted() {
+//---------------------------------------------------------------------------------
+ return 1; //sdmmc_cardready;
+}
+
+//---------------------------------------------------------------------------------
+void sdmmc_controller_init(bool force) {
+//---------------------------------------------------------------------------------
+ deviceSD.isSDHC = 0;
+ deviceSD.SDOPT = 0;
+ deviceSD.res = 0;
+ deviceSD.initarg = 0;
+ deviceSD.clk = 0x80;
+ deviceSD.devicenumber = 0;
+
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu;
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu;
+#ifdef DATA32_SUPPORT
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
+#else
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
+#endif
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
+#ifdef DATA32_SUPPORT
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu;
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu;
+ *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512;
+#else
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu;
+ *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu;
+ *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0;
+#endif
+ *(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1;
+ *(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu;
+ *(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u;
+ *(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL;
+ *(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
+ *(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
+ *(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
+ *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
+#ifdef DATA32_SUPPORT
+ *(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20;
+ *(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE;
+#else
+ *(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x40; //Nintendo sets this to 0x20
+ *(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EB; //Nintendo sets this to 0x40EE
+#endif
+ *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
+ *(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512;
+ *(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0;
+
+ setTarget(&deviceSD);
+}
+
+//---------------------------------------------------------------------------------
+int sdmmc_sdcard_init() {
+//---------------------------------------------------------------------------------
+ setTarget(&deviceSD);
+ swiDelay(0xF000);
+ sdmmc_send_command(&deviceSD,0,0);
+ sdmmc_send_command(&deviceSD,0x10408,0x1AA);
+ u32 temp = (deviceSD.error & 0x1) << 0x1E;
+
+ u32 temp2 = 0;
+ do {
+ do {
+ sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
+ sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp);
+ temp2 = 1;
+ } while ( !(deviceSD.error & 1) );
+
+ } while((deviceSD.ret[0] & 0x80000000) == 0);
+
+ if(!((deviceSD.ret[0] >> 30) & 1) || !temp)
+ temp2 = 0;
+
+ deviceSD.isSDHC = temp2;
+
+ sdmmc_send_command(&deviceSD,0x10602,0);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x10403,0);
+ if (deviceSD.error & 0x4) return -1;
+ deviceSD.initarg = deviceSD.ret[0] >> 0x10;
+
+ sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10);
+ if (deviceSD.error & 0x4) return -1;
+
+ deviceSD.clk = 1;
+ setckl(1);
+
+ sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x1076A,0x0);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
+ if (deviceSD.error & 0x4) return -1;
+
+ deviceSD.SDOPT = 1;
+ sdmmc_send_command(&deviceSD,0x10446,0x2);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10);
+ if (deviceSD.error & 0x4) return -1;
+
+ sdmmc_send_command(&deviceSD,0x10410,0x200);
+ if (deviceSD.error & 0x4) return -1;
+ deviceSD.clk |= 0x200;
+
+ return 0;
+
+}
+
+//---------------------------------------------------------------------------------
+int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out) {
+//---------------------------------------------------------------------------------
+ if (deviceSD.isSDHC == 0)
+ sector_no <<= 9;
+ setTarget(&deviceSD);
+ sdmmc_write16(REG_SDSTOP,0x100);
+
+#ifdef DATA32_SUPPORT
+ sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
+ sdmmc_write16(REG_SDBLKLEN32,0x200);
+#endif
+
+ sdmmc_write16(REG_SDBLKCOUNT,numsectors);
+ deviceSD.data = out;
+ deviceSD.size = numsectors << 9;
+ sdmmc_send_command(&deviceSD,0x33C12,sector_no);
+ return geterror(&deviceSD);
+}
+#endif
\ No newline at end of file
diff --git a/bootstub/Makefile b/bootstub/Makefile
new file mode 100644
index 0000000..aea4f55
--- /dev/null
+++ b/bootstub/Makefile
@@ -0,0 +1,13 @@
+include $(DEVKITARM)/base_tools
+
+TARGET := bootstub
+
+../data/$(TARGET).bin: $(TARGET).elf
+ $(OBJCOPY) -O binary $< $@
+
+$(TARGET).elf: $(TARGET).s Makefile
+ $(CC) -Wl,-Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $(TARGET).s -o $@
+
+
+clean:
+ rm -f $(TARGET).elf $(TARGET).bin
diff --git a/bootstub/bootstub.s b/bootstub/bootstub.s
new file mode 100644
index 0000000..579fb99
--- /dev/null
+++ b/bootstub/bootstub.s
@@ -0,0 +1,176 @@
+/*-----------------------------------------------------------------
+
+ Copyright (C) 2010 Dave "WinterMute" Murphy
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+------------------------------------------------------------------*/
+ .global _start
+
+//-----------------------------------------------------------------
+_start:
+//-----------------------------------------------------------------
+ .ascii "bootstub"
+ .word hook7from9 - _start
+ .word hook9from7 - _start
+_loader_size:
+ .word 0
+
+ .arch armv4t
+ .cpu arm7tdmi
+
+//-----------------------------------------------------------------
+hook9from7:
+//-----------------------------------------------------------------
+ ldr r0, arm9bootaddr
+ adr r1, hook7from9
+ str r1, [r0]
+
+ mov r3, #0x04000000
+ ldr r0, resetcode
+ str r0, [r3, #0x188]
+ add r3, r3, #0x180
+
+ adr r0, waitcode_start
+ ldr r1, arm7base
+ adr r2, waitcode_end
+1: ldr r4, [r0],#4
+ str r4, [r1],#4
+ cmp r2, r0
+ bne 1b
+
+ ldr r1, arm7base
+ bx r1
+
+//-----------------------------------------------------------------
+waitcode_start:
+//-----------------------------------------------------------------
+ push {lr}
+ mov r2, #1
+ bl waitsync
+
+ mov r0, #0x100
+ strh r0, [r3]
+
+ mov r2, #0
+ bl waitsync
+
+ mov r0, #0
+ strh r0, [r3]
+ pop {lr}
+
+ bx lr
+
+waitsync:
+ ldrh r0, [r3]
+ and r0, r0, #0x000f
+ cmp r0, r2
+ bne waitsync
+ bx lr
+waitcode_end:
+
+arm7base:
+ .word 0x037f8000
+arm7bootaddr:
+ .word 0x02FFFE34
+arm9bootaddr:
+ .word 0x02FFFE24
+tcmpudisable:
+ .word 0x2078
+
+resetcode:
+ .word 0x0c04000c
+
+//-----------------------------------------------------------------
+hook7from9:
+//-----------------------------------------------------------------
+ mov r12, #0x04000000
+ strb r12, [r12,#0x208]
+
+ .arch armv5te
+ .cpu arm946e-s
+
+ ldr r1, tcmpudisable @ disable TCM and protection unit
+ mcr p15, 0, r1, c1, c0
+
+ @ Disable cache
+ mov r0, #0
+ mcr p15, 0, r0, c7, c5, 0 @ Instruction cache
+ mcr p15, 0, r0, c7, c6, 0 @ Data cache
+ mcr p15, 0, r0, c3, c0, 0 @ write buffer
+
+ @ Wait for write buffer to empty
+ mcr p15, 0, r0, c7, c10, 4
+
+ add r3, r12, #0x180 @ r3 = 4000180
+
+ mov r0,#0x80
+ strb r0,[r3,#0x242-0x180]
+
+ adr r0, _loader
+ ldr r2, _loader_size
+ mov r1, #0x06800000
+ add r1, r1, #0x40000
+ add r2, r0, r2
+_copyloader:
+ ldr r4, [r0], #4
+ str r4, [r1], #4
+ cmp r0, r2
+ blt _copyloader
+
+ mov r0,#0x82
+ strb r0,[r3,#0x242-0x180]
+
+ ldrh r0,[r3,#0x204-0x180]
+ orr r0,r0,#(1<<11) | (1<<7)
+ strh r0,[r3,#0x204-0x180]
+
+
+ ldr r0, arm7bootaddr
+ mov r1, #0x06000000
+ str r1, [r0]
+
+ ldr r0, resetcode
+ str r0, [r12, #0x188]
+
+ mov r2, #1
+ bl waitsync
+
+ mov r0, #0x100
+ strh r0, [r3]
+
+ mov r2, #0
+ bl waitsync
+
+ mov r0, #0
+ strh r0, [r3]
+
+// set up and enter passme loop
+
+ ldr r0,arm9branchaddr
+ ldr r1,branchinst
+ str r1,[r0]
+ str r0,[r0,#0x20]
+
+ bx r0
+
+branchinst:
+ .word 0xE59FF018
+
+arm9branchaddr:
+ .word 0x02fffe04
+
+
+_loader:
diff --git a/clean and compile.bat b/clean and compile.bat
new file mode 100644
index 0000000..3260f43
--- /dev/null
+++ b/clean and compile.bat
@@ -0,0 +1,3 @@
+make clean
+make
+pause
diff --git a/compile.bat b/compile.bat
new file mode 100644
index 0000000..9bd9005
--- /dev/null
+++ b/compile.bat
@@ -0,0 +1,2 @@
+make
+pause