Initial commit

This commit is contained in:
Gericom
2025-11-16 15:41:34 +01:00
commit 4511cab6d1
78 changed files with 35088 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

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

51
.gitignore vendored Normal file
View File

@@ -0,0 +1,51 @@
# 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/
roms/*.nds
data/uartBufv060.bin

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "pico-sdk"]
path = pico-sdk
url = https://github.com/raspberrypi/pico-sdk.git

90
CMakeLists.txt Normal file
View File

@@ -0,0 +1,90 @@
cmake_minimum_required(VERSION 3.13)
#set(PICO_SDK_PATH "../FIRMWARE/pico-sdk") # enable this if vscode or cmake complains about PICO_SDK_PATH not being set
include(pico_sdk_import.cmake)
project(DSpico_project C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_compile_definitions(DSpico
PICO_STACK_SIZE=0x600
PICO_CORE1_STACK_SIZE=0x400
ENABLE_R4_MODE
#DSPICO_ENABLE_WRFUXXED # This macro enables the WRFUXXED exploit to work.
# ENABLE_PREVENT_DSI_AUTOBOOT # meant to be used with WRFU, doesn't work properly on the 3ds
)
add_executable(DSpico
src/main.cpp
src/common.h
src/romData.S
src/romData.h
src/blowfish.c
src/blowfish.h
src/powerSaving.c
src/powerSaving.h
src/scrambler.c
src/scrambler.h
src/scramblerRing.c
src/scramblerRing.h
src/ntrCardRom.c
src/ntrCardRom.h
src/ntrCardRomNorm.c
src/ntrCardRomSecure.c
src/ntrCardRomGame.c
src/ntrCardRomGame.h
src/ntrCardRomGameNoScramble.c
src/ntrCardRomGameNoScramble.h
src/ntrCardRomGameSd.cpp
src/ntrCardRomGameR4.cpp
src/ntrCardRomGameUsb.cpp
src/ntrCardRomGameUsb.h
src/usbEventQueue.c
src/usbEventQueue.h
src/tinyusb/dcd_rp2040.c
src/tinyusb/rp2040_usb.c
src/tinyusb/rp2040_usb.h
src/tinyusb/dcd.h
src/tinyusb/tusb_option.h
src/tinyusb/tusb_compiler.h
src/tinyusb/tusb_common.h
src/tinyusb/tusb_config.h
src/tinyusb/tusb_mcu.h
src/tinyusb/tusb_verify.h
src/tinyusb/tusb_types.h
src/tinyusb/tusb_debug.h
src/tinyusb/osal.h
src/tinyusb/osal_none.h
src/ntrCardSpiUart.c
src/ntrCardSpiUart.h
src/wrfuxxed.h
src/wrfuxxed.S
src/xor.S
src/xor.h
src/ntrCardIrq.S
src/r4.h
src/sd/rp2040_sdio.cpp
src/sd/rp2040_sdio.h
src/sd/SdCardInfo.h
src/sd/SdCardInfo.h
src/sd/SdCard.cpp
src/sd/fatfs/diskio.cpp
src/sd/fatfs/diskio.h
src/sd/fatfs/ff.c
src/sd/fatfs/ff.h
src/sd/fatfs/ffconf.h
src/sd/fatfs/ffunicode.c
src/sd/fatfs/ffsystem.c
)
if(EXISTS "${CMAKE_SOURCE_DIR}/roms/default.nds" AND EXISTS "${CMAKE_SOURCE_DIR}/roms/dsimode.nds")
target_compile_definitions(DSpico PRIVATE
DETECT_CONSOLE_TYPE # This macro enables the firmware to switch the rom based on which console is detected. You shouldn't change this.
)
endif()
pico_generate_pio_header(DSpico ${CMAKE_CURRENT_LIST_DIR}/src/ntrCard.pio)
pico_generate_pio_header(DSpico ${CMAKE_CURRENT_LIST_DIR}/src/sd/rp2040_sdio.pio)
pico_enable_stdio_uart(DSpico 1)
pico_add_extra_outputs(DSpico)
target_link_libraries(DSpico pico_stdlib hardware_pio hardware_dma pico_multicore)

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.

134
README.md Normal file
View File

@@ -0,0 +1,134 @@
# DSpico Firmware
This is the repository for the DSpico firmware. The firmware emulates a DS cartridge, with extended features for SD access and USB. PIO is used for an SDIO interface for the SD card and for interfacing the DS cartridge bus.
For an overview of the supported card commands, see [commands.md](docs/commands.md).
## Features
- Emulates a retail DS(i) cartridge
- Interfaces with an SD card using SDIO and exposes card commands to access it from the DS side
- Exposes card commands to the DS side to allow interfacing with the USB port of the RP2040
- Can emulate an R4 to support software such as the Wood R4 kernel
- Supports having a separate rom for DS and DSi/3DS systems
- Supports emulating the IS-SPI-USB-ADAPTER for the WRFUxxed exploit
- Easy updating; starting the firmware with an ejected SD card reboots to BOOTSEL
- Optimized for minimal power use when idle
## Pinout
| **Peripheral** | **Pin name - Peripheral** | **Pin name - RP2040** |
|-------------|-----------------------|-------------------|
| **DS Slot** | D0 | GPIO12 |
| | D1 | GPIO13 |
| | D2 | GPIO14 |
| | D3 | GPIO15 |
| | D4 | GPIO16 |
| | D5 | GPIO17 |
| | D6 | GPIO18 |
| | D7 | GPIO19 |
| | CLK_DS | GPIO11 |
| | ROM_CS | GPIO10 |
| | SPI_CS | GPIO21 |
| | IRQ | GPIO20 |
| | RST_DS | GPIO09 |
| **SDIO** | CLK_SD | GPIO03 |
| | DAT0 | GPIO05 |
| | DAT1 | GPIO06 |
| | DAT2 | GPIO07 |
| | DAT3 | GPIO08 |
| | CMD | GPIO04 |
## Setup & configuration
We recommend using WSL (Windows Subsystem for Linux), or a Unix-based machine to compile this repository.
The steps provided will assume a Linux environment. Alternatively, you can run the `setup_environment.sh` bash script.
1. Run `sudo apt update && sudo apt install cmake gcc-arm-none-eabi build-essential git`
2. Clone this repository
3. Run the following commands:
```bash
git submodule update --init
cd pico-sdk
git submodule update --init
cd ..
```
Note that you shouldn't use `--recursive` because it draws in a lot of unnecessary submodules inside the pico-sdk.
### CMakeList
The `CMakeList.txt` file contains a couple of options that you can configure.
* `ENABLE_R4_MODE` - Enables R4 emulation. This allows you to use R4 software, such as the Wood R4 kernel. As R4 emulation can be used together with regular DSpico software, it can usually be kept enabled.
* Note that to be able to use R4 software, your SD card must be at most 4 GB, or have a single partition in the first 4 GB of the SD card. R4 card commands cannot address SD sectors above 4 GB!
* `DSPICO_ENABLE_WRFUXXED` - Enables emulation of the IS-SPI-USB-ADAPTER to support the WRFUxxed exploit. This requires <code>uartBufv060.bin</code> to be placed in the `data/` folder.
* `ENABLE_PREVENT_DSI_AUTOBOOT` - Experimental feature that prevents DSi consoles from autobooting when the autoboot flag is set. It was intended to be used with WRFU Tester, which has the autoboot flag set. It is generally not recommended to use this, as it does not work properly with the 3DS and has not been tested much.
### Setting up the rom(s)
To compile and properly use the firmware, you will need to place a valid DS rom in the `roms/` folder, named `default.nds`. Additionally, you may include a second rom in the `roms/` folder named `dsimode.nds`, if you wish to have a different rom for DS consoles and DSi/3DS consoles.
<table>
<tr>
<th>Usage</th>
<th>default.nds</th>
<th>dsimode.nds</th>
<th>Notes</th>
</tr>
<tr>
<td>Single rom for DS and/or DSi</td>
<td>Your rom</td>
<td>-</td>
<td>The rom must contain NTR blowfish keys.<br>If the rom is hybrid or DSi exclusive, it must additionally contain TWL blowfish keys.</td>
</tr>
<tr>
<td>Hybrid bootloader</td>
<td>Bootloader</td>
<td>-</td>
<td>The bootloader rom must be patched with the DSpico DLDI.<br>The bootloader rom must contain NTR and TWL blowfish keys.</td>
</tr>
<tr>
<td>DSi ntrboot</td>
<td>GCD rom</td>
<td>-</td>
<td>The rom must contain GCD blowfish keys and must be properly signed.<br>The DSpico must be using USB power, such that the firmware is booted before starting the DSi. Without external power, the firmware currently does not boot fast enough to keep up with DSi ntrboot.</td>
</tr>
<tr>
<td>3DS ntrboot</td>
<td>3DS ntrboot rom</td>
<td>-</td>
<td>A 3DS ntrboot rom consists of a header, the blowfish keys and the firm to boot.</td>
</tr>
<tr>
<td>Separate rom for DS and DSi</td>
<td>Your DS rom</td>
<td>Your DSi rom</td>
<td>The DS rom must contain NTR blowfish keys.<br>The DSi rom must contain both NTR and TWL blowfish keys.</td>
</tr>
<tr>
<td>WRFUxxed</td>
<td>Bootloader</td>
<td>WRFU Tester v0.60</td>
<td>The bootloader rom and <code>uartBufv060.bin</code> must be patched with the DSpico DLDI.<br><code>uartBufv060.bin</code> must be placed in the <code>/data</code> folder.<br>The bootloader rom must contain NTR blowfish keys.<br>The <code>DSPICO_ENABLE_WRFUXXED</code> define in <code>CMakeLists.txt</code> must be enabled.</td>
</tr>
</table>
## Compiling
Simply run `./compile.sh` to compile the firmware. Once it is complete, you will be able to find `DSpico.uf2` in the `build/` folder, which you can use to flash your DSpico board with.
> [!IMPORTANT]
> The firmware only works correctly when build with optimization. Recommended is `RelWithDebInfo`.
## License
The firmware for the DSpico project includes code that is licensed under the following:
SPDX-License-Identifier: Zlib
- Miscellaneous source files
SPDX-License-Identifier: BSD-3-Clause
- Pico SDK
SPDX-License-Identifier: GPL-3.0-or-later
- ZuluSCSI
For details, see the `license` directory, as well as `LICENSE.txt`.
## Contributors
- [@Gericom](https://github.com/Gericom)
- [@XLuma](https://github.com/XLuma)
- [@Dartz150](https://github.com/Dartz150)
- [@pedro-javierf](https://github.com/pedro-javierf)

34
compile.sh Normal file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
###############################################################
# File: compile.sh
# Creation Date: 10/11/2022 (DD/MM/YYYY)
# Description: All-in-one script to update, setup and
# build the DSpico firmware.
#
# Author: pedro-javierf
# Copyright: LNH team (c) 2022, all rights reserved
################################################################
echo "[>] Configuring project with CMake.."
# Clean previous build/ folders if they exist
rm -rf build/
mkdir build
# Export the SDK Path before running CMAKE
export PICO_SDK_PATH=../pico-sdk
# Specify CMAKE where we want the build tree to be at.
# In our case, the build/ directory.
# The source directory will be . (the current directory,
# where the CMakeLists.txt is located).
cmake -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo -B build/ .
echo "[>] Building FIRMWARE: "
# Go and build the firmware
cd build
make
echo "[>] Build completed. Find the DSpico.uf2 file inside build/ folder"

0
data/.gitkeep Normal file
View File

396
docs/commands.md Normal file
View File

@@ -0,0 +1,396 @@
# Supported commands
This page describes the card commands supported by the DSpico firmware. It is recommended to use at least 4 latency cycles for commands that read data (DSpico -> DS) and at least 8 latency cycles for commands that write data (DS -> DSpico). Normally a 6.7 MHz clock should be used.
## Normal mode
When the cartridge boots, or after a card reset, the cartridge is in normal mode.
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>00 xx xx xP PP xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_READ_PAGE</code></td>
<td>Reads a rom header page (512 bytes).<br><code>PPP</code> must be a multiple of 512.</td>
</tr>
<tr>
<td><code>3C xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_CHANGE_MODE_NTR</code></td>
<td>Switches to NTR secure mode.</td>
</tr>
<tr>
<td><code>3D xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_CHANGE_MODE_TWL</code></td>
<td>Switches to TWL secure mode.</td>
</tr>
<tr>
<td><code>71 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_3DS_DETECT</code></td>
<td>Ignored, but used for console type detection.</td>
</tr>
<tr>
<td><code>90 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_READ_ID</code></td>
<td>Reads the card id (4 bytes).</td>
</tr>
<tr>
<td><code>9F xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_NORMAL_LOAD_TABLE</code></td>
<td>Ignored, but used for console type detection.</td>
</tr>
</table>
## Secure mode
In secure mode the received commands are blowfish encrypted.
<table>
<tr>
<th></th>
<th>NTR location</th>
<th>TWL location</th>
</tr>
<tr>
<td>P table</td>
<td><code>0x1600</code></td>
<td><code>twlArea + 0x600</code></td>
</tr>
<tr>
<td>S boxes</td>
<td><code>0x1C00</code></td>
<td><code>twlArea + 0xC00</code></td>
</tr>
<tr>
<td>Secure area</td>
<td><code>0x4000</code> to <code>0x8000</code></td>
<td><code>twlArea + 0x3000</code> to <code>twlArea + 0x7000</code></td>
</tr>
</table>
`twlArea` is the 16-bit value at `0x92` multiplied by `0x80000`.
In secure mode all commands need to be issued at least two times. The first time is used to decrypt the command. No data is returned. The second time the command is actually executed.
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>1x xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_SECURE_READ_ID</code></td>
<td>Reads the card id (4 bytes).</td>
</tr>
<tr>
<td><code>2S SS Sx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_SECURE_READ_SEGMENT</code></td>
<td>Reads a 4 KB secure segment.<br>Segment number <code>SSS</code> must be 4, 5, 6 or 7.<br>The command needs to be issued 9 times to get the entire segment.<br>The first time, the command is decrypted and no data is returned.<br>The next 8 times 512 bytes are returned.</td>
</tr>
<tr>
<td><code>4x xx xM MM NN Nx xx xx</code></td>
<td><code>NTR_CMD_ID_SECURE_SCRAMBLER_ON</code></td>
<td>Enables the scrambler. <code>MMMNNN</code> is used to seed the scrambler.</td>
</tr>
<tr>
<td><code>Ax xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_SECURE_CHANGE_MODE</code></td>
<td>Switches to game mode.</td>
</tr>
</table>
## Game mode
Game mode is the mode that is used while a DS application is running. Blowfish is no longer used and commands only need to be issued once.
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>B7 PP PP PP PP xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_READ_PAGE</code></td>
<td>Reads a rom page (512 bytes).<br><code>PPPPPPPP</code> must be a multiple of 512.</td>
</tr>
<tr>
<td><code>B8 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_READ_ID</code></td>
<td>Reads the card id (4 bytes).</td>
</tr>
<tr>
<td><code>FC xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_DISABLE_SCRAMBLING</code></td>
<td>Switches to unscrambled game mode.</td>
</tr>
</table>
## Unscrambled game mode
In this mode no scrambling is used and the extended features of the DSpico are available. On the DS side it is recommended to set the scrambler seeds to zero, such that no scrambling is used no matter if scrambling is enabled or disabled in the control register (when the seed is zero, the scrambler only outputs zeros). By doing this, R4 specific applications will automatically not have scrambling.
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>B7&nbsp;PP&nbsp;PP&nbsp;PP&nbsp;PP&nbsp;xx&nbsp;xx&nbsp;xx</code></td>
<td><code>NTR_CMD_ID_GAME_READ_PAGE</code></td>
<td>Returns 512 bytes of garbage unless the <code>ENABLE_R4_MODE</code> define is used (see below).</td>
</tr>
<tr>
<td><code>B8 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_READ_ID</code></td>
<td>Reads the card id (4 bytes).</td>
</tr>
<tr>
<td><code>E3 xx xx xx SS SS SS SS</code></td>
<td><code>NTR_CMD_ID_GAME_REQ_SD_READ</code></td>
<td>Requests an SD read of sector <code>SSSSSSSS</code>.</td>
</tr>
<tr>
<td><code>E4 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_GET_SD_STAT</code></td>
<td>Returns 1 when the current SD operation (read or write) is complete,<br>or 0 otherwise. (4 bytes)</td>
</tr>
<tr>
<td><code>E5 xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_GET_SD_DATA</code></td>
<td>Returns 512 bytes of SD data that was read previously.</td>
</tr>
<tr>
<td><code>E8 SS xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_USB_COMMAND</code></td>
<td>Performs a USB command. This command has no payload data.<br>See below for the list of sub commands (<code>SS</code>).</td>
</tr>
<tr>
<td><code>E9 xI PP xT LL LL LL LL</code></td>
<td><code>NTR_CMD_ID_GAME_USB_WRITE_DATA</code></td>
<td>Writes 512 bytes of data to an endpoint buffer.<br><code>PP</code> is the endpoint id. <code>I</code> specifies the buffer index (0 or 1).<br><code>T</code> specifies whether a transfer should be started (1) or not (0).<br><code>LLLLLLLL</code> specifies the actual data length (0 <= length <= 512).<br>Length zero is special cased to expect no payload data, for zero length transfers.</td>
</tr>
<tr>
<td><code>EA xx PP xI xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_USB_READ_DATA</code></td>
<td>Reads 512 bytes of data from an endpoint buffer.<br><code>PP</code> is the endpoint id. <code>I</code> specifies the buffer index (0 or 1).</td>
</tr>
<tr>
<td><code>EB xx xx xx xx xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_USB_GET_EVENT</code></td>
<td>Dequeues an event from the USB event queue and returns it (4 bytes).</td>
</tr>
<tr>
<td><code>F6 E1 0D 9Q SS SS SS SS</code></td>
<td><code>NTR_CMD_ID_GAME_WRITE_SD_DATA</code></td>
<td>Writes 512 bytes of SD sector data. <code>Q</code> is <code>10XY</code> (binary) with<br><code>X</code> the <code>WRITE_SD_DATA_IS_FIRST_FLAG</code> indicating the start of a sequential write, and<br><code>Y</code> the <code>WRITE_SD_DATA_IS_LAST_FLAG</code> indicating the last sector of a sequential write.</td>
</tr>
</table>
### SD read
This pseudocode illustrates how to do a (sequential) SD read. When a sector read is requested, the DSpico will automatically start reading the next sector when the read of the first sector completes. This improves throughput, as the completed sector can be transferred to the DS, while the next sector is being read from the SD card.
```C
read(sector, count, data)
{
NTR_CMD_ID_GAME_REQ_SD_READ(sector);
do
{
while (NTR_CMD_ID_GAME_GET_SD_STAT() == 0);
NTR_CMD_ID_GAME_GET_SD_DATA(data);
} while (--count != 0);
while (NTR_CMD_ID_GAME_GET_SD_STAT() == 0);
}
```
### SD write
This pseudocode illustrates how to do a (sequential) SD write. While the DSpico is writing a sector to the SD card, the next sector can already be transferred to the DSpico to improve throughput.
```C
write(sector, count, data)
{
if (count == 1)
{
NTR_CMD_ID_GAME_WRITE_SD_DATA(sector, first: true, last: true, data); // send 0 = last
}
else if (count > 1)
{
NTR_CMD_ID_GAME_WRITE_SD_DATA(sector, first: true, last: false, data); // send 0
sector++;
for (i = 1; i < count - 1; i++)
{
NTR_CMD_ID_GAME_WRITE_SD_DATA(sector, first: false, last: false, data); // send i
sector++;
while (NTR_CMD_ID_GAME_GET_SD_STAT() == 0); // wait i - 1
}
NTR_CMD_ID_GAME_WRITE_SD_DATA(sector, first: false, last: true, data); // send last
while (NTR_CMD_ID_GAME_GET_SD_STAT() == 0); // wait last - 1
}
while (NTR_CMD_ID_GAME_GET_SD_STAT() == 0); // wait last
}
```
### NTR_CMD_ID_GAME_USB_COMMAND sub commands
Most of the sub commands directly map to tinyusb functions.
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>E8 01 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_INIT</code></td>
<td>Initializes USB.</td>
</tr>
<tr>
<td><code>E8 02 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_BEGIN_SET_ADDRESS</code></td>
<td><code>dcd_set_address(0, 0)</code></td>
</tr>
<tr>
<td><code>E8 03 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_REMOTE_WAKEUP</code></td>
<td><code>dcd_remote_wakeup(0)</code></td>
</tr>
<tr>
<td><code>E8 04 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_CONNECT</code></td>
<td><code>dcd_connect(0)</code></td>
</tr>
<tr>
<td><code>E8 05 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_DISCONNECT</code></td>
<td><code>dcd_disconnect(0)</code></td>
</tr>
<tr>
<td><code>E8 06 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_SOF_ENABLE</code></td>
<td><code>dcd_sof_enable(0, true)</code></td>
</tr>
<tr>
<td><code>E8 07 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_SOF_DISABLE</code></td>
<td><code>dcd_sof_enable(0, false)</code></td>
</tr>
<tr>
<td><code>E8 08 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_EP_CLOSE_ALL</code></td>
<td><code>dcd_edpt_close_all(0)</code></td>
</tr>
<tr>
<td><code>E8 09 PP xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_EP_STALL</code></td>
<td><code>dcd_edpt_stall(0, PP)</code></td>
</tr>
<tr>
<td><code>E8 0A PP xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_EP_CLEAR_STALL</code></td>
<td><code>dcd_edpt_clear_stall(0, PP)</code></td>
</tr>
<tr>
<td><code>E8 0B xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_EP_CLOSE</code></td>
<td><code>dcd_edpt_close(0, PP)</code></td>
</tr>
<tr>
<td><code>E8 0C RR xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_FINISH_SET_ADDRESS</code></td>
<td><code>usb_hw->dev_addr_ctrl = RR</code></td>
</tr>
<tr>
<td><code>E8 0D PP xT xx xx xS SS</code></td>
<td><code>USB_SUB_COMMAND_EP_OPEN</code></td>
<td>Opens endpoint <code>PP</code>. The endpoint will have type <code>T</code><br>(0 = control, 1 = isochronous, 2 = bulk, 3 = interrupt)<br>and maximum packet size <code>SSS</code>.</td>
</tr>
<tr>
<td><code>E8 0E xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_CLEAR_EVENT_QUEUE</code></td>
<td>Clears the USB event queue.</td>
</tr>
<tr>
<td><code>E8 0F xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_DEINIT</code></td>
<td>Deinitializes USB.</td>
</tr>
<tr>
<td><code>E8 10 PP xI LL LL LL LL</code></td>
<td><code>USB_SUB_COMMAND_BEGIN_TRANSFER</code></td>
<td>Starts a transfer on endpoint <code>PP</code>. <code>I</code> is the buffer index (0 or 1),<br>and <code>LLLLLLLL</code> is the length (0 <= length <= 512).</td>
</tr>
<tr>
<td><code>E8 11 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_INTERRUPT_ENABLE</code></td>
<td><code>dcd_int_enable(0)</code></td>
</tr>
<tr>
<td><code>E8 12 xx xx xx xx xx xx</code></td>
<td><code>USB_SUB_COMMAND_INTERRUPT_DISABLE</code></td>
<td><code>dcd_int_disable(0)</code></td>
</tr>
</table>
### R4 emulation
The following commands are additionally supported for R4 emulation when the `ENABLE_R4_MODE` define is used. See also [r4.md](r4.md).
<table>
<tr>
<th>Command</th>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td><code>00&nbsp;00&nbsp;00&nbsp;00&nbsp;xx&nbsp;xx&nbsp;xx&nbsp;xx</code></td>
<td>-</td>
<td>Returns zero (4 bytes).</td>
</tr>
<tr>
<td><code>B0 xx xx xx xx xx xx xx</code></td>
<td>-</td>
<td>Get R4 card info. Returns 0x1F4 (4 bytes).</td>
</tr>
<tr>
<td><code>B2 PP PP PP PP xx xx xx</code></td>
<td>-</td>
<td>Starts a save read at address <code>PPPPPPPP</code>. The address must be 512 byte aligned. The command returns 1 while the card is busy reading, and 0 once the read is complete (4 bytes).</td>
</tr>
<tr>
<td><code>B3 xx xx xx xx xx xx xx</code></td>
<td>-</td>
<td>Returns the 512 bytes of save data that were previously requested with command <code>B2</code>.</td>
</tr>
<tr>
<td><code>B4 PP PP PP PP xx xx xx</code></td>
<td>-</td>
<td>Initializes the cluster map for a rom (lsb of address is 0) or save file (lsb of address is 1). <code>PPPPPPPP</code> is the 2-byte aligned SD address of the FAT entry of the file. The command returns 1 while the cluster map is being initialized,<br>and 0 once complete (4 bytes).</td>
</tr>
<tr>
<td><code>B6 PP PP PP PP xx xx xx</code></td>
<td>-</td>
<td>Starts a rom read at address <code>PPPPPPPP</code>. The address must be 512 byte aligned. The command returns 1 while the card is busy reading, and 0 once the read is complete (4 bytes).</td>
</tr>
<tr>
<td><code>B7 PP PP PP PP xx xx xx</code></td>
<td><code>NTR_CMD_ID_GAME_READ_PAGE</code></td>
<td>Returns the 512 bytes of rom data that were previously requested with command <code>B6</code>. <code>PPPPPPPP</code> is the 512 byte aligned rom address.</td>
</tr>
<tr>
<td><code>B9 PP PP PP PP xx xx xx</code></td>
<td>-</td>
<td>Starts an SD read at SD address <code>PPPPPPPP</code>. The address must be 512 byte aligned. The command returns 0x1F4 while the card is busy reading, and 0 once the read is complete (4 bytes).</td>
</tr>
<tr>
<td><code>BA xx xx xx xx xx xx xx</code></td>
<td>-</td>
<td>Returns the 512 bytes of SD data that were previously requested with command <code>B9</code>.</td>
</tr>
<tr>
<td><code>BB PP PP PP PP xx xx xx</code></td>
<td>-</td>
<td>Starts an SD write to SD address <code>PPPPPPPP</code>. The address must be 512 byte aligned. The command expects 512 bytes of data to write.</td>
</tr>
<tr>
<td><code>BE xx xx xx xx xx xx xx</code></td>
<td>-</td>
<td>Gets the status of the SD write that was started previously with command <code>BB</code>. The command returns 1 while the card is busy writing, and 0 once the write is complete (4 bytes).</td>
</tr>
</table>

30
docs/r4.md Normal file
View File

@@ -0,0 +1,30 @@
# R4
This file documents the extra card commands supported by the original R4. Note that the original R4 only supported non-HC SD cards. As such the commands can only address up to 4GB on a SD card. All extra card commands for the R4 are for game mode and use the scrambler for both command and data as is usual for game mode commands. Write data (not command) send from the DS to the card appears never to be scrambled by the DS card hardware, although the scrambler state itself does advance.
It appears that all R4 commands use 0 cycles of initial latency (L1) and 24 cycles of page latency (L2), with the 6.7 MHz clock. Card id commands are not patched and use the timings from the retail game rom header, but without L1 latency (that's what the sdk does). Those can be as fast as a single cycle of L2 latency, depending on the game. Failing to respond to card id reads will trigger a card pull-out response in retail games. The card id should never be adapted to the retail game being played and should stay equal to the one that the console received the very first time at boot in normal mode. The actual value of the card id does not seem to matter much, as long as bit 31 reflects the protocol version that should be used.
* `0000000000000000` - No idea what it does, sending back `0x00000000` works.
* `B000000000000000` - Get card info. Normally returns `0x000001F4`.
* The dldi driver checks for `(cardInfo & 7) == 4` in the `isInserted` function.
* `B2XXXXXXXX000000` - Start save read at address `0xXXXXXXXX`.
* Address should most likely be 512 byte aligned. Returns `0x00000001` while the card is busy performing the read and `0x00000000` once the data is available. This command is polled as long as the return value is 1.
* `B3XXXXXXXX000000` - Get save data at address `0xXXXXXXXX` that was previously requested by command `B2`.
* Not sure if the address is actually used by the card or can be any dummy value. Returns the 512 byte block of save data.
* `B4XXXXXXXX000000` - Sets the address (`0xXXXXXXXX`) of the FAT entry for rom (lsb of address is 0) or save (lsb of address is 1).
* This is a 2-byte aligned SD address. Returns `0x00000001` while the card is busy initializing the cluster map and `0x00000000` once ready. This command is polled as long as the return value is 1.
* `B6XXXXXXXX000000` - Start rom read at address `0xXXXXXXXX`.
* Address should be 512 byte aligned. Returns `0x00000001` while the card is busy performing the read and `0x00000000` once the data is available. This command is polled as long as the return value is 1. The data can then be read with the standard `B7` command.
* `B9XXXXXXXX000000` - Start SD read at address `0xXXXXXXXX`.
* Address is in bytes, but should be 512 byte aligned. Returns `0x000001F4` while the card is busy performing the read and `0x00000000` once the data is available. This command is polled as long as the return value is not zero.
* `BAXXXXXXXX000000` - Get SD data at address `0xXXXXXXXX` that was previously requested by command `B9`.
* Not sure if the address is actually used by the card or can be any dummy value. Returns the 512 byte block of SD data.
* `BBXXXXXXXX000000` - Start SD write to address `0xXXXXXXXX`.
* Address is in bytes, but should be 512 byte aligned. The command expects 512 bytes of data that will be written to the SD card.
* `BCXXXXXXXX000000` - Get SD write status.
* Not sure if the address is actually used by the card or can be any dummy value. Returns `0x00000001` while the card is busy performing the write and `0x00000000` once done. This command is polled as long as the return value is 1.
* `BDXXXXXXXX000000` - Start save write to address `0xXXXXXXXX`.
* Address should most likely be 512 byte aligned. The command expects 512 bytes of data that will be written to the save.
* `BEXXXXXXXX000000` - Get save write status.
* Not sure if the address is actually used by the card or can be any dummy value. Returns `0x00000001` while the card is busy performing the write and `0x00000000` once done. This command is polled as long as the return value is 1.
When initializing the R4 for retail rom use it seems there are some `0000000000000000` dummy commands send. Not sure if that is actually needed to make it work. After that `B4` is used to initialize the cluster map for rom and save file. The save file should have already been created on the SD card by the software on the DS side. The retail game is patched by the DS side software to perform rom reads using the requesting mechanism (`B6`/`B7`) and to perform save reads and writes using the corresponding card commands (`B2`/`B3` and `BD`/`BE`) instead of SPI. The advantage of the requesting/feedback mechanism for rom reads is that the data can be read as soon as it is ready. SD cards can have varying read latency and without a feedback mechanism you'd always have to account for the worst case, which can be more than the maximum amount of latency that can be specified in the IO registers and would be awful for the performance.

674
licenses/ZuluSCSI.txt Normal file

File diff suppressed because it is too large Load Diff

21
licenses/pico-sdk.txt Normal file
View File

@@ -0,0 +1,21 @@
Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1
pico-sdk Submodule

Submodule pico-sdk added at 6a7db34ff6

73
pico_sdk_import.cmake Normal file
View File

@@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

0
roms/.gitkeep Normal file
View File

60
setup_environment.sh Normal file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
###############################################################
# File: setup_environment.sh
# Creation Date: 10/11/2022 (DD/MM/YYYY)
# Description: All-in-one script to update and setup
# the DSpico firmware repository.
#
# Author: pedro-javierf
# Copyright: LNH team (c) 2022, all rights reserved
################################################################
# TODO: See if packages need to be installed
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) machine=Linux;;
Darwin*) machine=Mac;;
CYGWIN*) machine=Cygwin;;
MINGW*) machine=MinGw;;
*) machine="UNKNOWN:${unameOut}"
esac
# if linux or CYGWIN
if [[ "$machine" == "Linux" ]]
then
echo "Detected Linux OS. Installing needed libs."
sudo apt update
sudo apt install cmake gcc-arm-none-eabi build-essential git
fi
if [[ "$machine" == "Cygwin" ]]
then
echo "Detected Cygwin. Installing needed libs."
sudo apt update
sudo apt install cmake gcc-arm-none-eabi build-essential git
fi
# if MacOS
if [[ "$machine" == "Mac" ]]
then
# Install cmake
brew install cmake
# Install the arm eabi toolchain
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc
# The equivalent to build-essential on linux, you probably already have this.
xcode-select --install
fi
echo "[>] Updating pico-sdk submodule.."
git submodule update --init -- pico-sdk/
echo "[>] Updating pico-sdk dependencies.."
cd pico-sdk/
git submodule update --init

28
src/blowfish.c Normal file
View File

@@ -0,0 +1,28 @@
#include "common.h"
#include "blowfish.h"
void bf_init(bf_context_t* context, const u32* pTable, const bf_sboxes_t* sBoxes)
{
context->pTable = pTable;
context->sBoxes = sBoxes;
}
u64 __time_critical_func(bf_decrypt)(const bf_context_t* context, u64 block)
{
const u32* pTable = context->pTable;
const bf_sboxes_t* sBoxes = context->sBoxes;
u32 y = block;
u32 x = block >> 32;
for (int i = 0x11; i >= 2; i--)
{
u32 z = pTable[i] ^ x;
u32 a = sBoxes->sbox0[z >> 24];
u32 b = sBoxes->sbox1[(u8)(z >> 16)];
u32 c = sBoxes->sbox2[(u8)(z >> 8)];
u32 d = sBoxes->sbox3[(u8)z];
x = (d + (c ^ (b + a))) ^ y;
y = z;
}
return (x ^ pTable[1]) | ((u64) (y ^ pTable[0]) << 32);
}

38
src/blowfish.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include "common.h"
/// @brief Blowfish s boxes struct.
typedef struct
{
u32 sbox0[0x100];
u32 sbox1[0x100];
u32 sbox2[0x100];
u32 sbox3[0x100];
} bf_sboxes_t;
/// @brief Blowfish context struct.
typedef struct
{
const u32* pTable;
const bf_sboxes_t* sBoxes;
} bf_context_t;
#ifdef __cplusplus
extern "C" {
#endif
/// @brief Initializes the given blowfish \p context with the given \p pTable and \p sBoxes.
/// @param context The blowfish context to initialize.
/// @param pTable The p table to use.
/// @param sBoxes The s boxes to use.
void bf_init(bf_context_t* context, const u32* pTable, const bf_sboxes_t* sBoxes);
/// @brief Decrypts the given blowfish \p block using the given \p context.
/// @param context The blowfish context to use.
/// @param block The blowfish block to decrypt.
/// @return The decrypted blowfish block.
u64 bf_decrypt(const bf_context_t* context, u64 block);
#ifdef __cplusplus
}
#endif

67
src/common.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include "pico/stdlib.h"
#include "pico/time.h"
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef volatile uint8_t vu8;
typedef volatile int8_t vs8;
typedef volatile uint16_t vu16;
typedef volatile int16_t vs16;
typedef volatile uint32_t vu32;
typedef volatile int32_t vs32;
typedef volatile uint64_t vu64;
typedef volatile int64_t vs64;
#define SD_USE_SDIO
#define SDIO_CLK 3
#define SDIO_CMD 4
#define SDIO_D0 5
#define SDIO_D1 6
#define SDIO_D2 7
#define SDIO_D3 8
#define PIN_RST 9
#define PIN_CEB 10
#define PIN_WREB 11 //clk
#define PIN_D0 12
#define PIN_D1 13
#define PIN_D2 14
#define PIN_D3 15
#define PIN_D4 16
#define PIN_D5 17
#define PIN_D6 18
#define PIN_D7 19
#define PIN_IRQ 20
#define PIN_CS2 21
#define PIN_USB_VBUS 24
#define PIN_DEV_TX0 0
#define PIN_DEV_RX0 1
#define PIN_INPUT_MASK 0x2FFE00
#define CARD_ID_NTR 0x800000C2
#define CARD_ID_TWL 0xC00000C2
typedef void (*sd_callback_t)(uint32_t bytes_complete);
static inline uint millis(void)
{
return us_to_ms(time_us_64());
}
#ifdef __cplusplus
#include "sd/SdCard.h"
extern SdCard gSdCard;
#endif

299
src/main.cpp Normal file
View File

@@ -0,0 +1,299 @@
#include "common.h"
#include <string.h>
#include <stdio.h>
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/structs/scb.h"
#include "pico/binary_info.h"
#include "ntrCard.pio.h"
#include "romData.h"
#include "pico/multicore.h"
#include "blowfish.h"
#include "scrambler.h"
#include "sd/fatfs/ff.h"
#include "ntrCardRom.h"
#include "ntrCardSpiUart.h"
#include "r4.h"
#include "sd/SdCard.h"
#include "scramblerRing.h"
#include "pico/bootrom.h"
#include "hardware/xosc.h"
#include "powerSaving.h"
static u32 sProgramOffset;
FATFS sFatFs;
SdCard gSdCard;
static bool sIsSdCardMounted;
#ifdef DETECT_CONSOLE_TYPE
static void setRomToDsiRom(void)
{
gNtrRomEmu.romData = gDsiRom;
gNtrRomEmu.romSize = ((u32)gDsiRomSize + 511) & ~511;
}
#endif
static void resetNtrCard(void)
{
ntrc_resetUsb();
pwr_disableAfterBootPowerSaving();
ntrc_setNormalMode();
gNtrRomEmu.securePhase1 = false;
gNtrRomEmu.cmdScramble = false;
gNtrRomEmu.dataScramble = false;
gComputeScrambler = false;
gNtrRomEmu.scrRingRPtr = gScramblerRing;
gScramblerRingWPtr = gScramblerRing;
gNtrRomEmu.wordIdx = 0;
gNtrRomEmu.twlMode = false;
gNtrRomEmu.readDataDestination = nullptr;
gNtrRomEmu.readDataCompleteHandler = nullptr;
gNtrRomEmu.readDataLimit = 0;
#ifdef ENABLE_R4_MODE
ntrc_resetR4();
#endif
dma_channel_abort(0);
pio_sm_set_enabled(pio0, 0, false);
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
pio_sm_clear_fifos(pio0, 0);
pio_sm_restart(pio0, 0);
pio_sm_clkdiv_restart(pio0, 0);
irq_clear(PIO0_IRQ_0);
irq_set_enabled(PIO0_IRQ_0, true);
pio_sm_exec(pio0, 0, pio_encode_jmp(sProgramOffset));
pio_sm_set_enabled(pio0, 0, true);
#ifdef DETECT_CONSOLE_TYPE
setRomToDsiRom();
gNtrRomEmu.cardId = 0xC00000C2;
#endif
#ifdef DSPICO_ENABLE_WRFUXXED
ntrc_resetSpiUart();
#endif
}
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
static u64 sResetStart;
#endif
static void __time_critical_func(gpioIrq)(uint gpio, u32 events)
{
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
u64 time = time_us_64();
#endif
if (gpio == PIN_RST)
{
if (events & GPIO_IRQ_EDGE_FALL)
{
pio_sm_set_enabled(pio0, 0, false);
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
}
if (events & GPIO_IRQ_EDGE_RISE)
{
resetNtrCard();
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
u32 resetTime = time - sResetStart;
if (resetTime > 700000)
pio_sm_set_enabled(pio0, 0, false);
sResetStart = time;
#endif
}
}
}
void __scratch_x("cpu1") core1_entry(void)
{
irq_set_mask_enabled(~0u, false);
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
while (!gComputeScrambler)
{
gScramblerRingWPtr = gScramblerRing;
__wfe();
}
while (1)
{
u32* wPtr = gScramblerRingWPtr;
u32* next = SCR_RING_WRAP(wPtr + 1);
if (next == gNtrRomEmu.scrRingRPtr)
{
__wfe();
continue;
}
*wPtr = scr_getNext32(&gScramblerState);
gScramblerRingWPtr = next;
}
}
static void initSd(void)
{
memset(&sFatFs, 0, sizeof(sFatFs));
//try mounting 16 times
bool ok = false;
for (int i = 0; i < 16; i++)
{
if (f_mount(&sFatFs, "0:", 1) == FR_OK)
{
ok = true;
sIsSdCardMounted = true;
break;
}
}
if (!ok)
{
sIsSdCardMounted = false;
}
}
static void tryRebootToBootsel(void)
{
if (!sIsSdCardMounted)
{
xosc_init();
reset_usb_boot(0, 0);
}
}
int __time_critical_func(main)()
{
bi_decl(bi_program_description("Ntr card emulator"));
bi_decl(bi_pin_mask_with_name(0xFF000, "Ntr card D0-D7"));
bi_decl(bi_1pin_with_name(PIN_IRQ, "Ntr card irq"));
bi_decl(bi_1pin_with_name(PIN_CEB, "Ntr card ceb (rom enable)"));
bi_decl(bi_1pin_with_name(PIN_WREB, "Ntr card wreb (clock)"));
bi_decl(bi_1pin_with_name(PIN_RST, "Ntr card reset"));
bi_decl(bi_1pin_with_name(PIN_CS2, "Ntr card cs2 (spi enable)"));
// u64 bootTime = time_us_64();
// set_sys_clock_khz(/*125000*/200000, true);
// 200 MHz = 1200 MHz / 6 / 1
set_sys_clock_pll(1200000000, 6, 1);
dma_channel_claim(0);
memset(&gNtrRomEmu, 0, sizeof(gNtrRomEmu));
// We support DSi mode if the firmware is dual mode, or if a single rom has the DSi flag set
#ifndef DETECT_CONSOLE_TYPE
if (gDefaultRom[0x12] & 2)
{
#endif
gNtrRomEmu.cardId = CARD_ID_TWL;
#ifndef DETECT_CONSOLE_TYPE
}
else
{
gNtrRomEmu.cardId = CARD_ID_NTR;
}
#endif
multicore_launch_core1(core1_entry);
gpio_init_mask(PIN_INPUT_MASK);
gpio_set_dir_in_masked(PIN_INPUT_MASK);
gpio_init(PIN_IRQ);
gpio_put(PIN_IRQ, 0);
gpio_set_dir(PIN_IRQ, GPIO_OUT);
gpio_disable_pulls(PIN_D0);
gpio_disable_pulls(PIN_D1);
gpio_disable_pulls(PIN_D2);
gpio_disable_pulls(PIN_D3);
gpio_disable_pulls(PIN_D4);
gpio_disable_pulls(PIN_D5);
gpio_disable_pulls(PIN_D6);
gpio_disable_pulls(PIN_D7);
gpio_set_slew_rate(PIN_D0, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D1, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D2, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D3, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D4, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D5, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D6, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(PIN_D7, GPIO_SLEW_RATE_FAST);
gpio_pull_up(PIN_CEB);
gpio_pull_up(PIN_WREB);
gpio_pull_down(PIN_RST);
gpio_pull_up(PIN_CS2);
gpio_set_drive_strength(PIN_D0, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D1, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D2, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D3, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D4, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D5, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D6, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_drive_strength(PIN_D7, GPIO_DRIVE_STRENGTH_2MA);
#ifdef DETECT_CONSOLE_TYPE
setRomToDsiRom();
gNtrRomEmu.isDSMode = true;
#else
gNtrRomEmu.romData = gDefaultRom;
gNtrRomEmu.romSize = (u32)gDefaultRomSize;
gNtrRomEmu.romSize = (gNtrRomEmu.romSize + 511) & ~511;
#endif
sProgramOffset = pio_add_program(pio0, &ntr_card_program);
#ifdef DSPICO_ENABLE_WRFUXXED
u32 spiUartProgOffs = pio_add_program(pio0, &ntr_card_spi_program);
#endif
pio_sm_config c = ntr_card_program_get_default_config(sProgramOffset);
sm_config_set_out_pins(&c, PIN_D0, 8);
sm_config_set_in_pins(&c, PIN_D0);
sm_config_set_sideset_pins(&c, PIN_D5);
sm_config_set_set_pins(&c, PIN_D0, 5);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_in_shift(&c, false, true, 32);
sm_config_set_clkdiv(&c, 1);
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
pio_sm_set_pins_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
pio0->input_sync_bypass = 0xFF000;
pio_gpio_init(pio0, PIN_D0);
pio_gpio_init(pio0, PIN_D1);
pio_gpio_init(pio0, PIN_D2);
pio_gpio_init(pio0, PIN_D3);
pio_gpio_init(pio0, PIN_D4);
pio_gpio_init(pio0, PIN_D5);
pio_gpio_init(pio0, PIN_D6);
pio_gpio_init(pio0, PIN_D7);
pio_sm_init(pio0, 0, sProgramOffset, &c);
pio_set_irq0_source_enabled(pio0, pis_sm0_rx_fifo_not_empty, true);
irq_set_exclusive_handler(PIO0_IRQ_0, ntrc_pioIrq);
#ifdef DSPICO_ENABLE_WRFUXXED
ntrc_initSpiUart(spiUartProgOffs);
#endif
gpio_set_irq_callback(gpioIrq);
gpio_set_irq_enabled(PIN_RST, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
irq_set_enabled(IO_IRQ_BANK0, true);
irq_init_priorities();
irq_set_priority(PIO0_IRQ_0, 0x40);
irq_set_priority(IO_IRQ_BANK0, 0x40);
irq_set_priority(USBCTRL_IRQ, 0x80);
irq_set_priority(DMA_IRQ_1, 0x80);
irq_set_priority(TIMER_IRQ_0, 0x80);
// printf("Starting\n");
// printf("sProgramOffset %d\n", sProgramOffset);
// printf("Boot time %d\n", (u32)bootTime);
resetNtrCard();
sIsSdCardMounted = false;
initSd();
tryRebootToBootsel();
pwr_initPowerSaving();
while (1)
{
gSdCard.Update();
gSdCard.Update();
#ifdef ENABLE_R4_MODE
ntrc_gameR4Update();
#endif
__wfi();
}
}

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