Initial commit

This commit is contained in:
Gericom
2025-11-23 14:02:39 +01:00
commit 3bb550c12e
53 changed files with 4301 additions and 0 deletions

49
.gitignore vendored Normal file
View File

@@ -0,0 +1,49 @@
# Ignore build directories #
############################
Debug/
Release/
build/
out/
# Compiled source #
###################
*.com
*.class
*.dll
*.d
*.map
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
*.nds
*.elf
*.a
.vscode/

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "libs/tinyusb"]
path = libs/tinyusb
url = https://github.com/hathach/tinyusb.git
[submodule "libs/libtwl"]
path = libs/libtwl
url = https://github.com/Gericom/libtwl

17
LICENSE.txt Normal file
View File

@@ -0,0 +1,17 @@
Copyright (c) 2025 LNH team
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.

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# DSpico USB Examples
This repository contains examples of using the DSpico USB port from a DS application.
The `examples` folder contains the following examples:
- usb-speaker - Makes the DS/DSi/3DS work as a USB speaker for your PC
- mass-storage - Allows access to the DSpico micro SD card over USB
The `platform` folder contains the tiny usb platform code for the DSpico.
> [!IMPORTANT]
> There is currently a bug in the DSpico Firmware that sometimes causes the DSpico the crash if you use USB, reset the console and use USB again. If this happens, unplug USB and reset the console such that the DSpico performs a power cycle. You can plug the USB back in afterwards.
## Environment
The examples require a pre-calico devkitpro/libnds environment to build. With such an environment setup, just run `make` in the folder of the example to compile it.
## License
The platform code and examples are licensed under the Zlib license. For details, see `LICENSE.txt`.

View File

@@ -0,0 +1,60 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
export TARGET := $(shell basename $(CURDIR))
export TOPDIR := $(CURDIR)
# specify a directory which contains the nitro filesystem
# this is relative to the Makefile
NITRO_FILES :=
# These set the information text in the nds file
#GAME_TITLE := My Wonderful Homebrew
#GAME_SUBTITLE1 := built with devkitARM
#GAME_SUBTITLE2 := http://devitpro.org
include $(DEVKITARM)/ds_rules
.PHONY: checklibtwl checkarm7 checkarm9 clean
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: checklibtwl checkarm7 checkarm9 $(TARGET).nds
#---------------------------------------------------------------------------------
checklibtwl:
$(MAKE) -C ../../libs/libtwl/libtwl7
$(MAKE) -C ../../libs/libtwl/libtwl9
#---------------------------------------------------------------------------------
checkarm7: checklibtwl
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
checkarm9: checklibtwl
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \
$(_ADDFILES)
#---------------------------------------------------------------------------------
arm7/$(TARGET).elf:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
arm9/$(TARGET).elf:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
clean:
$(MAKE) -C arm9 clean
$(MAKE) -C arm7 clean
rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9

View File

