Initial commit

This commit is contained in:
Gericom
2025-11-09 16:07:01 +01:00
commit 29671d041f
39 changed files with 26346 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.bin binary

36
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Build DSpico Bootloader
on:
push:
branches: ["develop"]
paths-ignore:
- 'README.md'
pull_request:
branches: ["develop"]
paths-ignore:
- 'README.md'
workflow_dispatch:
jobs:
dspico_bootloader:
runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-v1.13.1
name: Build DSpico Bootloader
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: true
- name: Run build script
run: |
make
- name: Publish build to GH Actions
uses: actions/upload-artifact@v4
with:
path: |
BOOTLOADER.nds
name: DSpico Bootloader

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/

3
.gitmodules vendored Normal file
View File

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

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.

112
Makefile Normal file
View File

@@ -0,0 +1,112 @@
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2023
BLOCKSDS ?= /opt/blocksds/core
BLOCKSDSEXT ?= /opt/blocksds/external
export LIBTWL ?= $(shell pwd)/libs/libtwl
# User config
# ===========
NAME := BOOTLOADER
GAME_TITLE := DSpico
GAME_AUTHOR := LNH team
GAME_ICON := icon.bmp
# DLDI and internal SD slot of DSi
# --------------------------------
# Root folder of the SD image
SDROOT := sdroot
# Name of the generated image it "DSi-1.sd" for no$gba in DSi mode
SDIMAGE := image.bin
# Source code paths
# -----------------
# A single directory that is the root of NitroFS:
NITROFSDIR :=
# Tools
# -----
MAKE := make
RM := rm -rf
# Verbose flag
# ------------
ifeq ($(VERBOSE),1)
V :=
else
V := @
endif
# Directories
# -----------
ARM9DIR := arm9
ARM7DIR := arm7
# Build artfacts
# --------------
ROM := $(NAME).nds
# Targets
# -------
.PHONY: all clean arm9 arm7 dldipatch sdimage checklibtwl
all: $(ROM)
clean:
@echo " CLEAN"
$(V)$(MAKE) -f Makefile.arm9 clean --no-print-directory
$(V)$(MAKE) -f Makefile.arm7 clean --no-print-directory
$(V)$(RM) $(ROM) build $(SDIMAGE)
arm9: checklibtwl
$(V)+$(MAKE) -f Makefile.arm9 --no-print-directory
arm7: checklibtwl
$(V)+$(MAKE) -f Makefile.arm7 --no-print-directory
checklibtwl:
$(MAKE) -C $(LIBTWL)
ifneq ($(strip $(NITROFSDIR)),)
# Additional arguments for ndstool
NDSTOOL_ARGS := -d $(NITROFSDIR)
# Make the NDS ROM depend on the filesystem only if it is needed
$(ROM): $(NITROFSDIR)
endif
# Combine the title strings
ifeq ($(strip $(GAME_SUBTITLE)),)
GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_AUTHOR)
else
GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_SUBTITLE);$(GAME_AUTHOR)
endif
$(ROM): arm9 arm7
@echo " NDSTOOL $@"
$(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \
-7 build/arm7.elf -9 build/arm9.elf \
-b $(GAME_ICON) "$(GAME_FULL_TITLE)" \
$(NDSTOOL_ARGS) \
-n 1623 1 -n1 2296 24 \
-g DSPI
sdimage:
@echo " MKFATIMG $(SDIMAGE) $(SDROOT)"
$(V)$(BLOCKSDS)/tools/mkfatimg/mkfatimg -t $(SDROOT) $(SDIMAGE)
dldipatch: $(ROM)
@echo " DLDIPATCH $(ROM)"
$(V)$(BLOCKSDS)/tools/dldipatch/dldipatch patch \
$(BLOCKSDS)/sys/dldi_r4/r4tf.dldi $(ROM)

189
Makefile.arm7 Normal file
View File

@@ -0,0 +1,189 @@
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2023
export BLOCKSDS ?= /opt/blocksds/core
export BLOCKSDSEXT ?= /opt/blocksds/external
export LIBTWL ?= $(shell pwd)/libs/libtwl
export WONDERFUL_TOOLCHAIN ?= /opt/wonderful
ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/
# Source code paths
# -----------------
SOURCEDIRS := arm7/source common
INCLUDEDIRS := arm7/source common
BINDIRS :=
# Defines passed to all files
# ---------------------------
DEFINES := -DLIBTWL_ARM7
# Libraries
# ---------
LIBS := -ltwl7 -lnds7
LIBDIRS := $(BLOCKSDS)/libs/libnds \
$(LIBTWL)/libtwl7 $(LIBTWL)/common $(LIBTWL)
# Build artifacts
# -----------------
NAME := arm7
BUILDDIR := build/$(NAME)
ELF := build/$(NAME).elf
DUMP := build/$(NAME).dump
MAP := build/$(NAME).map
# Tools
# -----
PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi-
CC := $(PREFIX)gcc
CXX := $(PREFIX)g++
LD := $(PREFIX)gcc
OBJDUMP := $(PREFIX)objdump
MKDIR := mkdir
RM := rm -rf
# Verbose flag
# ------------
ifeq ($(VERBOSE),1)
V :=
else
V := @
endif
# Source files
# ------------
ifneq ($(BINDIRS),)
SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin")
INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS))
endif
SOURCES_S := $(shell find -L $(SOURCEDIRS) -name "*.s")
SOURCES_C := $(shell find -L $(SOURCEDIRS) -name "*.c")
SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -name "*.cpp")
# Compiler and linker flags
# -------------------------
ARCH := -marm -mthumb-interwork -mcpu=arm7tdmi
SPECS := arm7/dldi_ds_arm7.specs
WARNFLAGS := -Wall
ifeq ($(SOURCES_CPP),)
LIBS += -lc
else
LIBS += -lstdc++ -lc
endif
INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \
$(foreach path,$(LIBDIRS),-I$(path)/include)
LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib)
ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -ffunction-sections -fdata-sections \
-specs=$(SPECS)
CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \
-specs=$(SPECS)
CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \
-fno-exceptions -fno-rtti \
-fno-threadsafe-statics \
-Wsuggest-override -Werror=suggest-override \
-specs=$(SPECS)
LDFLAGS := $(ARCH) $(LIBDIRSFLAGS) -Wl,-Map,$(MAP),--gc-sections $(DEFINES) \
-Wl,--start-group $(LIBS) -Wl,--end-group -specs=$(SPECS)
# Intermediate build files
# ------------------------
OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN)))
HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN)))
OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP)))
OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES)
DEPS := $(OBJS:.o=.d)
# Targets
# -------
.PHONY: all clean dump
all: $(ELF)
$(ELF): $(OBJS)
@echo " LD.7 $@"
$(V)$(LD) -o $@ $(OBJS) $(LDFLAGS)
$(DUMP): $(ELF)
@echo " OBJDUMP.7 $@"
$(V)$(OBJDUMP) -h -C -S $< > $@
dump: $(DUMP)
clean:
@echo " CLEAN.7"
$(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR)
# Rules
# -----
$(BUILDDIR)/%.s.o : %.s
@echo " AS.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.c.o : %.c
@echo " CC.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.arm.c.o : %.arm.c
@echo " CC.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $<
$(BUILDDIR)/%.cpp.o : %.cpp
@echo " CXX.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.arm.cpp.o : %.arm.cpp
@echo " CXX.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(CXX) $(CXXFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $<
$(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin
@echo " BIN2C.7 $<"
@$(MKDIR) -p $(@D)
$(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c
# All assets must be built before the source code
# -----------------------------------------------
$(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS)
# Include dependency files if they exist
# --------------------------------------
-include $(DEPS)

243
Makefile.arm9 Normal file
View File

@@ -0,0 +1,243 @@
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2023
export BLOCKSDS ?= /opt/blocksds/core
export BLOCKSDSEXT ?= /opt/blocksds/external
export LIBTWL ?= $(shell pwd)/libs/libtwl
export WONDERFUL_TOOLCHAIN ?= /opt/wonderful
ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/
# Source code paths
# -----------------
SOURCEDIRS := arm9/source common
INCLUDEDIRS := arm9/source common
GFXDIRS :=
BINDIRS := arm9/data
AUDIODIRS :=
# Defines passed to all files
# ---------------------------
DEFINES := -DLIBTWL_ARM9
# Libraries
# ---------
LIBS := -ltwl9 -lnds9
LIBDIRS := $(BLOCKSDS)/libs/libnds \
$(LIBTWL)/libtwl9 $(LIBTWL)/common $(LIBTWL)
# Build artifacts
# ---------------
NAME := arm9
BUILDDIR := build/$(NAME)
ELF := build/$(NAME).elf
DUMP := build/$(NAME).dump
MAP := build/$(NAME).map
SOUNDBANKDIR := $(BUILDDIR)/maxmod
# Tools
# -----
PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi-
CC := $(PREFIX)gcc
CXX := $(PREFIX)g++
LD := $(PREFIX)gcc
OBJDUMP := $(PREFIX)objdump
MKDIR := mkdir
RM := rm -rf
# Verbose flag
# ------------
ifeq ($(VERBOSE),1)
V :=
else
V := @
endif
# Source files
# ------------
ifneq ($(BINDIRS),)
SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin")
SOURCES_NFT2 := $(shell find -L $(BINDIRS) -name "*.nft2")
INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS))
endif
ifneq ($(GFXDIRS),)
SOURCES_PNG := $(shell find -L $(GFXDIRS) -name "*.png")
INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(GFXDIRS))
endif
ifneq ($(AUDIODIRS),)
SOURCES_AUDIO := $(shell find -L $(AUDIODIRS) -regex '.*\.\(it\|mod\|s3m\|wav\|xm\)')
ifneq ($(SOURCES_AUDIO),)
INCLUDEDIRS += $(SOUNDBANKDIR)
endif
endif
SOURCES_S := $(shell find -L $(SOURCEDIRS) -name "*.s")
SOURCES_C := $(shell find -L $(SOURCEDIRS) -name "*.c")
SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -name "*.cpp")
# Compiler and linker flags
# -------------------------
ARCH := -marm -mthumb-interwork -mcpu=arm946e-s+nofp
SPECS := $(BLOCKSDS)/sys/crts/ds_arm9.specs
WARNFLAGS := -Wall
ifeq ($(SOURCES_CPP),)
LIBS += -lc
else
LIBS += -lstdc++ -lc
endif
INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \
$(foreach path,$(LIBDIRS),-I$(path)/include)
LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib)
ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -ffunction-sections -fdata-sections \
-specs=$(SPECS)
CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \
-fno-devirtualize-speculatively \
-Werror=return-type \
-specs=$(SPECS)
CXXFLAGS += -std=gnu++23 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \
$(ARCH) -O2 -ffunction-sections -fdata-sections \
-fno-exceptions -fno-rtti \
-fno-devirtualize-speculatively \
-Werror=return-type \
-fno-threadsafe-statics \
-Wno-volatile -Wsuggest-override -Werror=suggest-override \
-specs=$(SPECS)
LDFLAGS := $(ARCH) $(LIBDIRSFLAGS) -Wl,-Map,$(MAP),--gc-sections $(DEFINES) \
-Wl,--start-group $(LIBS) -Wl,--end-group -specs=$(SPECS)
# Intermediate build files
# ------------------------
OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_NFT2))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG)))
HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \
$(patsubst %.nft2,%_nft2.h,$(addprefix $(BUILDDIR)/,$(SOURCES_NFT2))) \
$(patsubst %.png,%.h,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG)))
ifneq ($(SOURCES_AUDIO),)
OBJS_ASSETS += $(SOUNDBANKDIR)/soundbank.c.o
HEADERS_ASSETS += $(SOUNDBANKDIR)/soundbank.h
endif
OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \
$(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP)))
OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES)
DEPS := $(OBJS:.o=.d)
# Targets
# -------
.PHONY: all clean dump
all: $(ELF)
$(ELF): $(OBJS)
@echo " LD.9 $@"
$(V)$(LD) -o $@ $(OBJS) $(LDFLAGS)
$(DUMP): $(ELF)
@echo " OBJDUMP.9 $@"
$(V)$(OBJDUMP) -h -C -S $< > $@
dump: $(DUMP)
clean:
@echo " CLEAN.9"
$(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR)
# Rules
# -----
$(BUILDDIR)/%.s.o : %.s
@echo " AS.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.c.o : %.c
@echo " CC.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.arm.c.o : %.arm.c
@echo " CC.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $<
$(BUILDDIR)/%.cpp.o : %.cpp
@echo " CXX.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.arm.cpp.o : %.arm.cpp
@echo " CXX.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(CXX) $(CXXFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $<
$(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin
@echo " BIN2C.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c
$(BUILDDIR)/%.nft2.o $(BUILDDIR)/%_nft2.h : %.nft2
@echo " BIN2C.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D)
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.nft2.o $(BUILDDIR)/$*_nft2.c
$(BUILDDIR)/%.png.o $(BUILDDIR)/%.h : %.png %.grit
@echo " GRIT.9 $<"
@$(MKDIR) -p $(@D)
$(V)$(BLOCKSDS)/tools/grit/grit $< -ftc -W1 -o$(BUILDDIR)/$*
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.png.o $(BUILDDIR)/$*.c
$(V)touch $(BUILDDIR)/$*.png.o $(BUILDDIR)/$*.h
$(SOUNDBANKDIR)/soundbank.h: $(SOURCES_AUDIO)
@echo " MMUTIL $^"
@$(MKDIR) -p $(@D)
@$(BLOCKSDS)/tools/mmutil/mmutil $^ -d \
-o$(SOUNDBANKDIR)/soundbank.bin -h$(SOUNDBANKDIR)/soundbank.h
$(SOUNDBANKDIR)/soundbank.c.o: $(SOUNDBANKDIR)/soundbank.h
@echo " BIN2C soundbank.bin"
$(V)$(BLOCKSDS)/tools/bin2c/bin2c $(SOUNDBANKDIR)/soundbank.bin \
$(SOUNDBANKDIR)
@echo " CC.9 soundbank_bin.c"
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(SOUNDBANKDIR)/soundbank.c.o \
$(SOUNDBANKDIR)/soundbank_bin.c
# All assets must be built before the source code
# -----------------------------------------------
$(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS)
# Include dependency files if they exist
# --------------------------------------
-include $(DEPS)

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
# DSpico Bootloader
This is the repository for the DSpico Bootloader. It is a small NDS rom that initializes the DSpico and uses [Pico Loader](https://github.com/LNH-team/pico-loader) to boot the nds file at `fat:/_picoboot.nds`.
## Setup & configuration
We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository.
The steps provided will assume you already have one of those environments set up.
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/).
2. To be able to use this bootloader, compile [Pico Loader](https://github.com/LNH-team/pico-loader) and place the files on your SD card.
3. Place the rom to boot on the root of your SD card as `_picoboot.nds`.
## Compiling
1. Compile the bootloader by running `make`
2. Patch `BOOTLOADER.nds` with the [DSpico DLDI driver](https://github.com/LNH-team/dspico-dldi) using dlditool:
- `dlditool DSpico.dldi BOOTLOADER.nds`
3. Prepare `BOOTLOADER.nds` for use on a cartridge using [DSRomEncryptor](https://github.com/Gericom/DSRomEncryptor):
- `DSRomEncryptor BOOTLOADER.nds BOOTLOADER.nds`
- Note that this requires the ntr and twl blowfish keys (see also the readme of DSRomEncryptor) and [.NET 9.0](`https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet9&pivots=os-linux-ubuntu-2404`)
4. Rename `BOOTLOADER.nds` to `default.nds` and place it in the `roms` folder of the [DSpico Firmware](https://github.com/LNH-team/dspico-firmware)
5. Compile the DSpico Firmware and flash it to the DSpico
## License
This software is licensed under the zlib license.
Additional licenses may apply to the project.
For details, see the `license` directory, as well as `LICENSE.txt`.
## Contributors
- [@Gericom](https://github.com/Gericom)
- [@XLuma](https://github.com/XLuma)
- [@Dartz150](https://github.com/Dartz150)
- [@lifehackerhansol](https://github.com/lifehackerhansol)

340
arm7/dldi_ds_arm7.ld Normal file
View File

@@ -0,0 +1,340 @@
/* Copyright (C) 2014-2024 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
/* SPDX-License-Identifier: MPL-2.0 AND FSFAP */
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
/* User-configurable symbols. */
/* The size, in bytes, of the amount of shared WRAM used by ARM7 code. */
/* Only 0 and 32768 are valid values. */
PROVIDE(__shared_wram_size = 16384);
ASSERT(__shared_wram_size == 0 || __shared_wram_size == 16384, "ARM7 shared WRAM size must be 0 KB or 32 KB");
/* The size, in bytes, of the reserved section at the end of IWRAM. */
/* Traditionally, ARM7 reserves 0x40 bytes here. */
PROVIDE(__iwram_reserved_size = 0x40);
ASSERT((__iwram_reserved_size & 3) == 0, "__iwram_reserved_size must be a multiple of 4");
/* ARM supervisor (SWI calls) stack size. */
PROVIDE(__svc_stack_size = 0x100);
ASSERT((__svc_stack_size & 3) == 0, "__svc_stack_size must be a multiple of 4");
/* ARM interrupt handler stack size. */
PROVIDE(__irq_stack_size = 0x100);
ASSERT((__irq_stack_size & 3) == 0, "__irq_stack_size must be a multiple of 4");
PHDRS {
crt0 PT_LOAD FLAGS(7);
arm7 PT_LOAD FLAGS(7);
arm7i PT_LOAD FLAGS(0x100007); /* (DSi flag for ndstool | 7) */
}
MEMORY {
ewram : ORIGIN = 0x02380000, LENGTH = 12M - 512K
iwram : ORIGIN = 0x03800000 - __shared_wram_size, LENGTH = 65536 + __shared_wram_size
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 - __iwram_reserved_size;
__sp_svc = __sp_irq - __irq_stack_size;
__sp_usr = __sp_svc - __svc_stack_size;
__irq_flags = 0x04000000 - 8;
__irq_flagsaux = 0x04000000 - 0x40;
__irq_vector = 0x04000000 - 4;
SECTIONS
{
.twl :
{
__arm7i_lma__ = LOADADDR(.twl);
__arm7i_start__ = .;
*(.twl)
*(.twl.text .twl.text.*)
*(.twl.rodata .twl.rodata.*)
*(.twl.data .twl.data.*)
*.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_bss.*)
*(.twl.bss .twl.bss.*)
*.twl.*(.dynbss)
*.twl.*(.gnu.linkonce.b*)
*.twl.*(.bss*)
*.twl.*(COMMON)
. = ALIGN(4);
__twl_bss_end__ = .;
} >twl_iwram :NONE
.twl_noinit ALIGN(4) (NOLOAD):
{
__twl_noinit_start__ = ABSOLUTE(.);
*(.twl_noinit .twl_noinit.*)
*(.twl.noinit .twl.noinit.*)
*.twl*(.noinit)
*.twl*(.noinit.*)
*.twl*(.gnu.linkonce.n.*)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
__twl_noinit_end__ = ABSOLUTE(.);
__twl_end__ = ABSOLUTE(.);
} >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) *(.v4_bx)
. = 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. */
. = ALIGN(32 / 8);
.init_array :
{
PROVIDE (__preinit_array_start = .);
PROVIDE (__bothinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE (__init_array_end = .);
PROVIDE (__bothinit_array_end = .);
} >iwram AT>ewram
.fini_array :
{
PROVIDE (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
/* Required by pico-exitprocs.c. */
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 (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *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 (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *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 .gcc_except_table.*)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >iwram AT>ewram
.got : { *(.got.plt) *(.got) } >iwram AT>ewram
.data ALIGN(4) : {
__data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
. = ALIGN(4);
} >iwram AT>ewram
.tdata ALIGN(4) :
{
__tdata_start = ABSOLUTE(.);
*(.tdata .tdata.* .gnu.linkonce.td.*)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
__tdata_end = ABSOLUTE(.);
__data_end = . ;
} >iwram AT>ewram
__tdata_size = __tdata_end - __tdata_start ;
.tbss ALIGN(4) (NOLOAD) :
{
__tbss_start = ABSOLUTE(.);
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
__tbss_end = ABSOLUTE(.);
} >iwram AT>ewram
__tbss_size = __tbss_end - __tbss_start ;
.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(.);
} >iwram
.noinit (NOLOAD):
{
__noinit_start = ABSOLUTE(.);
*(.noinit .noinit.* .gnu.linkonce.n.*)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
__noinit_end = ABSOLUTE(.);
} >iwram
/* Space reserved for the thread local storage of main() */
.tls ALIGN(4) (NOLOAD) :
{
__tls_start = ABSOLUTE(.);
. = . + __tdata_size + __tbss_size;
__tls_end = ABSOLUTE(.);
__end__ = ABSOLUTE(.);
} >iwram
__tls_size = __tls_end - __tls_start;
HIDDEN(__arm7_size__ = __arm7_end__ - __arm7_start__);
HIDDEN(__arm7i_size__ = __arm7i_end__ - __arm7i_start__);
HIDDEN(__bss_size__ = __bss_end__ - __bss_start__);
HIDDEN(__twl_bss_size__ = __twl_bss_end__ - __twl_bss_start__);
/* 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 (INFO) : { *(.comment); LINKER_VERSION; }
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
/* 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 .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
.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) }
/* DWARF 3. */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF 5. */
.debug_addr 0 : { *(.debug_addr) }
.debug_line_str 0 : { *(.debug_line_str) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_names 0 : { *(.debug_names) }
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.debug_sup 0 : { *(.debug_sup) }
.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

23
arm7/dldi_ds_arm7.specs Normal file
View File

@@ -0,0 +1,23 @@
%include <picolibc.specs>
%rename cc1plus blocksds_cc1plus
%rename cpp blocksds_cpp
%rename link blocksds_link
*cpp:
-D__NDS__ -D__BLOCKSDS__ -DARM7 %(blocksds_cpp)
*cc1_cpu:
-mcpu=arm7tdmi
*cc1plus:
%(cpp) %(blocksds_cc1plus)
*link:
%(blocksds_link) -T arm7/dldi_ds_arm7.ld --gc-sections --no-warn-rwx-segments
*startfile:
%:getenv(BLOCKSDS /sys/crts/ds_arm7_crt0%O)
*lib:
%(libgcc)

2
arm7/source/common.h Normal file
View File

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

44
arm7/source/main.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include "common.h"
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosThread.h>
#include <libtwl/rtos/rtosEvent.h>
#include <libtwl/ipc/ipcSync.h>
#include <libtwl/ipc/ipcFifoSystem.h>
#include <libtwl/gfx/gfxStatus.h>
#include "picoLoaderBootstrap.h"
static rtos_event_t sVBlankEvent;
static void vblankIrq(u32 irqMask)
{
rtos_signalEvent(&sVBlankEvent);
}
int main()
{
rtos_initIrq();
rtos_startMainThread();
ipc_initFifoSystem();
pload_init();
rtos_createEvent(&sVBlankEvent);
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq);
rtos_enableIrqMask(RTOS_IRQ_VBLANK);
gfx_setVBlankIrqEnabled(true);
ipc_setArm7SyncBits(7);
while (true)
{
rtos_waitEvent(&sVBlankEvent, true, true);
if (pload_shouldStart())
{
pload_start();
}
}
return 0;
}

View File

@@ -0,0 +1,39 @@
#include "common.h"
#include <string.h>
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/ipc/ipcFifoSystem.h>
#include <libtwl/sound/sound.h>
#include "ipcChannels.h"
#include "picoLoader7.h"
#include "picoLoaderBootstrap.h"
typedef void (*pico_loader_7_func_t)(void);
static volatile bool sShouldStart = false;
static void ipcMessageHandler(u32 channel, u32 data, void* arg)
{
if (data == 1)
{
sShouldStart = true;
}
}
void pload_init()
{
ipc_setChannelHandler(IPC_CHANNEL_LOADER, ipcMessageHandler, nullptr);
}
bool pload_shouldStart()
{
return sShouldStart;
}
void pload_start()
{
snd_setMasterEnable(false);
rtos_disableIrqs();
REG_IME = 0;
auto header7 = (pload_header7_t*)0x06000000;
((pico_loader_7_func_t)header7->entryPoint)();
}

View File

@@ -0,0 +1,5 @@
#pragma once
void pload_init();
bool pload_shouldStart();
void pload_start();

BIN
arm9/data/font.nft2 Normal file

Binary file not shown.

3
arm9/source/common.h Normal file
View File

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

View File

@@ -0,0 +1,13 @@
#include <nds.h>
#include "Environment.h"
u32 Environment::_flags;
void Environment::Initialize()
{
_flags = ENVIRONMENT_FLAGS_NONE;
if (isDSiMode())
{
_flags |= ENVIRONMENT_FLAGS_DSI_MODE;
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
class Environment
{
enum EnvironmentFlags : u32
{
ENVIRONMENT_FLAGS_NONE = 0,
ENVIRONMENT_FLAGS_DSI_MODE = (1 << 0),
ENVIRONMENT_FLAGS_NOCASH_PRINT = (1 << 1),
ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR = (1 << 2),
ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING = (1 << 3),
ENVIRONMENT_FLAGS_AGB_SEMIHOSTING = (1 << 4),
ENVIRONMENT_FLAGS_DLDI = (1 << 5),
ENVIRONMENT_FLAGS_ARGV = (1 << 6),
ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER = (1 << 7)
};
static u32 _flags;
public:
static void Initialize();
static inline bool IsDsiMode() { return _flags & ENVIRONMENT_FLAGS_DSI_MODE; }
static inline bool SupportsDldi() { return _flags & ENVIRONMENT_FLAGS_DLDI; }
static inline bool SupportsArgv() { return _flags & ENVIRONMENT_FLAGS_ARGV; }
};

View File

@@ -0,0 +1,48 @@
#include "common.h"
#include <libtwl/mem/memVram.h>
#include <libtwl/gfx/gfx.h>
#include <libtwl/gfx/gfxStatus.h>
#include <libtwl/gfx/gfxPalette.h>
#include <libtwl/gfx/gfxBackground.h>
#include "nitroFont2.h"
#include "font_nft2.h"
#include "ErrorDisplay.h"
void ErrorDisplay::PrintError(const char* errorString)
{
mem_setVramEMapping(MEM_VRAM_E_MAIN_BG_00000);
auto textBuffer = (u8*)0x02100000;
memset(textBuffer, 0, 256 * 192);
nft2_unpack((nft2_header_t*)font_nft2);
nft2_string_render_params_t renderParams =
{
x: 0,
y: 0,
width: 256,
height: 192
};
nft2_renderString((const nft2_header_t*)font_nft2, errorString, textBuffer, 256, &renderParams);
memcpy((void*)GFX_BG_MAIN, textBuffer, 256 * 192);
while (gfx_getVCount() != 191);
while (gfx_getVCount() == 191);
// 4 bit grayscale palette
for (int i = 0; i < 16; i++)
{
int gray = i * 2 + (i == 0 ? 0 : 1);
GFX_PLTT_BG_MAIN[i] = gray | (gray << 5) | (gray << 10);
}
REG_BG3PA = 256;
REG_BG3PB = 0;
REG_BG3PC = 0;
REG_BG3PD = 256;
REG_BG3X = 0;
REG_BG3Y = 0;
REG_BG3CNT = (1 << 7) | (1 << 14);
REG_BLDCNT = 0;
REG_DISPCNT = 3 | (1 << 11) | (1 << 16);
REG_MASTER_BRIGHT = 0;
GFX_PLTT_BG_SUB[0] = 0;
REG_MASTER_BRIGHT_SUB = 0x8010;
REG_DISPCNT_SUB = 0x10000;
while (true);
}

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