Initial commit

This commit is contained in:
Gericom
2025-11-22 17:21:45 +01:00
commit 5d6f67c612
517 changed files with 63025 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

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

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

@@ -0,0 +1,37 @@
name: Build Pico Launcher
on:
push:
branches: ["develop"]
paths-ignore:
- 'README.md'
pull_request:
branches: ["develop"]
paths-ignore:
- 'README.md'
workflow_dispatch:
jobs:
pico_launcher:
runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-v1.13.1
name: Build Pico Launcher
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: |
_pico/
LAUNCHER.nds
name: Pico Launcher

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.

110
Makefile Normal file
View File

@@ -0,0 +1,110 @@
# 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 := LAUNCHER
GAME_TITLE := Pico Launcher
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)
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 := arm9/gfx
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,--use-blx $(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)

81
README.md Normal file
View File

@@ -0,0 +1,81 @@
# Pico Launcher
This repository contains Pico Launcher, which is a front-end for [Pico Loader](https://github.com/LNH-team/pico-loader).
![Horizontal display mode with custom theme](docs/images/HorizontalCustom.png)
![Banner list display mode](docs/images/List.png)
![Coverflow display mode](docs/images/Coverflow.png)
## Features
- Can load homebrew and retail games using [Pico Loader](https://github.com/LNH-team/pico-loader).
- Various display modes
- Horizontal and vertical icon grid
- Banner list
- Coverflow
- [File associations](docs/FileAssociations.md)
- [Covers](docs/Covers.md)
- [Material Design 3 and custom themes](docs/Themes.md)
- Support for background music (see [Themes](docs/Themes.md))
General usage documentation can be found here: [Usage](docs/Usage.md).
## 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/)
## Compiling
1. Run `make`
The launcher can be found in the root directory under the name `LAUNCHER.nds`.
2. Copy `LAUNCHER.nds` to your SD card.
- If you are using DSpico, rename to `_picoboot.nds` and place it in the root of your SD card.
3. Copy the `_pico` pico folder to the root of your SD card.
> [!NOTE]
> To use Pico Launcher, the Pico Loader files (`aplist.bin`, `savelist.bin`, `picoLoader7.bin` and `picoLoader9.bin`) must also be present in the `/_pico` folder on your SD card.
For DSpico the final directory structure will look like this:
```
.
├── _pico
│ ├── themes
│ │ ├── material
│ │ │ └── theme.json
│ │ └── raspberry
│ │ ├── bannerListCell.bin
│ │ ├── bannerListCellPltt.bin
│ │ ├── bannerListCellSelected.bin
│ │ ├── bannerListCellSelectedPltt.bin
│ │ ├── bottombg.bin
│ │ ├── gridcell.bin
│ │ ├── gridcellPltt.bin
│ │ ├── gridcellSelected.bin
│ │ ├── gridcellSelectedPltt.bin
│ │ ├── scrim.bin
│ │ ├── scrimPltt.bin
│ │ ├── theme.json
│ │ └── topbg.bin
│ ├── aplist.bin
│ ├── savelist.bin
│ ├── picoLoader7.bin
│ └── picoLoader9.bin
└── _picoboot.nds
```
Note: If you want to play DSiWare on the DSpico, additional files are required. See the [Pico Loader](https://github.com/LNH-team/pico-loader) readme for more information.
## License
Icons by [icons8](https://icons8.com/)
This project is licensed under the Zlib license. For details, see `LICENSE.txt`.
Additional licenses may apply to the project. For details, see the `license` directory.
## Contributors
- [@Gericom](https://github.com/Gericom)
- [@XLuma](https://github.com/XLuma)
- [@Dartz150](https://github.com/Dartz150)
- [@lifehackerhansol](https://github.com/lifehackerhansol)

View File

@@ -0,0 +1,12 @@
{
"type": "material",
"name": "Material Design 3",
"description": "Theme based on Google's Material Design 3.",
"author": "Gericom",
"primaryColor": {
"r": 138,
"g": 217,
"b": 255
},
"darkTheme": false
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,12 @@
{
"type": "custom",
"name": "Raspberry",
"description": "Theme based on raspberries.",
"author": "Gericom",
"primaryColor": {
"r": 138,
"g": 217,
"b": 255
},
"darkTheme": false
}

File diff suppressed because one or more lines are too long

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)

8
arm7/source/Arm7State.h Normal file
View File

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

12
arm7/source/ExitMode.h Normal file
View File

@@ -0,0 +1,12 @@
#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,
/// @brief Launch an application through pico loader.
PicoLoader
};

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

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

16
arm7/source/dldi.s Normal file
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,46 @@
#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 = _DLDI_startup_ptr();
SendResponseMessage(result);
}
void DldiIpcService::ReadSectors(const dldi_ipc_cmd_t* cmd) const
{
_DLDI_readSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
SendResponseMessage(0);
}
void DldiIpcService::WriteSectors(const dldi_ipc_cmd_t* cmd) const
{
_DLDI_writeSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
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,19 @@
#pragma once
#include "ipc/ThreadIpcService.h"
#include "dsiSdIpcCommand.h"
#include "ipcChannels.h"
class DsiSdIpcService : public ThreadIpcService
{
u32 _threadStack[128];
void ReadSectors(const dsisd_ipc_cmd_t* cmd) const;
void WriteSectors(const dsisd_ipc_cmd_t* cmd) const;
public:
DsiSdIpcService()
: ThreadIpcService(IPC_CHANNEL_DSI_SD, 5, _threadStack, sizeof(_threadStack)) { }
void Start() override;
void HandleMessage(u32 data) override;
};

View File

@@ -0,0 +1,36 @@
#include <nds/ndstypes.h>
#include "../mmc/sdmmc.h"
#include "DsiSdIpcService.h"
void DsiSdIpcService::Start()
{
pico_SDMMC_init(SDMMC_DEV_CARD);
ThreadIpcService::Start();
}
void DsiSdIpcService::HandleMessage(u32 data)
{
auto cmd = reinterpret_cast<const dsisd_ipc_cmd_t*>(data << 2);
switch (cmd->cmd)
{
case DSI_SD_IPC_CMD_READ_SECTORS:
ReadSectors(cmd);
break;
case DSI_SD_IPC_CMD_WRITE_SECTORS:
WriteSectors(cmd);
break;
}
}
void DsiSdIpcService::ReadSectors(const dsisd_ipc_cmd_t* cmd) const
{
pico_SDMMC_readSectors(SDMMC_DEV_CARD, cmd->sector, cmd->buffer, cmd->count);
SendResponseMessage(0);
}
void DsiSdIpcService::WriteSectors(const dsisd_ipc_cmd_t* cmd) const
{
pico_SDMMC_writeSectors(SDMMC_DEV_CARD, cmd->sector, cmd->buffer, cmd->count);
SendResponseMessage(0);
}

View File

@@ -0,0 +1,9 @@
#include "common.h"
#include <libtwl/sio/sioRtc.h>
#include "RtcIpcService.h"
void RtcIpcService::HandleMessage(u32 data)
{
rtc_readDateTime(reinterpret_cast<rtc_datetime_t*>(data << 2));
SendResponseMessage(1);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "ipc/ThreadIpcService.h"
#include "ipcChannels.h"
class RtcIpcService : public ThreadIpcService
{
u32 _threadStack[128];
public:
RtcIpcService()
: ThreadIpcService(IPC_CHANNEL_RTC, 10, _threadStack, sizeof(_threadStack)) { }
void HandleMessage(u32 data) override;
};

View File

@@ -0,0 +1,49 @@
#include "common.h"
#include <libtwl/sound/soundChannel.h>
#include "soundIpcCommand.h"
#include "SoundIpcService.h"
void SoundIpcService::OnMessageReceived(u32 data)
{
const u32* commandList = reinterpret_cast<const u32*>(data);
u32 cmdCount = *commandList++;
for (u32 i = 0; i < cmdCount; i++)
{
u32 cmdValue = *commandList++;
u32 cmd = cmdValue & 0xFF;
u32 cmdArg = cmdValue >> 8;
switch (cmd)
{
case SND_IPC_CMD_START_CHANNELS:
{
u32 channelsMask = cmdArg;
for (u32 j = 0; j < 16; j++)
{
if (channelsMask & (1u << j))
snd_startChannel(j);
}
break;
}
case SND_IPC_CMD_STOP_CHANNELS:
{
u32 channelsMask = cmdArg;
for (u32 j = 0; j < 16; j++)
{
if (channelsMask & (1u << j))
snd_stopChannel(j);
}
break;
}
case SND_IPC_CMD_SETUP_CHANNEL:
{
u32 channel = cmdArg;
REG_SOUNDxSAD(channel) = *commandList++;
REG_SOUNDxTMR(channel) = *commandList++;
REG_SOUNDxPNT(channel) = *commandList++;
REG_SOUNDxLEN(channel) = *commandList++;
REG_SOUNDxCNT(channel) = *commandList++;
break;
}
}
}
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "ipc/IpcService.h"
#include "ipcChannels.h"
class SoundIpcService : public IpcService
{
public:
SoundIpcService()
: IpcService(IPC_CHANNEL_SOUND) { }
void OnMessageReceived(u32 data) override;
};

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

@@ -0,0 +1,241 @@
#include "common.h"
#include <nds/system.h>
#include <libtwl/sound/sound.h>
#include <libtwl/sound/soundChannel.h>
#include <libtwl/sound/soundCapture.h>
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosThread.h>
#include <libtwl/rtos/rtosEvent.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 "logger/PlainLogger.h"
#include "logger/NocashOutputStream.h"
#include "logger/NullLogger.h"
#include "logger/ThreadSafeLogger.h"
#include "picoLoaderBootstrap.h"
#include "sharedMemory.h"
#include "ipcServices/DsiSdIpcService.h"
#include "ipcServices/DldiIpcService.h"
#include "ipcServices/SoundIpcService.h"
#include "ipcServices/RtcIpcService.h"
#include "ExitMode.h"
#include "Arm7State.h"
#include "mmc/tmio.h"
static NocashOutputStream sNocashOutputStream;
static PlainLogger sPlainLogger = PlainLogger(LogLevel::All, std::unique_ptr<IOutputStream>(&sNocashOutputStream));
static ThreadSafeLogger sThreadSafeLogger = ThreadSafeLogger(std::unique_ptr<ILogger>(&sPlainLogger));
static DsiSdIpcService sDsiSdIpcService;
static DldiIpcService sDldiIpcService;
static SoundIpcService sSoundIpcService;
static RtcIpcService sRtcIpcService;
ILogger* gLogger = &sThreadSafeLogger;
static rtos_event_t sVBlankEvent;
static ExitMode sExitMode;
static Arm7State sState;
static volatile u8 sMcuIrqFlag = false;
static void vblankIrq(u32 irqMask)
{
rtos_signalEvent(&sVBlankEvent);
}
static void vcountIrq(u32 irqMask)
{
SHARED_KEY_XY = REG_RCNT0_H;
}
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 clearSoundRegisters()
{
REG_SOUNDCNT = 0;
REG_SNDCAP0CNT = 0;
REG_SNDCAP1CNT = 0;
for (int i = 0; i < 16; i++)
{
REG_SOUNDxCNT(i) = 0;
REG_SOUNDxSAD(i) = 0;
REG_SOUNDxTMR(i) = 0;
REG_SOUNDxPNT(i) = 0;
REG_SOUNDxLEN(i) = 0;
}
}
static void initializeArm7()
{
rtos_initIrq();
rtos_startMainThread();
ipc_initFifoSystem();
clearSoundRegisters();
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();
if (isDSiMode())
{
TMIO_init();
sDsiSdIpcService.Start();
}
sDldiIpcService.Start();
pload_init();
snd_setMasterVolume(127);
snd_setMasterEnable(true);
sSoundIpcService.Start();
sRtcIpcService.Start();
gfx_setVCountMatchLine(96);
rtos_setIrqFunc(RTOS_IRQ_VCOUNT, vcountIrq);
rtos_enableIrqMask(RTOS_IRQ_VCOUNT);
gfx_setVCountMatchIrqEnabled(true);
initializeVBlankIrq();
if (isDSiMode())
{
rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq);
rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
}
ipc_setArm7SyncBits(7);
}
static void updateArm7IdleState()
{
if (pload_shouldStart())
{
sExitMode = ExitMode::PicoLoader;
sState = Arm7State::ExitRequested;
}
else
{
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;
}
case ExitMode::PicoLoader:
{
pload_start();
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;
}

235
arm7/source/mmc/mmc_spec.h Normal file
View File

@@ -0,0 +1,235 @@
#pragma once
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
// Based on JEDEC eMMC Card Product Standard V4.41.
#include "tmio.h"
// Controller specific macros. Add controller specific bits here.
// MMC_CMD_[response type]_[transfer type]
// Transfer type: R = read, W = write.
#define MMC_CMD_NONE(id) (CMD_RESP_NONE | (id))
#define MMC_CMD_R1(id) (CMD_RESP_R1 | (id))
#define MMC_CMD_R1b(id) (CMD_RESP_R1b | (id))
#define MMC_CMD_R2(id) (CMD_RESP_R2 | (id))
#define MMC_CMD_R3(id) (CMD_RESP_R3 | (id))
#define MMC_CMD_R4(id) (CMD_RESP_R4 | (id))
#define MMC_CMD_R5(id) (CMD_RESP_R5 | (id))
#define MMC_CMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | (id))
#define MMC_CMD_R1_W(id) (CMD_DATA_W | CMD_DATA_EN | CMD_RESP_R1 | (id))
// Basic commands and read-stream command (class 0 and class 1).
#define MMC_GO_IDLE_STATE MMC_CMD_NONE(0u) // -, [31:0] 0x00000000 GO_IDLE_STATE, 0xF0F0F0F0 GO_PRE_IDLE_STATE, 0xFFFFFFFA BOOT_INITIATION.
#define MMC_SEND_OP_COND MMC_CMD_R3(1u) // R3, [31:0] OCR with-out busy.
#define MMC_ALL_SEND_CID MMC_CMD_R2(2u) // R2, [31:0] stuff bits.
#define MMC_SET_RELATIVE_ADDR MMC_CMD_R1(3u) // R1, [31:16] RCA [15:0] stuff bits.
#define MMC_SET_DSR MMC_CMD_NONE(4u) // -, [31:16] DSR [15:0] stuff bits.
#define MMC_SLEEP_AWAKE MMC_CMD_R1b(5u) // R1b, [31:16] RCA [15] Sleep/Awake [14:0] stuff bits.
#define MMC_SWITCH MMC_CMD_R1b(6u) // R1b, [31:26] Set to 0 [25:24] Access [23:16] Index [15:8] Value [7:3] Set to 0 [2:0] Cmd Set.
#define MMC_SELECT_CARD MMC_CMD_R1b(7u) // R1/R1b, [31:16] RCA [15:0] stuff bits. Note: "R1b while selecting from Disconnected State to Programming State."
#define MMC_DESELECT_CARD MMC_CMD_NONE(7u) // -, [31:16] RCA [15:0] stuff bits.
#define MMC_SEND_EXT_CSD MMC_CMD_R1_R(8u) // R1, [31:0] stuff bits.
#define MMC_SEND_CSD MMC_CMD_R2(9u) // R2, [31:16] RCA [15:0] stuff bits.
#define MMC_SEND_CID MMC_CMD_R2(10u) // R2, [31:16] RCA [15:0] stuff bits.
#define MMC_READ_DAT_UNTIL_STOP MMC_CMD_R1_R(11u) // R1, [31:0] data address.
#define MMC_STOP_TRANSMISSION MMC_CMD_R1b(12u) // R1/R1b, [31:16] RCA [15:1] stuff bits [0] HPI. Note: "RCA in CMD12 is used only if HPI bit is set." Note 2: "R1 for read cases and R1b for write cases."
#define MMC_SEND_STATUS MMC_CMD_R1(13u) // R1, [31:16] RCA [15:1] stuff bits [0] HPI.
#define MMC_BUSTEST_R MMC_CMD_R1_R(14u) // R1, [31:0] stuff bits.
#define MMC_GO_INACTIVE_STATE MMC_CMD_NONE(15u) // -, [31:16] RCA [15:0] stuff bits.
#define MMC_BUSTEST_W MMC_CMD_R1_W(19u) // R1, [31:0] stuff bits.
// Block-oriented read commands (class 2).
#define MMC_SET_BLOCKLEN MMC_CMD_R1(16u) // R1, [31:0] block length.
#define MMC_READ_SINGLE_BLOCK MMC_CMD_R1_R(17u) // R1, [31:0] data address.
#define MMC_READ_MULTIPLE_BLOCK MMC_CMD_R1_R(18u) // R1, [31:0] data address.
// Stream write commands (class 3).
#define MMC_WRITE_DAT_UNTIL_STOP MMC_CMD_R1_W(20u) // R1, [31:0] data address.
// Block-oriented write commands (class 4).
#define MMC_SET_BLOCK_COUNT MMC_CMD_R1(23u) // R1, [31] Reliable Write Request [30:16] set to 0 [15:0] number of blocks.
#define MMC_WRITE_BLOCK MMC_CMD_R1_W(24u) // R1, [31:0] data address.
#define MMC_WRITE_MULTIPLE_BLOCK MMC_CMD_R1_W(25u) // R1, [31:0] data address.
#define MMC_PROGRAM_CID MMC_CMD_R1_W(26u) // R1, [31:0] stuff bits.
#define MMC_PROGRAM_CSD MMC_CMD_R1_W(27u) // R1, [31:0] stuff bits.
// Block-oriented write protection commands (class 6).
#define MMC_SET_WRITE_PROT MMC_CMD_R1b(28u) // R1b, [31:0] data address.
#define MMC_CLR_WRITE_PROT MMC_CMD_R1b(29u) // R1b, [31:0] data address.
#define MMC_SEND_WRITE_PROT MMC_CMD_R1_R(30u) // R1, [31:0] write protect data address.
#define MMC_SEND_WRITE_PROT_TYPE MMC_CMD_R1_R(31u) // R1, [31:0] write protect data address.
// Erase commands (class 5).
#define MMC_ERASE_GROUP_START MMC_CMD_R1(35u) // R1, [31:0] data address.
#define MMC_ERASE_GROUP_END MMC_CMD_R1(36u) // R1, [31:0] data address.
#define MMC_ERASE MMC_CMD_R1b(38u) // R1b, [31] Secure request [30:16] set to 0 [15] Force Garbage Collect request [14:1] set to 0 [0] Identify Write block for Erase.
// I/O mode commands (class 9).
#define MMC_FAST_IO MMC_CMD_R4(39u) // R4, [31:16] RCA [15:15] register write flag [14:8] register address [7:0] register data.
#define MMC_GO_IRQ_STATE MMC_CMD_R5(40u) // R5, [31:0] stuff bits.
// Lock card commands (class 7).
#define MMC_LOCK_UNLOCK MMC_CMD_R1_W(42u) // R1, [31:0] stuff bits.
// Application-specific commands (class 8).
#define MMC_APP_CMD MMC_CMD_R1(55u) // R1, [31:16] RCA [15:0] stuff bits.
#define MMC_GEN_CMD_R MMC_CMD_R1_R(56u) // R1, [31:1] stuff bits [0] RD/WR = 1.
#define MMC_GEN_CMD_W MMC_CMD_R1_W(56u) // R1, [31:1] stuff bits [0] RD/WR = 0.
// 7.13 Card status.
// Type:
// E: Error bit.
// S: Status bit.
// R: Detected and set for the actual command response.
// X: Detected and set during command execution. The host can get the status by issuing a command with R1 response.
//
// Clear Condition:
// A: These bits are persistent, they are set and cleared in accordance with the card status.
// B: These bits are cleared as soon as the response (reporting the error) is sent out.
#define MMC_R1_APP_CMD (1u<<5) // S R A, The card will expect ACMD, or indication that the command has been interpreted as ACMD.
#define MMC_R1_URGENT_BKOPS (1u<<6) // S R A, If set, device needs to perform backgroundoperations urgently. Host can check EXT_CSD field BKOPS_STATUS for the detailed level.
#define MMC_R1_SWITCH_ERROR (1u<<7) // E X B, If set, the card did not switch to the expected mode as requested by the SWITCH command.
#define MMC_R1_READY_FOR_DATA (1u<<8) // S R A, Corresponds to buffer empty signalling on the bus.
#define MMC_R1_STATE_IDLE (0u<<9) // S R A
#define MMC_R1_STATE_READY (1u<<9) // S R A
#define MMC_R1_STATE_IDENT (2u<<9) // S R A
#define MMC_R1_STATE_STBY (3u<<9) // S R A
#define MMC_R1_STATE_TRAN (4u<<9) // S R A
#define MMC_R1_STATE_DATA (5u<<9) // S R A
#define MMC_R1_STATE_RCV (6u<<9) // S R A
#define MMC_R1_STATE_PRG (7u<<9) // S R A
#define MMC_R1_STATE_DIS (8u<<9) // S R A
#define MMC_R1_STATE_BTST (9u<<9) // S R A
#define MMC_R1_STATE_SLP (10u<<9) // S R A
#define MMC_R1_ERASE_RESET (1u<<13) // E R B, An erase sequence was cleared before executing because an out of erase sequence command was received (commands other than CMD35, CMD36, CMD38 or CMD13.
#define MMC_R1_WP_ERASE_SKIP (1u<<15) // E X B, Only partial address space was erased due to existing write protected blocks.
#define MMC_R1_CXD_OVERWRITE (1u<<16) // E X B, Can be either one of the following errors: - The CID register has been already written and can not be overwritten - The read only section of the CSD does not match the card content. - An attempt to reverse the copy (set as original) or permanent WP (unprotected) bits was made.
#define MMC_R1_OVERRUN (1u<<17) // E X B, The card could not sustain data programming in stream write mode.
#define MMC_R1_UNDERRUN (1u<<18) // E X B, The card could not sustain data transfer in stream read mode.
#define MMC_R1_ERROR (1u<<19) // E X B, (Undefined by the standard) A generic card error related to the (and detected during) execution of the last host command (e.g. read or write failures).
#define MMC_R1_CC_ERROR (1u<<20) // E R B, (Undefined by the standard) A card error occurred, which is not related to the host command.
#define MMC_R1_CARD_ECC_FAILED (1u<<21) // E X B, Card internal ECC was applied but failed to correct the data.
#define MMC_R1_ILLEGAL_COMMAND (1u<<22) // E R B, Command not legal for the card state.
#define MMC_R1_COM_CRC_ERROR (1u<<23) // E R B, The CRC check of the previous command failed.
#define MMC_R1_LOCK_UNLOCK_FAILED (1u<<24) // E X B, Set when a sequence or password error has been detected in lock/unlock card command.
#define MMC_R1_CARD_IS_LOCKED (1u<<25) // S R A, When set, signals that the card is locked by the host.
#define MMC_R1_WP_VIOLATION (1u<<26) // E X B, Attempt to program a write protected block.
#define MMC_R1_ERASE_PARAM (1u<<27) // E X B, An invalid selection of erase groups for erase occurred.
#define MMC_R1_ERASE_SEQ_ERROR (1u<<28) // E R B, An error in the sequence of erase commands occurred.
#define MMC_R1_BLOCK_LEN_ERROR (1u<<29) // E R B, Either the argument of a SET_BLOCKLEN command exceeds the maximum value allowed for the card, or the previously defined block length is illegal for the current command (e.g. the host issues a write command, the current block length is smaller than the cards maximum and write partial blocks is not allowed).
#define MMC_R1_ADDRESS_MISALIGN (1u<<30) // E R/X B, The command s address argument (in accordance with the currently set block length) positions the first data block misaligned to the card physical blocks. A multiple block read/write operation (although started with a valid address/blocklength combination) is attempting to read or write a data block which does not align with the physical blocks of the card.
#define MMC_R1_ADDRESS_OUT_OF_RANGE (1u<<31) // E R/X B, The commands address argument was out of the allowed range for this card. A multiple block or stream read/write operation is (although started in a valid address) attempting to read or write beyond the card capacity.
#define MMC_R1_ERR_ALL (MMC_R1_ADDRESS_OUT_OF_RANGE | MMC_R1_ADDRESS_MISALIGN | \
MMC_R1_BLOCK_LEN_ERROR | MMC_R1_ERASE_SEQ_ERROR | \
MMC_R1_ERASE_PARAM | MMC_R1_WP_VIOLATION | MMC_R1_LOCK_UNLOCK_FAILED | \
MMC_R1_COM_CRC_ERROR | MMC_R1_ILLEGAL_COMMAND | MMC_R1_CARD_ECC_FAILED | \
MMC_R1_CC_ERROR | MMC_R1_ERROR | MMC_R1_UNDERRUN | MMC_R1_OVERRUN | \
MMC_R1_CXD_OVERWRITE | MMC_R1_WP_ERASE_SKIP | MMC_R1_ERASE_RESET | \
MMC_R1_SWITCH_ERROR)
// 8.1 OCR register.
// Same bits for CMD1 argument.
#define MMC_OCR_1_7_1_95V (1u<<7) // 1.701.95V.
#define MMC_OCR_2_0_2_1V (1u<<8) // 2.0-2.1V.
#define MMC_OCR_2_1_2_2V (1u<<9) // 2.1-2.2V.
#define MMC_OCR_2_2_2_3V (1u<<10) // 2.2-2.3V.
#define MMC_OCR_2_3_2_4V (1u<<11) // 2.3-2.4V.
#define MMC_OCR_2_4_2_5V (1u<<12) // 2.4-2.5V.
#define MMC_OCR_2_5_2_6V (1u<<13) // 2.5-2.6V.
#define MMC_OCR_2_6_2_7V (1u<<14) // 2.6-2.7V.
#define MMC_OCR_2_7_2_8V (1u<<15) // 2.7-2.8V.
#define MMC_OCR_2_8_2_9V (1u<<16) // 2.8-2.9V.
#define MMC_OCR_2_9_3_0V (1u<<17) // 2.9-3.0V.
#define MMC_OCR_3_0_3_1V (1u<<18) // 3.0-3.1V.
#define MMC_OCR_3_1_3_2V (1u<<19) // 3.1-3.2V.
#define MMC_OCR_3_2_3_3V (1u<<20) // 3.2-3.3V.
#define MMC_OCR_3_3_3_4V (1u<<21) // 3.3-3.4V.
#define MMC_OCR_3_4_3_5V (1u<<22) // 3.4-3.5V.
#define MMC_OCR_3_5_3_6V (1u<<23) // 3.5-3.6V.
#define MMC_OCR_BYTE_MODE (0u<<29) // Access mode = byte mode.
#define MMC_OCR_SECT_MODE (2u<<29) // Access mode = sector mode.
#define MMC_OCR_READY (1u<<31) // Card power up status bit (busy). 0 = busy.
// 7.6.1 Command sets and extended settings.
#define MMC_SWITCH_ACC_CMD_SET (0u)
#define MMC_SWITCH_ACC_SET_BITS (1u)
#define MMC_SWITCH_ACC_CLR_BITS (2u)
#define MMC_SWITCH_ACC_WR_BYTE (3u)
#define MMC_SWITCH_ARG(acc, idx, val, cmdSet) (((acc)&3u)<<24 | ((idx)&0xFFu)<<16 | ((val)&0xFFu)<<8 | ((cmdSet)&7u))
// 8.4 Extended CSD register.
// size in bytes, access, description.
#define EXT_CSD_SEC_BAD_BLK_MGMNT (134u) // 1, R/W, Bad Block Management mode.
#define EXT_CSD_ENH_START_ADDR (136u) // 4, R/W, Enhanced User Data Start Address.
#define EXT_CSD_ENH_SIZE_MULT (140u) // 3, R/W, Enhanced User Data Area Size.
#define EXT_CSD_GP_SIZE_MULT (143u) // 12, R/W, General Purpose Partition Size.
#define EXT_CSD_PARTITION_SETTING_COMPLETED (155u) // 1, R/W, Paritioning Setting.
#define EXT_CSD_PARTITIONS_ATTRIBUTE (156u) // 1, R/W, Partitions attribute.
#define EXT_CSD_MAX_ENH_SIZE_MULT (157u) // 3, R, Max Enhanced Area Size.
#define EXT_CSD_PARTITIONING_SUPPORT (160u) // 1, R, Partitioning Support.
#define EXT_CSD_HPI_MGMT (161u) // 1, R/W/E_P, HPI management.
#define EXT_CSD_RST_n_FUNCTION (162u) // 1, R/W, H/W reset function.
#define EXT_CSD_BKOPS_EN (163u) // 1, R/W, Enable background operations handshake.
#define EXT_CSD_BKOPS_START (164u) // 1, W/E_P, Manually start background operations.
#define EXT_CSD_WR_REL_PARAM (166u) // 1, R, Write reliability parameter register.
#define EXT_CSD_WR_REL_SET (167u) // 1, R/W, Write reliability setting register.
#define EXT_CSD_RPMB_SIZE_MULT (168u) // 1, R, RPMB Size.
#define EXT_CSD_FW_CONFIG (169u) // 1, R/W, FW configuration.
#define EXT_CSD_USER_WP (171u) // 1, R/W, R/W/C_P & R/W/E_P, User area write protection register.
#define EXT_CSD_BOOT_WP (173u) // 1, R/W & R/W/C_P, Boot area write protection register.
#define EXT_CSD_ERASE_GROUP_DEF (175u) // 1, R/W/E_P, High-density erase group definition.
#define EXT_CSD_BOOT_BUS_WIDTH (177u) // 1, R/W/E, Boot bus width1.
#define EXT_CSD_BOOT_CONFIG_PROT (178u) // 1, R/W & R/W/C_P, Boot config protection.
#define EXT_CSD_PARTITION_CONFIG (179u) // 1, R/W/E & R/W/E_P, Partition configuration.
#define EXT_CSD_ERASED_MEM_CONT (181u) // 1, R, Erased memory content.
#define EXT_CSD_BUS_WIDTH (183u) // 1, W/E_P, Bus width mode.
#define EXT_CSD_HS_TIMING (185u) // 1, R/W/E_P, High-speed interface timing.
#define EXT_CSD_POWER_CLASS (187u) // 1, R/W/E_P, Power class.
#define EXT_CSD_CMD_SET_REV (189u) // 1, R, Command set revision.
#define EXT_CSD_CMD_SET (191u) // 1, R/W/E_P, Command set.
#define EXT_CSD_EXT_CSD_REV (192u) // 1, R, Extended CSD revision.
#define EXT_CSD_CSD_STRUCTURE (194u) // 1, R, CSD structure version.
#define EXT_CSD_CARD_TYPE (196u) // 1, R, Card type.
#define EXT_CSD_OUT_OF_INTERRUPT_TIME (198u) // 1, R, Out-of-interrupt busy timing.
#define EXT_CSD_PARTITION_SWITCH_TIME (199u) // 1, R, Partition switching timing.
#define EXT_CSD_PWR_CL_52_195 (200u) // 1, R, Power class for 52MHz at 1.95V.
#define EXT_CSD_PWR_CL_26_195 (201u) // 1, R, Power class for 26MHz at 1.95V.
#define EXT_CSD_PWR_CL_52_360 (202u) // 1, R, Power class for 52MHz at 3.6V.
#define EXT_CSD_PWR_CL_26_360 (203u) // 1, R, Power class for 26MHz at 3.6V.
#define EXT_CSD_MIN_PERF_R_4_26 (205u) // 1, R, Minimum Read Performance for 4bit at 26MHz.
#define EXT_CSD_MIN_PERF_W_4_26 (206u) // 1, R, Minimum Write Performance for 4bit at 26MHz.
#define EXT_CSD_MIN_PERF_R_8_26_4_52 (207u) // 1, R, Minimum Read Performance for 8bit at 26MHz, for 4bit at 52MHz.
#define EXT_CSD_MIN_PERF_W_8_26_4_52 (208u) // 1, R, Minimum Write Performance for 8bit at 26MHz, for 4bit at 52MHz.
#define EXT_CSD_MIN_PERF_R_8_52 (209u) // 1, R, Minimum Read Performance for 8bit at 52MHz.
#define EXT_CSD_MIN_PERF_W_8_52 (210u) // 1, R, Minimum Write Performance for 8bit at 52MHz.
#define EXT_CSD_SEC_COUNT (212u) // 4, R, Sector Count.
#define EXT_CSD_S_A_TIMEOUT (217u) // 1, R, Sleep/awake timeout.
#define EXT_CSD_S_C_VCCQ (219u) // 1, R, Sleep current (VCCQ).
#define EXT_CSD_S_C_VCC (220u) // 1, R, Sleep current (VCC).
#define EXT_CSD_HC_WP_GRP_SIZE (221u) // 1, R, High-capacity write protect group size.
#define EXT_CSD_REL_WR_SEC_C (222u) // 1, R, Reliable write sector count.
#define EXT_CSD_ERASE_TIMEOUT_MULT (223u) // 1, R, High-capacity erase timeout.
#define EXT_CSD_HC_ERASE_GRP_SIZE (224u) // 1, R, High-capacity erase unit size.
#define EXT_CSD_ACC_SIZE (225u) // 1, R, Access size.
#define EXT_CSD_BOOT_SIZE_MULTI (226u) // 1, R, Boot partition size.
#define EXT_CSD_BOOT_INFO (228u) // 1, R, Boot information.
#define EXT_CSD_SEC_TRIM_MULT (229u) // 1, R, Secure TRIM Multiplier.
#define EXT_CSD_SEC_ERASE_MULT (230u) // 1, R, Secure Erase Multiplier.
#define EXT_CSD_SEC_FEATURE_SUPPORT (231u) // 1, R, Secure Feature support.
#define EXT_CSD_TRIM_MULT (232u) // 1, R, TRIM Multiplier.
#define EXT_CSD_MIN_PERF_DDR_R_8_52 (234u) // 1, R, Minimum Read Performance for 8bit at 52MHz in DDR mode.
#define EXT_CSD_MIN_PERF_DDR_W_8_52 (235u) // 1, R, Minimum Write Performance for 8bit at 52MHz in DDR mode.
#define EXT_CSD_PWR_CL_DDR_52_195 (238u) // 1, R, Power class for 52MHz, DDR at 1.95V.
#define EXT_CSD_PWR_CL_DDR_52_360 (239u) // 1, R, Power class for 52MHz, DDR at 3.6V.
#define EXT_CSD_INI_TIMEOUT_AP (241u) // 1, R, 1st initialization time after partitioning.
#define EXT_CSD_CORRECTLY_PRG_SECTORS_NUM (242u) // 4, R, Number of correctly programmed sectors.
#define EXT_CSD_BKOPS_STATUS (246u) // 1, R, Background operations status.
#define EXT_CSD_BKOPS_SUPPORT (502u) // 1, R, Background operations support.
#define EXT_CSD_HPI_FEATURES (503u) // 1, R, HPI features.
#define EXT_CSD_S_CMD_SET (504u) // 1, R, Supported Command Sets.

192
arm7/source/mmc/sd_spec.h Normal file
View File

@@ -0,0 +1,192 @@
#pragma once
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
// Based on SD specification version 8.00.
#include "tmio.h"
// Controller specific macros. Add controller specific bits here.
// SD_[command type]_[response type]_[transfer type]
// Command type: CMD = regular command, ACMD = Application-Specific Command.
// Transfer type: R = read, W = write.
#define SD_CMD_NONE(id) (CMD_RESP_NONE | (id))
#define SD_CMD_R1(id) (CMD_RESP_R1 | (id))
#define SD_CMD_R1b(id) (CMD_RESP_R1b | (id))
#define SD_CMD_R2(id) (CMD_RESP_R2 | (id))
#define SD_CMD_R6(id) (CMD_RESP_R6 | (id))
#define SD_CMD_R7(id) (CMD_RESP_R7 | (id))
#define SD_CMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | (id))
#define SD_CMD_R1_W(id) (CMD_DATA_W | CMD_DATA_EN | CMD_RESP_R1 | (id))
#define SD_ACMD_R1(id) (CMD_RESP_R1 | CMD_ACMD | (id))
#define SD_ACMD_R3(id) (CMD_RESP_R3 | CMD_ACMD | (id))
#define SD_ACMD_R1_R(id) (CMD_DATA_R | CMD_DATA_EN | CMD_RESP_R1 | CMD_ACMD | (id))
// Basic Commands (class 0).
#define SD_GO_IDLE_STATE SD_CMD_NONE(0u) // -, [31:0] stuff bits.
#define SD_ALL_SEND_CID SD_CMD_R2(2u) // R2, [31:0] stuff bits.
#define SD_SEND_RELATIVE_ADDR SD_CMD_R6(3u) // R6, [31:0] stuff bits.
#define SD_SET_DSR SD_CMD_NONE(4u) // -, [31:16] DSR [15:0] stuff bits.
#define SD_SELECT_CARD SD_CMD_R1b(7u) // R1b, [31:16] RCA [15:0] stuff bits.
#define SD_DESELECT_CARD SD_CMD_NONE(7u) // -, [31:16] RCA [15:0] stuff bits.
#define SD_SEND_IF_COND SD_CMD_R7(8u) // R7, [31:12] reserved bits [11:8] supply voltage (VHS) [7:0] check pattern.
#define SD_SEND_CSD SD_CMD_R2(9u) // R2, [31:16] RCA [15:0] stuff bits.
#define SD_SEND_CID SD_CMD_R2(10u) // R2, [31:16] RCA [15:0] stuff bits.
#define SD_VOLTAGE_SWITCH SD_CMD_R1(11u) // R1, [31:0] reserved bits (all 0).
#define SD_STOP_TRANSMISSION SD_CMD_R1b(12u) // R1b, [31:0] stuff bits.
#define SD_SEND_STATUS SD_CMD_R1(13u) // R1, [31:16] RCA [15] Send Task Status Register [14:0] stuff bits.
#define SD_SEND_TASK_STATUS SD_CMD_R1(13u) // R1, [31:16] RCA [15] Send Task Status Register [14:0] stuff bits.
#define SD_GO_INACTIVE_STATE SD_CMD_NONE(15u) // -, [31:16] RCA [15:0] reserved bits.
// Block-Oriented Read Commands (class 2).
#define SD_SET_BLOCKLEN SD_CMD_R1(16u) // R1, [31:0] block length.
#define SD_READ_SINGLE_BLOCK SD_CMD_R1_R(17u) // R1, [31:0] data address.
#define SD_READ_MULTIPLE_BLOCK SD_CMD_R1_R(18u) // R1, [31:0] data address.
#define SD_SEND_TUNING_BLOCK SD_CMD_R1_R(19u) // R1, [31:0] reserved bits (all 0).
#define SD_SPEED_CLASS_CONTROL SD_CMD_R1b(20u) // R1b, [31:28] Speed Class Control [27:0] See command description.
#define SD_ADDRESS_EXTENSION SD_CMD_R1(22u) // R1, [31:6] reserved bits (all 0) [5:0] extended address.
#define SD_SET_BLOCK_COUNT SD_CMD_R1(23u) // R1, [31:0] Block Count.
// Block-Oriented Write Commands (class 4).
// SET_BLOCKLEN
// SPEED_CLASS_CONTROL
// ADDRESS_EXTENSION
// SET_BLOCK_COUNT
#define SD_WRITE_BLOCK SD_CMD_R1_W(24u) // R1, [31:0] data address.
#define SD_WRITE_MULTIPLE_BLOCK SD_CMD_R1_W(25u) // R1, [31:0] data address.
#define SD_PROGRAM_CSD SD_CMD_R1_W(27u) // R1, [31:0] stuff bits.
// Block Oriented Write Protection Commands (class 6).
#define SD_SET_WRITE_PROT SD_CMD_R1b(28u) // R1b, [31:0] data address.
#define SD_CLR_WRITE_PROT SD_CMD_R1b(29u) // R1b, [31:0] data address.
#define SD_SEND_WRITE_PROT SD_CMD_R1_R(30u) // R1, [31:0] write protect data address.
// Erase Commands (class 5).
#define SD_ERASE_WR_BLK_START SD_CMD_R1(32u) // R1, [31:0] data address.
#define SD_ERASE_WR_BLK_END SD_CMD_R1(33u) // R1, [31:0] data address.
#define SD_ERASE SD_CMD_R1b(38u) // R1b, [31:0] Erase Function.
// Lock Card (class 7).
// SET_BLOCKLEN
// Command 40 "Defined by DPS Spec.".
#define SD_LOCK_UNLOCK SD_CMD_R1_W(42u) // R1, [31:0] Reserved bits (Set all 0).
// Application-Specific Commands (class 8).
#define SD_APP_CMD SD_CMD_R1(55u) // R1, [31:16] RCA [15:0] stuff bits.
#define SD_GEN_CMD_R SD_CMD_R1_R(56u) // R1, [31:1] stuff bits. [0]: RD/WR = 1.
#define SD_GEN_CMD_W SD_CMD_R1_W(56u) // R1, [31:1] stuff bits. [0]: RD/WR = 0.
// Application Specific Commands used/reserved by SD Memory Card.
#define SD_APP_SET_BUS_WIDTH SD_ACMD_R1(6u) // R1, [31:2] stuff bits [1:0] bus width.
#define SD_APP_SD_STATUS SD_ACMD_R1_R(13u) // R1, [31:0] stuff bits.
#define SD_APP_SEND_NUM_WR_BLOCKS SD_ACMD_R1_R(22u) // R1, [31:0] stuff bits.
#define SD_APP_SET_WR_BLK_ERASE_COUNT SD_ACMD_R1(23u) // R1, [31:23] stuff bits [22:0] Number of blocks.
#define SD_APP_SD_SEND_OP_COND SD_ACMD_R3(41u) // R3, [31] reserved bit [30] HCS (OCR[30]) [29] reserved for eSD [28] XPC [27:25] reserved bits [24] S18R [23:0] VDD Voltage Window (OCR[23:0]).
#define SD_APP_SET_CLR_CARD_DETECT SD_ACMD_R1(42u) // R1, [31:1] stuff bits [0] set_cd.
#define SD_APP_SEND_SCR SD_ACMD_R1_R(51u) // R1, [31:0] stuff bits.
// Switch Function Commands (class 10).
#define SD_SWITCH_FUNC SD_CMD_R1_R(6u) // R1, [31] Mode 0: Check function 1: Switch function [30:24] reserved (All '0') [23:20] reserved for function group 6 (0h or Fh) [19:16] reserved for function group 5 (0h or Fh) [15:12] function group 4 for PowerLimit [11:8] function group 3 for Drive Strength [7:4] function group 2 for Command System [3:0] function group 1 for Access Mode.
// Function Extension Commands (class 11).
#define SD_READ_EXTR_SINGLE SD_CMD_R1_R(48u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO[26] Reserved (=0) [25:9] ADDR [8:0] LEN.
#define SD_WRITE_EXTR_SINGLE SD_CMD_R1_W(49u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] MW [25:9] ADDR [8:0] LEN/MASK.
#define SD_READ_EXTR_MULTI SD_CMD_R1_R(58u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] BUS0: 512B, 1: 32KB [25:9] ADDR [8:0] BUC.
#define SD_WRITE_EXTR_MULTI SD_CMD_R1_W(59u) // R1, [31] MIO0: Memory, 1: I/O [30:27] FNO [26] BUS0: 512B, 1: 32KB [25:9] ADDR [8:0] BUC.
// Command Queue Function Commands (class 1).
#define SD_Q_MANAGEMENT SD_CMD_R1b(43u) // R1b, [31:21] Reserved [20:16]: Task ID [3:0]: Operation Code (Abort tasks etc.).
#define SD_Q_TASK_INFO_A SD_CMD_R1(44u) // R1, [31] Reserved [30] Direction [29:24] Extended Address [23] Priority [22:21] Reserved [20:16] Task ID [15:0] Number of Blocks.
#define SD_Q_TASK_INFO_B SD_CMD_R1(45u) // R1, [31:0] Start block address.
#define SD_Q_RD_TASK SD_CMD_R1_R(46u) // R1, [31:21] Reserved [20:16] Task ID [15:0] Reserved.
#define SD_Q_WR_TASK SD_CMD_R1_W(47u) // R1, [31:21] Reserved [20:16] Task ID [15:0] Reserved.
// 4.10.1 Card Status.
// Type:
// E: Error bit.
// S: Status bit.
// R: Detected and set for the actual command response.
// X: Detected and set during command execution. The host can get the status by issuing a command with R1 response.
//
// Clear Condition:
// A: According to the card current status.
// B: Always related to the previous command. Reception of a valid command will clear it (with a delay of one command).
// C: Clear by read.
#define SD_R1_AKE_SEQ_ERROR (1u<<3) // E R C, Error in the sequence of the authentication process.
#define SD_R1_APP_CMD (1u<<5) // S R C, The card will expect ACMD, or an indication that the command has been interpreted as ACMD.
#define SD_R1_FX_EVENT (1u<<6) // S X A, ExtensionFunctions may set this bit to get host to deal with events.
#define SD_R1_READY_FOR_DATA (1u<<8) // S X A, Corresponds to buffer empty signaling on the bus.
#define SD_R1_STATE_IDLE (0u<<9) // S X B
#define SD_R1_STATE_READY (1u<<9) // S X B
#define SD_R1_STATE_IDENT (2u<<9) // S X B
#define SD_R1_STATE_STBY (3u<<9) // S X B
#define SD_R1_STATE_TRAN (4u<<9) // S X B
#define SD_R1_STATE_DATA (5u<<9) // S X B
#define SD_R1_STATE_RCV (6u<<9) // S X B
#define SD_R1_STATE_PRG (7u<<9) // S X B
#define SD_R1_STATE_DIS (8u<<9) // S X B
#define SD_R1_ERASE_RESET (1u<<13) // S R C, An erase sequence was cleared before executing because an out of erase sequence command was received.
#define SD_R1_CARD_ECC_DISABLED (1u<<14) // S X A, The command has been executed without using the internal ECC.
#define SD_R1_WP_ERASE_SKIP (1u<<15) // E R X C, Set when only partial address space was erased due to existing write protected blocks or the temporary or permanent write protected cardwas erased.
#define SD_R1_CSD_OVERWRITE (1u<<16) // E R X C, Can be either one of the following errors: -The read only section of the CSD does not match the card content. -An attempt to reverse the copy (set as original) or permanent WP (unprotected) bits was made.
// 17 reserved for DEFERRED_RESPONSE (Refer to eSD Addendum)
#define SD_R1_ERROR (1u<<19) // E R X C, A general or an unknown error occurred during the operation.
#define SD_R1_CC_ERROR (1u<<20) // E R X C, Internal card controller error:
#define SD_R1_CARD_ECC_FAILED (1u<<21) // E R X C, Card internal ECC was applied but failed to correct the data.
#define SD_R1_ILLEGAL_COMMAND (1u<<22) // E R B, Command not legal for the card state.
#define SD_R1_COM_CRC_ERROR (1u<<23) // E R B, The CRC check of the previous command failed.
#define SD_R1_LOCK_UNLOCK_FAILED (1u<<24) // E R X C, Set when a sequence or password error has been detected in lock/unlock card command.
#define SD_R1_CARD_IS_LOCKED (1u<<25) // S X A, When set, signals that the card is locked by the host.
#define SD_R1_WP_VIOLATION (1u<<26) // E R X C, Set when the host attempts to write to a protected block or to thetemporary or permanent write protected card.
#define SD_R1_ERASE_PARAM (1u<<27) // E R X C, An invalid selection of write-blocks for erase occurred.
#define SD_R1_ERASE_SEQ_ERROR (1u<<28) // E R C, An error in the sequence of erase commands occurred.
#define SD_R1_BLOCK_LEN_ERROR (1u<<29) // E R X C, The transferred block length is not allowed for this card, or the number of transferred bytes does not match the block length.
#define SD_R1_ADDRESS_ERROR (1u<<30) // E R X C, A misaligned address which did not match the block length was used in the command.
#define SD_R1_OUT_OF_RANGE (1u<<31) // E R X C, The command's argument was out of the allowed range for this card.
#define SD_R1_ERR_ALL (SD_R1_OUT_OF_RANGE | SD_R1_ADDRESS_ERROR | SD_R1_BLOCK_LEN_ERROR | \
SD_R1_ERASE_SEQ_ERROR | SD_R1_ERASE_PARAM | SD_R1_WP_VIOLATION | \
SD_R1_LOCK_UNLOCK_FAILED | SD_R1_COM_CRC_ERROR | SD_R1_ILLEGAL_COMMAND | \
SD_R1_CARD_ECC_FAILED | SD_R1_CC_ERROR | SD_R1_ERROR | \
SD_R1_CSD_OVERWRITE | SD_R1_WP_ERASE_SKIP | SD_R1_AKE_SEQ_ERROR)
// Argument bits for SEND_IF_COND (CMD8).
#define SD_CMD8_CHK_PATT (0xAAu) // Check pattern.
#define SD_CMD8_VHS_2_7_3_6V (1u<<8) // Voltage supplied (VHS) 2.7-3.6V.
#define SD_CMD8_PCIe (1u<<12) // PCIe Avail-ability.
#define SD_CMD8_PCIe_1_2V (1u<<13) // PCIe 1.2V Support.
// 5.1 OCR register.
#define SD_OCR_2_7_2_8V (1u<<15) // 2.7-2.8V.
#define SD_OCR_2_8_2_9V (1u<<16) // 2.8-2.9V.
#define SD_OCR_2_9_3_0V (1u<<17) // 2.9-3.0V.
#define SD_OCR_3_0_3_1V (1u<<18) // 3.0-3.1V.
#define SD_OCR_3_1_3_2V (1u<<19) // 3.1-3.2V.
#define SD_OCR_3_2_3_3V (1u<<20) // 3.2-3.3V.
#define SD_OCR_3_3_3_4V (1u<<21) // 3.3-3.4V.
#define SD_OCR_3_4_3_5V (1u<<22) // 3.4-3.5V.
#define SD_OCR_3_5_3_6V (1u<<23) // 3.5-3.6V.
#define SD_OCR_S18A (1u<<24) // S18A: Switching to 1.8V Accepted. 0b: Continues current voltage signaling, 1b: Ready for switching signal voltage.
#define SD_OCR_CO2T (1u<<27) // Over 2TB Card. CCS must also be 1 if this is 1.
#define SD_OCR_UHS_II (1u<<29) // UHS-II Card Status. 0b: Non UHS-II Card, 1b: UHS-II Card.
#define SD_OCR_CCS (1u<<30) // Card Capacity Status. 0b: SDSC, 1b: SDHC or SDXC.
#define SD_OCR_READY (1u<<31) // Busy Status. 0b: On Initialization, 1b: Initialization Complete.
// Argument bits for SEND_OP_COND (ACMD41).
// For voltage bits see OCR register above.
#define SD_ACMD41_S18R (1u<<24) // S18R: Switching to 1.8V Request. 0b: Use current signal voltage, 1b: Switch to 1.8V signal voltage.
#define SD_ACMD41_HO2T (1u<<27) // Over 2TB Supported Host. HCS must also be 1 if this is 1.
#define SD_ACMD41_XPC (1u<<28) // SDXC Power Control. 0b: Power Saving, 1b: Maximum Performance.
#define SD_ACMD41_HCS (1u<<30) // Host Capacity Support. 0b: SDSC Only Host, 1b: SDHC or SDXC Supported.
// 4.3.10 Switch Function Command.
// mode: 0 = check function, 1 = set function
// pwr: Function group 4 Power Limit.
// driver: Function group 3 Driver Strength.
// cmd: Function group 2 Command system.
// acc: Function group 1 Access mode.
#define SD_SWITCH_FUNC_ARG(mode, pwr, driver, cmd, acc) ((mode)<<31 | 0xFFu<<16 | ((pwr)&0xFu)<<12 | ((driver)&0xFu)<<8 | ((cmd)&0xFu)<<4 | ((acc)&0xFu))

240
arm7/source/mmc/sdmmc.h Normal file
View File

@@ -0,0 +1,240 @@
#pragma once
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
// Possible error codes for most of the functions below.
enum
{
SDMMC_ERR_NONE = 0u, // No error.
SDMMC_ERR_INVAL_PARAM = 1u, // Invalid parameter.
SDMMC_ERR_INITIALIZED = 2u, // The device is already initialized.
SDMMC_ERR_GO_IDLE_STATE = 3u, // GO_IDLE_STATE CMD error.
SDMMC_ERR_SEND_IF_COND = 4u, // SEND_IF_COND CMD error.
SDMMC_ERR_IF_COND_RESP = 5u, // IF_COND response pattern mismatch or unsupported voltage.
SDMMC_ERR_SEND_OP_COND = 6u, // SEND_OP_COND CMD error.
SDMMC_ERR_OP_COND_TMOUT = 7u, // Card initialization timeout.
SDMMC_ERR_VOLT_SUPPORT = 8u, // Voltage not supported.
SDMMC_ERR_ALL_SEND_CID = 9u, // ALL_SEND_CID CMD error.
SDMMC_ERR_SET_SEND_RCA = 10u, // SET/SEND_RELATIVE_ADDR CMD error.
SDMMC_ERR_SEND_CSD = 11u, // SEND_CSD CMD error.
SDMMC_ERR_SELECT_CARD = 12u, // SELECT_CARD CMD error.
SDMMC_ERR_LOCKED = 13u, // Card is locked with a password.
SDMMC_ERR_SEND_EXT_CSD = 14u, // SEND_EXT_CSD CMD error.
SDMMC_ERR_SWITCH_HS = 15u, // Error on switching to high speed mode.
SDMMC_ERR_SET_CLR_CD = 16u, // SET_CLR_CARD_DETECT CMD error.
SDMMC_ERR_SET_BUS_WIDTH = 17u, // Error on switching to a different bus width.
SDMMC_ERR_SEND_STATUS = 18u, // SEND_STATUS CMD error.
SDMMC_ERR_CARD_STATUS = 19u, // The card returned an error via its status.
SDMMC_ERR_NO_CARD = 20u, // Card unitialized or not inserted.
SDMMC_ERR_SECT_RW = 21u, // Sector read/write error.
SDMMC_ERR_WRITE_PROT = 22u, // The card is write protected.
SDMMC_ERR_SEND_CMD = 23u, // An error occured while sending a custom CMD via SDMMC_sendCommand().
SDMMC_ERR_SET_BLOCKLEN = 24u, // SET_BLOCKLEN CMD error.
SDMMC_ERR_LOCK_UNLOCK = 25u, // LOCK_UNLOCK CMD error.
SDMMC_ERR_LOCK_UNLOCK_FAIL = 26u, // Lock/unlock operation failed (R1 status).
SDMMC_ERR_SLEEP_AWAKE = 27u // (e)MMC SLEEP_AWAKE CMD error.
};
// (e)MMC/SD device numbers.
enum
{
SDMMC_DEV_CARD = 0u, // SD card/MMC.
SDMMC_DEV_eMMC = 1u, // Builtin eMMC.
// Alias for internal use only.
SDMMC_MAX_DEV_NUM = SDMMC_DEV_eMMC
};
// Bit definition for SdmmcInfo.prot.
// Each bit 1 = protected.
#define SDMMC_PROT_SLIDER (1u) // SD card write protection slider.
#define SDMMC_PROT_TEMP (1u<<1) // Temporary write protection (CSD).
#define SDMMC_PROT_PERM (1u<<2) // Permanent write protection (CSD).
#define SDMMC_PROT_PASSWORD (1u<<3) // (e)MMC/SD card is password protected.
typedef struct
{
u8 type; // 0 = none, 1 = (e)MMC, 2 = High capacity (e)MMC, 3 = SDSC, 4 = SDHC/SDXC, 5 = SDUC.
u8 prot; // See SDMMC_PROT_... defines above for details.
u16 rca; // Relative Card Address (RCA).
u32 sectors; // Size in 512 byte units.
u32 clock; // The current clock frequency in Hz.
u32 cid[4]; // Raw CID without the CRC.
u16 ccc; // (e)MMC/SD command class support from CSD. One per bit starting at 0.
u8 busWidth; // The current bus width used to talk to the card.
} SdmmcInfo;
typedef struct
{
u16 cmd; // Command. T̲h̲e̲ ̲f̲o̲r̲m̲a̲t̲ ̲i̲s̲ ̲c̲o̲n̲t̲r̲o̲l̲l̲e̲r̲ ̲s̲p̲e̲c̲i̲f̲i̲c̲!̲
u32 arg; // Command argument.
u32 resp[4]; // Card response. Length depends on command.
u32 *buf; // In/out data buffer.
u16 blkLen; // Block length. Usually 512.
u16 count; // Number of blkSize blocks to transfer.
} MmcCommand;
// Mode bits for SDMMC_lockUnlock().
#define SDMMC_LK_CLR_PWD (1u<<1) // Clear password.
#define SDMMC_LK_UNLOCK (0u) // Unlock.
#define SDMMC_LK_LOCK (1u<<2) // Lock.
#define SDMMC_LK_ERASE (1u<<3) // Force erase a locked (e)MMC/SD card.
#define SDMMC_LK_COP (1u<<4) // SD cards only. Card Ownership Protection operation.
#ifdef __cplusplus
extern "C"{
#endif
/**
* @brief Initializes a (e)MMC/SD card device.
*
* @param[in] devNum The device to initialize.
*
* @return Returns SDMMC_ERR_NONE on success or
* one of the errors listed above on failure.
*/
u32 pico_SDMMC_init(const u8 devNum);
/**
* @brief Switches a (e)MMC/SD card device between sleep/awake mode.
* Note that SD cards don't have a true sleep mode.
*
* @param[in] devNum The device.
* @param[in] enabled The mode. true to enable sleep and false to wake up.
*
* @return Returns SDMMC_ERR_NONE on success or
* one of the errors listed above on failure.
*/
u32 pico_SDMMC_setSleepMode(const u8 devNum, const bool enabled);
/**
* @brief Deinitializes a (e)MMC/SD card device.
*
* @param[in] devNum The device to deinitialize.
*
* @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure.
*/
u32 pico_SDMMC_deinit(const u8 devNum);
/**
* @brief Manage password protection for a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param[in] mode The mode of operation. See defines above.
* @param[in] pwd The password buffer pointer.
* @param[in] pwdLen The password length. Maximum 32 for password replace. Otherwise 16.
*
* @return Returns SDMMC_ERR_NONE on success or
* one of the errors listed above on failure.
*/
u32 pico_SDMMC_lockUnlock(const u8 devNum, const u8 mode, const u8 *const pwd, const u8 pwdLen);
/**
* @brief Exports the internal device state for fast init (bootloaders ect.).
*
* @param[in] devNum The device state to export.
* @param devOut A pointer to a u8[60] array.
*
* @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM/SDMMC_ERR_NO_CARD on failure.
*/
u32 pico_SDMMC_exportDevState(const u8 devNum, u8 devOut[64]);
/**
* @brief Imports a device state for fast init (bootloaders ect.).
* The state should be validated for example with a checksum.
*
* @param[in] devNum The device state to import.
* @param[in] devIn A pointer to a u8[60] array.
*
* @return Returns SDMMC_ERR_NONE on success or
* SDMMC_ERR_INVAL_PARAM/SDMMC_ERR_NO_CARD/SDMMC_ERR_INITIALIZED on failure.
*/
u32 pico_SDMMC_importDevState(const u8 devNum, const u8 devIn[64]);
/**
* @brief Outputs infos about a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param infoOut A pointer to a SdmmcInfo struct.
*
* @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure.
*/
u32 pico_SDMMC_getDevInfo(const u8 devNum, SdmmcInfo *const infoOut);
/**
* @brief Outputs the CID of a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param cidOut A u32[4] pointer for storing the CID.
*
* @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_INVAL_PARAM on failure.
*/
u32 pico_SDMMC_getCid(const u8 devNum, u32 cidOut[4]);
/**
* @brief Returns the DSTATUS bits of a (e)MMC/SD card device. See FatFs diskio.h.
*
* @param[in] devNum The device.
*
* @return Returns the DSTATUS bits or STA_NODISK | STA_NOINIT on failure.
*/
//u8 SDMMC_getDiskStatus(const u8 devNum);
/**
* @brief Outputs the number of sectors for a (e)MMC/SD card device.
*
* @param[in] devNum The device.
*
* @return Returns the number of sectors or 0 on failure.
*/
u32 pico_SDMMC_getSectors(const u8 devNum);
/**
* @brief Reads one or more sectors from a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param[in] sect The start sector.
* @param buf The output buffer pointer. NULL for DMA.
* @param[in] count The number of sectors to read.
*
* @return Returns SDMMC_ERR_NONE on success or
* one of the errors listed above on failure.
*/
u32 pico_SDMMC_readSectors(const u8 devNum, u32 sect, void *const buf, const u16 count);
/**
* @brief Writes one or more sectors to a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param[in] sect The start sector.
* @param[in] buf The input buffer pointer. NULL for DMA.
* @param[in] count The count
*
* @return Returns SDMMC_ERR_NONE on success or
* one of the errors listed above on failure.
*/
u32 pico_SDMMC_writeSectors(const u8 devNum, u32 sect, const void *const buf, const u16 count);
/**
* @brief Sends a custom command to a (e)MMC/SD card device.
*
* @param[in] devNum The device.
* @param cmd MMC command struct pointer (see above).
*
* @return Returns SDMMC_ERR_NONE on success or SDMMC_ERR_SEND_CMD on failure.
*/
u32 pico_SDMMC_sendCommand(const u8 devNum, MmcCommand *const mmcCmd);
/**
* @brief Returns the R1 card status for a previously failed read/write/custom command.
*
* @param[in] devNum The device.
*
* @return Returns the R1 card status or 0 if there was either no command error or invalid devNum.
*/
u32 pico_SDMMC_getLastR1error(const u8 devNum);
// TODO: TRIM/erase support.
#ifdef __cplusplus
}
#endif

751
arm7/source/mmc/sdmmc.twl.c Normal file
View File

@@ -0,0 +1,751 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
#include <stdalign.h>
#include <string.h>
#include <nds/ndstypes.h>
#include "mmc/sdmmc.h" // Includes types.h.
#include "tmio.h"
#include "mmc/mmc_spec.h"
#include "mmc/sd_spec.h"
// Note on INIT_CLOCK:
// 400 kHz is allowed by the specs. 523 kHz has been proven to work reliably
// for SD cards and eMMC but very early MMCs can fail at init.
// We lose about 5 ms of time on init by using 261 kHz.
#define INIT_CLOCK (400000u) // Maximum 400 kHz.
#define DEFAULT_CLOCK (20000000u) // Maximum 20 MHz.
#define HS_CLOCK (50000000u) // Maximum 50 MHz.
// ARM7 timer clock = controller clock = CPU clock.
// swiDelay() doesn't seem to be cycle accurate meaning
// one cycle is 4 (?) CPU cycles.
#define SLEEP_MS_FUNC(ms) swi_waitByLoop(8378 * (ms))
#define MMC_OCR_VOLT_MASK (MMC_OCR_3_2_3_3V) // We support 3.3V only.
#define SD_OCR_VOLT_MASK (SD_OCR_3_2_3_3V) // We support 3.3V only.
#define SD_IF_COND_ARG (SD_CMD8_VHS_2_7_3_6V | SD_CMD8_CHK_PATT)
#define SD_OP_COND_ARG (SD_OCR_VOLT_MASK) // We support 100 mA and 3.3V. Without HCS bit.
#define MMC_OP_COND_ARG (MMC_OCR_SECT_MODE | MMC_OCR_VOLT_MASK) // We support sector addressing and 3.3V.
// Note: DEV_TYPE_NONE must be zero.
enum
{
// Device types.
DEV_TYPE_NONE = 0u, // Unitialized/no device.
DEV_TYPE_MMC = 1u, // (e)MMC.
DEV_TYPE_MMCHC = 2u, // High capacity (e)MMC (>2 GB).
DEV_TYPE_SDSC = 3u, // SDSC.
DEV_TYPE_SDHC = 4u, // SDHC, SDXC.
DEV_TYPE_SDUC = 5u // SDUC.
};
#define IS_DEV_MMC(dev) ((dev) < DEV_TYPE_SDSC)
typedef struct
{
TmioPort port;
u8 type; // Device type. 0 = none, 1 = (e)MMC, 2 = High capacity (e)MMC,
// 3 = SDSC, 4 = SDHC/SDXC, 5 = SDUC.
u8 prot; // Protection bits. Each bit 1 = protected.
// Bit 0 SD card slider, bit 1 temporary write protection (CSD),
// bit 2 permanent write protection (CSD) and bit 3 password protection.
u16 rca; // Relative Card Address (RCA).
u16 ccc; // (e)MMC/SD command class support from CSD. One per bit starting at 0.
u32 sectors; // Size in 512 byte units.
u32 status; // R1 card status on error. Only updated on errors.
// Cached card infos.
u32 cid[4]; // Raw CID without the CRC.
} SdmmcDev;
static SdmmcDev g_devs[2] = {0};
static u32 sendAppCmd(TmioPort *const port, const u16 cmd, const u32 arg, const u32 rca)
{
// Send app CMD. Same CMD for (e)MMC/SD.
// TODO: Check the APP_CMD bit in the response?
// Linux does it but is it really necessary? SD spec 4.3.9.1.
u32 res = TMIO_sendCommand(port, MMC_APP_CMD, rca);
if(res == 0)
{
res = TMIO_sendCommand(port, cmd, arg);
}
return res;
}
static u32 goIdleState(TmioPort *const port)
{
// Enter idle state before we start the init procedure.
// Works from all but inactive state. CMD is the same for (e)MMC/SD.
// For (e)MMC there are optional init paths:
// arg = 0x00000000 -> GO_IDLE_STATE.
// arg = 0xF0F0F0F0 -> GO_PRE_IDLE_STATE.
// arg = 0xFFFFFFFA -> BOOT_INITIATION.
u32 res = TMIO_sendCommand(port, MMC_GO_IDLE_STATE, 0);
if(res != 0) return SDMMC_ERR_GO_IDLE_STATE;
return SDMMC_ERR_NONE;
}
static u32 initIdleState(TmioPort *const port, u8 *const devTypeOut)
{
// Tell the card what interfaces and voltages we support.
// Only SD v2 and up will respond. (e)MMC won't respond.
u32 res = TMIO_sendCommand(port, SD_SEND_IF_COND, SD_IF_COND_ARG);
if(res == 0)
{
// If the card supports the interfaces and voltages
// it should echo back the check pattern and set the
// support bits.
// Since we don't support anything but the
// standard SD interface at 3.3V we can check
// the whole response at once.
if(port->resp[0] != SD_IF_COND_ARG) return SDMMC_ERR_IF_COND_RESP;
}
else if(res != STATUS_ERR_CMD_TIMEOUT) return SDMMC_ERR_SEND_IF_COND; // Card responded but an error occured.
// Send the first app CMD. If this times out it's (e)MMC.
// If SEND_IF_COND timed out tell the SD card we are a v1 host.
const u32 opCondArg = SD_OP_COND_ARG | (res<<8 ^ SD_ACMD41_HCS); // Caution! Controller specific hack.
u8 devType = DEV_TYPE_SDSC;
res = sendAppCmd(port, SD_APP_SD_SEND_OP_COND, opCondArg, 0);
if(res == STATUS_ERR_CMD_TIMEOUT) devType = DEV_TYPE_MMC; // Continue with (e)MMC init.
else if(res != 0) return SDMMC_ERR_SEND_OP_COND; // Unknown error.
if(devType == DEV_TYPE_MMC) // (e)MMC.
{
// Loop until a timeout of 1 second or the card is ready.
u32 tries = 200;
u32 ocr;
while(1)
{
res = TMIO_sendCommand(port, MMC_SEND_OP_COND, MMC_OP_COND_ARG);
if(res != 0) return SDMMC_ERR_SEND_OP_COND;
ocr = port->resp[0];
if(!--tries || (ocr & MMC_OCR_READY)) break;
// Linux uses 10 ms but the card doesn't become ready faster
// when polling with delay. Use 5 ms as compromise so not much
// time is wasted when the card becomes ready in the middle of the delay.
SLEEP_MS_FUNC(5);
}
// (e)MMC didn't finish init within 1 second.
if(tries == 0) return SDMMC_ERR_OP_COND_TMOUT;
// Check if the (e)MMC supports the voltage and if it's high capacity.
if(!(ocr & MMC_OCR_VOLT_MASK)) return SDMMC_ERR_VOLT_SUPPORT; // Voltage not supported.
if(ocr & MMC_OCR_SECT_MODE) devType = DEV_TYPE_MMCHC; // 7.4.3.
}
else // SD card.
{
// Loop until a timeout of 1 second or the card is ready.
u32 tries = 200;
u32 ocr;
while(1)
{
ocr = port->resp[0];
if(!--tries || (ocr & SD_OCR_READY)) break;
// Linux uses 10 ms but the card doesn't become ready faster
// when polling with delay. Use 5 ms as compromise so not much
// time is wasted when the card becomes ready in the middle of the delay.
SLEEP_MS_FUNC(5);
res = sendAppCmd(port, SD_APP_SD_SEND_OP_COND, opCondArg, 0);
if(res != 0) return SDMMC_ERR_SEND_OP_COND;
}
// SD card didn't finish init within 1 second.
if(tries == 0) return SDMMC_ERR_OP_COND_TMOUT;
if(!(ocr & SD_OCR_VOLT_MASK)) return SDMMC_ERR_VOLT_SUPPORT; // Voltage not supported.
if(ocr & SD_OCR_CCS) devType = DEV_TYPE_SDHC;
}
*devTypeOut = devType;
return SDMMC_ERR_NONE;
}
static u32 initReadyState(SdmmcDev *const dev)
{
TmioPort *const port = &dev->port;
// SD card voltage switch sequence goes here if supported.
// Get the CID. CMD is the same for (e)MMC/SD.
u32 res = TMIO_sendCommand(port, MMC_ALL_SEND_CID, 0);
if(res != 0) return SDMMC_ERR_ALL_SEND_CID;
memcpy(dev->cid, port->resp, 16);
return SDMMC_ERR_NONE;
}
static u32 initIdentState(SdmmcDev *const dev, const u8 devType, u32 *const rcaOut)
{
TmioPort *const port = &dev->port;
u32 rca;
if(IS_DEV_MMC(devType)) // (e)MMC.
{
// Set the RCA of the (e)MMC to 1. 0 is reserved.
// The RCA is in the upper 16 bits of the argument.
rca = 1;
u32 res = TMIO_sendCommand(port, MMC_SET_RELATIVE_ADDR, rca<<16);
if(res != 0) return SDMMC_ERR_SET_SEND_RCA;
}
else // SD card.
{
// Ask the SD card to send its RCA.
u32 res = TMIO_sendCommand(port, SD_SEND_RELATIVE_ADDR, 0);
if(res != 0) return SDMMC_ERR_SET_SEND_RCA;
// RCA in upper 16 bits. Discards lower status bits of R6 response.
rca = port->resp[0]>>16;
}
dev->rca = rca;
*rcaOut = rca<<16;
return SDMMC_ERR_NONE;
}
// Based on UNSTUFF_BITS from linux/drivers/mmc/core/sd.c.
// Extracts up to 32 bits from a u32[4] array.
static inline u32 extractBits(const u32 resp[4], const u32 start, const u32 size)
{
const u32 mask = (size < 32 ? 1u<<size : 0u) - 1;
const u32 off = 3 - (start / 32);
const u32 shift = start & 31u;
u32 res = resp[off]>>shift;
if(size + shift > 32)
res |= resp[off - 1]<<((32u - shift) & 31u);
return res & mask;
}
static void parseCsd(SdmmcDev *const dev, const u8 devType, u8 *const spec_vers_out)
{
// Note: The MSBs are in csd[0].
const u32 *const csd = dev->port.resp;
const u8 structure = extractBits(csd, 126, 2); // [127:126]
*spec_vers_out = extractBits(csd, 122, 4); // [125:122] All 0 for SD cards.
dev->ccc = extractBits(csd, 84, 12); // [95:84]
u32 sectors = 0;
if(structure == 0 || devType == DEV_TYPE_MMC) // structure = 0 is CSD version 1.0.
{
const u32 read_bl_len = extractBits(csd, 80, 4); // [83:80]
const u32 c_size = extractBits(csd, 62, 12); // [73:62]
const u32 c_size_mult = extractBits(csd, 47, 3); // [49:47]
// For SD cards with CSD 1.0 and <=2 GB (e)MMC this calculation is used.
// Note: READ_BL_LEN is at least 9.
// Modified/simplified to calculate sectors instead of bytes.
sectors = (c_size + 1)<<(c_size_mult + 2 + read_bl_len - 9);
}
else if(devType != DEV_TYPE_MMCHC)
{
// SD CSD version 3.0 format.
// For version 2.0 this is 22 bits however the upper bits
// are reserved and zero filled so this is fine.
const u32 c_size = extractBits(csd, 48, 28); // [75:48]
// Calculation for SD cards with CSD >1.0.
sectors = (c_size + 1)<<10;
}
// Else for high capacity (e)MMC the sectors will be read later from EXT_CSD.
dev->sectors = sectors;
// Parse temporary and permanent write protection bits.
u8 prot = extractBits(csd, 12, 1)<<1; // [12:12] Not checked by Linux.
prot |= extractBits(csd, 13, 1)<<2; // [13:13]
dev->prot |= prot;
}
static u32 initStandbyState(SdmmcDev *const dev, const u8 devType, const u32 rca, u8 *const spec_vers_out)
{
TmioPort *const port = &dev->port;
// Get the CSD. CMD is the same for (e)MMC/SD.
u32 res = TMIO_sendCommand(port, MMC_SEND_CSD, rca);
if(res != 0) return SDMMC_ERR_SEND_CSD;
parseCsd(dev, devType, spec_vers_out);
// CMD is the same for (e)MMC/SD however both R1 and R1b responses are used.
// We assume R1b and hope it doesn't time out.
res = TMIO_sendCommand(port, MMC_SELECT_CARD, rca);
if(res != 0) return SDMMC_ERR_SELECT_CARD;
// The SD card spec mentions that we should check the lock bit in the
// response to CMD7 to identify cards requiring a password to unlock.
// Same seems to apply for (e)MMC.
// Same bit for (e)MMC/SD R1 card status.
dev->prot |= (port->resp[0] & MMC_R1_CARD_IS_LOCKED)>>22; // Bit 3.
return SDMMC_ERR_NONE;
}
// TODO: Set the timeout based on clock speed (Tmio uses SDCLK for timeouts).
// The tmio driver sets a sane default but we should calculate it anyway.
static u32 initTranState(SdmmcDev *const dev, const u8 devType, const u32 rca, const u8 spec_vers)
{
TmioPort *const port = &dev->port;
if(IS_DEV_MMC(devType)) // (e)MMC.
{
// EXT_CSD, non-1 bit bus width and HS timing are only
// supported by (e)MMC SPEC_VERS 4.1 and higher.
if(spec_vers > 3) // Version 4.14.24.3 or higher.
{
// The (e)MMC spec says to check the card status after a SWITCH CMD (7.6.1).
// I think we can get away without checking this because support for HS timing
// and 4 bit bus width is mandatory for this spec version. If the card is
// non-standard we will encounter errors on the next CMD anyway.
// Switch to 4 bit bus mode.
const u32 busWidthArg = MMC_SWITCH_ARG(MMC_SWITCH_ACC_WR_BYTE, EXT_CSD_BUS_WIDTH, 1, 0);
u32 res = TMIO_sendCommand(port, MMC_SWITCH, busWidthArg);
if(res != 0) return SDMMC_ERR_SET_BUS_WIDTH;
TMIO_setBusWidth(port, 4);
// We should also check in the EXT_CSD the power budget for the card.
// Nintendo seems to leave it on default (no change).
if(devType == DEV_TYPE_MMCHC)
{
// Note: The EXT_CSD is normally read before touching HS timing and bus width.
// We can take advantage of the faster data transfer with this order.
alignas(4) u8 ext_csd[512];
TMIO_setBuffer(port, (u32*)ext_csd, 1);
res = TMIO_sendCommand(port, MMC_SEND_EXT_CSD, 0);
if(res != 0) return SDMMC_ERR_SEND_EXT_CSD;
// Get sector count from EXT_CSD only if sector addressing is used because
// byte addressed (e)MMC may set sector count to 0.
dev->sectors = ext_csd[EXT_CSD_SEC_COUNT + 3]<<24 | ext_csd[EXT_CSD_SEC_COUNT + 2]<<16 |
ext_csd[EXT_CSD_SEC_COUNT + 1]<<8 | ext_csd[EXT_CSD_SEC_COUNT + 0];
}
}
}
else // SD card.
{
// Remove DAT3 pull-up. Linux doesn't do it but the SD spec recommends it.
u32 res = sendAppCmd(port, SD_APP_SET_CLR_CARD_DETECT, 0, rca); // arg = 0 removes the pull-up.
if(res != 0) return SDMMC_ERR_SET_CLR_CD;
// Switch to 4 bit bus mode.
res = sendAppCmd(port, SD_APP_SET_BUS_WIDTH, 2, rca); // arg = 2 is 4 bit bus width.
if(res != 0) return SDMMC_ERR_SET_BUS_WIDTH;
TMIO_setBusWidth(port, 4);
}
// SD: The description for CMD SET_BLOCKLEN says 512 bytes is the default.
// (e)MMC: The description for READ_BL_LEN (CSD) says 512 bytes is the default.
// So it's not required to set the block length.
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_init(const u8 devNum)
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
SdmmcDev *const dev = &g_devs[devNum];
if(dev->type != DEV_TYPE_NONE) return SDMMC_ERR_INITIALIZED;
// Check SD card write protection slider.
if(devNum == SDMMC_DEV_CARD)
dev->prot = !TMIO_cardWritable();
// Init port, enable clock output and wait 74 clocks.
TmioPort *const port = &dev->port;
TMIO_initPort(port, devNum);
TMIO_powerupSequence(port); // Setup continuous clock and wait 74 clocks.
u32 res = goIdleState(port);
if(res != SDMMC_ERR_NONE) return res;
// (e)MMC/SD now in idle state (idle).
u8 devType;
res = initIdleState(port, &devType);
if(res != SDMMC_ERR_NONE) return res;
// Stop clock at idle, init clock.
TMIO_setClock(port, INIT_CLOCK);
// (e)MMC/SD now in ready state (ready).
res = initReadyState(dev);
if(res != SDMMC_ERR_NONE) return res;
// (e)MMC/SD now in identification state (ident).
u32 rca;
res = initIdentState(dev, devType, &rca);
if(res != SDMMC_ERR_NONE) return res;
// (e)MMC/SD now in stand-by state (stby).
// Maximum at this point would be 20 MHz for (e)MMC and 25 for SD.
// SD: We can increase the clock after end of identification state.
// TODO: eMMC spec section 7.6
// "Until the contents of the CSD register is known by the host,
// the fPP clock rate must remain at fOD. (See Section 12.7 on page 176.)"
// Since the absolute minimum clock rate is 20 MHz and we are in push-pull
// mode already can we cheat and switch to <=20 MHz before getting the CSD?
// Note: This seems to be working just fine in all tests.
TMIO_setClock(port, DEFAULT_CLOCK);
u8 spec_vers;
res = initStandbyState(dev, devType, rca, &spec_vers);
if(res != SDMMC_ERR_NONE) return res;
// (e)MMC/SD now in transfer state (tran).
res = initTranState(dev, devType, rca, spec_vers);
if(res != SDMMC_ERR_NONE) return res;
// Only set dev type on successful init.
dev->type = devType;
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_setSleepMode(const u8 devNum, const bool enabled)
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
SdmmcDev *const dev = &g_devs[devNum];
TmioPort *const port = &dev->port;
const u32 rca = (u32)dev->rca<<16;
const u8 devType = dev->type;
if(enabled)
{
// Deselect card to go back to stand-by state.
// CMD is the same for (e)MMC/SD.
u32 res = TMIO_sendCommand(port, MMC_DESELECT_CARD, 0);
if(res != 0) return SDMMC_ERR_SELECT_CARD;
// Only (e)MMC can go into true sleep mode.
if(IS_DEV_MMC(devType))
{
// Switch (e)MMC into sleep mode.
res = TMIO_sendCommand(port, MMC_SLEEP_AWAKE, rca | 1u<<15);
if(res != 0) return SDMMC_ERR_SLEEP_AWAKE;
// TODO: Power down eMMC. This is confirmed working on 3DS.
}
}
else
{
if(IS_DEV_MMC(devType))
{
// TODO: Power up eMMC. This is confirmed working on 3DS.
// Wake (e)MMC up from sleep mode.
u32 res = TMIO_sendCommand(port, MMC_SLEEP_AWAKE, rca);
if(res != 0) return SDMMC_ERR_SLEEP_AWAKE;
}
// Select card to go back to transfer state.
// CMD is the same for (e)MMC/SD.
u32 res = TMIO_sendCommand(port, MMC_SELECT_CARD, rca);
if(res != 0) return SDMMC_ERR_SELECT_CARD;
}
return SDMMC_ERR_NONE;
}
// TODO: Is there any "best practice" way of deinitializing cards?
// Kick the card back into idle state maybe?
// Linux seems to deselect cards on "suspend".
u32 pico_SDMMC_deinit(const u8 devNum)
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
memset(&g_devs[devNum], 0, sizeof(SdmmcDev));
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_lockUnlock(const u8 devNum, const u8 mode, const u8 *const pwd, const u8 pwdLen)
{
// Password length is maximum 16 bytes except when replacing a password.
if(devNum > SDMMC_MAX_DEV_NUM || pwdLen > 32) return SDMMC_ERR_INVAL_PARAM;
// Set block length on (e)MMC/SD side and host.
// Same CMD for (e)MMC/SD.
SdmmcDev *const dev = &g_devs[devNum];
TmioPort *const port = &dev->port;
const u32 blockLen = (mode != SDMMC_LK_ERASE ? 2 + pwdLen : 1);
u32 res = TMIO_sendCommand(port, MMC_SET_BLOCKLEN, blockLen);
if(res != 0) return SDMMC_ERR_SET_BLOCKLEN;
TMIO_setBlockLen(port, blockLen);
do
{
// Prepare lock/unlock data block.
alignas(4) u8 buf[36] = {0}; // Size multiple of 4 (TMIO driver limitation).
buf[0] = mode;
buf[1] = pwdLen;
memcpy(&buf[2], pwd, pwdLen);
// Dirty hack to extend the data timeout to a bit over 4 minutes with TMIO controller.
// We need 3 minutes minimum for erase.
const u16 clk_ctrl_backup = port->sd_clk_ctrl;
TMIO_setClock(port, 130913);
// Note: Command class 7 support is mandatory for (e)MMC. Not for SD cards until 2.00.
// Same CMD for (e)MMC/SD.
TMIO_setBuffer(port, (u32*)buf, 1);
res = TMIO_sendCommand(port, MMC_LOCK_UNLOCK, 0);
port->sd_clk_ctrl = clk_ctrl_backup; // Undo the data timeout hack.
if(res != 0)
{
res = SDMMC_ERR_LOCK_UNLOCK;
break;
}
// Restore default block length and get the R1 status.
// Same CMD for (e)MMC/SD.
res = TMIO_sendCommand(port, MMC_SET_BLOCKLEN, 512);
if(res != 0)
{
res = SDMMC_ERR_SET_BLOCKLEN;
break;
}
TMIO_setBlockLen(port, 512);
// Check if lock/unlock worked.
// Same bit for (e)MMC/SD R1 card status.
const u32 status = port->resp[0];
if(status & MMC_R1_LOCK_UNLOCK_FAILED)
res = SDMMC_ERR_LOCK_UNLOCK_FAIL;
// Update lock status.
const u8 prot = dev->prot & ~(1u<<3);
dev->prot = prot | (status>>22 & 1u<<3);
} while(0);
return res;
}
// People should not mess with the state which is the reason
// why the struct is not exposed directly.
static_assert(sizeof(SdmmcDev) == 64, "Wrong SDMMC dev export/import size.");
u32 pico_SDMMC_exportDevState(const u8 devNum, u8 devOut[64])
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
// Check if the device is initialized.
const SdmmcDev *const dev = &g_devs[devNum];
if(dev->type == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD;
memcpy(devOut, dev, 64);
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_importDevState(const u8 devNum, const u8 devIn[64])
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
// Make sure there is a card inserted.
if(devNum == SDMMC_DEV_CARD && !TMIO_cardDetected()) return SDMMC_ERR_NO_CARD;
// Check if the device is initialized.
SdmmcDev *const dev = &g_devs[devNum];
if(dev->type != DEV_TYPE_NONE) return SDMMC_ERR_INITIALIZED;
memcpy(dev, devIn, 64);
// Update write protection slider state just in case.
dev->prot |= !TMIO_cardWritable();
return SDMMC_ERR_NONE;
}
// TODO: Less controller dependent code.
u32 pico_SDMMC_getDevInfo(const u8 devNum, SdmmcInfo *const infoOut)
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
const SdmmcDev *const dev = &g_devs[devNum];
const TmioPort *const port = &dev->port;
infoOut->type = dev->type;
infoOut->prot = dev->prot;
infoOut->rca = dev->rca;
infoOut->sectors = dev->sectors;
const u32 clkSetting = port->sd_clk_ctrl & 0xFFu;
infoOut->clock = TMIO_HCLK / (clkSetting ? clkSetting<<2 : 2);
memcpy(infoOut->cid, dev->cid, 16);
infoOut->ccc = dev->ccc;
infoOut->busWidth = (port->sd_option & OPTION_BUS_WIDTH1 ? 1 : 4);
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_getCid(const u8 devNum, u32 cidOut[4])
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
if(cidOut != NULL) memcpy(cidOut, g_devs[devNum].cid, 16);
return SDMMC_ERR_NONE;
}
/*#include "fatfs/ff.h" // Needed for the "byte" type used in diskio.h.
#include "fatfs/diskio.h"
u8 SDMMC_getDiskStatus(const u8 devNum)
{
if(devNum > SDMMC_MAX_DEV_NUM) return STA_NODISK | STA_NOINIT;
u8 status = 0;
if(devNum == SDMMC_DEV_CARD)
status = (TMIO_cardDetected() == true ? 0 : STA_NODISK | STA_NOINIT);
const SdmmcDev *const dev = &g_devs[devNum];
status |= (dev->prot != 0 ? STA_PROTECT : 0);
if(dev->type == DEV_TYPE_NONE)
status |= STA_NOINIT;*/
// "Not valid if STA_NODISK is set."
/*if(status & STA_NODISK)
status &= ~STA_PROTECT;*/
// return status;
//}
u32 pico_SDMMC_getSectors(const u8 devNum)
{
if(devNum > SDMMC_MAX_DEV_NUM) return 0;
return g_devs[devNum].sectors;
}
static u32 updateStatus(SdmmcDev *const dev, const bool stopTransmission)
{
TmioPort *const port = &dev->port;
// MMC_STOP_TRANSMISSION: Same CMD for (e)MMC/SD. Relies on the driver returning a proper response.
// MMC_SEND_STATUS: Same CMD for (e)MMC/SD but the argument format differs slightly.
u32 res;
if(stopTransmission) res = TMIO_sendCommand(port, MMC_STOP_TRANSMISSION, 0);
else res = TMIO_sendCommand(port, MMC_SEND_STATUS, (u32)dev->rca<<16);
dev->status = (res == 0 ? port->resp[0] : 0); // Don't update the status with stale data.
return res;
}
// Note: On multi-block read from the last 2 sectors there are no errors reported by the controller
// however the R1 card status may report ADDRESS_OUT_OF_RANGE on next(?) status read.
// This error is normal for (e)MMC and can be ignored.
u32 pico_SDMMC_readSectors(const u8 devNum, u32 sect, void *const buf, const u16 count)
{
if(devNum > SDMMC_MAX_DEV_NUM || count == 0) return SDMMC_ERR_INVAL_PARAM;
// Check if the device is initialized.
SdmmcDev *const dev = &g_devs[devNum];
const u8 devType = dev->type;
if(devType == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD;
// Set destination buffer and sector count.
TmioPort *const port = &dev->port;
TMIO_setBuffer(port, buf, count);
// Read a single 512 bytes block. Same CMD for (e)MMC/SD.
// Read multiple 512 bytes blocks. Same CMD for (e)MMC/SD.
const u16 readCmd = (count == 1 ? MMC_READ_SINGLE_BLOCK : MMC_READ_MULTIPLE_BLOCK);
if(devType == DEV_TYPE_MMC || devType == DEV_TYPE_SDSC) sect *= 512; // Byte addressing.
u32 res = TMIO_sendCommand(port, readCmd, sect);
if(res != 0)
{
// On error in the middle of multi-block reads the card will be stuck
// in data state and we need to send STOP_TRANSMISSION to bring it
// back to tran state.
// Otherwise for single-block reads just update the status.
updateStatus(dev, count > 1);
return SDMMC_ERR_SECT_RW;
}
return SDMMC_ERR_NONE;
}
// Note: On multi-block write to the last 2 sectors there are no errors reported by the controller
// however the R1 card status may report ADDRESS_OUT_OF_RANGE on next(?) status read.
// This error is normal for (e)MMC and can be ignored.
u32 pico_SDMMC_writeSectors(const u8 devNum, u32 sect, const void *const buf, const u16 count)
{
if(devNum > SDMMC_MAX_DEV_NUM || count == 0) return SDMMC_ERR_INVAL_PARAM;
// Check if the device is initialized.
SdmmcDev *const dev = &g_devs[devNum];
const u8 devType = dev->type;
if(devType == DEV_TYPE_NONE) return SDMMC_ERR_NO_CARD;
// Check if the device is write protected.
if(dev->prot != 0) return SDMMC_ERR_WRITE_PROT;
// Set source buffer and sector count.
TmioPort *const port = &dev->port;
TMIO_setBuffer(port, (void*)buf, count);
// Write a single 512 bytes block. Same CMD for (e)MMC/SD.
// Write multiple 512 bytes blocks. Same CMD for (e)MMC/SD.
const u16 writeCmd = (count == 1 ? MMC_WRITE_BLOCK : MMC_WRITE_MULTIPLE_BLOCK);
if(devType == DEV_TYPE_MMC || devType == DEV_TYPE_SDSC) sect *= 512; // Byte addressing.
const u32 res = TMIO_sendCommand(port, writeCmd, sect);
if(res != 0)
{
// On error in the middle of multi-block writes the card will be stuck
// in data state and we need to send STOP_TRANSMISSION to bring it
// back to tran state.
// Otherwise for single-block writes just update the status.
updateStatus(dev, count > 1);
return SDMMC_ERR_SECT_RW;
}
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_sendCommand(const u8 devNum, MmcCommand *const mmcCmd)
{
if(devNum > SDMMC_MAX_DEV_NUM) return SDMMC_ERR_INVAL_PARAM;
SdmmcDev *const dev = &g_devs[devNum];
TmioPort *const port = &dev->port;
TMIO_setBlockLen(port, mmcCmd->blkLen);
TMIO_setBuffer(port, mmcCmd->buf, mmcCmd->count);
const u32 res = TMIO_sendCommand(port, mmcCmd->cmd, mmcCmd->arg);
TMIO_setBlockLen(port, 512); // Restore default block length.
if(res != 0)
{
updateStatus(dev, false);
return SDMMC_ERR_SEND_CMD;
}
memcpy(mmcCmd->resp, port->resp, 16);
return SDMMC_ERR_NONE;
}
u32 pico_SDMMC_getLastR1error(const u8 devNum)
{
if(devNum > SDMMC_MAX_DEV_NUM) return 0;
SdmmcDev *const dev = &g_devs[devNum];
const u32 status = dev->status;
dev->status = 0;
return status;
}

414
arm7/source/mmc/tmio.h Normal file
View File

@@ -0,0 +1,414 @@
#pragma once
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
#include <assert.h>
#include <stddef.h>
#include <nds/ndstypes.h>
#include <libtwl/sys/swi.h>
#include <libtwl/rtos/rtosIrq.h>
#include <libtwl/rtos/rtosEvent.h>
// For simplicity we will name the accessible 2 controllers 1 and 2.
// The real controller number is in the comment.
#define TMIO1_REGS_BASE (0x04004800u) // Controller 1.
#define TMIO2_REGS_BASE (0x04004A00u) // Controller 2.
#define TMIO_HCLK (33513982u) // In Hz.
typedef struct
{
vu16 sd_cmd; // 0x000
vu16 sd_portsel; // 0x002
vu32 sd_arg; // 0x004 SD_ARG0 and SD_ARG1 combined.
vu16 sd_stop; // 0x008
vu16 sd_blockcount; // 0x00A
const vu32 sd_resp[4]; // 0x00C SD_RESP0-7 16 bit reg pairs combined.
vu32 sd_status; // 0x01C SD_STATUS1 and SD_STATUS2 combined.
vu32 sd_status_mask; // 0x020 SD_STATUS1_MASK and SD_STATUS2_MASK combined.
vu16 sd_clk_ctrl; // 0x024
vu16 sd_blocklen; // 0x026
vu16 sd_option; // 0x028 Card detect timer, data timeout and bus width.
u8 _0x2a[2];
const vu32 sd_err_status; // 0x02C SD_ERR_STATUS1 and SD_ERR_STATUS2 combined.
vu16 sd_fifo; // 0x030
u8 _0x32[2];
vu16 sdio_mode; // 0x034
vu16 sdio_status; // 0x036
vu16 sdio_status_mask; // 0x038
u8 _0x3a[0x9e];
vu16 dma_ext_mode; // 0x0D8
u8 _0xda[6];
vu16 soft_rst; // 0x0E0
const vu16 revision; // 0x0E2 Controller version/revision?
u8 _0xe4[0xe];
vu16 unkF2; // 0x0F2 Power related? Default 0. Other values do nothing?
vu16 ext_sdio_irq; // 0x0F4 Port 1/2/3 SDIO IRQ control.
const vu16 ext_wrprot; // 0x0F6 Apparently for eMMC.
vu16 ext_cdet; // 0x0F8 Card detect status.
vu16 ext_cdet_dat3; // 0x0FA DAT3 card detect status.
vu16 ext_cdet_mask; // 0x0FC Card detect mask (IRQ).
vu16 ext_cdet_dat3_mask; // 0x0FE DAT3 card detect mask (IRQ).
vu16 sd_fifo32_cnt; // 0x100
u8 _0x102[2];
vu16 sd_blocklen32; // 0x104
u8 _0x106[2];
vu16 sd_blockcount32; // 0x108
u8 _0x10a[2];
vu32 sd_fifo32; // 0x10C Note: This is in the FIFO region on ARM11 (3DS).
} Tmio;
#ifdef __cplusplus
extern "C"
{
#endif
static_assert(offsetof(Tmio, sd_fifo32) == 0x10C, "Error: Member sd_fifo32 of Tmio is not at offset 0x10C!");
__attribute__((always_inline)) static inline Tmio* getTmioRegs(const u8 controller)
{
return (controller == 0 ? (Tmio*)TMIO1_REGS_BASE : (Tmio*)TMIO2_REGS_BASE);
}
__attribute__((always_inline)) static inline vu32* getTmioFifo(Tmio *const regs)
{
return &regs->sd_fifo32;
}
#ifdef __cplusplus
}
#endif
// REG_SD_CMD
// Auto response supported commands:
// CMD0, CMD2, CMD3 (only SD?), CMD7 (only select?), CMD9, CMD10, CMD12, CMD13,
// CMD16, CMD17, CMD18, CMD25, CMD28, CMD55, ACMD6, ACMD23, ACMD42, ACMD51.
//
// When using auto response leave bits 11-13 unset (zero).
// Bit 0-5 command index.
#define CMD_ACMD (1u<<6) // Application command.
#define CMD_RESP_AUTO (0u) // Response type auto. Only works with certain commands.
#define CMD_RESP_NONE (3u<<8) // Response type none.
#define CMD_RESP_R1 (4u<<8) // Response type R1 48 bit.
#define CMD_RESP_R5 (CMD_RESP_R1) // Response type R5 48 bit.
#define CMD_RESP_R6 (CMD_RESP_R1) // Response type R6 48 bit.
#define CMD_RESP_R7 (CMD_RESP_R1) // Response type R7 48 bit.
#define CMD_RESP_R1b (5u<<8) // Response type R1b 48 bit + busy.
#define CMD_RESP_R5b (CMD_RESP_R1b) // Response type R5b 48 bit + busy.
#define CMD_RESP_R2 (6u<<8) // Response type R2 136 bit.
#define CMD_RESP_R3 (7u<<8) // Response type R3 48 bit OCR without CRC.
#define CMD_RESP_R4 (CMD_RESP_R3) // Response type R4 48 bit OCR without CRC.
#define CMD_RESP_MASK (CMD_RESP_R3)
#define CMD_DATA_EN (1u<<11) // Data transfer enable.
#define CMD_DATA_R (1u<<12) // Data transfer direction read.
#define CMD_DATA_W (0u) // Data transfer direction write.
#define CMD_MULTI_DATA (1u<<13) // Multi block transfer (auto STOP_TRANSMISSION).
#define CMD_SEC_SDIO (1u<<14) // Security/SDIO command.
// REG_SD_PORTSEL
#define PORTSEL_P0 (0u) // Controller port 0.
#define PORTSEL_P1 (1u) // Controller port 1.
#define PORTSEL_P2 (2u) // Controller port 2.
#define PORTSEL_P3 (3u) // Controller port 3.
#define PORTSEL_MASK (PORTSEL_P3)
// Bit 8-9 number of supported ports?
#define PORTSEL_UNK10 (1u<<10) // Unknown writable bit 10?
// REG_SD_STOP
#define STOP_STOP (1u) // Abort data transfer and send STOP_TRANSMISSION CMD.
#define STOP_AUTO_STOP (1u<<8) // Automatically send STOP_TRANSMISSION on multi-block transfer end.
// REG_SD_STATUS1/2 Write 0 to acknowledge a bit.
// REG_SD_STATUS1/2_MASK (M) = Maskable bit. 1 = disabled.
// Unmaskable bits act as status only, don't trigger IRQs and can't be acknowledged.
#define STATUS_RESP_END (1u) // (M) Response end.
#define STATUS_DATA_END (1u<<2) // (M) Data transfer end (triggers after last block).
#define STATUS_REMOVE (1u<<3) // (M) Card got removed.
#define STATUS_INSERT (1u<<4) // (M) Card got inserted. Set at the same time as DETECT.
#define STATUS_DETECT (1u<<5) // Card detect status (SD_OPTION detection timer). 1 = inserted.
#define STATUS_NO_WRPROT (1u<<7) // Write protection slider unlocked (low).
#define STATUS_DAT3_REMOVE (1u<<8) // (M) Card DAT3 got removed (low).
#define STATUS_DAT3_INSERT (1u<<9) // (M) Card DAT3 got inserted (high).
#define STATUS_DAT3_DETECT (1u<<10) // Card DAT3 status. 1 = inserted.
#define STATUS_ERR_CMD_IDX (1u<<16) // (M) Bad CMD index in response.
#define STATUS_ERR_CRC (1u<<17) // (M) Bad CRC in response.
#define STATUS_ERR_STOP_BIT (1u<<18) // (M) Stop bit error. Failed to recognize response frame end?
#define STATUS_ERR_DATA_TIMEOUT (1u<<19) // (M) Response data timeout.
#define STATUS_ERR_RX_OVERF (1u<<20) // (M) Receive FIFO overflow.
#define STATUS_ERR_TX_UNDERF (1u<<21) // (M) Send FIFO underflow.
#define STATUS_ERR_CMD_TIMEOUT (1u<<22) // (M) Response start bit timeout.
#define STATUS_SD_BUSY (1u<<23) // SD card signals busy if this bit is 0 (DAT0 held low).
#define STATUS_RX_RDY (1u<<24) // (M) FIFO ready for read.
#define STATUS_TX_REQ (1u<<25) // (M) FIFO write request.
// Bit 27 is maskable. Purpose unknown.
// Bit 29 exists (not maskable). Signals when clock divider changes are allowed?
#define STATUS_CMD_BUSY (1u<<30) // Command register busy.
#define STATUS_ERR_ILL_ACC (1u<<31) // (M) Illegal access error. TODO: What does that mean?
#define STATUS_MASK_ALL (0xFFFFFFFFu)
#define STATUS_MASK_DEFAULT ((1u<<27) | STATUS_TX_REQ | STATUS_RX_RDY | \
STATUS_DAT3_INSERT | STATUS_DAT3_REMOVE)
#define STATUS_MASK_ERR (STATUS_ERR_ILL_ACC | STATUS_ERR_CMD_TIMEOUT | STATUS_ERR_TX_UNDERF | \
STATUS_ERR_RX_OVERF | STATUS_ERR_DATA_TIMEOUT | STATUS_ERR_STOP_BIT | \
STATUS_ERR_CRC | STATUS_ERR_CMD_IDX)
// REG_SD_CLK_CTRL
#define SD_CLK_DIV_2 (0u) // Clock divider 2.
#define SD_CLK_DIV_4 (1u) // Clock divider 4.
#define SD_CLK_DIV_8 (1u<<1) // Clock divider 8.
#define SD_CLK_DIV_16 (1u<<2) // Clock divider 16.
#define SD_CLK_DIV_32 (1u<<3) // Clock divider 32.
#define SD_CLK_DIV_64 (1u<<4) // Clock divider 64.
#define SD_CLK_DIV_128 (1u<<5) // Clock divider 128.
#define SD_CLK_DIV_256 (1u<<6) // Clock divider 256.
#define SD_CLK_DIV_512 (1u<<7) // Clock divider 512.
#define SD_CLK_EN (1u<<8) // Clock enable.
#define SD_CLK_PWR_SAVE (1u<<9) // Disables clock on idle.
// Bit 10 is writable... at least according to gbatek (can't confirm). Purpose unknown.
// Outputs the matching divider for clk.
// Shift the output right by 2 to get the value for REG_SD_CLK_CTRL.
#define TMIO_CLK2DIV(clk) \
({ \
u32 __shift = 1; \
while((clk) < TMIO_HCLK>>__shift) ++__shift; \
1u<<__shift; \
})
// Clock off by default.
// Nearest possible for 400 kHz is 261.827984375 kHz.
#define SD_CLK_DEFAULT (TMIO_CLK2DIV(400000)>>2)
// REG_SD_OPTION
// Note on card detection time:
// The card detection timer starts only on inserting cards (including cold boot with inserted card)
// and when mapping ports between controllers. Card power doesn't have any effect on the timer.
//
// Bit 0-3 card detect timer 0x400<<x HCLKs. 0xF timer test (0x100 HCLKs).
// Bit 4-7 data timeout 0x2000<<x SDCLKs. 0xF timeout test (0x100 SDCLKs).
#define OPTION_UNK14 (1u<<14) // "no C2 module" What the fuck is a C2 module?
#define OPTION_BUS_WIDTH4 (0u) // 4 bit bus width.
#define OPTION_BUS_WIDTH1 (1u<<15) // 1 bit bus width.
// Card detect time: 0x400<<8 / 33513982 = 0.007821929 seconds.
// Data timeout: 0x2000<<11 / (33513982 / 2) = 1.001206959 seconds.
#define OPTION_DEFAULT_TIMINGS (11u<<4 | 8u)
// REG_SD_ERR_STATUS1/2 Write 0 to acknowledge a bit.
// TODO: Are all of these actually supported on this controller?
#define ERR_RESP_CMD_IDX (1u) // Manual command index error in response.
#define ERR_RESP_CMD12_IDX (1u<<1) // Auto command index error in response.
#define ERR_RESP_STOP_BIT (1u<<2) // Manual command response stop bit error.
#define ERR_RESP_STOP_BIT_CMD12 (1u<<3) // Auto command response stop bit error.
#define ERR_STOP_BIT_DATA_READ (1u<<4) // Stop bit error in read data.
#define ERR_STOP_BIT_WR_CRC (1u<<5) // Stop bit error for write CRC status. What the hell does that mean?
#define ERR_CMD_RESP_CRC (1u<<8) // Manual command response CRC error.
#define ERR_CMD12_RESP_CRC (1u<<9) // Auto command response CRC error.
#define ERR_DATA_READ_CRC (1u<<10) // CRC error for read data.
#define ERR_WR_CRC_STAT (1u<<11) // "CRC error for Write CRC status for a write command". What the hell does that mean?
// Bit 13 always 1.
#define ERR_CMD_RESP_TMOUT (1u<<16) // Manual command response timeout.
#define ERR_CMD12_RESP_TMOUT (1u<<17) // Auto command response timeout.
// TODO: Add the correct remaining ones.
// REG_SDIO_MODE
#define SDIO_MODE_SDIO_IRQ_EN (1u) // SDIO IRQ enable (DAT1 low).
#define SDIO_MODE_UNK2_EN (1u<<2) // IRQ on "read wait" requests?
#define SDIO_MODE_UNK8 (1u<<8) // Aborts command and data transfer?
#define SDIO_MODE_UNK9 (1u<<9) // Aborts command but not data transfer? CMD52 related.
// REG_SDIO_STATUS Write 0 to acknowledge a bit.
// REG_SDIO_STATUS_MASK (M) = Maskable bit. 1 = disabled.
#define SDIO_STATUS_SDIO_IRQ (1u) // (M) SDIO IRQ (DAT1 low).
#define SDIO_STATUS_UNK1_IRQ (1u<<1) // (M) IRQ once CMD52 can be used after abort?
#define SDIO_STATUS_UNK2_IRQ (1u<<2) // (M) Related to SDIO_MODE_UNK2_EN?
#define SDIO_STATUS_UNK14_IRQ (1u<<14) // (M) Related to SDIO_MODE_UNK9?
#define SDIO_STATUS_UNK15_IRQ (1u<<15) // (M) Related to SDIO_MODE_UNK2_EN?
#define SDIO_STATUS_MASK_ALL (0xFFFFu)
// REG_DMA_EXT_MODE
#define DMA_EXT_CPU_MODE (0u) // Disables DMA requests. Actually also turns off the 32 bit FIFO.
#define DMA_EXT_DMA_MODE (1u<<1) // Enables DMA requests.
#define DMA_EXT_UNK5 (1u<<5) // "Buffer status mode"?
// REG_SOFT_RST
#define SOFT_RST_RST (0u) // Reset.
#define SOFT_RST_NORST (1u) // No reset.
// REG_EXT_SDIO_IRQ
#define EXT_SDIO_IRQ_P1 (1u) // Port 1 SDIO IRQ (DAT1 low). Write 0 to acknowledge.
#define EXT_SDIO_IRQ_P2 (1u<<1) // Port 2 SDIO IRQ (DAT1 low). Write 0 to acknowledge.
#define EXT_SDIO_IRQ_P3 (1u<<2) // Port 3 SDIO IRQ (DAT1 low). Write 0 to acknowledge.
#define EXT_SDIO_IRQ_P1_EN (1u<<4) // Port 1 SDIO IRQ enable (controller).
#define EXT_SDIO_IRQ_P2_EN (1u<<5) // Port 2 SDIO IRQ enable (controller).
#define EXT_SDIO_IRQ_P3_EN (1u<<6) // Port 3 SDIO IRQ enable (controller).
#define EXT_SDIO_IRQ_P1_MASK (1u<<8) // Port 1 SDIO IRQ mask. 1 = disable IRQ (CPU).
#define EXT_SDIO_IRQ_P2_MASK (1u<<9) // Port 2 SDIO IRQ mask. 1 = disable IRQ (CPU).
#define EXT_SDIO_IRQ_P3_MASK (1u<<10) // Port 3 SDIO IRQ mask. 1 = disable IRQ (CPU).
#define EXT_SDIO_IRQ_MASK_ALL (EXT_SDIO_IRQ_P3_MASK | EXT_SDIO_IRQ_P2_MASK | EXT_SDIO_IRQ_P1_MASK)
// REG_EXT_WRPROT Each bit 1 = write protected unlike SD_STATUS.
#define EXT_WRPROT_P1 (1u)
#define EXT_WRPROT_P2 (1u<<1)
#define EXT_WRPROT_P3 (1u<<2)
// REG_EXT_CDET Acknowledgeable?
// REG_EXT_CDET_MASK (M) = Maskable bit. 1 = disabled (no IRQ).
#define EXT_CDET_P1_REMOVE (1u) // (M) Port 1 card got removed.
#define EXT_CDET_P1_INSERT (1u<<1) // (M) Port 1 card got inserted. TODO: With detection timer?
#define EXT_CDET_P1_DETECT (1u<<2) // Port 1 card detect status. 1 = inserted. TODO: With detection timer?
#define EXT_CDET_P2_REMOVE (1u<<3) // (M) Port 2 card got removed.
#define EXT_CDET_P2_INSERT (1u<<4) // (M) Port 2 card got inserted. TODO: With detection timer?
#define EXT_CDET_P2_DETECT (1u<<5) // Port 2 card detect status. 1 = inserted. TODO: With detection timer?
#define EXT_CDET_P3_REMOVE (1u<<6) // (M) Port 3 card got removed.
#define EXT_CDET_P3_INSERT (1u<<7) // (M) Port 3 card got inserted. TODO: With detection timer?
#define EXT_CDET_P3_DETECT (1u<<8) // Port 3 card detect status. 1 = inserted. TODO: With detection timer?
#define EXT_CDET_MASK_ALL (0xFFFFu)
// REG_EXT_CDET_DAT3 Acknowledgeable?
// REG_EXT_CDET_DAT3_MASK (M) = Maskable bit. 1 = disabled (no IRQ).
#define EXT_CDET_DAT3_P1_REMOVE (1u) // (M) Port 1 card DAT3 got removed (low).
#define EXT_CDET_DAT3_P1_INSERT (1u<<1) // (M) Port 1 card DAT3 got inserted (high).
#define EXT_CDET_DAT3_P1_DETECT (1u<<2) // Port 1 card DAT3 status. 1 = inserted.
#define EXT_CDET_DAT3_P2_REMOVE (1u<<3) // (M) Port 2 card DAT3 got removed (low).
#define EXT_CDET_DAT3_P2_INSERT (1u<<4) // (M) Port 2 card DAT3 got inserted (high).
#define EXT_CDET_DAT3_P2_DETECT (1u<<5) // Port 2 card DAT3 status. 1 = inserted.
#define EXT_CDET_DAT3_P3_REMOVE (1u<<6) // (M) Port 3 card DAT3 got removed (low).
#define EXT_CDET_DAT3_P3_INSERT (1u<<7) // (M) Port 3 card DAT3 got inserted (high).
#define EXT_CDET_DAT3_P3_DETECT (1u<<8) // Port 3 card DAT3 status. 1 = inserted.
#define EXT_CDET_DAT3_MASK_ALL (0xFFFFu)
// REG_SD_FIFO32_CNT
// Bit 0 unknown, non-writable.
#define FIFO32_EN (1u<<1) // Enables the 32 bit FIFO.
#define FIFO32_FULL (1u<<8) // FIFO is full.
#define FIFO32_NOT_EMPTY (1u<<9) // FIFO is not empty. Inverted bit. 0 means empty.
#define FIFO32_CLEAR (1u<<10) // Clears the FIFO.
#define FIFO32_FULL_IE (1u<<11) // FIFO full IRQ enable.
#define FIFO32_NOT_EMPTY_IE (1u<<12) // FIFO not empty IRQ enable.
typedef struct
{
u8 portNum;
u16 sd_clk_ctrl;
u16 sd_blocklen; // Also sd_blocklen32.
u16 sd_option;
void *buf;
u16 blocks;
u32 resp[4]; // Little endian, MSB first.
} TmioPort;
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Initializes the tmio driver.
*/
void TMIO_init(void);
/**
* @brief Deinitializes the tmio driver.
*/
void TMIO_deinit(void);
/**
* @brief Initializes a tmio port to defaults.
*
* @param port A pointer to the port struct.
* @param[in] portNum The port number.
*/
void TMIO_initPort(TmioPort *const port, const u8 portNum);
/**
* @brief Checks if a MMC/SD card is inserted.
*
* @return Returns true if a card is inserted.
*/
bool TMIO_cardDetected(void);
/**
* @brief Checks if the write protect slider is set to locked.
*
* @return Returns true if the card is unlocked.
*/
bool TMIO_cardWritable(void);
/**
* @brief Handles the device specific powerup sequence including the 74 clocks.
*
* @param port A pointer to the port struct.
*/
void TMIO_powerupSequence(TmioPort *const port);
/**
* @brief Sends a command.
*
* @param port A pointer to the port struct.
* @param[in] cmd The command.
* @param[in] arg The argument for the command.
*
* @return Returns 0 on success otherwise see REG_SD_STATUS1/2 bits.
*/
u32 TMIO_sendCommand(TmioPort *const port, const u16 cmd, const u32 arg);
/**
* @brief Sets the clock for a tmio port.
*
* @param port A pointer to the port struct.
* @param[in] clk The target clock in Hz.
*/
__attribute__((always_inline)) static inline void TMIO_setClock(TmioPort *const port, const u32 clk)
{
port->sd_clk_ctrl = SD_CLK_PWR_SAVE | SD_CLK_EN | TMIO_CLK2DIV(clk)>>2;
}
/**
* @brief Sets the transfer block length for a tmio port.
*
* @param port A pointer to the port struct.
* @param[in] blockLen The block length. Caution: Provide a buffer with multiple of 4 size regardless of block length.
*/
__attribute__((always_inline)) static inline void TMIO_setBlockLen(TmioPort *const port, u16 blockLen)
{
if(blockLen > 512) blockLen = 512;
port->sd_blocklen = blockLen;
}
/**
* @brief Sets the bus width for a tmio port.
*
* @param port A pointer to the port struct.
* @param[in] width The bus width.
*/
__attribute__((always_inline)) static inline void TMIO_setBusWidth(TmioPort *const port, const u8 width)
{
port->sd_option = (width == 4 ? OPTION_BUS_WIDTH4 : OPTION_BUS_WIDTH1) |
OPTION_UNK14 | OPTION_DEFAULT_TIMINGS;
}
/**
* @brief Sets a transfer buffer for a tmio port.
*
* @param port A pointer to the port struct.
* @param buf The buffer pointer.
* @param[in] blocks The number of blocks to transfer.
*/
__attribute__((always_inline)) static inline void TMIO_setBuffer(TmioPort *const port, void *buf, const u16 blocks)
{
port->buf = buf;
port->blocks = blocks;
}
#ifdef __cplusplus
}
#endif

279
arm7/source/mmc/tmio.twl.c Normal file
View File

@@ -0,0 +1,279 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 profi200
#include <stdatomic.h>
#include "tmio.h"
// Using atomic load/store produces better code than volatile
// but still ensures that the status is always read from memory.
#define GET_STATUS(ptr) atomic_load_explicit((ptr), memory_order_relaxed)
#define SET_STATUS(ptr, val) atomic_store_explicit((ptr), (val), memory_order_relaxed)
// ARM7 timer clock = controller clock = CPU clock.
// swiDelay() doesn't seem to be cycle accurate meaning
// one cycle is 4 (?) CPU cycles.
#define INIT_DELAY_FUNC() swi_waitByLoop(TMIO_CLK2DIV(400000u) * 74 / 4)
static u32 g_status[2] = {0};
static rtos_event_t sSdEvent;
__attribute__((always_inline)) static inline u8 port2Controller(const u8 portNum)
{
return portNum / 2;
}
static void tmio1Isr(u32 irqMask) // SD/eMMC.
{
Tmio *const regs = getTmioRegs(0);
g_status[0] |= regs->sd_status;
regs->sd_status = STATUS_CMD_BUSY; // Never acknowledge STATUS_CMD_BUSY.
rtos_signalEvent(&sSdEvent);
// TODO: Some kind of event to notify the main loop for remove/insert.
}
static void tmio2Isr(u32 irqMask) // WiFi SDIO.
{
Tmio *const regs = getTmioRegs(1);
g_status[1] |= regs->sd_status;
regs->sd_status = STATUS_CMD_BUSY; // Never acknowledge STATUS_CMD_BUSY.
}
void TMIO_init(void)
{
rtos_createEvent(&sSdEvent);
// Register ISR and enable IRQs.
rtos_setIrq2Func(RTOS_IRQ2_SDMMC, tmio1Isr);
rtos_setIrq2Func(RTOS_IRQ2_SDIO, tmio2Isr);
rtos_enableIrq2Mask(RTOS_IRQ2_SDMMC);
rtos_enableIrq2Mask(RTOS_IRQ2_SDIO);
// Reset all controllers.
for(u32 i = 0; i < 2; i++)
{
// Setup 32 bit FIFO.
Tmio *const regs = getTmioRegs(i);
regs->sd_fifo32_cnt = FIFO32_CLEAR | FIFO32_EN;
regs->sd_blocklen32 = 512;
regs->sd_blockcount32 = 1;
regs->dma_ext_mode = DMA_EXT_DMA_MODE;
// Reset. Unlike similar controllers no delay is needed.
// Resets the following regs:
// REG_SD_STOP, REG_SD_RESP0-7, REG_SD_STATUS1-2, REG_SD_ERR_STATUS1-2,
// REG_SD_CLK_CTRL, REG_SD_OPTION, REG_SDIO_STATUS.
regs->soft_rst = SOFT_RST_RST;
regs->soft_rst = SOFT_RST_NORST;
regs->sd_portsel = PORTSEL_P0;
regs->sd_blockcount = 1;
regs->sd_status_mask = STATUS_MASK_DEFAULT;
regs->sd_clk_ctrl = SD_CLK_DEFAULT;
regs->sd_blocklen = 512;
regs->sd_option = OPTION_BUS_WIDTH1 | OPTION_UNK14 | OPTION_DEFAULT_TIMINGS;
regs->ext_cdet_mask = EXT_CDET_MASK_ALL;
regs->ext_cdet_dat3_mask = EXT_CDET_DAT3_MASK_ALL;
// Disable SDIO.
regs->sdio_mode = 0;
regs->sdio_status_mask = SDIO_STATUS_MASK_ALL;
regs->ext_sdio_irq = EXT_SDIO_IRQ_MASK_ALL;
}
}
void TMIO_deinit(void)
{
rtos_disableIrq2Mask(RTOS_IRQ2_SDMMC);
rtos_setIrq2Func(RTOS_IRQ2_SDMMC, NULL);
rtos_disableIrq2Mask(RTOS_IRQ2_SDIO);
rtos_setIrq2Func(RTOS_IRQ2_SDIO, NULL);
// Mask all IRQs.
for(u32 i = 0; i < 2; i++)
{
// 32 bit FIFO IRQs.
Tmio *const regs = getTmioRegs(i);
regs->sd_fifo32_cnt = 0; // FIFO and all IRQs disabled/masked.
// Regular IRQs.
regs->sd_status_mask = STATUS_MASK_ALL;
// SDIO IRQs.
regs->sdio_status_mask = SDIO_STATUS_MASK_ALL;
}
}
void TMIO_initPort(TmioPort *const port, const u8 portNum)
{
// Reset port state.
port->portNum = portNum;
port->sd_clk_ctrl = SD_CLK_DEFAULT;
port->sd_blocklen = 512;
port->sd_option = OPTION_BUS_WIDTH1 | OPTION_UNK14 | OPTION_DEFAULT_TIMINGS;
}
// TODO: What if we get rid of setPort() and only use one port per controller?
static void setPort(Tmio *const regs, const TmioPort *const port)
{
// TODO: Can we somehow prevent all these reg writes each time?
// Maybe some kind of dirty flag + active port check?
regs->sd_portsel = port->portNum % 2u;
regs->sd_clk_ctrl = port->sd_clk_ctrl;
const u16 blocklen = port->sd_blocklen;
regs->sd_blocklen = blocklen;
regs->sd_option = port->sd_option;
regs->sd_blocklen32 = blocklen;
}
bool TMIO_cardDetected(void)
{
return getTmioRegs(0)->sd_status & STATUS_DETECT;
}
bool TMIO_cardWritable(void)
{
return getTmioRegs(0)->sd_status & STATUS_NO_WRPROT;
}
void TMIO_powerupSequence(TmioPort *const port)
{
port->sd_clk_ctrl = SD_CLK_EN | SD_CLK_DEFAULT;
setPort(getTmioRegs(port2Controller(port->portNum)), port);
INIT_DELAY_FUNC();
}
static void getResponse(const Tmio *const regs, TmioPort *const port, const u16 cmd)
{
// We could check for response type none as well but it's not worth it.
if((cmd & CMD_RESP_MASK) != CMD_RESP_R2)
{
port->resp[0] = regs->sd_resp[0];
}
else // 136 bit R2 responses need special treatment...
{
u32 resp[4];
for(u32 i = 0; i < 4; i++) resp[i] = regs->sd_resp[i];
port->resp[0] = resp[3]<<8 | resp[2]>>24;
port->resp[1] = resp[2]<<8 | resp[1]>>24;
port->resp[2] = resp[1]<<8 | resp[0]>>24;
port->resp[3] = resp[0]<<8; // TODO: Add the missing CRC7 and bit 0?
}
}
// Note: Using STATUS_DATA_END to detect transfer end doesn't work reliably
// because STATUS_DATA_END fires before we even read anything from FIFO
// on single block read transfer.
static void doCpuTransfer(Tmio *const regs, const u16 cmd, u8 *buf, const u32 *const statusPtr)
{
const u32 blockLen = regs->sd_blocklen;
u32 blockCount = regs->sd_blockcount;
vu32 *const fifo = getTmioFifo(regs);
if(cmd & CMD_DATA_R)
{
while((GET_STATUS(statusPtr) & STATUS_MASK_ERR) == 0 && blockCount > 0)
{
if(regs->sd_fifo32_cnt & FIFO32_FULL) // RX ready.
{
const u8 *const blockEnd = buf + blockLen;
do
{
if((uintptr_t)buf % 4 == 0)
{
*((u32*)buf) = *fifo;
}
else
{
const u32 tmp = *fifo;
buf[0] = tmp;
buf[1] = tmp>>8;
buf[2] = tmp>>16;
buf[3] = tmp>>24;
}
buf += 4;
} while(buf < blockEnd);
blockCount--;
}
else rtos_waitEvent(&sSdEvent, false, true);
}
}
else
{
// TODO: Write first block ahead of time?
// gbatek Command/Param/Response/Data at bottom of page.
while((GET_STATUS(statusPtr) & STATUS_MASK_ERR) == 0 && blockCount > 0)
{
if(!(regs->sd_fifo32_cnt & FIFO32_NOT_EMPTY)) // TX request.
{
const u8 *const blockEnd = buf + blockLen;
do
{
if((uintptr_t)buf % 4 == 0)
{
*fifo = *((u32*)buf);
}
else
{
u32 tmp = buf[0];
tmp |= (u32)buf[1]<<8;
tmp |= (u32)buf[2]<<16;
tmp |= (u32)buf[3]<<24;
*fifo = tmp;
}
buf += 4;
} while(buf < blockEnd);
blockCount--;
}
else rtos_waitEvent(&sSdEvent, false, true);
}
}
}
u32 TMIO_sendCommand(TmioPort *const port, const u16 cmd, const u32 arg)
{
const u8 controller = port2Controller(port->portNum);
Tmio *const regs = getTmioRegs(controller);
// Clear status before sending another command.
u32 *const statusPtr = &g_status[controller];
SET_STATUS(statusPtr, 0);
setPort(regs, port);
const u16 blocks = port->blocks;
regs->sd_blockcount = blocks; // sd_blockcount32 doesn't need to be set.
regs->sd_stop = STOP_AUTO_STOP; // Auto STOP_TRANSMISSION (CMD12) on multi-block transfer.
regs->sd_arg = arg;
// We don't need FIFO IRQs when using DMA. buf = NULL means DMA.
u8 *buf = port->buf;
u16 f32Cnt = FIFO32_CLEAR | FIFO32_EN;
if(buf != NULL) f32Cnt |= (cmd & CMD_DATA_R ? FIFO32_FULL_IE : FIFO32_NOT_EMPTY_IE);
regs->sd_fifo32_cnt = f32Cnt;
regs->sd_cmd = (blocks > 1 ? CMD_MULTI_DATA | cmd : cmd); // Start.
// TODO: Benchmark if this order is ideal?
// Response end comes immediately after the
// command so we need to check before __wfi().
// On error response end still fires.
while((GET_STATUS(statusPtr) & STATUS_RESP_END) == 0) rtos_waitEvent(&sSdEvent, false, true);
getResponse(regs, port, cmd);
if((cmd & CMD_DATA_EN) != 0)
{
// If we have to transfer data do so now.
if(buf != NULL) doCpuTransfer(regs, cmd, buf, statusPtr);
// Wait for data end if needed.
// On error data end still fires.
while((GET_STATUS(statusPtr) & STATUS_DATA_END) == 0) rtos_waitEvent(&sSdEvent, false, true);
}
// STATUS_CMD_BUSY is no longer set at this point.
return GET_STATUS(statusPtr) & STATUS_MASK_ERR;
}

View File

@@ -0,0 +1,39 @@
#include "common.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;
header7->dldiDriver = (void*)0x037F8000;
((pico_loader_7_func_t)header7->entryPoint)();
}

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

7
arm9/gfx/backIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/backIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/bannerListIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

View File

@@ -0,0 +1,5 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

View File

@@ -0,0 +1,18 @@
# tile format
-gt
# tile reduction by tiles, palette and hflip/vflip
-mRtpf
-ma2
# graphics bit depth is 4 (16 color)
-gB4
-p
-pn16
# map layout standard bg format
-mLs
#-gzl
#-mzl

BIN
arm9/gfx/bottomSheetBg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

View File

@@ -0,0 +1,3 @@
-gb
-gB8
-p!

BIN
arm9/gfx/carouselMask.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

5
arm9/gfx/chipFilled.grit Normal file
View File

@@ -0,0 +1,5 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4

BIN
arm9/gfx/chipFilled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/coverflowIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View File

@@ -0,0 +1,3 @@
-gb
-gB8
-p

BIN
arm9/gfx/folderCover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

7
arm9/gfx/gamesIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/gamesIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

7
arm9/gfx/hGridIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/hGridIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

7
arm9/gfx/heartIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/heartIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

View File

@@ -0,0 +1,3 @@
-gb
-gB8
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

7
arm9/gfx/iconCell.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/iconCell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

5
arm9/gfx/iconCell2.grit Normal file
View File

@@ -0,0 +1,5 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4

BIN
arm9/gfx/iconCell2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

5
arm9/gfx/iconCell3.grit Normal file
View File

@@ -0,0 +1,5 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4

BIN
arm9/gfx/iconCell3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/largeFileIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

7
arm9/gfx/listIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/listIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

13
arm9/gfx/mainBg.grit Normal file
View File

@@ -0,0 +1,13 @@
# tile format
-gt
# tile reduction
-mR8
# graphics bit depth is 8 (256 color)
-gB8
-p!
# map layout standard bg format
-mLs

BIN
arm9/gfx/mainBg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

7
arm9/gfx/moviesIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/moviesIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

7
arm9/gfx/musicIcon.grit Normal file
View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/musicIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

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