@@ -0,0 +1,137 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := source \
../common/ipc \
source/ipcServices \
../../../platform \
../../../libs/tinyusb/src \
../../../libs/tinyusb/src/device \
../../../libs/tinyusb/src/class/audio \
../../../libs/tinyusb/src/class/cdc \
../../../libs/tinyusb/src/class/msc \
../../../libs/tinyusb/src/common
INCLUDES := source include build ../common ../../../libs/tinyusb/src ../../../platform
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM7 -DARM7
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffunction-sections -fdata-sections\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
-Wsuggest-override -Werror=suggest-override
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=../dldi_ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map,--gc-sections -ffunction-sections -fdata-sections
LIBS := -ltwl7 -lnds7
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl7 $(CURDIR)/../../../libs/libtwl/common
#---------------------------------------------------------------------------------
# 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 ARM7ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
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)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(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 CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM7ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -0,0 +1,232 @@
/*--------------------------------------------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
PHDRS {
crt0 PT_LOAD FLAGS(7);
arm7 PT_LOAD FLAGS(7);
arm7i PT_LOAD FLAGS(0x100007);
}
MEMORY {
ewram : ORIGIN = 0x02380000, LENGTH = 12M - 512K
rom : ORIGIN = 0x08000000, LENGTH = 32M
iwram : ORIGIN = 0x037fc000, LENGTH = (96K - 16K)
twl_ewram : ORIGIN = 0x02e80000, LENGTH = 512K - 64K
twl_iwram : ORIGIN = 0x03000000, LENGTH = 256K
}
__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
{
.twl :
{
__arm7i_lma__ = LOADADDR(.twl);
__arm7i_start__ = .;
*(.twl)
*.twl*(.text .stub .text.* .gnu.linkonce.t.*)
*.twl*(.rodata)
*.twl*(.roda)
*.twl*(.rodata.*)
*.twl*(.data)
*.twl*(.data.*)
*.twl*(.gnu.linkonce.d*)
. = ALIGN(4);
__arm7i_end__ = .;
} >twl_iwram AT>twl_ewram :arm7i
.twl_bss ALIGN(4) (NOLOAD) :
{
__twl_bss_start__ = .;
*(.twl_bss)
*.twl.*(.dynbss)
*.twl.*(.gnu.linkonce.b*)
*.twl.*(.bss*)
*.twl.*(COMMON)
. = ALIGN(4);
__twl_bss_end__ = .;
} >twl_iwram :NONE
.crt0 :
{
KEEP (*(.crt0))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >ewram :crt0
.text :
{
__arm7_lma__ = LOADADDR(.text);
__arm7_start__ = .;
KEEP (*(SORT_NONE(.init)))
*(.plt)
*(.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. */
} >iwram AT>ewram :arm7
.fini :
{
KEEP (*(.fini))
} >iwram AT>ewram
.rodata :
{
*(.rodata)
*all.rodata*(*)
*(.roda)
*(.rodata.*)
*(.gnu.linkonce.r*)
SORT(CONSTRUCTORS)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >iwram AT>ewram
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >iwram AT>ewram
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
__exidx_end = .;
} >iwram AT>ewram
/* 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. */
.preinit_array : {
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
} >iwram AT>ewram
.init_array : {
PROVIDE (__init_array_start = .);
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
} >iwram AT>ewram
.fini_array : {
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
PROVIDE (__fini_array_end = .);
} >iwram AT>ewram
.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. */
} >iwram AT>ewram
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >iwram AT>ewram
.eh_frame :
{
KEEP (*(.eh_frame))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >iwram AT>ewram
.gcc_except_table :
{
*(.gcc_except_table)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >iwram AT>ewram
.jcr : { KEEP (*(.jcr)) } >iwram AT>ewram
.got : { *(.got.plt) *(.got) } >iwram AT>ewram
.data ALIGN(4) : {
__data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
. = ALIGN(4);
__data_end = ABSOLUTE(.) ;
} >iwram AT>ewram
.bss ALIGN(4) (NOLOAD) :
{
__arm7_end__ = .;
__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(.);
} >iwram
/* 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) }
/* These must appear regardless of . */
}

View File

@@ -0,0 +1,8 @@
%rename link old_link
*link:
%(old_link) -T ../dldi_ds_arm7.ld%s --gc-sections --no-warn-rwx-segments
*startfile:
ds_arm7_crt0%O%s crti%O%s crtbegin%O%s

View File

@@ -0,0 +1,8 @@
#pragma once
/// @brief Enum representing the arm7 state.
enum class Arm7State
{
Idle,
ExitRequested
};

View File

@@ -0,0 +1,10 @@
#pragma once
/// @brief Enum representing the exit mode of launcher.
enum class ExitMode
{
/// @brief Reset the system (DSi mode only).
Reset,
/// @brief Power off the system.
PowerOff
};

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nds.h>
#include <libtwl/rtos/rtosMutex.h>
extern rtos_mutex_t gCardMutex;

View File

@@ -0,0 +1,16 @@
.global _dldi_start
.equ _dldi_start, 0x037F8000
.global _io_dldi
.equ _io_dldi, (_dldi_start + 0x60)
.global _DLDI_startup_ptr
.equ _DLDI_startup_ptr, (_io_dldi + 0x8)
.global _DLDI_isInserted_ptr
.equ _DLDI_isInserted_ptr, (_io_dldi + 0xC)
.global _DLDI_readSectors_ptr
.equ _DLDI_readSectors_ptr, (_io_dldi + 0x10)
.global _DLDI_writeSectors_ptr
.equ _DLDI_writeSectors_ptr, (_io_dldi + 0x14)
.global _DLDI_clearStatus_ptr
.equ _DLDI_clearStatus_ptr, (_io_dldi + 0x18)
.global _DLDI_shutdown_ptr
.equ _DLDI_shutdown_ptr, (_io_dldi + 0x1C)

View File

@@ -0,0 +1,53 @@
#include "common.h"
#include <string.h>
#include <nds/disc_io.h>
#include "DldiIpcService.h"
extern FN_MEDIUM_STARTUP _DLDI_startup_ptr;
extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr;
extern FN_MEDIUM_WRITESECTORS _DLDI_writeSectors_ptr;
void DldiIpcService::HandleMessage(u32 data)
{
auto cmd = reinterpret_cast<const dldi_ipc_cmd_t*>(data << 2);
switch (cmd->cmd)
{
case DLDI_IPC_CMD_SETUP:
SetupDldi(cmd);
break;
case DLDI_IPC_CMD_READ_SECTORS:
ReadSectors(cmd);
break;
case DLDI_IPC_CMD_WRITE_SECTORS:
WriteSectors(cmd);
break;
}
}
void DldiIpcService::SetupDldi(const dldi_ipc_cmd_t* cmd) const
{
memcpy((void*)0x037F8000, cmd->buffer, 16 * 1024);
bool result;
rtos_lockMutex(&gCardMutex);
result = _DLDI_startup_ptr();
rtos_unlockMutex(&gCardMutex);
SendResponseMessage(result);
}
void DldiIpcService::ReadSectors(const dldi_ipc_cmd_t* cmd) const
{
rtos_lockMutex(&gCardMutex);
_DLDI_readSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
rtos_unlockMutex(&gCardMutex);
SendResponseMessage(0);
}
void DldiIpcService::WriteSectors(const dldi_ipc_cmd_t* cmd) const
{
rtos_lockMutex(&gCardMutex);
_DLDI_writeSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
rtos_unlockMutex(&gCardMutex);
SendResponseMessage(0);
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "ipc/ThreadIpcService.h"
#include "dldiIpcCommand.h"
#include "ipcChannels.h"
class DldiIpcService : public ThreadIpcService
{
u32 _threadStack[128];
void SetupDldi(const dldi_ipc_cmd_t* cmd) const;
void ReadSectors(const dldi_ipc_cmd_t* cmd) const;
void WriteSectors(const dldi_ipc_cmd_t* cmd) const;
public:
DldiIpcService()
: ThreadIpcService(IPC_CHANNEL_DLDI, 6, _threadStack, sizeof(_threadStack)) { }
void HandleMessage(u32 data) override;
};

View File

@@ -0,0 +1,388 @@
#include "common.h"
#include <nds/disc_io.h>
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosThread.h>
#include <libtwl/rtos/rtosEvent.h>
#include <libtwl/sound/soundChannel.h>
#include <libtwl/timer/timer.h>
#include <libtwl/sound/sound.h>
#include <libtwl/ipc/ipcSync.h>
#include <libtwl/ipc/ipcFifoSystem.h>
#include <libtwl/sys/sysPower.h>
#include <libtwl/sio/sioRtc.h>
#include <libtwl/sio/sio.h>
#include <libtwl/gfx/gfxStatus.h>
#include <libtwl/mem/memSwap.h>
#include <libtwl/i2c/i2cMcu.h>
#include <libtwl/spi/spiPmic.h>
#include "ipcServices/DldiIpcService.h"
#include "ExitMode.h"
#include "Arm7State.h"
#include "tusb.h"
#include "usb_descriptors.h"
static DldiIpcService sDldiIpcService;
rtos_mutex_t gCardMutex;
static rtos_event_t sVBlankEvent;
static ExitMode sExitMode;
static Arm7State sState;
static volatile u8 sMcuIrqFlag = false;
static u32 sSdBlockCount;
static u8 sSector0Buffer[512] alignas(4);
static rtos_thread_t sUsbThread;
static u32 sUsbThreadStack[512];
extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr;
extern FN_MEDIUM_WRITESECTORS _DLDI_writeSectors_ptr;
static void vblankIrq(u32 irqMask)
{
rtos_signalEvent(&sVBlankEvent);
}
static void mcuIrq(u32 irq2Mask)
{
sMcuIrqFlag = true;
}
static void checkMcuIrq(void)
{
// mcu only exists in DSi mode
if (isDSiMode())
{
// check and ack the flag atomically
if (mem_swapByte(false, &sMcuIrqFlag))
{
// check the irq mask
u32 irqMask = mcu_getIrqMask();
if (irqMask & MCU_IRQ_RESET)
{
// power button was released
sExitMode = ExitMode::Reset;
sState = Arm7State::ExitRequested;
}
else if (irqMask & MCU_IRQ_POWER_OFF)
{
// power button was held long to trigger a power off
sExitMode = ExitMode::PowerOff;
sState = Arm7State::ExitRequested;
}
}
}
}
static void initializeVBlankIrq()
{
rtos_createEvent(&sVBlankEvent);
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq);
rtos_enableIrqMask(RTOS_IRQ_VBLANK);
gfx_setVBlankIrqEnabled(true);
}
static void usbThreadMain(void* arg)
{
while (true)
{
tud_task();
}
}
// Based on https://github.com/asiekierka/nrio-usb-disk/blob/main/source/msc.c msc_find_block_count by Asie
// Note: This might not work correctly in some cases. It would be better if the DSpico would expose the actual SD capacity.
static u32 findSdCardBlockCount()
{
rtos_lockMutex(&gCardMutex);
_DLDI_readSectors_ptr(0, 1, sSector0Buffer);
rtos_unlockMutex(&gCardMutex);
u16 footer = *(u16*)(sSector0Buffer + 510);
if (footer == 0xAA55)
{
u8 bootOpcode = sSector0Buffer[0];
if (bootOpcode == 0xEB || bootOpcode == 0xE9 || bootOpcode == 0xE8)
{
if (!memcmp(sSector0Buffer + 54, "FAT", 3) || !memcmp(sSector0Buffer + 82, "FAT32 ", 8))
{
u32 totalSectors = *(u32*)(sSector0Buffer + 32);
if (totalSectors < 0x10000)
{
totalSectors = sSector0Buffer[19] | (sSector0Buffer[20] << 8);
}
return totalSectors;
}
}
u32 blockCount = 0;
for (u32 tableEntry = 0x1BE; tableEntry < 0x1FE; tableEntry += 16)
{
u32 pStart = *(u16*)(sSector0Buffer + tableEntry + 8) | (*(u16*)(sSector0Buffer + tableEntry + 10) << 16);
u32 pCount = *(u16*)(sSector0Buffer + tableEntry + 12) | (*(u16*)(sSector0Buffer + tableEntry + 14) << 16);
u32 pEnd = pStart + pCount;
if (pEnd > blockCount)
{
blockCount = pEnd;
}
}
return blockCount;
}
return 0;
}
static void initializeArm7()
{
rtos_initIrq();
rtos_startMainThread();
ipc_initFifoSystem();
rtos_createMutex(&gCardMutex);
// clear sound registers
dmaFillWords(0, (void*)0x04000400, 0x100);
pmic_setAmplifierEnable(true);
sys_setSoundPower(true);
readUserSettings();
pmic_setPowerLedBlink(PMIC_CONTROL_POWER_LED_BLINK_NONE);
sio_setGpioSiIrq(false);
sio_setGpioMode(RCNT0_L_MODE_GPIO);
rtc_init();
sDldiIpcService.Start();
snd_setMasterVolume(127);
snd_setMasterEnable(true);
initializeVBlankIrq();
if (isDSiMode())
{
rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq);
rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
}
ipc_setArm7SyncBits(7);
while (ipc_getArm9SyncBits() != 6)
{
rtos_waitEvent(&sVBlankEvent, true, true);
}
sSdBlockCount = findSdCardBlockCount();
tusb_rhport_init_t dev_init =
{
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(0, &dev_init);
rtos_createThread(&sUsbThread, 3, usbThreadMain, NULL, sUsbThreadStack, sizeof(sUsbThreadStack));
rtos_wakeupThread(&sUsbThread);
}
static void updateArm7IdleState()
{
checkMcuIrq();
if (sState == Arm7State::ExitRequested)
{
snd_setMasterVolume(0); // mute sound
}
}
static bool performExit(ExitMode exitMode)
{
switch (exitMode)
{
case ExitMode::Reset:
{
mcu_setWarmBootFlag(true);
mcu_hardReset();
break;
}
case ExitMode::PowerOff:
{
pmic_shutdown();
break;
}
}
while (true); // wait infinitely for exit
}
static void updateArm7ExitRequestedState()
{
performExit(sExitMode);
}
static void updateArm7()
{
switch (sState)
{
case Arm7State::Idle:
{
updateArm7IdleState();
break;
}
case Arm7State::ExitRequested:
{
updateArm7ExitRequestedState();
break;
}
}
}
int main()
{
sState = Arm7State::Idle;
initializeArm7();
while (true)
{
rtos_waitEvent(&sVBlankEvent, true, true);
updateArm7();
}
return 0;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
(void) lun;
const char vid[] = "DSpico";
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
memcpy(vendor_id , vid, strlen(vid));
memcpy(product_id , pid, strlen(pid));
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true;
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = sSdBlockCount;
*block_size = 512;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
(void) lun;
(void) power_condition;
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
if (offset != 0 || (bufsize % 512) != 0)
{
return -1;
}
rtos_lockMutex(&gCardMutex);
_DLDI_readSectors_ptr(lba, bufsize / 512, buffer);
rtos_unlockMutex(&gCardMutex);
return (int32_t)bufsize;
}
bool tud_msc_is_writable_cb(uint8_t lun)
{
(void) lun;
return true;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
{
(void) lun;
if (offset != 0 || (bufsize % 512) != 0)
{
return -1;
}
rtos_lockMutex(&gCardMutex);
_DLDI_writeSectors_ptr(lba, bufsize / 512, buffer);
rtos_unlockMutex(&gCardMutex);
return (int32_t)bufsize;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
void const* response = NULL;
int32_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
switch (scsi_cmd[0])
{
default:
{
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// negative means error -> tinyusb could stall and/or response with failed status
resplen = -1;
break;
}
}
// return resplen must not larger than bufsize
if (resplen > bufsize)
{
resplen = bufsize;
}
if (response && (resplen > 0))
{
if(in_xfer)
{
memcpy(buffer, response, (size_t)resplen);
}
else
{
// SCSI output
}
}
return (int32_t)resplen;
}

View File

@@ -0,0 +1,112 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#include "usb_descriptors.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#define CFG_TUSB_MCU OPT_MCU_DSPICO
#endif
#define TUP_DCD_ENDPOINT_MAX 16
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_CUSTOM
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 1
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_AUDIO 0
#define CFG_TUD_VENDOR 0
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 8192
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,198 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
// #include "bsp/board_api.h"
#include "tusb.h"
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110,
// Use Interface Association Descriptor (IAD) for Audio
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
#define EPNUM_MSC_OUT 0x01
#define EPNUM_MSC_IN 0x81
uint8_t const desc_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// array of pointer to string descriptors
char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"LNH", // 1: Manufacturer
"DSpico", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"DSpico MSC" // 4: MSC Interface
};
static uint16_t _desc_str[32 + 1];
// Get USB Serial number string from unique ID if available. Return number of character.
// Input is string descriptor from index 1 (index 0 is type + len)
static inline size_t board_usb_get_serial(uint16_t desc_str1[], size_t max_chars) {
uint8_t uid[16] TU_ATTR_ALIGNED(4);
size_t uid_len;
// TODO work with make, but not working with esp32s3 cmake
// if ( board_get_unique_id ) {
// uid_len = board_get_unique_id(uid, sizeof(uid));
// }else {
// fixed serial string is 01234567889ABCDEF
uint32_t* uid32 = (uint32_t*) (uintptr_t) uid;
uid32[0] = 0x67452301;
uid32[1] = 0xEFCDAB89;
uid_len = 8;
// }
if ( uid_len > max_chars / 2 ) uid_len = max_chars / 2;
for ( size_t i = 0; i < uid_len; i++ ) {
for ( size_t j = 0; j < 2; j++ ) {
const char nibble_to_hex[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
uint8_t const nibble = (uid[i] >> (j * 4)) & 0xf;
desc_str1[i * 2 + (1 - j)] = nibble_to_hex[nibble]; // UTF-16-LE
}
}
return 2 * uid_len;
}
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
if ( chr_count > max_count ) chr_count = max_count;
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,7 @@
#pragma once
enum
{
ITF_NUM_MSC,
ITF_NUM_TOTAL
};

View File

@@ -0,0 +1,152 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := source \
source/core
INCLUDES := include source ../common
DATA := data
GRAPHICS := gfx
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM9 -DARM9
CFLAGS := -g -Wall -O2\
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffunction-sections -fdata-sections\
-ffast-math \
-fno-devirtualize-speculatively \
-Werror=return-type \
$(ARCH)
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
-Wsuggest-override -Werror=suggest-override
CFLAGS += -Werror=implicit-function-declaration
ASFLAGS := -g $(ARCH) $(INCLUDE) -march=armv5te -mtune=arm946e-s
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--gc-sections,--use-blx -ffunction-sections -fdata-sections
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -ltwl9 -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl9 $(CURDIR)/../../../libs/libtwl/common
#---------------------------------------------------------------------------------
# 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 ARM9ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(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)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
#---------------------------------------------------------------------------------
# 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)) \
$(PNGFILES:.png=.o)\
$(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)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.nds* *.bin
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.nft2.o : %.nft2
@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: %.png %.grit
#---------------------------------------------------------------------------------
grit $< -fts -o$*
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -0,0 +1,2 @@
#pragma once
#include <nds/ndstypes.h>

Some files were not shown because too many files have changed in this diff Show More