├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── Makefile.miniboot ├── README.md ├── blobs └── dldi │ ├── README.md │ ├── acep.dldi │ ├── ak2.dldi │ ├── ak2_cmd24.dldi │ ├── ez5h.dldi │ ├── ez5n.dldi │ ├── gmtf.dldi │ ├── m3ds.dldi │ ├── nmk6.dldi │ ├── r4idsn.dldi │ ├── r4tf.dldi │ ├── scds.dldi │ ├── scdssdhc.dldi │ ├── sg3d.dldi │ └── ttio.dldi ├── scripts ├── dsbize.lua ├── r4crypt.lua └── xorcrypt.lua └── source ├── arm7.ld ├── arm7 └── crt0.s ├── arm9.ld ├── arm9 ├── bootstub.h ├── bootstub.s ├── console.c ├── console.h ├── crt0.s ├── default_font.bin ├── dldi_patch.c ├── dldi_patch.h ├── dldi_stub.s ├── fatfs │ ├── diskio.c │ ├── dldi.h │ ├── ffconf.h │ └── ffsystem.c └── main.c ├── common ├── bios.h ├── common.h ├── cp15_asm.h ├── cpsr_asm.h ├── dka.h ├── libc │ ├── aeabi.h │ ├── macros.inc │ ├── memcmp.c │ ├── memcpy.s │ ├── memmove.s │ ├── memset.s │ ├── nanoprintf.c │ ├── nanoprintf.h │ ├── ndsabi.h │ ├── rmemcpy.s │ ├── strchr.c │ └── strlen.c ├── wonderful.h └── xor_constant.s └── misc ├── r4isdhc_pad.ld └── r4isdhc_pad.s /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | container: skylyrac/blocksds:slim-v1.7.3 14 | steps: 15 | - name: Clone project 16 | uses: actions/checkout@v4 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install missing dependencies 21 | run: wf-pacman -Sy --noconfirm wf-tools 22 | 23 | - name: Build 24 | run: make 25 | 26 | - name: Archive artifacts 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: miniboot archive 30 | path: dist 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fatfs"] 2 | path = fatfs 3 | url = https://github.com/WonderfulToolchain/wf-fatfs 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is provided ‘as-is’, without any express or implied 2 | warranty. In no event will the authors be held liable for any damages 3 | arising from the use of this software. 4 | 5 | Permission is granted to anyone to use this software for any purpose, 6 | including commercial applications, and to alter it and redistribute it 7 | freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 14 | 2. Altered source versions must be plainly marked as such, and must not be 15 | misrepresented as being the original software. 16 | 17 | 3. This notice may not be removed or altered from any source 18 | distribution. 19 | 20 | This software additionally includes software components licensed under other 21 | terms, which are listed below: 22 | 23 | ## FatFs License 24 | 25 | FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files. 26 | 27 | /*----------------------------------------------------------------------------/ 28 | / FatFs - Generic FAT Filesystem Module Rx.xx / 29 | /-----------------------------------------------------------------------------/ 30 | / 31 | / Copyright (C) 20xx, ChaN, all right reserved. 32 | / 33 | / FatFs module is an open source software. Redistribution and use of FatFs in 34 | / source and binary forms, with or without modification, are permitted provided 35 | / that the following condition is met: 36 | / 37 | / 1. Redistributions of source code must retain the above copyright notice, 38 | / this condition and the following disclaimer. 39 | / 40 | / This software is provided by the copyright holder and contributors "AS IS" 41 | / and any warranties related to this software are DISCLAIMED. 42 | / The copyright owner or contributors be NOT LIABLE for any damages caused 43 | / by use of this software. 44 | /----------------------------------------------------------------------------*/ 45 | 46 | Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license. 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: CC0-1.0 2 | # 3 | # SPDX-FileContributor: Adrian "asie" Siekierka, 2024 4 | 5 | export WONDERFUL_TOOLCHAIN ?= /opt/wonderful 6 | export BLOCKSDS ?= /opt/blocksds/core 7 | 8 | # Tools 9 | # ----- 10 | 11 | LUA := $(WONDERFUL_TOOLCHAIN)/bin/wf-lua 12 | DLDIPATCH := $(BLOCKSDS)/tools/dldipatch/dldipatch 13 | NDSTOOL := $(BLOCKSDS)/tools/ndstool/ndstool 14 | CC := $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/arm-none-eabi-gcc 15 | OBJCOPY := $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objcopy 16 | CP := cp 17 | MAKE := make 18 | MKDIR := mkdir 19 | RM := rm -rf 20 | 21 | # Verbose flag 22 | # ------------ 23 | 24 | ifeq ($(V),1) 25 | _V := 26 | else 27 | _V := @ 28 | endif 29 | 30 | # Build rules 31 | # ----------- 32 | 33 | ARM9ELF := build/arm9.elf 34 | ARM7ELF := build/arm7.elf 35 | NDSROM := build/miniboot.nds 36 | NDSROM_M3DS_BASE := build/miniboot.m3ds.nds 37 | 38 | SCRIPT_R4CRYPT := scripts/r4crypt.lua 39 | SCRIPT_DSBIZE := scripts/dsbize.lua 40 | SCRIPT_XORCRYPT := scripts/xorcrypt.lua 41 | 42 | NDSROM_ACE3DS_DLDI := blobs/dldi/acep.dldi 43 | NDSROM_AK2_DLDI := blobs/dldi/ak2.dldi 44 | NDSROM_DSONE_DLDI := blobs/dldi/scds.dldi 45 | NDSROM_DSONE_SDHC_DLDI := blobs/dldi/scdssdhc.dldi 46 | NDSROM_DSTT_DLDI := blobs/dldi/ttio.dldi 47 | NDSROM_EZ5_DLDI := blobs/dldi/ez5h.dldi 48 | NDSROM_EZ5N_DLDI := blobs/dldi/ez5n.dldi 49 | NDSROM_GMTF_DLDI := blobs/dldi/gmtf.dldi 50 | NDSROM_M3DS_DLDI := blobs/dldi/m3ds.dldi 51 | NDSROM_MKR6_DLDI := blobs/dldi/nmk6.dldi 52 | NDSROM_R4_DLDI := blobs/dldi/r4tf.dldi 53 | NDSROM_R4DSPRO_DLDI := blobs/dldi/ak2_cmd24.dldi 54 | NDSROM_R4IDSN_DLDI := blobs/dldi/r4idsn.dldi 55 | NDSROM_STARGATE_DLDI := blobs/dldi/sg3d.dldi 56 | 57 | NDSROM_ACE3DS := dist/ace3dsplus/_ds_menu.dat 58 | NDSROM_AK2 := dist/generic/akmenu4.nds 59 | NDSROM_DSONE := dist/generic/scfw.sc 60 | NDSROM_DSONE_SDHC := dist/dsonesdhc/scfw.sc 61 | NDSROM_DSTT := dist/generic/ttmenu.dat 62 | NDSROM_EDGEI := dist/generic/dsedgei.dat 63 | NDSROM_EZ5 := dist/generic/ez5sys.bin 64 | NDSROM_EZ5N := dist/generic/ezds.dat 65 | NDSROM_GMTF := dist/generic/bootme.nds 66 | NDSROM_GWBLUE := dist/gwblue/_dsmenu.dat 67 | NDSROM_ITDS_ENG := dist/m3ds/boot.eng 68 | NDSROM_ITDS_GB := dist/m3ds/boot.gb 69 | NDSROM_ITDS_JP := dist/m3ds/boot.jp 70 | NDSROM_M3DS := dist/m3ds/SYSTEM/g6dsload.eng 71 | NDSROM_MKR6 := dist/mkr6/_boot_ds.nds 72 | NDSROM_R4 := dist/generic/_DS_MENU.DAT 73 | NDSROM_R4DSPRO := dist/r4dspro/_ds_menu.dat 74 | NDSROM_R4IDSN := dist/r4idsn/_dsmenu.dat 75 | NDSROM_R4ILS := dist/ace3dsplus/_dsmenu.dat 76 | NDSROM_R4IRTSB := dist/m3ds/_ds_menu.sys 77 | NDSROM_R4ISDHC := dist/generic/r4.dat 78 | NDSROM_R4ITT := dist/r4itt/_ds_menu.dat 79 | NDSROM_R4RTS := dist/m3ds/loader.eng 80 | NDSROM_STARGATE := dist/stargate/_ds_menu.dat 81 | 82 | .PHONY: all clean arm9 arm9plus arm9_nobootstub arm7 83 | 84 | all: arm9plus \ 85 | $(NDSROM) \ 86 | $(NDSROM_ACE3DS) \ 87 | $(NDSROM_AK2) \ 88 | $(NDSROM_DSONE) \ 89 | $(NDSROM_DSONE_SDHC) \ 90 | $(NDSROM_DSTT) \ 91 | $(NDSROM_EDGEI) \ 92 | $(NDSROM_EZ5) \ 93 | $(NDSROM_EZ5N) \ 94 | $(NDSROM_GMTF) \ 95 | $(NDSROM_GWBLUE) \ 96 | $(NDSROM_ITDS_ENG) \ 97 | $(NDSROM_ITDS_GB) \ 98 | $(NDSROM_ITDS_JP) \ 99 | $(NDSROM_M3DS) \ 100 | $(NDSROM_MKR6) \ 101 | $(NDSROM_R4) \ 102 | $(NDSROM_R4DSPRO) \ 103 | $(NDSROM_R4IDSN) \ 104 | $(NDSROM_R4ILS) \ 105 | $(NDSROM_R4IRTSB) \ 106 | $(NDSROM_R4ISDHC) \ 107 | $(NDSROM_R4ITT) \ 108 | $(NDSROM_R4RTS) \ 109 | $(NDSROM_STARGATE) 110 | $(_V)$(CP) LICENSE README.md dist/ 111 | 112 | $(NDSROM_ACE3DS): $(NDSROM) $(NDSROM_ACE3DS_DLDI) $(SCRIPT_R4CRYPT) 113 | @$(MKDIR) -p $(@D) 114 | @echo " DLDI $@" 115 | $(_V)$(CP) $(NDSROM) $@ 116 | $(_V)$(DLDIPATCH) patch $(NDSROM_ACE3DS_DLDI) $@ 117 | @echo " R4CRYPT $@" 118 | $(_V)$(LUA) $(SCRIPT_R4CRYPT) $@ 4002 119 | 120 | $(NDSROM_GWBLUE): arm9 arm7 $(NDSROM_ACE3DS_DLDI) $(SCRIPT_R4CRYPT) 121 | @$(MKDIR) -p $(@D) 122 | @echo " NDSTOOL $@" 123 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 124 | -9 build/arm9.bin -7 build/arm7.bin \ 125 | -r7 0x2380000 -e7 0x2380000 \ 126 | -r9 0x2000450 -e9 0x2000450 -h 0x200 \ 127 | -g "####" "##" "R4IT" 128 | @echo " DLDI $@" 129 | $(_V)$(DLDIPATCH) patch $(NDSROM_ACE3DS_DLDI) $@ 130 | @echo " R4CRYPT $@" 131 | $(_V)$(LUA) $(SCRIPT_R4CRYPT) $@ 4002 132 | 133 | $(NDSROM_R4ILS): arm9 arm7 $(NDSROM_ACE3DS_DLDI) $(SCRIPT_R4CRYPT) 134 | @$(MKDIR) -p $(@D) 135 | @echo " NDSTOOL $@" 136 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 137 | -9 build/arm9.bin -7 build/arm7.bin \ 138 | -r7 0x2380000 -e7 0x2380000 \ 139 | -r9 0x2000450 -e9 0x2000450 -h 0x200 \ 140 | -g "####" "##" "R4XX" 141 | @echo " DLDI $@" 142 | $(_V)$(DLDIPATCH) patch $(NDSROM_ACE3DS_DLDI) $@ 143 | @echo " R4CRYPT $@" 144 | $(_V)$(LUA) $(SCRIPT_R4CRYPT) $@ 4002 145 | 146 | $(NDSROM_R4IDSN): arm9 arm7 $(NDSROM_R4IDSN_DLDI) 147 | @$(MKDIR) -p $(@D) 148 | @echo " NDSTOOL $@" 149 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 150 | -9 build/arm9.bin -7 build/arm7.bin \ 151 | -r7 0x2380000 -e7 0x2380000 \ 152 | -r9 0x2000000 -e9 0x2000000 -h 0x200 153 | @echo " DLDI $@" 154 | $(_V)$(DLDIPATCH) patch $(NDSROM_R4IDSN_DLDI) $@ 155 | 156 | $(NDSROM_MKR6): arm9 arm7 $(NDSROM_MKR6_DLDI) 157 | @$(MKDIR) -p $(@D) 158 | @echo " NDSTOOL $@" 159 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 160 | -9 build/arm9.bin -7 build/arm7.bin \ 161 | -r7 0x2380000 -e7 0x2380000 \ 162 | -r9 0x2000000 -e9 0x2000000 -h 0x200 163 | @echo " DLDI $@" 164 | $(_V)$(DLDIPATCH) patch $(NDSROM_MKR6_DLDI) $@ 165 | 166 | $(NDSROM_R4ITT): arm9 arm7 $(NDSROM_AK2_DLDI) 167 | @$(MKDIR) -p $(@D) 168 | @echo " NDSTOOL $@" 169 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 170 | -9 build/arm9.bin -7 build/arm7.bin \ 171 | -r7 0x2380000 -e7 0x2380000 \ 172 | -r9 0x2000800 -e9 0x2000800 -h 0x200 173 | @echo " DLDI $@" 174 | $(_V)$(DLDIPATCH) patch $(NDSROM_AK2_DLDI) $@ 175 | 176 | $(NDSROM_DSONE): arm9 arm7 $(NDSROM_DSONE_DLDI) 177 | @$(MKDIR) -p $(@D) 178 | @echo " NDSTOOL $@" 179 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 180 | -9 build/arm9.bin -7 build/arm7.bin \ 181 | -r7 0x2380000 -e7 0x2380000 \ 182 | -r9 0x2000450 -e9 0x2000450 -h 0x200 \ 183 | -g "ENG0" 184 | @echo " DLDI $@" 185 | $(_V)$(DLDIPATCH) patch $(NDSROM_DSONE_DLDI) $@ 186 | 187 | $(NDSROM_DSONE_SDHC): arm9 arm7 $(NDSROM_DSONE_SDHC_DLDI) 188 | @$(MKDIR) -p $(@D) 189 | @echo " NDSTOOL $@" 190 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 191 | -9 build/arm9.bin -7 build/arm7.bin \ 192 | -r7 0x2380000 -e7 0x2380000 \ 193 | -r9 0x2000450 -e9 0x2000450 -h 0x200 \ 194 | -g "ENG0" 195 | @echo " DLDI $@" 196 | $(_V)$(DLDIPATCH) patch $(NDSROM_DSONE_SDHC_DLDI) $@ 197 | 198 | $(NDSROM_M3DS_BASE): arm9_nobootstub arm7 $(NDSROM_M3DS_DLDI) $(SCRIPT_DSBIZE) 199 | @$(MKDIR) -p $(@D) 200 | @echo " NDSTOOL $@" 201 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 202 | -9 build/arm9_nobootstub.bin -7 build/arm7.bin \ 203 | -r7 0x23ad800 -e7 0x23ad800 \ 204 | -r9 0x2380000 -e9 0x2380000 -h 0x200 205 | @echo " DLDI $@" 206 | $(_V)$(DLDIPATCH) patch $(NDSROM_M3DS_DLDI) $@ 207 | @echo " DSBIZE $@" 208 | $(_V)$(LUA) $(SCRIPT_DSBIZE) $@ 209 | @echo " CRC $@" 210 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -fh $@ 211 | 212 | $(NDSROM_M3DS): $(NDSROM_M3DS_BASE) $(SCRIPT_XORCRYPT) 213 | @$(MKDIR) -p $(@D) 214 | @echo " XORCRYP $@" 215 | cp $(NDSROM_M3DS_BASE) $@ 216 | $(_V)$(LUA) $(SCRIPT_XORCRYPT) $@ 12 217 | @# M3 firmware checks the existence of this file but does nothing with it. 218 | @# The original kernel does check it, but our goal is to replace that. 219 | $(_V)touch $(@D)/g6dsload.1 220 | 221 | $(NDSROM_ITDS_ENG): $(NDSROM_M3DS_BASE) $(SCRIPT_XORCRYPT) 222 | @$(MKDIR) -p $(@D) 223 | @echo " XORCRYP $@" 224 | cp $(NDSROM_M3DS_BASE) $@ 225 | $(_V)$(LUA) $(SCRIPT_XORCRYPT) $@ 32 226 | 227 | $(NDSROM_ITDS_GB): $(NDSROM_M3DS_BASE) $(SCRIPT_XORCRYPT) 228 | @$(MKDIR) -p $(@D) 229 | @echo " XORCRYP $@" 230 | cp $(NDSROM_M3DS_BASE) $@ 231 | $(_V)$(LUA) $(SCRIPT_XORCRYPT) $@ 33 232 | 233 | $(NDSROM_ITDS_JP): $(NDSROM_M3DS_BASE) $(SCRIPT_XORCRYPT) 234 | @$(MKDIR) -p $(@D) 235 | @echo " XORCRYP $@" 236 | cp $(NDSROM_M3DS_BASE) $@ 237 | $(_V)$(LUA) $(SCRIPT_XORCRYPT) $@ 37 238 | 239 | $(NDSROM_R4IRTSB) $(NDSROM_R4RTS): $(NDSROM_M3DS_BASE) $(SCRIPT_XORCRYPT) 240 | @$(MKDIR) -p $(@D) 241 | @echo " XORCRYP $@" 242 | cp $(NDSROM_M3DS_BASE) $@ 243 | $(_V)$(LUA) $(SCRIPT_XORCRYPT) $@ 72 244 | 245 | $(NDSROM_R4ISDHC): arm9_r4isdhc arm7 $(NDSROM_DSTT_DLDI) 246 | @$(MKDIR) -p $(@D) 247 | @echo " NDSTOOL $@" 248 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 249 | -9 build/arm9_r4isdhc.bin -7 build/arm7.bin \ 250 | -r7 0x2380000 -e7 0x2380000 \ 251 | -r9 0x2000000 -e9 0x2000450 -h 0x200 252 | @echo " DLDI $@" 253 | $(_V)$(DLDIPATCH) patch $(NDSROM_DSTT_DLDI) $@ 254 | 255 | $(NDSROM_R4): $(NDSROM) $(NDSROM_R4_DLDI) $(SCRIPT_R4CRYPT) 256 | @$(MKDIR) -p $(@D) 257 | @echo " DLDI $@" 258 | $(_V)$(CP) $(NDSROM) $@ 259 | $(_V)$(DLDIPATCH) patch $(NDSROM_R4_DLDI) $@ 260 | @echo " R4CRYPT $@" 261 | $(_V)$(LUA) $(SCRIPT_R4CRYPT) $@ 262 | 263 | $(NDSROM_AK2) $(NDSROM_EDGEI): $(NDSROM) $(NDSROM_AK2_DLDI) 264 | @$(MKDIR) -p $(@D) 265 | @echo " DLDI $@" 266 | $(_V)$(CP) $(NDSROM) $@ 267 | $(_V)$(DLDIPATCH) patch $(NDSROM_AK2_DLDI) $@ 268 | 269 | $(NDSROM_EZ5): $(NDSROM) $(NDSROM_EZ5_DLDI) 270 | @$(MKDIR) -p $(@D) 271 | @echo " DLDI $@" 272 | $(_V)$(CP) $(NDSROM) $@ 273 | $(_V)$(DLDIPATCH) patch $(NDSROM_EZ5_DLDI) $@ 274 | 275 | $(NDSROM_EZ5N): $(NDSROM) $(NDSROM_EZ5N_DLDI) 276 | @$(MKDIR) -p $(@D) 277 | @echo " DLDI $@" 278 | $(_V)$(CP) $(NDSROM) $@ 279 | $(_V)$(DLDIPATCH) patch $(NDSROM_EZ5N_DLDI) $@ 280 | $(_V)sed -i "s|\xED\xA5\x8D\xBF|\x00\x00\x00\x00|g" $@ 281 | 282 | $(NDSROM_GMTF): $(NDSROM) $(NDSROM_GMTF_DLDI) 283 | @$(MKDIR) -p $(@D) 284 | @echo " DLDI $@" 285 | $(_V)$(CP) $(NDSROM) $@ 286 | $(_V)$(DLDIPATCH) patch $(NDSROM_GMTF_DLDI) $@ 287 | 288 | $(NDSROM_R4DSPRO): $(NDSROM) $(NDSROM_R4DSPRO_DLDI) 289 | @$(MKDIR) -p $(@D) 290 | @echo " DLDI $@" 291 | $(_V)$(CP) $(NDSROM) $@ 292 | $(_V)$(DLDIPATCH) patch $(NDSROM_R4DSPRO_DLDI) $@ 293 | 294 | $(NDSROM_STARGATE): $(NDSROM) $(NDSROM_STARGATE_DLDI) 295 | @$(MKDIR) -p $(@D) 296 | @echo " DLDI $@" 297 | $(_V)$(CP) $(NDSROM) $@ 298 | $(_V)$(DLDIPATCH) patch $(NDSROM_STARGATE_DLDI) $@ 299 | 300 | $(NDSROM_DSTT): $(NDSROM) $(NDSROM_DSTT_DLDI) 301 | @$(MKDIR) -p $(@D) 302 | @echo " DLDI $@" 303 | $(_V)$(CP) $(NDSROM) $@ 304 | $(_V)$(DLDIPATCH) patch $(NDSROM_DSTT_DLDI) $@ 305 | 306 | $(NDSROM): arm9 arm7 307 | @$(MKDIR) -p $(@D) 308 | @echo " NDSTOOL $@" 309 | $(_V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 310 | -9 build/arm9.bin -7 build/arm7.bin \ 311 | -r7 0x2380000 -e7 0x2380000 \ 312 | -r9 0x2000450 -e9 0x2000450 -h 0x200 313 | 314 | clean: 315 | @echo " CLEAN" 316 | $(_V)$(RM) build dist 317 | 318 | arm9: 319 | $(_V)+$(MAKE) -f Makefile.miniboot TARGET=arm9 --no-print-directory 320 | 321 | arm9plus: 322 | $(_V)+$(MAKE) -f Makefile.miniboot TARGET=arm9plus --no-print-directory 323 | 324 | arm9_nobootstub: 325 | $(_V)+$(MAKE) -f Makefile.miniboot TARGET=arm9_nobootstub --no-print-directory 326 | 327 | arm9_r4isdhc: arm9 328 | @echo " R4ISDHC" 329 | $(_V)$(CC) -o build/r4isdhc_pad.elf -nostartfiles -Tsource/misc/r4isdhc_pad.ld source/misc/r4isdhc_pad.s 330 | $(_V)$(OBJCOPY) -O binary build/r4isdhc_pad.elf build/r4isdhc_pad.bin 331 | $(_V)cat build/r4isdhc_pad.bin build/arm9.bin > build/arm9_r4isdhc.bin 332 | $(_V)truncate -s 433264 build/arm9_r4isdhc.bin 333 | 334 | arm7: 335 | $(_V)+$(MAKE) -f Makefile.miniboot TARGET=arm7 --no-print-directory 336 | -------------------------------------------------------------------------------- /Makefile.miniboot: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: CC0-1.0 2 | # 3 | # SPDX-FileContributor: Adrian "asie" Siekierka, 2024 4 | 5 | export WONDERFUL_TOOLCHAIN ?= /opt/wonderful 6 | export BLOCKSDS ?= /opt/blocksds/core 7 | 8 | # Configuration 9 | # ------------- 10 | 11 | DEFINES := 12 | SOURCEDIRS := source/common source/common/libc 13 | ASSETDIRS := 14 | 15 | ifeq ($(TARGET),arm9) 16 | CPU := arm9 17 | LINKSCRIPT := arm9 18 | SOURCEDIRS += fatfs/source source/arm9 source/arm9/fatfs 19 | ASSETDIRS += source/arm9 20 | else 21 | ifeq ($(TARGET),arm9plus) 22 | CPU := arm9 23 | LINKSCRIPT := arm9 24 | SOURCEDIRS += fatfs/source source/arm9 source/arm9/fatfs 25 | ASSETDIRS += source/arm9 26 | DEFINES += -DPLUS 27 | else 28 | ifeq ($(TARGET),arm9_nobootstub) 29 | CPU := arm9 30 | LINKSCRIPT := arm9 31 | SOURCEDIRS += fatfs/source source/arm9 source/arm9/fatfs 32 | ASSETDIRS += source/arm9 33 | DEFINES += -D_NO_BOOTSTUB 34 | else 35 | ifeq ($(TARGET),arm7) 36 | CPU := arm7 37 | LINKSCRIPT := arm7 38 | SOURCEDIRS += source/arm7 39 | else 40 | $(error no target defined) 41 | endif 42 | endif 43 | endif 44 | endif 45 | INCLUDEDIRS := $(SOURCEDIRS) 46 | 47 | BUILDDIR := build/$(TARGET) 48 | BIN := build/$(TARGET).bin 49 | ELF := build/$(TARGET).elf 50 | MAP := build/$(TARGET).map 51 | 52 | # Tools 53 | # ----- 54 | 55 | CC := $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/arm-none-eabi-gcc 56 | OBJCOPY := $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objcopy 57 | MKDIR := mkdir 58 | RM := rm -rf 59 | 60 | # Verbose flag 61 | # ------------ 62 | 63 | ifeq ($(V),1) 64 | _V := 65 | else 66 | _V := @ 67 | endif 68 | 69 | # Source files 70 | # ------------ 71 | 72 | ifneq ($(ASSETDIRS),) 73 | SOURCES_BIN := $(shell find -L $(ASSETDIRS) -maxdepth 1 -name "*.bin") 74 | INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(ASSETDIRS)) 75 | else 76 | SOURCES_BIN := 77 | endif 78 | 79 | SOURCES_S := $(shell find -L $(SOURCEDIRS) -maxdepth 1 -name "*.s") 80 | SOURCES_C := $(shell find -L $(SOURCEDIRS) -maxdepth 1 -name "*.c") 81 | 82 | # Compiler and linker flags 83 | # ------------------------- 84 | 85 | ifeq ($(CPU),arm9) 86 | DEFINES += -DARM9 87 | else 88 | ifeq ($(CPU),arm7) 89 | DEFINES += -DARM7 90 | else 91 | ifeq ($(CPU),arm79) 92 | DEFINES += -DARM7 -DARM9 93 | endif 94 | endif 95 | endif 96 | 97 | WARNFLAGS := -Wall 98 | 99 | INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) 100 | 101 | ASFLAGS := -x assembler-with-cpp $(DEFINES) \ 102 | $(INCLUDEFLAGS) -ffunction-sections -fdata-sections 103 | 104 | CFLAGS := -std=gnu2x $(WARNFLAGS) $(DEFINES) -ffreestanding -nostdlib \ 105 | $(INCLUDEFLAGS) -ffunction-sections -fdata-sections -Oz -flto 106 | 107 | LDFLAGS := $(DEFINES) -Wl,-Map,$(MAP) -Wl,--gc-sections -nostdlib -lgcc \ 108 | -Wl,--no-warn-rwx-segments -Tsource/$(LINKSCRIPT).ld -flto 109 | 110 | ifeq ($(CPU),arm9) 111 | CFLAGS += -marm -mcpu=arm946e-s+nofp 112 | LDFLAGS += -Wl,--use-blx 113 | else 114 | CFLAGS += -marm -mcpu=arm7tdmi 115 | endif 116 | 117 | # Intermediate build files 118 | # ------------------------ 119 | 120 | OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) 121 | 122 | OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ 123 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) 124 | 125 | OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES) 126 | 127 | DEPS := $(OBJS:.o=.d) 128 | 129 | # Targets 130 | # ------- 131 | 132 | .PHONY: all clean 133 | 134 | all: $(BIN) 135 | 136 | $(BIN): $(ELF) 137 | @echo " BIN $@" 138 | $(V)$(OBJCOPY) -O binary $(ELF) $(BIN) 139 | 140 | $(ELF): $(OBJS) 141 | @echo " LINK $@" 142 | $(_V)$(CC) -o $@ $(OBJS) $(LDFLAGS) 143 | 144 | clean: 145 | @echo " CLEAN" 146 | $(_V)$(RM) $(ELF) $(BUILDDIR) 147 | 148 | # Rules 149 | # ----- 150 | 151 | $(BUILDDIR)/%.s.o : %.s | $(OBJS_ASSETS) 152 | @echo " AS $<" 153 | @$(MKDIR) -p $(@D) 154 | $(_V)$(CC) $(ASFLAGS) -MMD -MP -MJ $(patsubst %.o,%.cc.json,$@) -c -o $@ $< 155 | 156 | $(BUILDDIR)/%.c.o : %.c | $(OBJS_ASSETS) 157 | @echo " CC $<" 158 | @$(MKDIR) -p $(@D) 159 | $(_V)$(CC) $(CFLAGS) -MMD -MP -MJ $(patsubst %.o,%.cc.json,$@) -c -o $@ $< 160 | 161 | $(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin 162 | @echo " BIN2C $<" 163 | @$(MKDIR) -p $(@D) 164 | $(_V)$(WONDERFUL_TOOLCHAIN)/bin/wf-bin2c -a 4 $(@D) $< 165 | $(_V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c 166 | 167 | # Include dependency files if they exist 168 | # -------------------------------------- 169 | 170 | -include $(DEPS) 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miniboot 2 | 3 | Minimal flashcart bootstrap for NDS consoles. Loads `/BOOT.NDS` and 4 | papers over various device-specific quirks, allowing running 100% 5 | clean homebrew launchers on a variety of such devices. 6 | 7 | ## Usage 8 | 9 | ### Installation 10 | 11 | Copy the contents of the specified directory to the root of your 12 | flashcart's memory card: 13 | 14 | | Device | Directory | Notes | 15 | | ------ | --------- | ----- | 16 | | Ace3DS+ / Ace3DS X | ace3dsplus | | 17 | | Acekard 2/2i | generic | | 18 | | Datel Games 'n' Music | generic | | 19 | | DSTT | generic | | 20 | | EDGEi | generic | | 21 | | EZ-Flash Parallel | generic | | 22 | | EZ-Flash V | generic | | 23 | | Gateway Blue | gwblue | | 24 | | iTouchDS | m3ds | No bootstub support | 25 | | M3 DS Real | m3ds | No bootstub support | 26 | | R4 (original) | generic | | 27 | | R4 i.L.S. | ace3dsplus | | 28 | | R4/R4i Ultra | r4itt | | 29 | | R4iDSN | r4idsn | | 30 | | R4iSDHC RTS (black) (r4isdhc.com) | m3ds | No bootstub support | 31 | | R4iTT 3DS | r4itt | | 32 | | R4/R4i RTS (r4rts.com) | m3ds | No bootstub support | 33 | | r4dspro.com | r4dspro | | 34 | | r4ids.cn | r4itt | | 35 | | R6 Gold/Silver | mkr6 | | 36 | | Stargate 3DS | stargate | | 37 | | SuperCard DSONE | generic | | 38 | | SuperCard DSONE SDHC | dsonesdhc | | 39 | | Various timebomb carts | generic | | 40 | 41 | Notes: 42 | 43 | - "No bootstub support" means that nds-miniboot does not install its own bootstub, enabling homebrew to return to the boot program on exit. As most people chain nds-miniboot with a menu that adds its own bootstub (like nds-hb-menu), this is not a problem in practice for most users. 44 | 45 | ### Troubleshooting 46 | 47 | Hold START while loading to enable debug output. Note that launching 48 | will only continue once you release START. 49 | 50 | ## Development 51 | 52 | To build miniboot, the [Wonderful toolchain](https://wonderful.asie.pl/)'s 53 | `wf-tools`, `toolchain-gcc-arm-none-eabi`, as well as [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/) 1.7.0+ (for 54 | `ndstool` and `dldipatch`) are required. Please follow their respective installation instructions. 55 | 56 | ### Motivation 57 | 58 | `.nds` files can be loaded essentially anywhere in RAM: in particular, 59 | 3.75MB out of the 4MB of main RAM can be used. As such, the easiest 60 | way to load such a file is to put the bootstrap code outside main RAM. 61 | 62 | As the fastest place to execute code from is ITCM, I started wondering 63 | if one could create a bootstrap that operates entirely from ITCM (32K) 64 | and DTCM (16K). That is not a lot of room - the DLDI driver which is 65 | provided by a flashcart vendor has to have 16KB of space reserved, 66 | leaving only 16KB for the remaining code. Thankfully, there's not much 67 | code involved: one needs a lightweight copy of FatFs to read files from 68 | the filesystem, a DLDI patcher for the loaded ARM9 binary, and some 69 | simple load/reset code on top of that. 70 | 71 | I've also never created a freestanding NDS homebrew program before, so 72 | I wanted to see what goes into that. 73 | 74 | ### Porting 75 | 76 | Assorted notes: 77 | 78 | * `arm9.bin` and `arm7.bin` are extracted from the ELF file as they are 79 | position-independent - the binaries relocate themselves upon execution 80 | to areas outside of main RAM, they just have to be started from their 81 | first word (offset 0). 82 | * Initiailization is deliberately sparse; if a given device needs 83 | additional cleanup, please document it! 84 | 85 | ## License 86 | 87 | The `miniboot` source code itself is covered under a mix of two licenses: 88 | 89 | * the Zlib license, 90 | * the FatFs license. 91 | 92 | Individual binaries for specific flashcarts may be covered under their own 93 | respective licenses. 94 | -------------------------------------------------------------------------------- /blobs/dldi/README.md: -------------------------------------------------------------------------------- 1 | Most of the DLDI drivers in this directory are provided by [lifehackerhansol](https://github.com/lifehackerhansol/DLDI), 2 | whose repository doubles as an unofficial maintenance hub. 3 | -------------------------------------------------------------------------------- /blobs/dldi/acep.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/acep.dldi -------------------------------------------------------------------------------- /blobs/dldi/ak2.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/ak2.dldi -------------------------------------------------------------------------------- /blobs/dldi/ak2_cmd24.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/ak2_cmd24.dldi -------------------------------------------------------------------------------- /blobs/dldi/ez5h.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/ez5h.dldi -------------------------------------------------------------------------------- /blobs/dldi/ez5n.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/ez5n.dldi -------------------------------------------------------------------------------- /blobs/dldi/gmtf.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/gmtf.dldi -------------------------------------------------------------------------------- /blobs/dldi/m3ds.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/m3ds.dldi -------------------------------------------------------------------------------- /blobs/dldi/nmk6.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/nmk6.dldi -------------------------------------------------------------------------------- /blobs/dldi/r4idsn.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/r4idsn.dldi -------------------------------------------------------------------------------- /blobs/dldi/r4tf.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/r4tf.dldi -------------------------------------------------------------------------------- /blobs/dldi/scds.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/scds.dldi -------------------------------------------------------------------------------- /blobs/dldi/scdssdhc.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/scdssdhc.dldi -------------------------------------------------------------------------------- /blobs/dldi/sg3d.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/sg3d.dldi -------------------------------------------------------------------------------- /blobs/dldi/ttio.dldi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/blobs/dldi/ttio.dldi -------------------------------------------------------------------------------- /scripts/dsbize.lua: -------------------------------------------------------------------------------- 1 | -- SPDX-License-Identifier: Zlib 2 | -- 3 | -- Copyright (c) 2024 lifehackerhansol 4 | 5 | -- Offsets: 6 | -- 0xC8: ARM9 ROM offset - 0xC8 7 | -- 0xCC: ARM9 address 8 | -- 0xD0: ARM9 size 9 | -- 0xD4: ARM7 ROM offset - 0xC8 10 | -- 0xD8: ARM7 address 11 | -- 0xDC: ARM7 size 12 | 13 | function read32(file, offset) 14 | file:seek("set", offset) 15 | return string.unpack(" = io.open(arg[1], "r+b") 25 | local i = 0 26 | 27 | -- Read ARM/ARM7 location data 28 | local arm9romOffset = read32(input_file, 0x20) 29 | local arm9executeAddress = read32(input_file, 0x24) 30 | local arm9destination = read32(input_file, 0x28) 31 | local arm9binarySize = read32(input_file, 0x2C) 32 | local arm7romOffset = read32(input_file, 0x30) 33 | local arm7executeAddress = read32(input_file, 0x34) 34 | local arm7destination = read32(input_file, 0x38) 35 | local arm7binarySize = read32(input_file, 0x3C) 36 | 37 | -- Verify execution address and destination is identical. 38 | -- That's all the M3 supports. 39 | if arm9executeAddress ~= arm9destination 40 | then 41 | error("e9 != r9. Rebuild ROM using ndstool.") 42 | exit() 43 | end 44 | if arm7executeAddress ~= arm7destination 45 | then 46 | error("e7 != r7. Rebuild ROM using ndstool.") 47 | exit() 48 | end 49 | 50 | -- Write addresses to the DSBooter expected area 51 | -- ldr pc, [pc,#0x10] 52 | write32(input_file, 0xC0, 0xE59FF010) 53 | -- ldr pc, [pc,#0x0C] 54 | write32(input_file, 0xC4, 0xE59FF00C) 55 | write32(input_file, 0xC8, arm9romOffset - 0xC8) 56 | write32(input_file, 0xCC, arm9destination) 57 | write32(input_file, 0xD0, arm9binarySize) 58 | write32(input_file, 0xD4, (arm7romOffset - 0xC8)) 59 | write32(input_file, 0xD8, arm7destination) 60 | write32(input_file, 0xDC, arm7binarySize) 61 | -------------------------------------------------------------------------------- /scripts/r4crypt.lua: -------------------------------------------------------------------------------- 1 | -- SPDX-License-Identifier: Zlib 2 | -- 3 | -- Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | -- Original algorithm discovered by yasu, 2007 6 | -- 7 | -- http://hp.vector.co.jp/authors/VA013928/ 8 | -- http://www.usay.jp/ 9 | -- http://www.yasu.nu/ 10 | 11 | local ADD_PADDING = false 12 | 13 | local function crypt(sector, key1, is_encrypt) 14 | local result = "" 15 | local slen = #sector 16 | if ADD_PADDING then slen = 512 end 17 | 18 | for i=1,slen do 19 | local b = 0 20 | if i <= #sector then b = string.byte(sector, i) end 21 | 22 | local key2 = ((key1 >> 7) & 0x80) 23 | | ((key1 >> 6) & 0x60) 24 | | ((key1 >> 5) & 0x10) 25 | | ((key1 >> 4) & 0x0C) 26 | | (key1 & 0x03); 27 | 28 | result = result .. string.char(b ~ key2) 29 | if is_encrypt then b = b ~ key2 end 30 | 31 | local tmp = (b << 8) ~ key1 32 | local tmpXor = 0 33 | for i=0,15 do tmpXor = tmpXor ~ (tmp >> i) end 34 | 35 | key1 = 0 36 | key1 = key1 | (((tmpXor & 0x80) | (tmp & 0x7C)) << 8) 37 | key1 = key1 | (((tmp ~ (tmpXor >> 14)) << 8) & 0x0300) 38 | key1 = key1 | ((((tmp >> 1) ~ tmp) >> 6) & 0xFC) 39 | key1 = key1 | (((tmp ~ (tmpXor >> 1)) >> 8) & 0x03) 40 | end 41 | 42 | return result 43 | end 44 | 45 | local input_file = io.open(arg[1], "rb") 46 | local input_data = input_file:read("a") 47 | input_file:close() 48 | input_file = io.open(arg[1], "wb") 49 | 50 | local encrypt = true 51 | local key = tonumber(arg[2] or "484A", 16) 52 | local i = 0 53 | 54 | while true do 55 | local sector = string.sub(input_data, i*512+1, i*512+512) 56 | if (sector == nil) or (#sector <= 0) then break end 57 | 58 | input_file:write(crypt(sector, (key ~ i) & 0xFFFF, encrypt)) 59 | 60 | i = i + 1 61 | end 62 | 63 | input_file:close() 64 | -------------------------------------------------------------------------------- /scripts/xorcrypt.lua: -------------------------------------------------------------------------------- 1 | -- SPDX-License-Identifier: Zlib 2 | -- 3 | -- Copyright (c) 2024 lifehackerhansol 4 | 5 | local input_file = io.open(arg[1], "r+b") 6 | local key = tonumber(arg[2], 16) 7 | 8 | -- Encrypt header 9 | if key ~= 0 then 10 | input_file:seek("set", 0) 11 | header = input_file:read(512) 12 | local encryptedHeader = "" 13 | for i=1,512 do encryptedHeader = encryptedHeader .. string.char(string.byte(header, i) ~ key) end 14 | 15 | -- Write encrypted header 16 | input_file:seek("set", 0x000) 17 | input_file:write(encryptedHeader) 18 | end 19 | -------------------------------------------------------------------------------- /source/arm7.ld: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | /* Copyright (c) 2024 Adrian "asie" Siekierka */ 3 | 4 | OUTPUT_FORMAT("elf32-littlearm") 5 | OUTPUT_ARCH(arm) 6 | ENTRY(_start) 7 | 8 | MEMORY { 9 | RAM : ORIGIN = 0x03800000, LENGTH = 64K 10 | } 11 | 12 | SECTIONS { 13 | .text : ALIGN(4) { 14 | *(.start) 15 | *(.text .text.* .gnu.linkonce.t.*) 16 | *(.rodata .rodata.* .gnu.linkonce.r.*) 17 | *(.data .data.* .gnu.linkonce.d.*) 18 | . = ALIGN(512); 19 | } >RAM 20 | 21 | .bss (NOLOAD) : ALIGN(4) { 22 | *(.arguments) 23 | *(.bss .bss.* .gnu.linkonce.b.*) 24 | *(COMMON) 25 | . = ALIGN(. != 0 ? 4 : 1); 26 | } >RAM 27 | 28 | .noinit (NOLOAD) : ALIGN(4) { 29 | *(.noinit .noinit.* .gnu.linkonce.n.*) 30 | . = ALIGN(. != 0 ? 4 : 1); 31 | } >RAM 32 | 33 | __bss_start = ADDR(.bss); 34 | __bss_chunks = (SIZEOF(.bss) + 31) >> 5; 35 | 36 | /* === DWARF 2 debug sections === */ 37 | 38 | .debug 0 : { *(.debug) } 39 | .line 0 : { *(.line) } 40 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 41 | .debug_sfnames 0 : { *(.debug_sfnames) } 42 | .debug_aranges 0 : { *(.debug_aranges) } 43 | .debug_pubnames 0 : { *(.debug_pubnames) } 44 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 45 | .debug_abbrev 0 : { *(.debug_abbrev) } 46 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } 47 | .debug_frame 0 : { *(.debug_frame) } 48 | .debug_str 0 : { *(.debug_str) } 49 | .debug_loc 0 : { *(.debug_loc) } 50 | .debug_macinfo 0 : { *(.debug_macinfo) } 51 | .debug_weaknames 0 : { *(.debug_weaknames) } 52 | .debug_funcnames 0 : { *(.debug_funcnames) } 53 | .debug_typenames 0 : { *(.debug_typenames) } 54 | .debug_varnames 0 : { *(.debug_varnames) } 55 | } 56 | -------------------------------------------------------------------------------- /source/arm7/crt0.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | .arm 6 | .syntax unified 7 | 8 | .global _start 9 | .section .start, "ax" 10 | 11 | _start: 12 | mov r11, #0 13 | // Disable IRQs. 14 | ldr r3, =0x4000000 15 | str r11, [r3, #0x208] // IME 16 | str r11, [r3, #0x210] // IE 17 | mvn r10, r11 18 | str r10, [r3, #0x214] // IF 19 | // Disable DMA. 20 | add r4, r3, #0xB0 21 | mov r5, #0x30 22 | _dma_clear: 23 | subs r5, r5, #4 24 | strge r11, [r4], #4 25 | bgt _dma_clear 26 | // Disable timers. 27 | str r11, [r3, #0x100] 28 | str r11, [r3, #0x104] 29 | str r11, [r3, #0x108] 30 | str r11, [r3, #0x10C] 31 | // Clear/initialize FIFO. 32 | str r11, [r3, #0x180] // IPCSYNC 33 | ldr r4, =0xC008 // Enable, acknowledge error, flush 34 | str r4, [r3, #0x184] // IPCFIFOCNT 35 | 36 | // Copy up to 160 bytes to the stack area. 37 | adr r0, _stage2 38 | ldr r1, =0x380FE00 39 | mov lr, r1 40 | ldmiage r0!, {r3-r10} 41 | stmiage r1!, {r3-r10} 42 | ldmiage r0!, {r3-r10} 43 | stmiage r1!, {r3-r10} 44 | ldmiage r0!, {r3-r10} 45 | stmiage r1!, {r3-r10} 46 | ldmiage r0!, {r3-r10} 47 | stmiage r1!, {r3-r10} 48 | ldmiage r0!, {r3-r10} 49 | stmiage r1!, {r3-r10} 50 | 51 | ldr r8, =0x4000180 52 | add r9, r8, #8 53 | mov r10, #0x4100000 54 | str r11, [r8] 55 | mov r12, r11 56 | 57 | ldr sp, =0x380FFB0 58 | bx lr 59 | 60 | .pool 61 | 62 | // stage2 must be at most 160 bytes. 63 | // r8 = IPCSYNC 64 | // r9 = IPCFIFOSEND 65 | // r10 = IPCFIFORECV 66 | // r11 = 0 67 | // r12 = counter for IPCSYNC 68 | _stage2: 69 | _stage2_init: 70 | mov r7, #1 71 | _stage2_sync_loop: 72 | bl _stage2_wait_recv_r7 73 | add r7, r7, #0x1 74 | ands r7, r7, #0xF 75 | bne _stage2_sync_loop 76 | _stage2_next_cmd: 77 | bl _stage2_bump_counter 78 | mov r7, #0 79 | bl _stage2_wait_recv_r7 80 | _stage2_wait_cmd: 81 | ldr r0, [r8] 82 | and r0, r0, #0xF 83 | // command 0xF = synchronize 84 | cmp r0, #0xF 85 | beq _stage2_next_cmd 86 | cmp r0, #0x2 87 | beq _stage2_cmd2 88 | cmp r0, #0x1 89 | bne _stage2_wait_cmd 90 | 91 | // command 0x1 = copy N1 bytes from N2 to N3 92 | _stage2_cmd1: 93 | ldr r0, [r10] 94 | ldr r2, [r10] 95 | ldr r1, [r10] 96 | // r1 = destination address 97 | // r2 = source address 98 | _stage2_copy: 99 | subs r0, r0, #4 100 | ldrge r3, [r2], #4 101 | strge r3, [r1], #4 102 | bgt _stage2_copy 103 | b _stage2_next_cmd 104 | 105 | // command 0x2 = jump to entrypoint, which is already set in the header 106 | _stage2_cmd2: 107 | bl _stage2_bump_counter 108 | swi 0 109 | 110 | // wait to receive the value in r7 from ARM9 via IPC, then send N+1 111 | _stage2_wait_recv_r7: 112 | ldr r0, [r8] 113 | and r0, r0, #0xF 114 | cmp r0, r7 115 | bne _stage2_wait_recv_r7 116 | 117 | // send N+1 to ARM9 via IPC 118 | _stage2_bump_counter: 119 | add r12, r12, #0x100 120 | and r12, r12, #0xF00 121 | str r12, [r8] 122 | bx lr 123 | -------------------------------------------------------------------------------- /source/arm9.ld: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Zlib */ 2 | /* Copyright (c) 2024 Adrian "asie" Siekierka */ 3 | 4 | OUTPUT_FORMAT("elf32-littlearm") 5 | OUTPUT_ARCH(arm) 6 | ENTRY(_start) 7 | 8 | MEMORY { 9 | ITCM : ORIGIN = 0x01000000, LENGTH = 32K 10 | DTCM : ORIGIN = 0x0E000000, LENGTH = 16K 11 | RAM : ORIGIN = 0x02000000, LENGTH = 3840K 12 | } 13 | 14 | SECTIONS { 15 | /* === Constants === */ 16 | PROVIDE(__dldi_size = 16384); 17 | __dldi_log2_size = LOG2CEIL(__dldi_size + 1) - 1; 18 | 19 | /* === ITCM === */ 20 | 21 | /* Keep everything in one section to make objcopy work fine. */ 22 | .text : ALIGN(4) { 23 | *(.start) 24 | *(.text .text.* .gnu.linkonce.t.*) 25 | *(.rodata .rodata.* .gnu.linkonce.r.*) 26 | *(.data .data.* .gnu.linkonce.d.*) 27 | . = ALIGN(512); 28 | __dldi_start = .; 29 | *(.dldi .dldi.*) 30 | . = __dldi_start + __dldi_size; 31 | . = ALIGN(512); 32 | } >ITCM AT>RAM 33 | 34 | /* === DTCM === */ 35 | 36 | .bss (NOLOAD) : ALIGN(4) { 37 | *(.arguments) 38 | *(.bss .bss.* .gnu.linkonce.b.*) 39 | *(COMMON) 40 | . = ALIGN(. != 0 ? 4 : 1); 41 | } >DTCM AT>RAM 42 | 43 | .noinit (NOLOAD) : ALIGN(4) { 44 | *(.noinit .noinit.* .gnu.linkonce.n.*) 45 | . = ALIGN(. != 0 ? 4 : 1); 46 | } >DTCM AT>RAM 47 | 48 | __itcm_start = ADDR(.text); 49 | __itcm_chunks = (SIZEOF(.text) + 31) >> 5; 50 | __bss_start = ADDR(.bss); 51 | __bss_chunks = (SIZEOF(.bss) + 15) >> 4; 52 | 53 | /* === DWARF 2 debug sections === */ 54 | 55 | .debug 0 : { *(.debug) } 56 | .line 0 : { *(.line) } 57 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 58 | .debug_sfnames 0 : { *(.debug_sfnames) } 59 | .debug_aranges 0 : { *(.debug_aranges) } 60 | .debug_pubnames 0 : { *(.debug_pubnames) } 61 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 62 | .debug_abbrev 0 : { *(.debug_abbrev) } 63 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } 64 | .debug_frame 0 : { *(.debug_frame) } 65 | .debug_str 0 : { *(.debug_str) } 66 | .debug_loc 0 : { *(.debug_loc) } 67 | .debug_macinfo 0 : { *(.debug_macinfo) } 68 | .debug_weaknames 0 : { *(.debug_weaknames) } 69 | .debug_funcnames 0 : { *(.debug_funcnames) } 70 | .debug_typenames 0 : { *(.debug_typenames) } 71 | .debug_varnames 0 : { *(.debug_varnames) } 72 | } 73 | -------------------------------------------------------------------------------- /source/arm9/bootstub.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __BOOTSTUB_H__ 6 | #define __BOOTSTUB_H__ 7 | 8 | #include 9 | 10 | typedef struct { 11 | uint32_t arm9_entry; 12 | uint32_t arm7_entry; 13 | void *arm9_target_entry; 14 | void *arm7_target_entry; 15 | } bootstub_header_t; 16 | 17 | extern bootstub_header_t bootstub; 18 | extern char bootstub_end; 19 | #define bootstub_size ((uint32_t) (((uint8_t*) &bootstub_end) - ((uint8_t*) &bootstub))) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /source/arm9/bootstub.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | .arm 6 | .syntax unified 7 | 8 | // Ensure the code is compatible with both ARM9 and ARM7 CPUs. 9 | .cpu arm7tdmi 10 | 11 | // This implements a dkA-compliant "bootstub", which uses libnds FIFO calls 12 | // to reset one CPU from the other and (hopefully) get both CPUs to execute at 13 | // the "target entrypoint". 14 | .global bootstub 15 | bootstub: 16 | b bootstub_arm9_entry // ARM9 entrypoint 17 | b bootstub_arm7_entry // ARM7 entrypoint 18 | bootstub_arm9_target: 19 | .word 0 // ARM9 target entrypoint, user-provided 20 | bootstub_arm7_target: 21 | .word 0 // ARM7 target entrypoint, user-provided 22 | 23 | // Bootstub code follows here. 24 | bootstub_arm9_entry: 25 | bootstub_arm7_entry: 26 | mov r0, #0 27 | ldr r8, =0x4000000 // I/O port base offset in memory. 28 | ldr r9, =0x2FFFE00 // .nds header base offset in memory 29 | 30 | // Disable IRQs. 31 | str r0, [r8, #0x208] // IME 32 | str r0, [r8, #0x210] // IE 33 | mvn r1, r0 34 | str r1, [r8, #0x214] // IF 35 | 36 | // Disable DMA. 37 | add r1, r8, #0xB0 38 | mov r2, #0x30 39 | .Ldma_clear: 40 | subs r2, r2, #4 41 | strge r0, [r1], #4 42 | bgt .Ldma_clear 43 | 44 | // Disable timers. 45 | str r0, [r8, #0x100] 46 | str r0, [r8, #0x104] 47 | str r0, [r8, #0x108] 48 | str r0, [r8, #0x10C] 49 | 50 | // Prepare environment for BIOS soft reset. 51 | ldr r0, bootstub_arm7_target 52 | str r0, [r9, #0x34] // Set ARM7 entrypoint. 53 | ldr r0, bootstub_arm9_target 54 | str r0, [r9, #0x24] // Set ARM9 entrypoint. 55 | 56 | // Reset the other CPU via libnds fifo. 57 | ldr r0, =0x0C04000C // Reset command. 58 | // BlocksDS uses ...000B for ARM7->ARM9 resets, 59 | // but it also never calls the ARM7 entrypoint, 60 | // so it's fine. 61 | str r0, [r8, #0x188] // Send reset command via FIFO. 62 | 63 | // Wait for the other CPU to signal 0x1. 64 | .Lwait_cpu_1: 65 | ldr r1, [r8, #0x180] 66 | and r1, r1, #0xF 67 | cmp r1, #1 68 | bne .Lwait_cpu_1 69 | 70 | // Send 0x1 to the other CPU. 71 | mov r0, #0x100 72 | str r0, [r8, #0x180] 73 | 74 | // Wait for the other CPU to signal 0x0. 75 | .Lwait_cpu_2: 76 | ldr r1, [r8, #0x180] 77 | ands r1, r1, #0xF 78 | bne .Lwait_cpu_2 79 | 80 | // Send 0x0 to the other CPU. 81 | str r1, [r8, #0x180] 82 | 83 | // BIOS soft reset time! 84 | swi 0 85 | 86 | .pool 87 | 88 | .global bootstub_end 89 | bootstub_end: 90 | -------------------------------------------------------------------------------- /source/arm9/console.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | /* Rudimentary text console implementation. */ 6 | 7 | #include "bios.h" 8 | #include "console.h" 9 | #include "dldi.h" 10 | 11 | static bool displayInitialized = false; 12 | bool debugEnabled = false; 13 | 14 | extern const uint8_t default_font[760]; 15 | #define DISPLAY_TILES ((uint32_t*) 0x6000000) 16 | #define DISPLAY_MAP ((uint16_t*) 0x6007800) 17 | 18 | #define FONT_X_MIN 0 19 | #define FONT_Y_MIN 3 20 | #define FONT_X_MAX 31 21 | #define FONT_Y_MAX 23 22 | 23 | static uint16_t fontPalette = 0x0000; 24 | static uint16_t fontX = 0; 25 | static uint16_t fontY = 0; 26 | static bool fontLimited = false; 27 | 28 | static void consolePutc(int ch, void *userdata) { 29 | if ((ch & 0xFF) == 10) 30 | goto newLine; 31 | 32 | DISPLAY_MAP[fontY * 32 + fontX] = (ch & 0xFF) | fontPalette; 33 | 34 | ++fontX; 35 | if (fontLimited && fontX > FONT_X_MAX) { 36 | newLine: 37 | fontX = FONT_X_MIN; 38 | if ((++fontY) > FONT_Y_MAX) { 39 | --fontY; 40 | __aeabi_memmove4(DISPLAY_MAP + (FONT_Y_MIN * 32), DISPLAY_MAP + ((FONT_Y_MIN + 1) * 32), (FONT_Y_MAX - FONT_Y_MIN) * 64); 41 | __aeabi_memset(DISPLAY_MAP + (fontY * 32) + FONT_X_MIN, (FONT_X_MAX - FONT_X_MIN + 1) * 2, 0); 42 | } 43 | } 44 | } 45 | 46 | void consoleInit(void); 47 | 48 | int eprintf(const char *format, ...) { 49 | consoleInit(); 50 | 51 | va_list val; 52 | va_start(val, format); 53 | int rv = npf_vpprintf(consolePutc, NULL, format, val); 54 | va_end(val); 55 | return rv; 56 | } 57 | 58 | void displayReset(void) { 59 | // Clear display registers, force blanking. 60 | REG_DISPCNT = DISPCNT_FORCE_DISABLE; 61 | __ndsabi_wordset4((void*) 0x4000004, 0x58 - 4, 0); 62 | } 63 | 64 | void consoleInit(void) { 65 | if (displayInitialized) return; 66 | 67 | // Configure palette 68 | MEM_PALETTE_BG[0] = RGB555(0, 0, 0); 69 | MEM_PALETTE_BG[1] = RGB555(31, 31, 31); 70 | MEM_PALETTE_BG[1 + (1 << 4)] = RGB555(16, 16, 16); 71 | 72 | // Clear background map, tile 0. 73 | __ndsabi_wordset4(DISPLAY_TILES, 32, 0); 74 | __ndsabi_wordset4(DISPLAY_MAP, 2048, 0); 75 | 76 | // Unpack font tiles. 77 | uint32_t unpackParams[2] = {760 | (1 << 16) | (4 << 24), 0}; 78 | swiBitUnpack(default_font, DISPLAY_TILES + (32 * 32 / 4), unpackParams); 79 | 80 | // Configure background layer 0. 81 | REG_BG0CNT = BGCNT_TILE_BASE(0) /* +0KB */ 82 | | BGCNT_MAP_BASE(15) /* +30KB */ 83 | | BGCNT_MAP_32x32 84 | | BGCNT_16_COLOR; 85 | 86 | // Enable main display. 87 | REG_DISPCNT = DISPCNT_BG_MODE(0) 88 | | DISPCNT_BG0_ENABLE 89 | | DISPCNT_BG_DISPLAY; 90 | 91 | // Draw header, initialize cursor X/Y. 92 | displayInitialized = true; 93 | fontX = (32 - 8) >> 1; 94 | fontY = 0; 95 | fontPalette = 0x0000; 96 | fontLimited = false; 97 | eprintf("miniboot"); 98 | 99 | { 100 | fontY = 1; 101 | fontPalette = 0x1000; 102 | int len = strlen(_io_dldi_stub.friendlyName); 103 | if (len > 32) { 104 | fontX = 0; 105 | char c = _io_dldi_stub.friendlyName[29]; 106 | _io_dldi_stub.friendlyName[29] = 0; 107 | eprintf("%s...", _io_dldi_stub.friendlyName); 108 | _io_dldi_stub.friendlyName[29] = c; 109 | } else { 110 | fontX = (32 - len) >> 1; 111 | eprintf("%s", _io_dldi_stub.friendlyName); 112 | } 113 | } 114 | 115 | fontX = FONT_X_MIN; 116 | fontY = FONT_Y_MIN; 117 | fontPalette = 0x0000; 118 | fontLimited = true; 119 | } 120 | -------------------------------------------------------------------------------- /source/arm9/console.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __CONSOLE_H__ 6 | #define __CONSOLE_H__ 7 | 8 | #include "common.h" 9 | 10 | #ifdef NDEBUG 11 | #define debugEnabled false 12 | #define dprintf(...) {} 13 | #else 14 | extern bool debugEnabled; 15 | #define dprintf(...) { if (debugEnabled) eprintf(__VA_ARGS__); } 16 | #endif 17 | 18 | /** 19 | * Reset 2D graphics engine registers, turn off display. 20 | */ 21 | void displayReset(void); 22 | 23 | /** 24 | * Initialize rudimentary console. 25 | */ 26 | void consoleInit(void); 27 | 28 | /** 29 | * Print a string to console; initialize if not initialized. 30 | * 31 | * To print only when debug mode is enabled, use dprintf. 32 | */ 33 | int eprintf(const char *format, ...); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /source/arm9/crt0.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #include "cp15_asm.h" 6 | #include "cpsr_asm.h" 7 | 8 | .arm 9 | .syntax unified 10 | 11 | .global _start 12 | .section .start, "ax" 13 | _start: 14 | // Vectors + jump to crt0. 15 | b .Lstart_real 16 | ldr pc, =0xFFFF0004 17 | ldr pc, =0xFFFF0008 18 | ldr pc, =0xFFFF000C 19 | ldr pc, =0xFFFF0010 20 | // ARM7 communications port (accessed as 0x2800014). 21 | .word 0 22 | ldr pc, =0xFFFF0018 23 | ldr pc, =0xFFFF001C 24 | 25 | .pool 26 | 27 | .Lstart_real: 28 | // Is the binary relocated? 29 | adr r1, _start 30 | mov r0, #0x01000000 31 | cmp r0, r1 32 | beq .Lat_itcm 33 | // If not, relocate it to ITCM. 34 | 35 | mov r11, #0 36 | // Disable IRQs. 37 | ldr r3, =0x4000000 38 | str r11, [r3, #0x208] // IME 39 | str r11, [r3, #0x210] // IE 40 | mvn r10, r11 41 | str r10, [r3, #0x214] // IF 42 | // Disable DMA (AK2i, and possibly others, leave unclean state.) 43 | add r4, r3, #0xB0 44 | mov r5, #0x30 45 | _dma_clear: 46 | subs r5, r5, #4 47 | strge r11, [r4], #4 48 | bgt _dma_clear 49 | // Disable timers. 50 | str r11, [r3, #0x100] 51 | str r11, [r3, #0x104] 52 | str r11, [r3, #0x108] 53 | str r11, [r3, #0x10C] 54 | // Clear/initialize FIFO. 55 | str r11, [r3, #0x180] // IPCSYNC 56 | ldr r4, =0xC008 // Enable, acknowledge error, flush 57 | str r4, [r3, #0x184] // IPCFIFOCNT 58 | 59 | // Initialize ITCM. 60 | mov r3, #(CP15_TCM_SIZE_32MB << 1) 61 | mcr CP15_REG9_ITCM_CONTROL(r3) 62 | 63 | // Enable ITCM. 64 | ldr r3, =(CP15_CONTROL_ALTERNATE_VECTOR_SELECT \ 65 | | CP15_CONTROL_ITCM_ENABLE \ 66 | | CP15_CONTROL_ICACHE_ENABLE \ 67 | | CP15_CONTROL_RESERVED_SBO_MASK) 68 | mcr CP15_REG1_CONTROL_REGISTER(r3) 69 | 70 | // Copy program to ITCM, cannot use DMA. 71 | // r1 = source (_start in RAM) 72 | // r0 = ITCM start 73 | mov r11, r0 74 | ldr r2, =__itcm_chunks 75 | .Litcm_copy: 76 | subs r2, r2, #1 77 | ldmiage r1!, {r3-r10} 78 | stmiage r0!, {r3-r10} 79 | bgt .Litcm_copy 80 | 81 | // Return to _start, now in the correct memory location. 82 | bx r11 83 | 84 | .Lat_itcm: 85 | // Initialize the rest of the MPU for our needs. 86 | // We can expect modern homebrew running after miniboot to do a better 87 | // job, so let's just do what is necessary here. 88 | 89 | // Initialize DTCM to 0x0E000000. 90 | mov r6, 0x0E000000 91 | orr r0, r6, (CP15_TCM_SIZE_16KB << 1) 92 | mcr CP15_REG9_DTCM_CONTROL(r0) 93 | 94 | // Enable ITCM/DTCM, disable DCache and PU. 95 | ldr r0, =(CP15_CONTROL_ITCM_ENABLE \ 96 | | CP15_CONTROL_DTCM_ENABLE \ 97 | | CP15_CONTROL_RESERVED_SBO_MASK) 98 | mcr CP15_REG1_CONTROL_REGISTER(r0) 99 | 100 | // Invalidate caches after disabling them. 101 | mcr CP15_REG7_FLUSH_ICACHE 102 | mcr CP15_REG7_FLUSH_DCACHE 103 | // Drain the write buffer, too, just in case. 104 | mcr CP15_REG7_DRAIN_WRITE_BUFFER 105 | 106 | // Clear BSS in DTCM. 107 | // r6 = BSS start (DTCM start) 108 | ldr r1, =__bss_chunks 109 | mov r2, #0 110 | mov r3, r2 111 | mov r4, r2 112 | mov r5, r2 113 | .Lbss_clear: 114 | subs r1, r1, #1 115 | stmiage r6!, {r2-r5} 116 | bgt .Lbss_clear 117 | 118 | // Initialize stacks 119 | and r0, r6, 0xFF000000 120 | add r0, r0, 0x00003EC0 121 | msr CPSR_fsxc, #(CPSR_I | CPSR_F | CPSR_SUPERVISOR) 122 | add sp, r0, 0x100 123 | msr CPSR_fsxc, #(CPSR_I | CPSR_F | CPSR_IRQ) 124 | add sp, r0, 0xE0 125 | msr CPSR_fsxc, #(CPSR_SYSTEM) 126 | mov sp, r0 127 | 128 | b main 129 | 130 | .pool 131 | -------------------------------------------------------------------------------- /source/arm9/default_font.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/source/arm9/default_font.bin -------------------------------------------------------------------------------- /source/arm9/dldi_patch.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // SPDX-FileNotice: Modified from the original version for miniboot. 3 | // 4 | // Copyright (c) 2006 Michael Chisholm (Chishm) and Tim Seidel (Mighty Max). 5 | // Copyright (c) 2024 Adrian "asie" Siekierka 6 | 7 | #include "dldi_patch.h" 8 | #include "aeabi.h" 9 | #include "console.h" 10 | 11 | #define XOR_CONSTANT_VALUE 0xAA55AA55 12 | #define OBFUSCATED_COMPARE(a, b) \ 13 | (xor_constant(a, XOR_CONSTANT_VALUE) == (( \ 14 | (((b) & 0xFF000000) >> 24) | \ 15 | (((b) & 0xFF0000) >> 8) | \ 16 | (((b) & 0xFF00) << 8) | \ 17 | (((b) & 0xFF) << 24)) ^ XOR_CONSTANT_VALUE)) 18 | 19 | static void dldi_relocate(DLDI_INTERFACE *io, void *targetAddress) { 20 | uint32_t offset; 21 | uint8_t **address; 22 | uint8_t *prevAddrStart; 23 | uint8_t *prevAddrSpaceEnd; 24 | uint8_t *prevAddrAllocEnd; 25 | 26 | offset = (uint32_t) targetAddress - (uint32_t) io->dldiStart; 27 | prevAddrStart = io->dldiStart; 28 | 29 | // For GOT sections, we can safely relocate the maximum possible driver size, 30 | // as that area can only include addresses. 31 | prevAddrSpaceEnd = io->dldiStart + (1 << io->driverSize); 32 | 33 | // For non-GOT sections, we need to minimize the range of addresses changed. 34 | // This is either the end of the data section or the end of the BSS section, 35 | // if the BSS section is valid. 36 | prevAddrAllocEnd = io->dldiEnd; 37 | if (io->bssStart >= prevAddrStart && io->bssStart < prevAddrSpaceEnd 38 | && io->bssEnd > io->dldiEnd && io->bssEnd <= prevAddrSpaceEnd) 39 | prevAddrAllocEnd = io->bssEnd; 40 | 41 | // Correct all pointers to the offsets from the location of this interface 42 | io->dldiStart = ((uint8_t*) io->dldiStart) + offset; 43 | io->dldiEnd = ((uint8_t*) io->dldiEnd) + offset; 44 | io->interworkStart = ((uint8_t*) io->interworkStart) + offset; 45 | io->interworkEnd = ((uint8_t*) io->interworkEnd) + offset; 46 | io->gotStart = ((uint8_t*) io->gotStart) + offset; 47 | io->gotEnd = ((uint8_t*) io->gotEnd) + offset; 48 | io->bssStart = ((uint8_t*) io->bssStart) + offset; 49 | io->bssEnd = ((uint8_t*) io->bssEnd) + offset; 50 | 51 | io->startup = (void*)((uint8_t*) io->startup + offset); 52 | io->isInserted = (void*)((uint8_t*) io->isInserted + offset); 53 | io->readSectors = (void*)((uint8_t*) io->readSectors + offset); 54 | io->writeSectors = (void*)((uint8_t*) io->writeSectors + offset); 55 | io->clearStatus = (void*)((uint8_t*) io->clearStatus + offset); 56 | io->shutdown = (void*)((uint8_t*) io->shutdown + offset); 57 | 58 | // Fix all addresses with in the DLDI 59 | if (io->fixSectionsFlags & FIX_ALL) { 60 | for (address = (uint8_t**) io->dldiStart; address < (uint8_t**) io->dldiEnd; address++) { 61 | if (prevAddrStart <= *address && *address < prevAddrAllocEnd) 62 | *address += offset; 63 | } 64 | } 65 | 66 | // Fix the interworking glue section 67 | if (io->fixSectionsFlags & FIX_GLUE) { 68 | for (address = (uint8_t**) io->interworkStart; address < (uint8_t**) io->interworkEnd; address++) { 69 | if (prevAddrStart <= *address && *address < prevAddrAllocEnd) 70 | *address += offset; 71 | } 72 | } 73 | 74 | // Fix the global offset table section 75 | if (io->fixSectionsFlags & FIX_GOT) { 76 | for (address = (uint8_t**) io->gotStart; address < (uint8_t**) io->gotEnd; address++) { 77 | if (prevAddrStart <= *address && *address < prevAddrSpaceEnd) 78 | *address += offset; 79 | } 80 | } 81 | 82 | // Initialise the BSS to 0 83 | if (io->fixSectionsFlags & FIX_BSS) { 84 | __aeabi_memset(io->bssStart, (uint8_t*) io->bssEnd - (uint8_t*) io->bssStart, 0); 85 | } 86 | } 87 | 88 | int dldi_patch_relocate(void *buffer, uint32_t size, DLDI_INTERFACE *driver) { 89 | uint32_t *data = (uint32_t*) buffer; 90 | for (; size; size -= 4, data++) { 91 | // Obfuscate the constants, so that DLDI patchers don't catch the DLDI patching code. 92 | if (OBFUSCATED_COMPARE(data[0], 0xEDA58DBF) && OBFUSCATED_COMPARE(data[1], 0x20436869) && OBFUSCATED_COMPARE(data[2], 0x73686d00)) { 93 | dprintf("DLDI found at %d\n", (uint8_t*)data - (uint8_t*)buffer); 94 | DLDI_INTERFACE *target = (DLDI_INTERFACE*) data; 95 | 96 | uint8_t allocatedSize = target->allocatedSize; 97 | if (allocatedSize < driver->driverSize) return DLPR_NOT_ENOUGH_SPACE; 98 | 99 | void *targetAddress = target->dldiStart; 100 | 101 | // Skip overwriting the magic number - the driver included as part of miniboot 102 | // does not always contain it, to evade auto-DLDI patchers in previous stage bootloaders. 103 | __aeabi_memcpy(((uint8_t*) target) + 4, ((uint8_t*) driver) + 4, (1 << allocatedSize) - 4); 104 | target->allocatedSize = allocatedSize; 105 | dldi_relocate(target, targetAddress); 106 | return DLPR_OK; 107 | } 108 | } 109 | 110 | return DLPR_OK; 111 | } 112 | -------------------------------------------------------------------------------- /source/arm9/dldi_patch.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __DLDI_PATCH_H__ 6 | #define __DLDI_PATCH_H__ 7 | 8 | #include "common.h" 9 | #include "dldi.h" 10 | 11 | #define DLPR_OK 0 12 | #define DLPR_NOT_ENOUGH_SPACE 1 13 | 14 | /** 15 | * @brief Patch a binary's DLDI driver, if any. 16 | * 17 | * @param buffer The buffer containing the binary to patch. 18 | * @param size The size of the binary, in bytes. 19 | * @param driver Source DLDI driver. 20 | * @return int The error code, if any. 21 | */ 22 | int dldi_patch_relocate(void *buffer, uint32_t size, DLDI_INTERFACE *driver); 23 | 24 | #endif /* __DLDI_PATCH_H__ */ 25 | -------------------------------------------------------------------------------- /source/arm9/dldi_stub.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // SPDX-FileNotice: Modified from the original version for miniboot. 3 | // 4 | // Copyright (C) 2006-2008 Michael Chisholm (Chishm) 5 | // Copyright (C) 2006-2016 Dave Murphy (WinterMute) 6 | 7 | // Built-in DLDI stub. 8 | 9 | .syntax unified 10 | 11 | .align 4 12 | 13 | .arm 14 | 15 | .global _io_dldi_stub 16 | .section .dldi, "ax" 17 | 18 | _io_dldi_stub: 19 | dldi_start: 20 | 21 | // Driver patch file standard header (16 bytes) 22 | 23 | .word 0xBF8DA5ED // Magic number to identify this region 24 | .asciz " Chishm" // Identifying Magic string (8 bytes with null terminator) 25 | .byte 0x01 // Version number 26 | .byte __dldi_log2_size // Log [base-2] of the size of this driver in bytes. 27 | .byte 0x00 // Sections to fix 28 | .byte __dldi_log2_size // Log [base-2] of the allocated space in bytes. 29 | 30 | // Text identifier. Up to 47 chars + terminating null (48 bytes) 31 | 32 | .align 4 33 | .asciz "Default (No interface)" 34 | 35 | // Offsets to important sections within the data (32 bytes) 36 | 37 | .align 6 38 | .word dldi_start // data start 39 | .word dldi_data_end // data end 40 | .word 0x00000000 // Interworking glue start -- Needs address fixing 41 | .word 0x00000000 // Interworking glue end 42 | .word 0x00000000 // GOT start -- Needs address fixing 43 | .word 0x00000000 // GOT end 44 | .word 0x00000000 // bss start -- Needs setting to zero 45 | .word 0x00000000 // bss end 46 | 47 | // DISC_INTERFACE data (32 bytes) 48 | 49 | .ascii "DLDI" // ioType 50 | .word 0x00000000 // Features 51 | // Function pointers to standard device driver functions 52 | .word _DLDI_startup 53 | .word _DLDI_isInserted 54 | .word _DLDI_readSectors 55 | .word _DLDI_writeSectors 56 | .word _DLDI_clearStatus 57 | .word _DLDI_shutdown 58 | 59 | _DLDI_startup: 60 | _DLDI_isInserted: 61 | _DLDI_readSectors: 62 | _DLDI_writeSectors: 63 | _DLDI_clearStatus: 64 | _DLDI_shutdown: 65 | mov r0, #0x00 // Return false for every function 66 | bx lr 67 | 68 | .align 69 | .pool 70 | 71 | dldi_data_end: 72 | .end 73 | -------------------------------------------------------------------------------- /source/arm9/fatfs/diskio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../../fatfs/source/ff.h" /* Obtains integer types */ 3 | #include "dldi.h" 4 | #include "../../../fatfs/source/diskio.h" /* Declarations of disk functions */ 5 | 6 | static DSTATUS status = STA_NOINIT; 7 | 8 | DSTATUS disk_status(BYTE pdrv) { 9 | return status; 10 | } 11 | 12 | DSTATUS disk_initialize(BYTE pdrv) { 13 | if (!_io_dldi_stub.startup()) 14 | status = STA_NOINIT; 15 | else if (!_io_dldi_stub.isInserted()) 16 | status = STA_NODISK; 17 | else 18 | status = 0; 19 | 20 | return status; 21 | } 22 | 23 | DRESULT disk_read ( 24 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 25 | BYTE *buff, /* Data buffer to store read data */ 26 | LBA_t sector, /* Start sector in LBA */ 27 | UINT count /* Number of sectors to read */ 28 | ) { 29 | if (!_io_dldi_stub.readSectors(sector, count, buff)) 30 | return RES_ERROR; 31 | return RES_OK; 32 | } 33 | 34 | DRESULT disk_ioctl ( 35 | BYTE pdrv, /* Physical drive nmuber (0..) */ 36 | BYTE cmd, /* Control code */ 37 | void *buff /* Buffer to send/receive control data */ 38 | ) { 39 | if (cmd == CTRL_SYNC) 40 | return RES_OK; 41 | 42 | return RES_PARERR; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /source/arm9/fatfs/dldi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // SPDX-FileNotice: Modified from the original version by the BlocksDS project. 3 | // 4 | // Copyright (c) 2006 Michael Chisholm (Chishm) and Tim Seidel (Mighty Max). 5 | 6 | #ifndef LIBNDS_NDS_ARM9_DLDI_H__ 7 | #define LIBNDS_NDS_ARM9_DLDI_H__ 8 | 9 | #include 10 | 11 | #define FEATURE_MEDIUM_CANREAD 0x00000001 12 | #define FEATURE_MEDIUM_CANWRITE 0x00000002 13 | #define FEATURE_SLOT_GBA 0x00000010 // This is a slot-2 flashcard 14 | #define FEATURE_SLOT_NDS 0x00000020 // This is a slot-1 flashcart 15 | 16 | #define FIX_ALL 0x01 17 | #define FIX_GLUE 0x02 18 | #define FIX_GOT 0x04 19 | #define FIX_BSS 0x08 20 | 21 | #define DLDI_SIZE_32KB 0x0f 22 | #define DLDI_SIZE_16KB 0x0e 23 | #define DLDI_SIZE_8KB 0x0d 24 | #define DLDI_SIZE_4KB 0x0c 25 | #define DLDI_SIZE_2KB 0x0b 26 | #define DLDI_SIZE_1KB 0x0a 27 | 28 | #define DLDI_MAGIC_STRING_LEN 8 29 | #define DLDI_FRIENDLY_NAME_LEN 48 30 | 31 | // I/O interface with DLDI extensions 32 | typedef struct DLDI_INTERFACE { 33 | uint32_t magicNumber; 34 | char magicString [DLDI_MAGIC_STRING_LEN]; 35 | uint8_t versionNumber; 36 | uint8_t driverSize; // Log-2 of driver size in bytes 37 | uint8_t fixSectionsFlags; 38 | uint8_t allocatedSize; // Log-2 of the allocated space in bytes 39 | 40 | char friendlyName [DLDI_FRIENDLY_NAME_LEN]; 41 | 42 | // Pointers to sections that need address fixing 43 | uint8_t *dldiStart; 44 | uint8_t *dldiEnd; 45 | uint8_t *interworkStart; 46 | uint8_t *interworkEnd; 47 | uint8_t *gotStart; 48 | uint8_t *gotEnd; 49 | uint8_t *bssStart; 50 | uint8_t *bssEnd; 51 | 52 | // Original I/O interface data 53 | uint32_t ioType; 54 | uint32_t features; 55 | bool (*startup)(void); 56 | bool (*isInserted)(void); 57 | bool (*readSectors)(uint32_t sector, uint32_t numSectors, void *buffer); 58 | bool (*writeSectors)(uint32_t sector, uint32_t numSectors, const void *buffer); 59 | bool (*clearStatus)(void); 60 | bool (*shutdown)(void); 61 | } DLDI_INTERFACE; 62 | 63 | extern DLDI_INTERFACE _io_dldi_stub; 64 | 65 | #endif // LIBNDS_NDS_ARM9_DLDI_H__ 66 | -------------------------------------------------------------------------------- /source/arm9/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------/ 2 | / Configurations of FatFs Module / 3 | /-------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2022, ChaN, all right reserved. 6 | / 7 | / FatFs module is an open source software. Redistribution and use of FatFs in 8 | / source and binary forms, with or without modification, are permitted provided 9 | / that the following condition is met: 10 | / 11 | / 1. Redistributions of source code must retain the above copyright notice, 12 | / this condition and the following disclaimer. 13 | / 14 | / This software is provided by the copyright holder and contributors "AS IS" 15 | / and any warranties related to this software are DISCLAIMED. 16 | / The copyright owner or contributors be NOT LIABLE for any damages caused 17 | / by use of this software. 18 | / 19 | /------------------------------------------------------------------------*/ 20 | 21 | #define FFCONF_DEF 5380 /* Revision ID */ 22 | 23 | /*---------------------------------------------------------------------------/ 24 | / Function Configurations 25 | /---------------------------------------------------------------------------*/ 26 | 27 | #define FF_FS_READONLY 1 28 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 29 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 30 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 31 | / and optional writing functions as well. */ 32 | 33 | 34 | #define FF_FS_MINIMIZE 2 35 | /* This option defines minimization level to remove some basic API functions. 36 | / 37 | / 0: Basic functions are fully enabled. 38 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 39 | / are removed. 40 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 41 | / 3: f_lseek() function is removed in addition to 2. */ 42 | 43 | 44 | #define FF_USE_FIND 0 45 | /* This option switches filtered directory read functions, f_findfirst() and 46 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 47 | 48 | 49 | #define FF_USE_MKFS 0 50 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 51 | 52 | 53 | #define FF_USE_FASTSEEK 0 54 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 55 | 56 | 57 | #define FF_USE_EXPAND 0 58 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 59 | 60 | 61 | #define FF_USE_CHMOD 0 62 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 63 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 64 | 65 | 66 | #define FF_USE_LABEL 0 67 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 68 | / (0:Disable or 1:Enable) */ 69 | 70 | 71 | #define FF_USE_FORWARD 0 72 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 73 | 74 | 75 | #define FF_USE_STRFUNC 0 76 | #define FF_PRINT_LLI 1 77 | #define FF_PRINT_FLOAT 1 78 | #define FF_STRF_ENCODE 3 79 | /* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and 80 | / f_printf(). 81 | / 82 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 83 | / 1: Enable without LF-CRLF conversion. 84 | / 2: Enable with LF-CRLF conversion. 85 | / 86 | / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 87 | / makes f_printf() support floating point argument. These features want C99 or later. 88 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 89 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 90 | / to be read/written via those functions. 91 | / 92 | / 0: ANSI/OEM in current CP 93 | / 1: Unicode in UTF-16LE 94 | / 2: Unicode in UTF-16BE 95 | / 3: Unicode in UTF-8 96 | */ 97 | 98 | 99 | /*---------------------------------------------------------------------------/ 100 | / Locale and Namespace Configurations 101 | /---------------------------------------------------------------------------*/ 102 | 103 | #ifdef PLUS 104 | #define FF_CODE_PAGE 437 105 | #else 106 | #define FF_CODE_PAGE 1 107 | #endif 108 | /* This option specifies the OEM code page to be used on the target system. 109 | / Incorrect code page setting can cause a file open failure. 110 | / 111 | / 437 - U.S. 112 | / 720 - Arabic 113 | / 737 - Greek 114 | / 771 - KBL 115 | / 775 - Baltic 116 | / 850 - Latin 1 117 | / 852 - Latin 2 118 | / 855 - Cyrillic 119 | / 857 - Turkish 120 | / 860 - Portuguese 121 | / 861 - Icelandic 122 | / 862 - Hebrew 123 | / 863 - Canadian French 124 | / 864 - Arabic 125 | / 865 - Nordic 126 | / 866 - Russian 127 | / 869 - Greek 2 128 | / 932 - Japanese (DBCS) 129 | / 936 - Simplified Chinese (DBCS) 130 | / 949 - Korean (DBCS) 131 | / 950 - Traditional Chinese (DBCS) 132 | / 0 - Include all code pages above and configured by f_setcp() 133 | */ 134 | 135 | #ifdef PLUS 136 | #define FF_USE_LFN 2 137 | #else 138 | #define FF_USE_LFN 0 139 | #endif 140 | #define FF_MAX_LFN 255 141 | /* The FF_USE_LFN switches the support for LFN (long file name). 142 | / 143 | / 0: Disable LFN. FF_MAX_LFN has no effect. 144 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 145 | / 2: Enable LFN with dynamic working buffer on the STACK. 146 | / 3: Enable LFN with dynamic working buffer on the HEAP. 147 | / 148 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 149 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 150 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 151 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 152 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 153 | / specification. 154 | / When use stack for the working buffer, take care on stack overflow. When use heap 155 | / memory for the working buffer, memory management functions, ff_memalloc() and 156 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 157 | 158 | 159 | #ifdef PLUS 160 | #define FF_LFN_UNICODE 2 161 | #else 162 | #define FF_LFN_UNICODE 0 163 | #endif 164 | /* This option switches the character encoding on the API when LFN is enabled. 165 | / 166 | / 0: ANSI/OEM in current CP (TCHAR = char) 167 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 168 | / 2: Unicode in UTF-8 (TCHAR = char) 169 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 170 | / 171 | / Also behavior of string I/O functions will be affected by this option. 172 | / When LFN is not enabled, this option has no effect. */ 173 | 174 | 175 | #define FF_LFN_BUF 255 176 | #define FF_SFN_BUF 12 177 | /* This set of options defines size of file name members in the FILINFO structure 178 | / which is used to read out directory items. These values should be suffcient for 179 | / the file names to read. The maximum possible length of the read file name depends 180 | / on character encoding. When LFN is not enabled, these options have no effect. */ 181 | 182 | 183 | #define FF_FS_RPATH 0 184 | /* This option configures support for relative path. 185 | / 186 | / 0: Disable relative path and remove related functions. 187 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 188 | / 2: f_getcwd() function is available in addition to 1. 189 | */ 190 | 191 | 192 | /*---------------------------------------------------------------------------/ 193 | / Drive/Volume Configurations 194 | /---------------------------------------------------------------------------*/ 195 | 196 | #define FF_VOLUMES 1 197 | /* Number of volumes (logical drives) to be used. (1-10) */ 198 | 199 | 200 | #define FF_STR_VOLUME_ID 0 201 | #define FF_VOLUME_STRS "fat","sd" 202 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 203 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 204 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 205 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 206 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 207 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 208 | / not defined, a user defined volume string table is needed as: 209 | / 210 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 211 | */ 212 | 213 | 214 | #define FF_MULTI_PARTITION 0 215 | /* This option switches support for multiple volumes on the physical drive. 216 | / By default (0), each logical drive number is bound to the same physical drive 217 | / number and only an FAT volume found on the physical drive will be mounted. 218 | / When this function is enabled (1), each logical drive number can be bound to 219 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 220 | / function will be available. */ 221 | 222 | 223 | #define FF_MIN_SS 512 224 | #define FF_MAX_SS 512 225 | /* This set of options configures the range of sector size to be supported. (512, 226 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 227 | / harddisk, but a larger value may be required for on-board flash memory and some 228 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 229 | / for variable sector size mode and disk_ioctl() function needs to implement 230 | / GET_SECTOR_SIZE command. */ 231 | 232 | 233 | #define FF_LBA64 0 234 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 235 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 236 | 237 | 238 | #define FF_MIN_GPT 0x10000000 239 | /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and 240 | / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ 241 | 242 | 243 | #define FF_USE_TRIM 0 244 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 245 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 246 | / disk_ioctl() function. */ 247 | 248 | 249 | 250 | /*---------------------------------------------------------------------------/ 251 | / System Configurations 252 | /---------------------------------------------------------------------------*/ 253 | 254 | #define FF_FS_TINY 1 255 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 256 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 257 | / Instead of private sector buffer eliminated from the file object, common sector 258 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 259 | 260 | 261 | #define FF_FS_EXFAT 0 262 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 263 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 264 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 265 | 266 | 267 | #define FF_FS_NORTC 1 268 | #define FF_NORTC_MON 1 269 | #define FF_NORTC_MDAY 1 270 | #define FF_NORTC_YEAR 2022 271 | /* The option FF_FS_NORTC switches timestamp feature. If the system does not have 272 | / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the 273 | / timestamp feature. Every object modified by FatFs will have a fixed timestamp 274 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 275 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 276 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 277 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 278 | / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ 279 | 280 | 281 | #define FF_FS_NOFSINFO 0 282 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 283 | / option, and f_getfree() function at the first time after volume mount will force 284 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 285 | / 286 | / bit0=0: Use free cluster count in the FSINFO if available. 287 | / bit0=1: Do not trust free cluster count in the FSINFO. 288 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 289 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 290 | */ 291 | 292 | 293 | #define FF_FS_LOCK 0 294 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 295 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 296 | / is 1. 297 | / 298 | / 0: Disable file lock function. To avoid volume corruption, application program 299 | / should avoid illegal open, remove and rename to the open objects. 300 | / >0: Enable file lock function. The value defines how many files/sub-directories 301 | / can be opened simultaneously under file lock control. Note that the file 302 | / lock control is independent of re-entrancy. */ 303 | 304 | 305 | #define FF_FS_REENTRANT 0 306 | #define FF_FS_TIMEOUT 1000 307 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 308 | / module itself. Note that regardless of this option, file access to different 309 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 310 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 311 | / to the same volume is under control of this featuer. 312 | / 313 | / 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. 314 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 315 | / ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give() 316 | / function, must be added to the project. Samples are available in ffsystem.c. 317 | / 318 | / The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. 319 | */ 320 | 321 | /*---------------------------------------------------------------------------/ 322 | / wf-fatfs Fork Configurations (Optimizations & Tweaks) 323 | /---------------------------------------------------------------------------*/ 324 | 325 | #define FF_WF_UNALIGNED_ACCESS 1 326 | /* FF_WF_UNALIGNED_ACCESS enables performance optimizations based on certain 327 | / CPU architecture assumptions. 328 | / 329 | / 0: Any endianness, unaligned access support not required. Slowest. 330 | / 1: Little-endian, unaligned access support not required. 331 | / 2: Little-endian, unaligned access support required. Fastest. 332 | */ 333 | 334 | 335 | #define FF_WF_LIST_DOTDOT 1 336 | /* FF_WF_LIST_DOTDOT controls whether or not f_readdir() and other functions 337 | / expose "." and ".." directory entries. 338 | / 339 | / 0: "." and ".." directory entries are hidden. 340 | / 1: "." and ".." directory entries are exposed. 341 | */ 342 | 343 | 344 | #define FF_WF_FAST_CONTIGUOUS_READ 1 345 | #define FF_WF_FAST_CONTIGUOUS_WRITE 1 346 | /* FF_WF_FAST_CONTIGUOUS_* controls whether or not contiguous reads or writes 347 | / of more than 1 cluster (>4-32KB) are optimized to use large disk_read() 348 | / and disk_write() calls. This can be useful on platforms where the cost of 349 | / initializing a sector read/write is large. 350 | / 351 | / 0: Do not optimize this scenario. 352 | / 1: Optimize this scenario. 353 | */ 354 | 355 | 356 | #define FF_WF_CACHE_CLUSTER_SHIFT 1 357 | /* FF_WF_CACHE_CLUSTER_SHIFT controls whether the cluster bitshift value 358 | / is cached. This is useful on platforms with slow divisions. 359 | */ 360 | 361 | 362 | #define FF_WF_MARK_WINDOW_READS 0 363 | /* FF_WF_MARK_WINDOW_READS allows marking reads done on the FATFS instance's 364 | / window (directory/cluster reads) with an "| 0x80" on the pdrv argument 365 | / in disk_read(). This can be used as information for sector caching 366 | / algorithms. 367 | */ 368 | 369 | 370 | /*---------------------------------------------------------------------------/ 371 | / wf-fatfs Fork Configurations (POSIX compatibility improvements) 372 | /---------------------------------------------------------------------------*/ 373 | 374 | #define FF_WF_FILINFO_LOCATION 1 375 | /* FF_WF_FILINFO_LOCATION controls whether or not the FILINFO structure 376 | / contains fpdrv (physical drive ID) and fclust (file cluster #) values. 377 | / 378 | / FIXME: This is not currently supported when FF_FS_EXFAT == 1. 379 | */ 380 | 381 | 382 | #define FF_WF_STAT_ORIGIN_DIRECTORY 1 383 | /* FF_WF_STAT_ORIGIN_DIRECTORY controls whether or not the origin directory 384 | / can pass an f_stat() call. 385 | */ 386 | 387 | 388 | #define FF_WF_GETFREE_NULL_PATH 1 389 | /* FF_WF_GETFREE_NULL_PATH controls whether or not f_getfree() can be called 390 | / on a FATFS instance directly by passing NULL to path and an instance 391 | / in the "return" pointer. 392 | */ 393 | 394 | /*--- End of configuration options ---*/ 395 | 396 | // FatFs does not use memcpy's return value, so we can use __aeabi_memcpy 397 | // instead. 398 | #include "aeabi.h" 399 | #define memcpy __aeabi_memcpy 400 | -------------------------------------------------------------------------------- /source/arm9/fatfs/ffsystem.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/nds-miniboot/cd178abb157ef79f528c95851683807d15c14ce7/source/arm9/fatfs/ffsystem.c -------------------------------------------------------------------------------- /source/arm9/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #include "common.h" 6 | #include "bios.h" 7 | #include "dka.h" 8 | #include "bootstub.h" 9 | #include "dldi_patch.h" 10 | #include "ff.h" 11 | #include "console.h" 12 | 13 | // #define DEBUG 14 | 15 | static FATFS fs; 16 | 17 | #define DLDI_BACKUP ((DLDI_INTERFACE*) 0x6820000) 18 | 19 | /* === Error reporting === */ 20 | 21 | void checkErrorFatFs(const char *msg, int result) { 22 | if (result == FR_OK) return; 23 | 24 | const char *error_detail = NULL; 25 | switch (result) { 26 | case FR_DISK_ERR: error_detail = "Drive I/O error."; break; 27 | case FR_INT_ERR: case FR_INVALID_PARAMETER: error_detail = "Internal FatFs error."; break; 28 | case FR_NOT_READY: error_detail = "Drive not ready."; break; 29 | case FR_NO_FILE: case FR_NO_PATH: error_detail = "File not found."; break; 30 | case FR_NO_FILESYSTEM: error_detail = "FAT filesystem not found.\nIs the memory card formatted\ncorrectly?"; break; 31 | } 32 | 33 | eprintf("%s.\n", msg); 34 | if (error_detail != NULL) { 35 | eprintf("%s", error_detail); 36 | } else { 37 | eprintf("FatFs error %d.", result); 38 | } 39 | 40 | while(1); 41 | } 42 | 43 | /* === Main logic === */ 44 | 45 | #define IPC_ARM7_NONE 0x000 46 | #define IPC_ARM7_COPY 0x100 47 | #define IPC_ARM7_RESET 0x200 48 | #define IPC_ARM7_SYNC 0xF00 49 | 50 | void ipc_arm7_cmd(uint32_t cmd) { 51 | uint32_t next_sync = REG_IPCSYNC & 0xF; 52 | uint32_t last_sync = next_sync; 53 | REG_IPCSYNC = cmd; 54 | while (last_sync == next_sync) next_sync = REG_IPCSYNC & 0xF; 55 | } 56 | 57 | const char *executable_path = "/BOOT.NDS"; 58 | 59 | int main(void) { 60 | FIL fp; 61 | unsigned int bytes_read; 62 | int result; 63 | 64 | // Initialize VRAM (128KB to main engine, rest to CPU, 32KB WRAM to ARM7). 65 | REG_VRAMCNT_ABCD = VRAMCNT_ABCD(0x81, 0x80, 0x82, 0x8A); 66 | REG_VRAMCNT_EFGW = VRAMCNT_EFGW(0x80, 0x80, 0x80, 0x03); 67 | REG_VRAMCNT_HI = VRAMCNT_HI(0x80, 0x80); 68 | 69 | REG_POWCNT = POWCNT_LCD | POWCNT_2D_MAIN | POWCNT_DISPLAY_SWAP; 70 | // Ensure ARM9 has control over the cartridge slots. 71 | REG_EXMEMCNT = 0x6000; // ARM9 memory priority, ARM9 slot access, "slow" GBA timings 72 | 73 | // Reset display. 74 | displayReset(); 75 | 76 | // If holding START while booting, or DEBUG is defined, enable 77 | // debug output. 78 | #ifndef NDEBUG 79 | #ifdef DEBUG 80 | debugEnabled = true; 81 | #else 82 | debugEnabled = !(REG_KEYINPUT & KEY_START); 83 | #endif 84 | #endif 85 | 86 | dprintf("ARM7 sync"); 87 | for (int i = 1; i <= 16; i++) { 88 | dprintf("."); 89 | ipc_arm7_cmd((i << 8) & 0xF00); 90 | } 91 | dprintf(" OK\n"); 92 | 93 | #ifndef _NO_BOOTSTUB 94 | // Create a bootstub in memory, if one doesn't already exist. 95 | if (DKA_BOOTSTUB->magic != DKA_BOOTSTUB_MAGIC) { 96 | uint8_t *bootstub_loc = ((uint8_t*) DKA_BOOTSTUB) + sizeof(dka_bootstub_t); 97 | uint8_t *arm9_bin_loc = bootstub_loc + bootstub_size; 98 | uint8_t *arm7_bin_loc = arm9_bin_loc + NDS_HEADER->arm9_size; 99 | 100 | bootstub.arm9_target_entry = arm9_bin_loc; 101 | bootstub.arm7_target_entry = arm7_bin_loc; 102 | 103 | __aeabi_memcpy(bootstub_loc, &bootstub, bootstub_size); 104 | __aeabi_memcpy(arm9_bin_loc, (void*) NDS_HEADER->arm9_start, NDS_HEADER->arm9_size); 105 | __aeabi_memcpy(arm7_bin_loc, (void*) NDS_HEADER->arm7_start, NDS_HEADER->arm7_size); 106 | 107 | DKA_BOOTSTUB->magic = DKA_BOOTSTUB_MAGIC; 108 | DKA_BOOTSTUB->arm9_entry = bootstub_loc; 109 | DKA_BOOTSTUB->arm7_entry = bootstub_loc + 4; 110 | DKA_BOOTSTUB->loader_size = 0; 111 | } 112 | #endif // _NO_BOOTSTUB 113 | 114 | // Create a copy of the DLDI driver in VRAM before initializing it. 115 | // We'll make use of this copy for patching the ARM9 binary later. 116 | __aeabi_memcpy4(DLDI_BACKUP, &_io_dldi_stub, 16384); 117 | 118 | // Mount the filesystem. Try to open BOOT.NDS. 119 | dprintf("Mounting FAT filesystem... "); 120 | checkErrorFatFs("Could not mount FAT filesystem", f_mount(&fs, "", 1)); 121 | dprintf("OK\n"); 122 | checkErrorFatFs("Could not find BOOT.NDS", f_open(&fp, executable_path, FA_READ)); 123 | dprintf("BOOT.NDS found.\n"); 124 | 125 | // Read the .nds file header. 126 | checkErrorFatFs("Could not read BOOT.NDS", f_read(&fp, NDS_HEADER, sizeof(nds_header_t), &bytes_read)); 127 | 128 | bool waiting_arm7 = false; 129 | // Load the ARM7 binary. 130 | { 131 | dprintf("ARM7: %d bytes @ %X\n", NDS_HEADER->arm7_size, NDS_HEADER->arm7_start); 132 | bool in_main_ram = IN_RANGE_EX(NDS_HEADER->arm7_start, 0x2000000, 0x23BFE00); 133 | bool in_arm7_ram = IN_RANGE_EX(NDS_HEADER->arm7_start, 0x37F8000, 0x380FE00); 134 | if (!NDS_HEADER->arm7_size 135 | || !IN_RANGE_EX(NDS_HEADER->arm7_entry - NDS_HEADER->arm7_start, 0, NDS_HEADER->arm7_size) 136 | || (!in_main_ram && !in_arm7_ram) 137 | || (in_main_ram && !IN_RANGE_EX(NDS_HEADER->arm7_start + NDS_HEADER->arm7_size, 0x2000001, 0x23BFE01)) 138 | || (in_arm7_ram && !IN_RANGE_EX(NDS_HEADER->arm7_start + NDS_HEADER->arm7_size, 0x37F8001, 0x380FE01))) { 139 | eprintf("Invalid ARM7 binary location."); while(1); 140 | } 141 | 142 | checkErrorFatFs("Could not read BOOT.NDS", f_lseek(&fp, NDS_HEADER->arm7_offset)); 143 | checkErrorFatFs("Could not read BOOT.NDS", f_read(&fp, (void*) (in_arm7_ram ? 0x2000000 : NDS_HEADER->arm7_start), NDS_HEADER->arm7_size, &bytes_read)); 144 | 145 | // If the ARM7 binary has to be relocated to ARM7 RAM, the ARM7 CPU 146 | // has to relocate it from main memory. 147 | if (in_arm7_ram) { 148 | REG_IPCFIFOSEND = NDS_HEADER->arm7_size; 149 | REG_IPCFIFOSEND = 0x2000000; 150 | REG_IPCFIFOSEND = NDS_HEADER->arm7_start; 151 | ipc_arm7_cmd(IPC_ARM7_COPY); 152 | waiting_arm7 = true; 153 | } 154 | } 155 | 156 | // Load the ARM9 binary. 157 | { 158 | dprintf("ARM9: %d bytes @ %X\n", NDS_HEADER->arm9_size, NDS_HEADER->arm9_start); 159 | bool in_main_ram = IN_RANGE_EX(NDS_HEADER->arm9_start, 0x2000000, 0x23BFE00); 160 | if (!NDS_HEADER->arm9_size 161 | || !IN_RANGE_EX(NDS_HEADER->arm9_entry - NDS_HEADER->arm9_start, 0, NDS_HEADER->arm9_size) 162 | || !in_main_ram 163 | || !IN_RANGE_EX(NDS_HEADER->arm9_start + NDS_HEADER->arm9_size, 0x2000001, 0x23BFE01)) { 164 | eprintf("Invalid ARM9 binary location."); while(1); 165 | } 166 | 167 | checkErrorFatFs("Could not read BOOT.NDS", f_lseek(&fp, NDS_HEADER->arm9_offset)); 168 | if (waiting_arm7) { 169 | ipc_arm7_cmd(IPC_ARM7_NONE); 170 | waiting_arm7 = false; 171 | } 172 | checkErrorFatFs("Could not read BOOT.NDS", f_read(&fp, (void*) NDS_HEADER->arm9_start, NDS_HEADER->arm9_size, &bytes_read)); 173 | 174 | // Try to apply the DLDI driver patch. 175 | result = dldi_patch_relocate((void*) NDS_HEADER->arm9_start, NDS_HEADER->arm9_size, DLDI_BACKUP); 176 | if (result) { 177 | eprintf("Failed to apply DLDI patch.\n"); 178 | switch (result) { 179 | case DLPR_NOT_ENOUGH_SPACE: eprintf("Not enough space."); break; 180 | } 181 | while(1); 182 | } 183 | } 184 | 185 | // Set up argv. 186 | DKA_ARGV->cmdline = (char*) 0x2FFFEB0; 187 | DKA_ARGV->cmdline_size = strlen(executable_path) + 1; 188 | __aeabi_memcpy(DKA_ARGV->cmdline, executable_path, DKA_ARGV->cmdline_size); 189 | DKA_ARGV->magic = DKA_ARGV_MAGIC; 190 | 191 | dprintf("Launching"); 192 | 193 | // If debug enabled, wait for user to stop holding START 194 | if (debugEnabled) while (!(REG_KEYINPUT & KEY_START)); 195 | 196 | // Restore/clear system state. 197 | displayReset(); 198 | REG_EXMEMCNT = 0xE880; 199 | 200 | // Start the ARM7 binary. 201 | ipc_arm7_cmd(IPC_ARM7_RESET); 202 | 203 | // Start the ARM9 binary. 204 | swiSoftReset(); 205 | } 206 | -------------------------------------------------------------------------------- /source/common/bios.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2022-2023 gba-hpp contributors 4 | // Copyright (c) 2024 Adrian "asie" Siekierka 5 | 6 | #ifndef __BIOS_H__ 7 | #define __BIOS_H__ 8 | 9 | #include "common.h" 10 | 11 | __attribute__((always_inline)) 12 | static inline void swiSoftReset(void) { 13 | asm volatile inline ("swi 0x0 << ((1f - . == 4) * -16); 1:"); 14 | __builtin_unreachable(); 15 | } 16 | 17 | __attribute__((always_inline)) 18 | static inline void swiBitUnpack(const uint8_t *source, uint32_t *destination, const void *params) { 19 | register const uint8_t* r0 asm("r0") = source; 20 | register uint32_t* r1 asm("r1") = destination; 21 | register const void* r2 asm("r2") = params; 22 | asm volatile inline ("swi 0x10 << ((1f - . == 4) * -16); 1:" : "+r"(r0), "+r"(r1), "+r"(r2) :: "r3", "memory"); 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /source/common/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __COMMON_H__ 6 | #define __COMMON_H__ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /* === stub libc - memory copy/fill functions === */ 13 | void *memcpy(void *s1, const void *s2, size_t n); 14 | void *memmove(void *s1, const void *s2, size_t n); 15 | void *memset(void *s, int c, size_t n); 16 | #include "libc/aeabi.h" 17 | #include "libc/ndsabi.h" // extensions provided by an agbabi fork 18 | 19 | /* === stub libc - other functions === */ 20 | int memcmp(const void *s1, const void *s2, size_t n); // used by FatFs 21 | char *strchr(const char *s, int c); // used by FatFs 22 | size_t strlen(const char * s); // used by nanoprintf 23 | #include "libc/nanoprintf.h" // printf replacement, used for error logging 24 | 25 | /* === other functions === */ 26 | uint32_t xor_constant(uint32_t a, uint32_t b); // a ^ b, implemented in ASM to prevent inlining 27 | 28 | /* === code/data attributes === */ 29 | #define THUMB_FUNC __attribute__((target("thumb"))) 30 | 31 | /* === helpers === */ 32 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 33 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 34 | #define RGB555(r, g, b) (0x8000 | (r) | ((g)<<5) | ((b)<<10)) 35 | 36 | /** 37 | * @brief Is the value (v) in range [a, b)? 38 | */ 39 | #define IN_RANGE_EX(v, a, b) ((v) >= (a) && (v) < (b)) 40 | 41 | /* === register defines === */ 42 | 43 | #define IPCSYNC_INPUT(n) (n) 44 | #define IPCSYNC_OUTPUT(n) ((n) << 8) 45 | 46 | #define REG_IPCSYNC (*((volatile uint32_t*) 0x4000180)) 47 | #define REG_IPCFIFOCNT (*((volatile uint32_t*) 0x4000184)) 48 | #define REG_IPCFIFOSEND (*((volatile uint32_t*) 0x4000188)) 49 | #define REG_IPCFIFORECV (*((volatile uint32_t*) 0x4100000)) 50 | #define REG_POWCNT (*((volatile uint16_t*) 0x4000304)) 51 | 52 | #if defined(ARM9) 53 | #define MEM_PALETTE_BG ((uint16_t*) 0x5000000) 54 | #define DISPCNT_BG_MODE(n) (n) 55 | #define DISPCNT_FORCE_DISABLE (1<<7) 56 | #define DISPCNT_BG0_ENABLE (1<<8) 57 | #define DISPCNT_BG_DISPLAY (1<<16) 58 | #define REG_DISPCNT (*((volatile uint32_t*) 0x4000000)) 59 | #define BGCNT_TILE_BASE(n) ((n)<<2) 60 | #define BGCNT_16_COLOR (0<<7) 61 | #define BGCNT_MAP_32x32 (0<<14) 62 | #define BGCNT_MAP_BASE(n) ((n)<<8) 63 | #define REG_BG0CNT (*((volatile uint16_t*) 0x4000008)) 64 | 65 | #define VRAMCNT_ABCD(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) 66 | #define REG_VRAMCNT_ABCD (*((volatile uint32_t*) 0x4000240)) 67 | 68 | #define VRAMCNT_EFGW(e,f,g,w) ((e) | ((f)<<8) | ((g)<<16) | ((w)<<24)) 69 | #define REG_VRAMCNT_EFGW (*((volatile uint32_t*) 0x4000244)) 70 | 71 | #define VRAMCNT_HI(h,i) ((h) | ((i)<<8)) 72 | #define REG_VRAMCNT_HI (*((volatile uint16_t*) 0x4000248)) 73 | 74 | #define KEY_A 0x001 75 | #define KEY_B 0x002 76 | #define KEY_SELECT 0x004 77 | #define KEY_START 0x008 78 | #define KEY_RIGHT 0x010 79 | #define KEY_LEFT 0x020 80 | #define KEY_UP 0x040 81 | #define KEY_DOWN 0x080 82 | #define KEY_R 0x100 83 | #define KEY_L 0x200 84 | #define REG_KEYINPUT (*((volatile uint16_t*) 0x4000130)) 85 | 86 | #define REG_EXMEMCNT (*((volatile uint16_t*) 0x4000204)) 87 | 88 | #define POWCNT_LCD 0x0001 89 | #define POWCNT_2D_MAIN 0x0002 90 | #define POWCNT_3D 0x0004 91 | #define POWCNT_3D_GEOMETRY 0x0008 92 | #define POWCNT_2D_SUB 0x0200 93 | #define POWCNT_DISPLAY_SWAP 0x8000 94 | #elif defined(ARM7) 95 | #else 96 | #error "No CPU defined!" 97 | #endif 98 | 99 | /* === NDS header format (only fields of interest) === */ 100 | 101 | typedef struct { 102 | char game_title[12]; 103 | union { 104 | char game_code[4]; 105 | uint32_t game_code_i; 106 | }; 107 | union { 108 | char maker_code[2]; 109 | uint16_t maker_code_i; 110 | }; 111 | uint8_t unit_code; 112 | uint8_t _ignored_1[0x20 - 0x13]; 113 | uint32_t arm9_offset; 114 | uint32_t arm9_entry; 115 | uint32_t arm9_start; 116 | uint32_t arm9_size; 117 | uint32_t arm7_offset; 118 | uint32_t arm7_entry; 119 | uint32_t arm7_start; 120 | uint32_t arm7_size; 121 | uint8_t _ignored_2[0x68 - 0x40]; 122 | uint32_t banner_offset; 123 | uint8_t _ignored_3[0x170 - 0x6C]; 124 | } nds_header_t; 125 | 126 | /** 127 | * Location of the .nds file header in memory. 128 | */ 129 | #define NDS_HEADER ((nds_header_t*) 0x27FFE00) 130 | 131 | #endif /* __COMMON_H__ */ 132 | -------------------------------------------------------------------------------- /source/common/cp15_asm.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2023 Antonio Niño Díaz 4 | 5 | #ifndef LIBNDS_NDS_ARM9_CP15_ASM_H__ 6 | #define LIBNDS_NDS_ARM9_CP15_ASM_H__ 7 | 8 | // Information from the "ARM 946E-S Technical Reference Manual" ARM DDI 0155A. 9 | // 10 | // NOTE: SBO means "should be one". They are bits that should always be set to 11 | // one even if they don't have a documented meaning. 12 | 13 | #ifndef BIT 14 | #define BIT(n) (1 << (n)) 15 | #endif 16 | 17 | // General definitions for the NDS 18 | 19 | #define ICACHE_SIZE 0x2000 20 | #define DCACHE_SIZE 0x1000 21 | #define CACHE_LINE_SIZE 32 22 | #define ENTRIES_PER_SEGMENT 4 23 | 24 | // Register 0, ID code register 25 | 26 | #define CP15_REG0_ID_CODE_REG(rd) p15, 0, rd, c0, c0, 0 27 | 28 | #define CP15_ID_IMPLEMENTOR_MASK 0xFF000000 // 0x41 29 | #define CP15_ID_ARCHITECTURE_VERSION_MASK 0x000F0000 // 0x4 30 | #define CP15_ID_PART_NUMBER_MASK 0x0000FFF0 // 0x946 31 | #define CP15_ID_VERSION_MASK 0x0000000F 32 | 33 | // Register 0, Cache type register 34 | 35 | #define CP15_REG0_CACHE_TYPE(rd) p15, 0, rd, c0, c0, 1 36 | 37 | #define CP15_CTYPE_CACHE_TYPE_MASK 0x1E000000 38 | #define CP15_CTYPE_HARVARD_UNIFIED 0x00400000 39 | #define CP15_CTYPE_DCACHE_SIZE_MASK 0x003C0000 40 | #define CP15_CTYPE_DCACHE_ASSOCIATIVITY_MASK 0x00038000 41 | #define CP15_CTYPE_DCACHE_BASE_SIZE 0x00004000 42 | #define CP15_CTYPE_DCACHE_WORDS_PER_LINE_MASK 0x00003000 // 0b10 = 8 words per line 43 | #define CP15_CTYPE_ICACHE_SIZE_MASK 0x000003C0 44 | #define CP15_CTYPE_ICACHE_ASSOCIATIVITY_MASK 0x00000038 45 | #define CP15_CTYPE_ICACHE_BASE_SIZE 0x00000004 46 | #define CP15_CTYPE_ICACHE_WORDS_PER_LINE_MASK 0x00000003 // 0b10 = 8 words per line 47 | 48 | #define CP15_CACHE_SIZE_0KB (0x0) 49 | #define CP15_CACHE_SIZE_4KB (0x3) 50 | #define CP15_CACHE_SIZE_8KB (0x4) 51 | #define CP15_CACHE_SIZE_16KB (0x5) 52 | #define CP15_CACHE_SIZE_32KB (0x6) 53 | #define CP15_CACHE_SIZE_64KB (0x7) 54 | #define CP15_CACHE_SIZE_128KB (0x8) 55 | #define CP15_CACHE_SIZE_256KB (0x9) 56 | #define CP15_CACHE_SIZE_512KB (0xA) 57 | #define CP15_CACHE_SIZE_1MB (0xB) 58 | 59 | // Register 0, Tightly-coupled memory size register 60 | 61 | #define CP15_REG0_TCM_SIZE(rd) p15, 0, rd, c0, c0, 2 62 | 63 | #define CP15_TCM_DATA_RAM_SIZE_MASK 0x003C0000 64 | #define CP15_TCM_DATA_RAM_ABSENT 0x00004000 65 | #define CP15_TCM_INSTRUCTION_RAM_SIZE_MASK 0x000003C0 66 | #define CP15_TCM_INSTRUCTION_RAM_ABSENT 0x00000004 67 | 68 | // Register 1, Control Register 69 | 70 | #define CP15_REG1_CONTROL_REGISTER(rd) p15, 0, rd, c1, c0, 0 71 | 72 | #define CP15_CONTROL_ITCM_LOAD_MODE BIT(19) 73 | #define CP15_CONTROL_ITCM_ENABLE BIT(18) 74 | #define CP15_CONTROL_DTCM_LOAD_MODE BIT(17) 75 | #define CP15_CONTROL_DTCM_ENABLE BIT(16) 76 | #define CP15_CONTROL_DISABLE_LOADING_TBIT BIT(15) 77 | #define CP15_CONTROL_ROUND_ROBIN BIT(14) 78 | #define CP15_CONTROL_ALTERNATE_VECTOR_SELECT BIT(13) 79 | #define CP15_CONTROL_ICACHE_ENABLE BIT(12) 80 | #define CP15_CONTROL_BIG_ENDIAN BIT(7) 81 | #define CP15_CONTROL_DCACHE_ENABLE BIT(2) 82 | #define CP15_CONTROL_PROTECTION_UNIT_ENABLE BIT(0) 83 | #define CP15_CONTROL_RESERVED_SBO_MASK 0x78 84 | 85 | // Register 2, Cache configuration registers 86 | 87 | #define CP15_REG2_DATA_CACHE_CONFIG(rd) p15, 0, rd, c2, c0, 0 88 | #define CP15_REG2_INSTRUCTION_CACHE_CONFIG(rd) p15, 0, rd, c2, c0, 1 89 | 90 | #define CP15_CONFIG_AREA_IS_CACHABLE(n) BIT(n) // 0 to 7 91 | 92 | // Register 3, Write buffer control register 93 | 94 | #define CP15_REG3_WRITE_BUFFER_CONTROL(rd) p15, 0, rd, c3, c0, 0 95 | 96 | #define CP15_CONFIG_AREA_IS_BUFFERABLE(n) BIT(n) // 0 to 7 97 | 98 | // Register 5, Access permission registers 99 | 100 | #define CP15_REG5_DATA_ACCESS_PERMISSION(rd) p15, 0, rd, c5, c0, 2 101 | #define CP15_REG5_INSTRUCTION_ACCESS_PERMISSION(rd) p15, 0, rd, c5, c0, 3 102 | 103 | #define CP15_ACCESS_PERMISSIONS_AREA_MASK(n) (0xF << ((n) * 4)) 104 | 105 | #define CP15_AREA_ACCESS_PERMISSIONS_PNO_UNO(n) (0x0 << ((n) * 4)) 106 | #define CP15_AREA_ACCESS_PERMISSIONS_PRW_UNO(n) (0x1 << ((n) * 4)) 107 | #define CP15_AREA_ACCESS_PERMISSIONS_PRW_URO(n) (0x2 << ((n) * 4)) 108 | #define CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(n) (0x3 << ((n) * 4)) 109 | #define CP15_AREA_ACCESS_PERMISSIONS_PRO_UNO(n) (0x5 << ((n) * 4)) 110 | #define CP15_AREA_ACCESS_PERMISSIONS_PRO_URO(n) (0x6 << ((n) * 4)) 111 | 112 | // Register 6, Protection region/base size registers 113 | 114 | #define CP15_REG6_PROTECTION_REGION(rd, n) p15, 0, rd, c6, c##n, 0 115 | 116 | #define CP15_CONFIG_REGION_BASE_MASK 0xFFFFF000 117 | #define CP15_CONFIG_REGION_SIZE_MASK 0x0000003E 118 | #define CP15_CONFIG_REGION_ENABLE BIT(0) 119 | 120 | #define CP15_REGION_SIZE_4KB (0x0B << 1) 121 | #define CP15_REGION_SIZE_8KB (0x0C << 1) 122 | #define CP15_REGION_SIZE_16KB (0x0D << 1) 123 | #define CP15_REGION_SIZE_32KB (0x0E << 1) 124 | #define CP15_REGION_SIZE_64KB (0x0F << 1) 125 | #define CP15_REGION_SIZE_128KB (0x10 << 1) 126 | #define CP15_REGION_SIZE_256KB (0x11 << 1) 127 | #define CP15_REGION_SIZE_512KB (0x12 << 1) 128 | #define CP15_REGION_SIZE_1MB (0x13 << 1) 129 | #define CP15_REGION_SIZE_2MB (0x14 << 1) 130 | #define CP15_REGION_SIZE_4MB (0x15 << 1) 131 | #define CP15_REGION_SIZE_8MB (0x16 << 1) 132 | #define CP15_REGION_SIZE_16MB (0x17 << 1) 133 | #define CP15_REGION_SIZE_32MB (0x18 << 1) 134 | #define CP15_REGION_SIZE_64MB (0x19 << 1) 135 | #define CP15_REGION_SIZE_128MB (0x1A << 1) 136 | #define CP15_REGION_SIZE_256MB (0x1B << 1) 137 | #define CP15_REGION_SIZE_512MB (0x1C << 1) 138 | #define CP15_REGION_SIZE_1GB (0x1D << 1) 139 | #define CP15_REGION_SIZE_2GB (0x1E << 1) 140 | #define CP15_REGION_SIZE_4GB (0x1F << 1) 141 | 142 | // Register 7, Cache operations register 143 | 144 | #define CP15_REG7_FLUSH_ICACHE p15, 0, r0, c7, c5, 0 145 | #define CP15_REG7_FLUSH_ICACHE_ENTRY(rd) p15, 0, rd, c7, c5, 1 146 | #define CP15_REG7_PREFETCH_ICACHE_LINE(rd) p15, 0, rd, c7, c13, 1 147 | #define CP15_REG7_FLUSH_DCACHE p15, 0, r0, c7, c6, 0 148 | #define CP15_REG7_FLUSH_DCACHE_ENTRY(rd) p15, 0, rd, c7, c6, 1 149 | #define CP15_REG7_CLEAN_DCACHE_ENTRY(rd) p15, 0, rd, c7, c10, 1 150 | #define CP15_REG7_CLEAN_FLUSH_DCACHE_ENTRY(rd) p15, 0, rd, c7, c14, 1 151 | #define CP15_REG7_CLEAN_DCACHE_ENTRY_BY_INDEX(rd) p15, 0, rd, c7, c10, 2 152 | #define CP15_REG7_CLEAN_FLUSH_DCACHE_ENTRY_BY_INDEX(rd) p15, 0, rd, c7, c14, 2 153 | 154 | #define CP15_REG7_DRAIN_WRITE_BUFFER p15, 0, r0, c7, c10, 4 155 | 156 | #define CP15_REG7_WAIT_FOR_INTERRUPT p15, 0, r0, c7, c0, 4 157 | 158 | // Register 9, Cache lockdown registers 159 | 160 | #define CP15_REG9_DATA_LOCKDOWN_CONTROL(rd) p15, 0, rd, c9, c0, 0 161 | #define CP15_REG9_INSTRUCTION_LOCKDOWN_CONTROL(rd) p15, 0, rd, c9, c0, 1 162 | 163 | #define CP15_CACHE_LOCKDOWN_LOAD_BIT BIT(31) 164 | #define CP15_CACHE_LOCKDOWN_SEGMENT_MASK 0x3 165 | 166 | // Register 9, Tightly-coupled memory region registers 167 | 168 | #define CP15_REG9_DTCM_CONTROL(rd) p15, 0, rd, c9, c1, 0 169 | #define CP15_REG9_ITCM_CONTROL(rd) p15, 0, rd, c9, c1, 1 170 | 171 | // The "ARM 946E-S Technical Reference Manual" has an erratum and it refers to 172 | // table 2-20, but it should be referring to "Table 2-23 Tightly-coupled memory 173 | // area size encoding". GBATEK has the right formula. 174 | #define CP15_TCM_SIZE_4KB (0x03) 175 | #define CP15_TCM_SIZE_8KB (0x04) 176 | #define CP15_TCM_SIZE_16KB (0x05) 177 | #define CP15_TCM_SIZE_32KB (0x06) 178 | #define CP15_TCM_SIZE_64KB (0x07) 179 | #define CP15_TCM_SIZE_128KB (0x08) 180 | #define CP15_TCM_SIZE_256KB (0x09) 181 | #define CP15_TCM_SIZE_512KB (0x0A) 182 | #define CP15_TCM_SIZE_1MB (0x0B) 183 | #define CP15_TCM_SIZE_2MB (0x0C) 184 | #define CP15_TCM_SIZE_4MB (0x0D) 185 | #define CP15_TCM_SIZE_8MB (0x0E) 186 | #define CP15_TCM_SIZE_16MB (0x0F) 187 | #define CP15_TCM_SIZE_32MB (0x10) 188 | #define CP15_TCM_SIZE_64MB (0x11) 189 | #define CP15_TCM_SIZE_128MB (0x12) 190 | #define CP15_TCM_SIZE_256MB (0x13) 191 | #define CP15_TCM_SIZE_512MB (0x14) 192 | #define CP15_TCM_SIZE_1GB (0x15) 193 | #define CP15_TCM_SIZE_2GB (0x16) 194 | #define CP15_TCM_SIZE_4GB (0x17) 195 | 196 | // Register 13, Trace process identifier register 197 | 198 | #define CP15_REG13_PROCESS_ID(rd) p15, 0, rd, c13, c1, 1 199 | 200 | // Register 15, RAM and TAG BIST test registers 201 | 202 | #define CP15_REG15_TAG_BIST_CONTROL(rd) p15, 0, rd, c15, c0, 1 203 | #define CP15_REG15_RAM_BIST_CONTROL(rd) p15, 1, rd, c15, c0, 1 204 | #define CP15_REG15_CACHE_RAM_BIST_CONTROL(rd) p15, 2, rd, c15, c0, 1 205 | 206 | #define CP15_REG15_INSTR_TAG_BIST_ADDRESS(rd) p15, 0, rd, c15, c0, 2 207 | #define CP15_REG15_INSTR_TAG_BIST_GENERAL(rd) p15, 0, rd, c15, c0, 3 208 | 209 | #define CP15_REG15_DATA_TAG_BIST_ADDRESS(rd) p15, 0, rd, c15, c0, 6 210 | #define CP15_REG15_DATA_TAG_BIST_GENERAL(rd) p15, 0, rd, c15, c0, 7 211 | 212 | #define CP15_REG15_ITCM_TAG_BIST_ADDRESS(rd) p15, 1, rd, c15, c0, 2 213 | #define CP15_REG15_ITCM_TAG_BIST_GENERAL(rd) p15, 1, rd, c15, c0, 3 214 | 215 | #define CP15_REG15_DTCM_TAG_BIST_ADDRESS(rd) p15, 1, rd, c15, c0, 6 216 | #define CP15_REG15_DTCM_TAG_BIST_GENERAL(rd) p15, 1, rd, c15, c0, 7 217 | 218 | #define CP15_REG15_INSTR_CACHE_RAM_TAG_BIST_ADDRESS(rd) p15, 2, Rd, c15, c0, 2 219 | #define CP15_REG15_INSTR_CACHE_RAM_TAG_BIST_GENERAL(rd) p15, 2, Rd, c15, c0, 3 220 | 221 | #define CP15_REG15_DATA_CACHE_RAM_TAG_BIST_ADDRESS(rd) p15, 2, Rd, c15, c0, 6 222 | #define CP15_REG15_DATA_CACHE_RAM_TAG_BIST_GENERAL(rd) p15, 2, Rd, c15, c0, 7 223 | 224 | // Register 15, Test state register 225 | 226 | #define CP15_REG15_TEST_STATE(rd) p15, 0, rd, c15, c0, 0 227 | 228 | #define CP15_TEST_STATE_DISABLE_DCACHE_STREAMING BIT(12) 229 | #define CP15_TEST_STATE_DISABLE_ICACHE_STREAMING BIT(11) 230 | #define CP15_TEST_STATE_DISABLE_DCACHE_LINEFILL BIT(10) 231 | #define CP15_TEST_STATE_DISABLE_ICACHE_LINEFILL BIT(9) 232 | 233 | // Register 15, Cache debug index register 234 | 235 | #define CP15_REG15_CACHE_DEBUG_BY_INDEX(rd) p15, 3, rd, c15, c0, 0 236 | #define CP15_REG15_INSTRUCTION_TAG(rd) p15, 3, rd, c15, c1, 0 237 | #define CP15_REG15_DATA_TAG(rd) p15, 3, rd, c15, c2, 0 238 | #define CP15_REG15_INSTRUCTION_CACHE(rd) p15, 3, rd, c15, c3, 0 239 | #define CP15_REG15_DATA_CACHE(rd) p15, 3, rd, c15, c4, 0 240 | 241 | #endif // LIBNDS_NDS_ARM9_CP15_ASM_H__ 242 | -------------------------------------------------------------------------------- /source/common/cpsr_asm.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __CPSR_ASM_H__ 6 | #define __CPSR_ASM_H__ 7 | 8 | #define CPSR_I 0x80 9 | #define CPSR_F 0x40 10 | #define CPSR_USER 0x10 11 | #define CPSR_FIQ 0x11 12 | #define CPSR_IRQ 0x12 13 | #define CPSR_SUPERVISOR 0x13 14 | #define CPSR_ABORT 0x17 15 | #define CPSR_UNDEFINED 0x1B 16 | #define CPSR_SYSTEM 0x1F 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /source/common/dka.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | #ifndef __DKA_H__ 6 | #define __DKA_H__ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /* === dkA standards: bootstub header === */ 13 | 14 | #define DKA_BOOTSTUB_MAGIC 0x62757473746F6F62ULL // "bootstub" in ASCII 15 | 16 | typedef struct { 17 | uint64_t magic; 18 | void *arm9_entry; 19 | void *arm7_entry; 20 | uint32_t loader_size; 21 | } dka_bootstub_t; 22 | 23 | #define DKA_BOOTSTUB ((dka_bootstub_t*) 0x2FF4000) 24 | 25 | /* === dkA standards: argv header === */ 26 | 27 | #define DKA_ARGV_MAGIC 0x5F617267 // "_arg" in ASCII 28 | 29 | typedef struct { 30 | uint32_t magic; 31 | char *cmdline; 32 | uint32_t cmdline_size; 33 | } dka_argv_t; 34 | 35 | #define DKA_ARGV ((dka_argv_t*) 0x2FFFE70) 36 | 37 | #endif /* __COMMON_H__ */ 38 | -------------------------------------------------------------------------------- /source/common/libc/aeabi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | 5 | #ifndef AEABI_H 6 | #define AEABI_H 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | /** 14 | * Alias of __aeabi_memcpy4 15 | * @param dest Destination address 16 | * @param src Source address 17 | * @param n Number of bytes to copy 18 | */ 19 | void __aeabi_memcpy8(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 20 | 21 | /** 22 | * Copies n bytes from src to dest (forward) 23 | * Assumes dest and src are 4-byte aligned 24 | * @param dest Destination address 25 | * @param src Source address 26 | * @param n Number of bytes to copy 27 | */ 28 | void __aeabi_memcpy4(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 29 | 30 | /** 31 | * Copies n bytes from src to dest (forward) 32 | * @param dest Destination address 33 | * @param src Source address 34 | * @param n Number of bytes to copy 35 | */ 36 | void __aeabi_memcpy(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 37 | 38 | /** 39 | * Alias of __aeabi_memmove4 40 | * @param dest Destination address 41 | * @param src Source address 42 | * @param n Number of bytes to copy 43 | */ 44 | void __aeabi_memmove8(void* dest, const void* src, size_t n) __attribute__((nonnull(1, 2))); 45 | 46 | /** 47 | * Safely copies n bytes of src to dest 48 | * Assumes dest and src are 4-byte aligned 49 | * @param dest Destination address 50 | * @param src Source address 51 | * @param n Number of bytes to copy 52 | */ 53 | void __aeabi_memmove4(void* dest, const void* src, size_t n) __attribute__((nonnull(1, 2))); 54 | 55 | /** 56 | * Safely copies n bytes of src to dest 57 | * @param dest Destination address 58 | * @param src Source address 59 | * @param n Number of bytes to copy 60 | */ 61 | void __aeabi_memmove(void* dest, const void* src, size_t n) __attribute__((nonnull(1, 2))); 62 | 63 | /** 64 | * Alias of __aeabi_memset4 65 | * @param dest Destination address 66 | * @param n Number of bytes to set 67 | * @param c Value to set 68 | */ 69 | void __aeabi_memset8(void* dest, size_t n, int c) __attribute__((nonnull(1))); 70 | 71 | /** 72 | * Set n bytes of dest to (c & 0xff) 73 | * Assumes dest is 4-byte aligned 74 | * @param dest Destination address 75 | * @param n Number of bytes to set 76 | * @param c Value to set 77 | */ 78 | void __aeabi_memset4(void* dest, size_t n, int c) __attribute__((nonnull(1))); 79 | 80 | /** 81 | * Set n bytes of dest to (c & 0xff) 82 | * @param dest Destination address 83 | * @param n Number of bytes to set 84 | * @param c Value to set 85 | */ 86 | void __aeabi_memset(void* dest, size_t n, int c) __attribute__((nonnull(1))); 87 | 88 | /** 89 | * Alias of __aeabi_memclr4 90 | * @param dest Destination address 91 | * @param n Number of bytes to clear 92 | */ 93 | void __aeabi_memclr8(void* dest, size_t n) __attribute__((nonnull(1))); 94 | 95 | /** 96 | * Clears n bytes of dest to 0 97 | * Assumes dest is 4-byte aligned 98 | * @param dest Destination address 99 | * @param n Number of bytes to clear 100 | */ 101 | void __aeabi_memclr4(void* dest, size_t n) __attribute__((nonnull(1))); 102 | 103 | /** 104 | * Clears n bytes of dest to 0 105 | * @param dest Destination address 106 | * @param n Number of bytes to clear 107 | */ 108 | void __aeabi_memclr(void* dest, size_t n) __attribute__((nonnull(1))); 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | #endif /* define AEABI_H */ 114 | -------------------------------------------------------------------------------- /source/common/libc/macros.inc: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ARM assembly support macros 6 | 7 | @ Shift and test upper two bits, clobbering \reg 8 | @ Use mi for first bit, cs for second bit 9 | .macro joaobapt_test_lsl reg shift = #0 10 | movs \reg, \reg, lsl \shift 11 | .endm 12 | 13 | @ Test lowest two bits, clobbering \reg 14 | @ Use mi for low bit, cs for high bit 15 | .macro joaobapt_test reg 16 | joaobapt_test_lsl \reg, #31 17 | .endm 18 | 19 | @ Test lowest two bits of \src, result stored in \dst 20 | @ Use mi for low bit, cs for high bit 21 | .macro joaobapt_test_into dst, src 22 | movs \dst, \src, lsl #31 23 | .endm 24 | 25 | @ Branches depending on lowest two bits, clobbering \reg 26 | @ b_mi = low bit case, b_cs = high bit case 27 | .macro joaobapt_switch reg, b_mi, b_cs 28 | joaobapt_test \reg 29 | bmi \b_mi 30 | bcs \b_cs 31 | .endm 32 | 33 | @ Branches depending on alignment of \a and \b, clobbering \scratch 34 | @ b_byte = off-by-byte case, b_half = off-by-half case 35 | .macro align_switch a, b, scratch, b_byte, b_half 36 | eor \scratch, \a, \b 37 | joaobapt_switch \scratch, \b_byte, \b_half 38 | .endm 39 | -------------------------------------------------------------------------------- /source/common/libc/memcmp.c: -------------------------------------------------------------------------------- 1 | /* memcmp( const void *, const void *, size_t ) 2 | 3 | This file is part of the Public Domain C Library (PDCLib). 4 | Permission is granted to use, modify, and / or redistribute at will. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef REGTEST 10 | 11 | int memcmp( const void * s1, const void * s2, size_t n ) 12 | { 13 | const unsigned char * p1 = ( const unsigned char * ) s1; 14 | const unsigned char * p2 = ( const unsigned char * ) s2; 15 | 16 | while ( n-- ) 17 | { 18 | if ( *p1 != *p2 ) 19 | { 20 | return *p1 - *p2; 21 | } 22 | 23 | ++p1; 24 | ++p2; 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | #endif 31 | 32 | #ifdef TEST 33 | 34 | #include "_PDCLIB_test.h" 35 | 36 | int main( void ) 37 | { 38 | const char xxxxx[] = "xxxxx"; 39 | TESTCASE( memcmp( abcde, abcdx, 5 ) < 0 ); 40 | TESTCASE( memcmp( abcde, abcdx, 4 ) == 0 ); 41 | TESTCASE( memcmp( abcde, xxxxx, 0 ) == 0 ); 42 | TESTCASE( memcmp( xxxxx, abcde, 1 ) > 0 ); 43 | return 0; 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /source/common/libc/memcpy.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memcpy, __aeabi_memcpy4, __aeabi_memcpy8 7 | // Standard: 8 | // memcpy 9 | // Support: 10 | // __ndsabi_memcpy2, __ndsabi_memcpy1 11 | 12 | #include "macros.inc" 13 | 14 | .arm 15 | .align 2 16 | 17 | .section .text.__aeabi_memcpy, "ax", %progbits 18 | .global __aeabi_memcpy 19 | .type __aeabi_memcpy, %function 20 | __aeabi_memcpy: 21 | @ >6-bytes is roughly the threshold when byte-by-byte copy is slower 22 | cmp r2, #6 23 | ble __ndsabi_memcpy1 24 | 25 | align_switch r0, r1, r3, __ndsabi_memcpy1, .Lcopy_halves 26 | 27 | @ Check if r0 (or r1) needs word aligning 28 | rsbs r3, r0, #4 29 | joaobapt_test r3 30 | 31 | @ Copy byte head to align 32 | ldrmib r3, [r1], #1 33 | strmib r3, [r0], #1 34 | submi r2, r2, #1 35 | @ r0, r1 are now half aligned 36 | 37 | @ Copy half head to align 38 | ldrcsh r3, [r1], #2 39 | strcsh r3, [r0], #2 40 | subcs r2, r2, #2 41 | @ r0, r1 are now word aligned 42 | 43 | .global __aeabi_memcpy8 44 | .type __aeabi_memcpy8, %function 45 | __aeabi_memcpy8: 46 | .global __aeabi_memcpy4 47 | .type __aeabi_memcpy4, %function 48 | __aeabi_memcpy4: 49 | cmp r2, #32 50 | blt .Lcopy_words 51 | 52 | @ Word aligned, 32-byte copy 53 | push {r4-r10} 54 | .Lloop_32: 55 | subs r2, r2, #32 56 | ldmgeia r1!, {r3-r10} 57 | stmgeia r0!, {r3-r10} 58 | bgt .Lloop_32 59 | pop {r4-r10} 60 | bxeq lr 61 | 62 | @ < 32 bytes remaining to be copied 63 | add r2, r2, #32 64 | 65 | .Lcopy_words: 66 | cmp r2, #4 67 | blt .Lcopy_halves 68 | .Lloop_4: 69 | subs r2, r2, #4 70 | ldrge r3, [r1], #4 71 | strge r3, [r0], #4 72 | bgt .Lloop_4 73 | bxeq lr 74 | 75 | @ Copy byte & half tail 76 | @ This test still works when r2 is negative 77 | joaobapt_test r2 78 | @ Copy half 79 | ldrcsh r3, [r1], #2 80 | strcsh r3, [r0], #2 81 | @ Copy byte 82 | ldrmib r3, [r1] 83 | strmib r3, [r0] 84 | bx lr 85 | 86 | .Lcopy_halves: 87 | @ Copy byte head to align 88 | tst r0, #1 89 | ldrneb r3, [r1], #1 90 | strneb r3, [r0], #1 91 | subne r2, r2, #1 92 | @ r0, r1 are now half aligned 93 | 94 | .global __ndsabi_memcpy2 95 | .type __ndsabi_memcpy2, %function 96 | __ndsabi_memcpy2: 97 | subs r2, r2, #2 98 | ldrgeh r3, [r1], #2 99 | strgeh r3, [r0], #2 100 | bgt __ndsabi_memcpy2 101 | bxeq lr 102 | 103 | @ Copy byte tail 104 | adds r2, r2, #2 105 | ldrneb r3, [r1] 106 | strneb r3, [r0] 107 | bx lr 108 | 109 | .global __ndsabi_memcpy1 110 | .type __ndsabi_memcpy1, %function 111 | __ndsabi_memcpy1: 112 | subs r2, r2, #1 113 | ldrgeb r3, [r1], #1 114 | strgeb r3, [r0], #1 115 | bgt __ndsabi_memcpy1 116 | bx lr 117 | 118 | .section .text.memcpy, "ax", %progbits 119 | .global memcpy 120 | .type memcpy, %function 121 | memcpy: 122 | push {r0, lr} 123 | bl __aeabi_memcpy 124 | pop {r0, lr} 125 | bx lr 126 | -------------------------------------------------------------------------------- /source/common/libc/memmove.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memmove, __aeabi_memmove4, __aeabi_memmove8 7 | // Standard: 8 | // memmove 9 | 10 | .arm 11 | .align 2 12 | 13 | .section .text.__aeabi_memmove, "ax", %progbits 14 | .global __aeabi_memmove 15 | .type __aeabi_memmove, %function 16 | __aeabi_memmove: 17 | cmp r0, r1 18 | .extern __ndsabi_rmemcpy 19 | bgt __ndsabi_rmemcpy 20 | .extern __aeabi_memcpy 21 | b __aeabi_memcpy 22 | 23 | .global __aeabi_memmove8 24 | .type __aeabi_memmove8, %function 25 | __aeabi_memmove8: 26 | .global __aeabi_memmove4 27 | .type __aeabi_memmove4, %function 28 | __aeabi_memmove4: 29 | cmp r0, r1 30 | .extern __ndsabi_rmemcpy 31 | bgt __ndsabi_rmemcpy 32 | .extern __aeabi_memcpy4 33 | b __aeabi_memcpy4 34 | 35 | .global __ndsabi_memmove1 36 | .type __ndsabi_memmove1, %function 37 | __ndsabi_memmove1: 38 | cmp r0, r1 39 | .extern __ndsabi_rmemcpy1 40 | bgt __ndsabi_rmemcpy1 41 | .extern __ndsabi_memcpy1 42 | b __ndsabi_memcpy1 43 | 44 | .section .text.memmove, "ax", %progbits 45 | .global memmove 46 | .type memmove, %function 47 | memmove: 48 | push {r0, lr} 49 | bl __aeabi_memmove 50 | pop {r0, lr} 51 | bx lr 52 | -------------------------------------------------------------------------------- /source/common/libc/memset.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memclr, __aeabi_memclr4, __aeabi_memclr8, 7 | // __aeabi_memset, __aeabi_memset4, __aeabi_memset8 8 | // Standard: 9 | // memset 10 | // Support: 11 | // __ndsabi_wordset4, __ndsabi_lwordset4, __ndsabi_memset1 12 | 13 | #include "macros.inc" 14 | 15 | .arm 16 | .align 2 17 | 18 | .section .text.__aeabi_memclr, "ax", %progbits 19 | .global __aeabi_memclr 20 | .type __aeabi_memclr, %function 21 | __aeabi_memclr: 22 | mov r2, #0 23 | b __aeabi_memset 24 | 25 | .global __aeabi_memclr8 26 | .type __aeabi_memclr8, %function 27 | __aeabi_memclr8: 28 | .global __aeabi_memclr4 29 | .type __aeabi_memclr4, %function 30 | __aeabi_memclr4: 31 | mov r2, #0 32 | b __ndsabi_wordset4 33 | 34 | .section .text.__aeabi_memset, "ax", %progbits 35 | .global __aeabi_memset 36 | .type __aeabi_memset, %function 37 | __aeabi_memset: 38 | @ < 8 bytes probably won't be aligned: go byte-by-byte 39 | cmp r1, #8 40 | blt __ndsabi_memset1 41 | 42 | @ Copy head to align to next word 43 | rsb r3, r0, #4 44 | joaobapt_test r3 45 | strmib r2, [r0], #1 46 | submi r1, r1, #1 47 | strcsb r2, [r0], #1 48 | strcsb r2, [r0], #1 49 | subcs r1, r1, #2 50 | 51 | .global __aeabi_memset8 52 | .type __aeabi_memset8, %function 53 | __aeabi_memset8: 54 | .global __aeabi_memset4 55 | .type __aeabi_memset4, %function 56 | __aeabi_memset4: 57 | lsl r2, r2, #24 58 | orr r2, r2, r2, lsr #8 59 | orr r2, r2, r2, lsr #16 60 | 61 | .global __ndsabi_wordset4 62 | .type __ndsabi_wordset4, %function 63 | __ndsabi_wordset4: 64 | mov r3, r2 65 | 66 | .global __ndsabi_lwordset4 67 | .type __ndsabi_lwordset4, %function 68 | __ndsabi_lwordset4: 69 | @ 16 words is roughly the threshold when lwordset is slower 70 | cmp r1, #64 71 | blt .Lset_2_words 72 | 73 | @ 8 word set 74 | push {r4-r9} 75 | mov r4, r2 76 | mov r5, r3 77 | mov r6, r2 78 | mov r7, r3 79 | mov r8, r2 80 | mov r9, r3 81 | 82 | .Lset_8_words: 83 | subs r1, r1, #32 84 | stmgeia r0!, {r2-r9} 85 | bgt .Lset_8_words 86 | pop {r4-r9} 87 | bxeq lr 88 | 89 | @ Fixup remaining 90 | add r1, r1, #32 91 | .Lset_2_words: 92 | subs r1, r1, #8 93 | stmgeia r0!, {r2-r3} 94 | bgt .Lset_2_words 95 | bxeq lr 96 | 97 | @ Test for remaining word 98 | adds r1, r1, #4 99 | strge r2, [r0], #4 100 | bxeq lr 101 | 102 | @ Set tail 103 | joaobapt_test r1 104 | strcsh r2, [r0], #2 105 | strmib r2, [r0], #1 106 | bx lr 107 | 108 | .global __ndsabi_memset1 109 | .type __ndsabi_memset, %function 110 | __ndsabi_memset1: 111 | subs r1, r1, #1 112 | strgeb r2, [r0], #1 113 | bgt __ndsabi_memset1 114 | bx lr 115 | 116 | .section .text.memset, "ax", %progbits 117 | .global memset 118 | .type memset, %function 119 | memset: 120 | mov r3, r1 121 | mov r1, r2 122 | mov r2, r3 123 | push {r0, lr} 124 | bl __aeabi_memset 125 | pop {r0, lr} 126 | bx lr 127 | -------------------------------------------------------------------------------- /source/common/libc/nanoprintf.c: -------------------------------------------------------------------------------- 1 | #define NANOPRINTF_IMPLEMENTATION 2 | #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 0 3 | #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 0 4 | #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 0 5 | #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 6 | #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 7 | #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 8 | #define NANOPRINTF_SNPRINTF_SAFE_TRIM_STRING_ON_OVERFLOW 1 9 | 10 | #include "nanoprintf.h" 11 | -------------------------------------------------------------------------------- /source/common/libc/nanoprintf.h: -------------------------------------------------------------------------------- 1 | /* nanoprintf v0.5.1: a tiny embeddable printf replacement written in C. 2 | https://github.com/charlesnicholson/nanoprintf 3 | charles.nicholson+nanoprintf@gmail.com 4 | dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */ 5 | 6 | #ifndef NANOPRINTF_H_INCLUDED 7 | #define NANOPRINTF_H_INCLUDED 8 | 9 | #include 10 | #include 11 | 12 | // Define this to fully sandbox nanoprintf inside of a translation unit. 13 | #ifdef NANOPRINTF_VISIBILITY_STATIC 14 | #define NPF_VISIBILITY static 15 | #else 16 | #define NPF_VISIBILITY extern 17 | #endif 18 | 19 | #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 20 | #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ 21 | __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) 22 | #else 23 | #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) 24 | #endif 25 | 26 | // Public API 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | // The npf_ functions all return the number of bytes required to express the 33 | // fully-formatted string, not including the null terminator character. 34 | // The npf_ functions do not return negative values, since the lack of 'l' length 35 | // modifier support makes encoding errors impossible. 36 | 37 | NPF_VISIBILITY int npf_snprintf( 38 | char *buffer, size_t bufsz, const char *format, ...) NPF_PRINTF_ATTR(3, 4); 39 | 40 | NPF_VISIBILITY int npf_vsnprintf( 41 | char *buffer, size_t bufsz, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); 42 | 43 | typedef void (*npf_putc)(int c, void *ctx); 44 | NPF_VISIBILITY int npf_pprintf( 45 | npf_putc pc, void *pc_ctx, char const *format, ...) NPF_PRINTF_ATTR(3, 4); 46 | 47 | NPF_VISIBILITY int npf_vpprintf( 48 | npf_putc pc, void *pc_ctx, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // NANOPRINTF_H_INCLUDED 55 | 56 | /* The implementation of nanoprintf begins here, to be compiled only if 57 | NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would 58 | be nanoprintf.c. */ 59 | 60 | #ifdef NANOPRINTF_IMPLEMENTATION 61 | 62 | #ifndef NANOPRINTF_IMPLEMENTATION_INCLUDED 63 | #define NANOPRINTF_IMPLEMENTATION_INCLUDED 64 | 65 | #include 66 | #include 67 | #include 68 | 69 | // The conversion buffer must fit at least UINT64_MAX in octal format with the leading '0'. 70 | #ifndef NANOPRINTF_CONVERSION_BUFFER_SIZE 71 | #define NANOPRINTF_CONVERSION_BUFFER_SIZE 23 72 | #endif 73 | #if NANOPRINTF_CONVERSION_BUFFER_SIZE < 23 74 | #error The size of the conversion buffer must be at least 23 bytes. 75 | #endif 76 | 77 | // Pick reasonable defaults if nothing's been configured. 78 | #if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ 79 | !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ 80 | !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ 81 | !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ 82 | !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ 83 | !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) 84 | #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 85 | #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 86 | #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 87 | #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 88 | #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 89 | #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 90 | #endif 91 | 92 | // If anything's been configured, everything must be configured. 93 | #ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 94 | #error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 95 | #endif 96 | #ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 97 | #error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 98 | #endif 99 | #ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 100 | #error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 101 | #endif 102 | #ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 103 | #error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 104 | #endif 105 | #ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 106 | #error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 107 | #endif 108 | #ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 109 | #error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 110 | #endif 111 | 112 | // Ensure flags are compatible. 113 | #if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ 114 | (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) 115 | #error Precision format specifiers must be enabled if float support is enabled. 116 | #endif 117 | 118 | // intmax_t / uintmax_t require stdint from c99 / c++11 119 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 120 | #ifndef _MSC_VER 121 | #ifdef __cplusplus 122 | #if __cplusplus < 201103L 123 | #error large format specifier support requires C++11 or later. 124 | #endif 125 | #else 126 | #if __STDC_VERSION__ < 199409L 127 | #error nanoprintf requires C99 or later. 128 | #endif 129 | #endif 130 | #endif 131 | #endif 132 | 133 | // Figure out if we can disable warnings with pragmas. 134 | #ifdef __clang__ 135 | #define NANOPRINTF_CLANG 1 136 | #define NANOPRINTF_GCC_PAST_4_6 0 137 | #else 138 | #define NANOPRINTF_CLANG 0 139 | #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) 140 | #define NANOPRINTF_GCC_PAST_4_6 1 141 | #else 142 | #define NANOPRINTF_GCC_PAST_4_6 0 143 | #endif 144 | #endif 145 | 146 | #if NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 147 | #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1 148 | #else 149 | #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 0 150 | #endif 151 | 152 | #if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 153 | #pragma GCC diagnostic push 154 | #pragma GCC diagnostic ignored "-Wunused-function" 155 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 156 | #ifdef __cplusplus 157 | #pragma GCC diagnostic ignored "-Wold-style-cast" 158 | #endif 159 | #pragma GCC diagnostic ignored "-Wpadded" 160 | #pragma GCC diagnostic ignored "-Wfloat-equal" 161 | #if NANOPRINTF_CLANG 162 | #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" 163 | #pragma GCC diagnostic ignored "-Wcovered-switch-default" 164 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 165 | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" 166 | #ifndef __APPLE__ 167 | #pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" 168 | #endif 169 | #elif NANOPRINTF_GCC_PAST_4_6 170 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 171 | #endif 172 | #endif 173 | 174 | #ifdef _MSC_VER 175 | #pragma warning(push) 176 | #pragma warning(disable:4619) // there is no warning number 'number' 177 | // C4619 has to be disabled first! 178 | #pragma warning(disable:4127) // conditional expression is constant 179 | #pragma warning(disable:4505) // unreferenced local function has been removed 180 | #pragma warning(disable:4514) // unreferenced inline function has been removed 181 | #pragma warning(disable:4701) // potentially uninitialized local variable used 182 | #pragma warning(disable:4706) // assignment within conditional expression 183 | #pragma warning(disable:4710) // function not inlined 184 | #pragma warning(disable:4711) // function selected for inline expansion 185 | #pragma warning(disable:4820) // padding added after struct member 186 | #pragma warning(disable:5039) // potentially throwing function passed to extern C function 187 | #pragma warning(disable:5045) // compiler will insert Spectre mitigation for memory load 188 | #pragma warning(disable:5262) // implicit switch fall-through 189 | #pragma warning(disable:26812) // enum type is unscoped 190 | #endif 191 | 192 | #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 193 | #define NPF_NOINLINE __attribute__((noinline)) 194 | #elif defined(_MSC_VER) 195 | #define NPF_NOINLINE __declspec(noinline) 196 | #else 197 | #define NPF_NOINLINE 198 | #endif 199 | 200 | #if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ 201 | (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) 202 | enum { 203 | NPF_FMT_SPEC_OPT_NONE, 204 | NPF_FMT_SPEC_OPT_LITERAL, 205 | NPF_FMT_SPEC_OPT_STAR, 206 | }; 207 | #endif 208 | 209 | enum { 210 | NPF_FMT_SPEC_LEN_MOD_NONE, 211 | NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' 212 | NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' 213 | NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' 214 | NPF_FMT_SPEC_LEN_MOD_LONG, // 'l' 215 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 216 | NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' 217 | NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' 218 | NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' 219 | NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't' 220 | #endif 221 | }; 222 | 223 | enum { 224 | NPF_FMT_SPEC_CONV_NONE, 225 | NPF_FMT_SPEC_CONV_PERCENT, // '%' 226 | NPF_FMT_SPEC_CONV_CHAR, // 'c' 227 | NPF_FMT_SPEC_CONV_STRING, // 's' 228 | NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' 229 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 230 | NPF_FMT_SPEC_CONV_BINARY, // 'b' 231 | #endif 232 | NPF_FMT_SPEC_CONV_OCTAL, // 'o' 233 | NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' 234 | NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' 235 | NPF_FMT_SPEC_CONV_POINTER, // 'p' 236 | #if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 237 | NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' 238 | #endif 239 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 240 | NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' 241 | NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' 242 | NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' 243 | NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' 244 | #endif 245 | }; 246 | 247 | typedef struct npf_format_spec { 248 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 249 | int field_width; 250 | uint8_t field_width_opt; 251 | char left_justified; // '-' 252 | char leading_zero_pad; // '0' 253 | #endif 254 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 255 | int prec; 256 | uint8_t prec_opt; 257 | #endif 258 | char prepend; // ' ' or '+' 259 | char alt_form; // '#' 260 | char case_adjust; // 'a' - 'A' 261 | uint8_t length_modifier; 262 | uint8_t conv_spec; 263 | } npf_format_spec_t; 264 | 265 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 266 | typedef long npf_int_t; 267 | typedef unsigned long npf_uint_t; 268 | #else 269 | typedef intmax_t npf_int_t; 270 | typedef uintmax_t npf_uint_t; 271 | #endif 272 | 273 | typedef struct npf_bufputc_ctx { 274 | char *dst; 275 | size_t len; 276 | size_t cur; 277 | } npf_bufputc_ctx_t; 278 | 279 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 280 | #ifdef _MSC_VER 281 | #include 282 | typedef SSIZE_T ssize_t; 283 | #else 284 | #include 285 | #endif 286 | #endif 287 | 288 | #ifdef _MSC_VER 289 | #include 290 | #endif 291 | 292 | static int npf_max(int x, int y) { return (x > y) ? x : y; } 293 | 294 | static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { 295 | char const *cur = format; 296 | 297 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 298 | out_spec->left_justified = 0; 299 | out_spec->leading_zero_pad = 0; 300 | #endif 301 | out_spec->case_adjust = 'a' - 'A'; // lowercase 302 | out_spec->prepend = 0; 303 | out_spec->alt_form = 0; 304 | 305 | while (*++cur) { // cur points at the leading '%' character 306 | switch (*cur) { // Optional flags 307 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 308 | case '-': out_spec->left_justified = '-'; out_spec->leading_zero_pad = 0; continue; 309 | case '0': out_spec->leading_zero_pad = !out_spec->left_justified; continue; 310 | #endif 311 | case '+': out_spec->prepend = '+'; continue; 312 | case ' ': if (out_spec->prepend == 0) { out_spec->prepend = ' '; } continue; 313 | case '#': out_spec->alt_form = '#'; continue; 314 | default: break; 315 | } 316 | break; 317 | } 318 | 319 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 320 | out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; 321 | if (*cur == '*') { 322 | out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; 323 | ++cur; 324 | } else { 325 | out_spec->field_width = 0; 326 | while ((*cur >= '0') && (*cur <= '9')) { 327 | out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; 328 | out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); 329 | } 330 | } 331 | #endif 332 | 333 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 334 | out_spec->prec = 0; 335 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; 336 | if (*cur == '.') { 337 | ++cur; 338 | if (*cur == '*') { 339 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; 340 | ++cur; 341 | } else { 342 | if (*cur == '-') { 343 | ++cur; 344 | } else { 345 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; 346 | } 347 | while ((*cur >= '0') && (*cur <= '9')) { 348 | out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); 349 | } 350 | } 351 | } 352 | #endif 353 | 354 | uint_fast8_t tmp_conv = NPF_FMT_SPEC_CONV_NONE; 355 | out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; 356 | switch (*cur++) { // Length modifier 357 | case 'h': 358 | out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; 359 | if (*cur == 'h') { 360 | out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; 361 | ++cur; 362 | } 363 | break; 364 | case 'l': 365 | out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; 366 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 367 | if (*cur == 'l') { 368 | out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; 369 | ++cur; 370 | } 371 | #endif 372 | break; 373 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 374 | case 'L': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; break; 375 | #endif 376 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 377 | case 'j': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; break; 378 | case 'z': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; break; 379 | case 't': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; break; 380 | #endif 381 | default: --cur; break; 382 | } 383 | 384 | switch (*cur++) { // Conversion specifier 385 | case '%': out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; 386 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 387 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; 388 | #endif 389 | break; 390 | 391 | case 'c': out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; 392 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 393 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; 394 | #endif 395 | break; 396 | 397 | case 's': out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; 398 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 399 | out_spec->leading_zero_pad = 0; 400 | #endif 401 | break; 402 | 403 | case 'i': 404 | case 'd': tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; 405 | case 'o': 406 | if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; } 407 | case 'u': 408 | if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; } 409 | case 'X': 410 | if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { out_spec->case_adjust = 0; } 411 | case 'x': 412 | if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; } 413 | out_spec->conv_spec = (uint8_t)tmp_conv; 414 | #if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) && \ 415 | (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) 416 | if (out_spec->prec_opt != NPF_FMT_SPEC_OPT_NONE) { out_spec->leading_zero_pad = 0; } 417 | #endif 418 | break; 419 | 420 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 421 | case 'F': out_spec->case_adjust = 0; 422 | case 'f': 423 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC; 424 | if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } 425 | break; 426 | 427 | case 'E': out_spec->case_adjust = 0; 428 | case 'e': 429 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI; 430 | if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } 431 | break; 432 | 433 | case 'G': out_spec->case_adjust = 0; 434 | case 'g': 435 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; 436 | if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } 437 | break; 438 | 439 | case 'A': out_spec->case_adjust = 0; 440 | case 'a': 441 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX; 442 | if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } 443 | break; 444 | #endif 445 | 446 | #if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 447 | case 'n': 448 | // todo: reject string if flags or width or precision exist 449 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; 450 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 451 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; 452 | #endif 453 | break; 454 | #endif 455 | 456 | case 'p': 457 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; 458 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 459 | out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; 460 | #endif 461 | break; 462 | 463 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 464 | case 'B': 465 | out_spec->case_adjust = 0; 466 | case 'b': 467 | out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; 468 | break; 469 | #endif 470 | 471 | default: return 0; 472 | } 473 | 474 | return (int)(cur - format); 475 | } 476 | 477 | static NPF_NOINLINE int npf_utoa_rev( 478 | npf_uint_t val, char *buf, uint_fast8_t base, char case_adj) { 479 | uint_fast8_t n = 0; 480 | do { 481 | int_fast8_t const d = (int_fast8_t)(val % base); 482 | *buf++ = (char)(((d < 10) ? '0' : ('A' - 10 + case_adj)) + d); 483 | ++n; 484 | val /= base; 485 | } while (val); 486 | return (int)n; 487 | } 488 | 489 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 490 | 491 | #include 492 | 493 | #if (DBL_MANT_DIG <= 11) && (DBL_MAX_EXP <= 16) 494 | typedef uint_fast16_t npf_double_bin_t; 495 | typedef int_fast8_t npf_ftoa_exp_t; 496 | #elif (DBL_MANT_DIG <= 24) && (DBL_MAX_EXP <= 128) 497 | typedef uint_fast32_t npf_double_bin_t; 498 | typedef int_fast8_t npf_ftoa_exp_t; 499 | #elif (DBL_MANT_DIG <= 53) && (DBL_MAX_EXP <= 1024) 500 | typedef uint_fast64_t npf_double_bin_t; 501 | typedef int_fast16_t npf_ftoa_exp_t; 502 | #else 503 | #error Unsupported width of the double type. 504 | #endif 505 | 506 | // The floating point conversion code works with an unsigned integer type of any size. 507 | #ifndef NANOPRINTF_CONVERSION_FLOAT_TYPE 508 | #define NANOPRINTF_CONVERSION_FLOAT_TYPE unsigned int 509 | #endif 510 | typedef NANOPRINTF_CONVERSION_FLOAT_TYPE npf_ftoa_man_t; 511 | 512 | #if (NANOPRINTF_CONVERSION_BUFFER_SIZE <= UINT_FAST8_MAX) && (UINT_FAST8_MAX <= INT_MAX) 513 | typedef uint_fast8_t npf_ftoa_dec_t; 514 | #else 515 | typedef int npf_ftoa_dec_t; 516 | #endif 517 | 518 | enum { 519 | NPF_DOUBLE_EXP_MASK = DBL_MAX_EXP * 2 - 1, 520 | NPF_DOUBLE_EXP_BIAS = DBL_MAX_EXP - 1, 521 | NPF_DOUBLE_MAN_BITS = DBL_MANT_DIG - 1, 522 | NPF_DOUBLE_BIN_BITS = sizeof(npf_double_bin_t) * CHAR_BIT, 523 | NPF_FTOA_MAN_BITS = sizeof(npf_ftoa_man_t) * CHAR_BIT, 524 | NPF_FTOA_SHIFT_BITS = 525 | ((NPF_FTOA_MAN_BITS < DBL_MANT_DIG) ? NPF_FTOA_MAN_BITS : DBL_MANT_DIG) - 1 526 | }; 527 | 528 | /* Generally, floating-point conversion implementations use 529 | grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) algorithms, 530 | which are mathematically exact and fast, but require large lookup tables. 531 | 532 | This implementation was inspired by Wojciech Muła's (zdjęcia@garnek.pl) 533 | algorithm (http://0x80.pl/notesen/2015-12-29-float-to-string.html) and 534 | extended further by adding dynamic scaling and configurable integer width by 535 | Oskars Rubenis (https://github.com/Okarss). */ 536 | 537 | static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { 538 | char const *ret = NULL; 539 | npf_double_bin_t bin; { // Union-cast is UB pre-C11, compiler optimizes byte-copy loop. 540 | char const *src = (char const *)&f; 541 | char *dst = (char *)&bin; 542 | for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; } 543 | } 544 | 545 | // Unsigned -> signed int casting is IB and can raise a signal but generally doesn't. 546 | npf_ftoa_exp_t exp = 547 | (npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_DOUBLE_MAN_BITS) & NPF_DOUBLE_EXP_MASK); 548 | 549 | bin &= ((npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS) - 1; 550 | if (exp == (npf_ftoa_exp_t)NPF_DOUBLE_EXP_MASK) { // special value 551 | ret = (bin) ? "NAN" : "FNI"; 552 | goto exit; 553 | } 554 | if (spec->prec > (NANOPRINTF_CONVERSION_BUFFER_SIZE - 2)) { goto exit; } 555 | if (exp) { // normal number 556 | bin |= (npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS; 557 | } else { // subnormal number 558 | ++exp; 559 | } 560 | exp = (npf_ftoa_exp_t)(exp - NPF_DOUBLE_EXP_BIAS); 561 | 562 | uint_fast8_t carry; carry = 0; 563 | npf_ftoa_dec_t end, dec; dec = (npf_ftoa_dec_t)spec->prec; 564 | if (dec || spec->alt_form) { 565 | buf[dec++] = '.'; 566 | } 567 | 568 | { // Integer part 569 | npf_ftoa_man_t man_i; 570 | 571 | if (exp >= 0) { 572 | int_fast8_t shift_i = 573 | (int_fast8_t)((exp > NPF_FTOA_SHIFT_BITS) ? (int)NPF_FTOA_SHIFT_BITS : exp); 574 | npf_ftoa_exp_t exp_i = (npf_ftoa_exp_t)(exp - shift_i); 575 | shift_i = (int_fast8_t)(NPF_DOUBLE_MAN_BITS - shift_i); 576 | man_i = (npf_ftoa_man_t)(bin >> shift_i); 577 | 578 | if (exp_i) { 579 | if (shift_i) { 580 | carry = (bin >> (shift_i - 1)) & 0x1; 581 | } 582 | exp = NPF_DOUBLE_MAN_BITS; // invalidate the fraction part 583 | } 584 | 585 | // Scale the exponent from base-2 to base-10. 586 | for (; exp_i; --exp_i) { 587 | if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) { 588 | man_i = (npf_ftoa_man_t)(man_i << 1); 589 | man_i = (npf_ftoa_man_t)(man_i | carry); carry = 0; 590 | } else { 591 | if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } 592 | buf[dec++] = '0'; 593 | carry = (((uint_fast8_t)(man_i % 5) + carry) > 2); 594 | man_i /= 5; 595 | } 596 | } 597 | } else { 598 | man_i = 0; 599 | } 600 | end = dec; 601 | 602 | do { // Print the integer 603 | if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } 604 | buf[end++] = (char)('0' + (char)(man_i % 10)); 605 | man_i /= 10; 606 | } while (man_i); 607 | } 608 | 609 | { // Fraction part 610 | npf_ftoa_man_t man_f; 611 | npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec; 612 | 613 | if (exp < NPF_DOUBLE_MAN_BITS) { 614 | int_fast8_t shift_f = (int_fast8_t)((exp < 0) ? -1 : exp); 615 | npf_ftoa_exp_t exp_f = (npf_ftoa_exp_t)(exp - shift_f); 616 | npf_double_bin_t bin_f = 617 | bin << ((NPF_DOUBLE_BIN_BITS - NPF_DOUBLE_MAN_BITS) + shift_f); 618 | 619 | // This if-else statement can be completely optimized at compile time. 620 | if (NPF_DOUBLE_BIN_BITS > NPF_FTOA_MAN_BITS) { 621 | man_f = (npf_ftoa_man_t)(bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - 622 | NPF_FTOA_MAN_BITS) % 623 | NPF_DOUBLE_BIN_BITS)); 624 | carry = (uint_fast8_t)((bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - 625 | NPF_FTOA_MAN_BITS - 1) % 626 | NPF_DOUBLE_BIN_BITS)) & 0x1); 627 | } else { 628 | man_f = (npf_ftoa_man_t)((npf_ftoa_man_t)bin_f 629 | << ((unsigned)(NPF_FTOA_MAN_BITS - 630 | NPF_DOUBLE_BIN_BITS) % NPF_FTOA_MAN_BITS)); 631 | carry = 0; 632 | } 633 | 634 | // Scale the exponent from base-2 to base-10 and prepare the first digit. 635 | for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) { 636 | if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) { 637 | carry = (uint_fast8_t)(man_f & 0x1); 638 | man_f = (npf_ftoa_man_t)(man_f >> 1); 639 | } else { 640 | man_f = (npf_ftoa_man_t)(man_f * 5); 641 | if (carry) { man_f = (npf_ftoa_man_t)(man_f + 3); carry = 0; } 642 | if (exp_f < 0) { 643 | buf[--dec_f] = '0'; 644 | } else { 645 | ++digit; 646 | } 647 | } 648 | } 649 | man_f = (npf_ftoa_man_t)(man_f + carry); 650 | carry = (exp_f >= 0); 651 | dec = 0; 652 | } else { 653 | man_f = 0; 654 | } 655 | 656 | if (dec_f) { 657 | // Print the fraction 658 | for (;;) { 659 | buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4))); 660 | man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4))); 661 | if (!dec_f) { break; } 662 | man_f = (npf_ftoa_man_t)(man_f * 10); 663 | } 664 | man_f = (npf_ftoa_man_t)(man_f << 4); 665 | } 666 | if (exp < NPF_DOUBLE_MAN_BITS) { 667 | carry &= (uint_fast8_t)(man_f >> (NPF_FTOA_MAN_BITS - 1)); 668 | } 669 | } 670 | 671 | // Round the number 672 | for (; carry; ++dec) { 673 | if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } 674 | if (dec >= end) { buf[end++] = '0'; } 675 | if (buf[dec] == '.') { continue; } 676 | carry = (buf[dec] == '9'); 677 | buf[dec] = (char)(carry ? '0' : (buf[dec] + 1)); 678 | } 679 | 680 | return (int)end; 681 | exit: 682 | if (!ret) { ret = "RRE"; } 683 | uint_fast8_t i; 684 | for (i = 0; ret[i]; ++i) { buf[i] = (char)(ret[i] + spec->case_adjust); } 685 | return (int)i; 686 | } 687 | 688 | #endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 689 | 690 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 691 | static int npf_bin_len(npf_uint_t u) { 692 | // Return the length of the binary string format of 'u', preferring intrinsics. 693 | if (!u) { return 1; } 694 | 695 | #ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. 696 | #ifdef _M_X64 697 | #define NPF_HAVE_BUILTIN_CLZ 698 | #define NPF_CLZ _BitScanReverse64 699 | #elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 700 | #define NPF_HAVE_BUILTIN_CLZ 701 | #define NPF_CLZ _BitScanReverse 702 | #endif 703 | #ifdef NPF_HAVE_BUILTIN_CLZ 704 | unsigned long idx; 705 | NPF_CLZ(&idx, u); 706 | return (int)(idx + 1); 707 | #endif 708 | #elif defined(NANOPRINTF_CLANG) || defined(NANOPRINTF_GCC_PAST_4_6) 709 | #define NPF_HAVE_BUILTIN_CLZ 710 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 711 | #define NPF_CLZ(X) ((sizeof(long long) * CHAR_BIT) - (size_t)__builtin_clzll(X)) 712 | #else 713 | #define NPF_CLZ(X) ((sizeof(long) * CHAR_BIT) - (size_t)__builtin_clzl(X)) 714 | #endif 715 | return (int)NPF_CLZ(u); 716 | #endif 717 | 718 | #ifndef NPF_HAVE_BUILTIN_CLZ 719 | int n; 720 | for (n = 0; u; ++n, u >>= 1); // slow but small software fallback 721 | return n; 722 | #else 723 | #undef NPF_HAVE_BUILTIN_CLZ 724 | #undef NPF_CLZ 725 | #endif 726 | } 727 | #endif 728 | 729 | static void npf_bufputc(int c, void *ctx) { 730 | npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; 731 | if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; } 732 | } 733 | 734 | static void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; } 735 | 736 | typedef struct npf_cnt_putc_ctx { 737 | npf_putc pc; 738 | void *ctx; 739 | int n; 740 | } npf_cnt_putc_ctx_t; 741 | 742 | static void npf_putc_cnt(int c, void *ctx) { 743 | npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; 744 | ++pc_cnt->n; 745 | pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization 746 | } 747 | 748 | #define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0) 749 | 750 | #define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ 751 | case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break 752 | 753 | #define NPF_WRITEBACK(MOD, TYPE) \ 754 | case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break 755 | 756 | int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { 757 | npf_format_spec_t fs; 758 | char const *cur = format; 759 | npf_cnt_putc_ctx_t pc_cnt; 760 | pc_cnt.pc = pc; 761 | pc_cnt.ctx = pc_ctx; 762 | pc_cnt.n = 0; 763 | 764 | while (*cur) { 765 | int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); 766 | if (!fs_len) { NPF_PUTC(*cur++); continue; } 767 | cur += fs_len; 768 | 769 | // Extract star-args immediately 770 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 771 | if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) { 772 | fs.field_width = va_arg(args, int); 773 | if (fs.field_width < 0) { 774 | fs.field_width = -fs.field_width; 775 | fs.left_justified = 1; 776 | } 777 | } 778 | #endif 779 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 780 | if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) { 781 | fs.prec = va_arg(args, int); 782 | if (fs.prec < 0) { fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; } 783 | } 784 | #endif 785 | 786 | union { char cbuf_mem[NANOPRINTF_CONVERSION_BUFFER_SIZE]; npf_uint_t binval; } u; 787 | char *cbuf = u.cbuf_mem, sign_c = 0; 788 | int cbuf_len = 0, need_0x = 0; 789 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 790 | int field_pad = 0; 791 | char pad_c = 0; 792 | #endif 793 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 794 | int prec_pad = 0; 795 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 796 | int zero = 0; 797 | #endif 798 | #endif 799 | 800 | // Extract and convert the argument to string, point cbuf at the text. 801 | switch (fs.conv_spec) { 802 | case NPF_FMT_SPEC_CONV_PERCENT: 803 | *cbuf = '%'; 804 | cbuf_len = 1; 805 | break; 806 | 807 | case NPF_FMT_SPEC_CONV_CHAR: 808 | *cbuf = (char)va_arg(args, int); 809 | cbuf_len = 1; 810 | break; 811 | 812 | case NPF_FMT_SPEC_CONV_STRING: { 813 | cbuf = va_arg(args, char *); 814 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 815 | for (char const *s = cbuf; 816 | ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)) && *s; 817 | ++s, ++cbuf_len); 818 | #else 819 | for (char const *s = cbuf; *s; ++s, ++cbuf_len); // strlen 820 | #endif 821 | } break; 822 | 823 | case NPF_FMT_SPEC_CONV_SIGNED_INT: { 824 | npf_int_t val = 0; 825 | switch (fs.length_modifier) { 826 | NPF_EXTRACT(NONE, int, int); 827 | NPF_EXTRACT(SHORT, short, int); 828 | NPF_EXTRACT(LONG_DOUBLE, int, int); 829 | NPF_EXTRACT(CHAR, char, int); 830 | NPF_EXTRACT(LONG, long, long); 831 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 832 | NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); 833 | NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); 834 | NPF_EXTRACT(LARGE_SIZET, ssize_t, ssize_t); 835 | NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); 836 | #endif 837 | default: break; 838 | } 839 | 840 | sign_c = (val < 0) ? '-' : fs.prepend; 841 | 842 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 843 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 844 | zero = !val; 845 | #endif 846 | // special case, if prec and value are 0, skip 847 | if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { 848 | cbuf_len = 0; 849 | } else 850 | #endif 851 | { 852 | npf_uint_t uval = (npf_uint_t)val; 853 | if (val < 0) { uval = 0 - uval; } 854 | cbuf_len = npf_utoa_rev(uval, cbuf, 10, fs.case_adjust); 855 | } 856 | } break; 857 | 858 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 859 | case NPF_FMT_SPEC_CONV_BINARY: 860 | #endif 861 | case NPF_FMT_SPEC_CONV_OCTAL: 862 | case NPF_FMT_SPEC_CONV_HEX_INT: 863 | case NPF_FMT_SPEC_CONV_UNSIGNED_INT: { 864 | npf_uint_t val = 0; 865 | 866 | switch (fs.length_modifier) { 867 | NPF_EXTRACT(NONE, unsigned, unsigned); 868 | NPF_EXTRACT(SHORT, unsigned short, unsigned); 869 | NPF_EXTRACT(LONG_DOUBLE, unsigned, unsigned); 870 | NPF_EXTRACT(CHAR, unsigned char, unsigned); 871 | NPF_EXTRACT(LONG, unsigned long, unsigned long); 872 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 873 | NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); 874 | NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); 875 | NPF_EXTRACT(LARGE_SIZET, size_t, size_t); 876 | NPF_EXTRACT(LARGE_PTRDIFFT, size_t, size_t); 877 | #endif 878 | default: break; 879 | } 880 | 881 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 882 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 883 | zero = !val; 884 | #endif 885 | if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { 886 | // Zero value and explicitly-requested zero precision means "print nothing". 887 | if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) { 888 | fs.prec = 1; // octal special case, print a single '0' 889 | } 890 | } else 891 | #endif 892 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 893 | if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { 894 | cbuf_len = npf_bin_len(val); u.binval = val; 895 | } else 896 | #endif 897 | { 898 | uint_fast8_t const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? 899 | 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ? 16u : 10u); 900 | cbuf_len = npf_utoa_rev(val, cbuf, base, fs.case_adjust); 901 | } 902 | 903 | if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) { 904 | cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. 905 | } 906 | 907 | if (val && fs.alt_form) { // 0x or 0b but can't write it yet. 908 | if (fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) { need_0x = 'X'; } 909 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 910 | else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; } 911 | #endif 912 | if (need_0x) { need_0x += fs.case_adjust; } 913 | } 914 | } break; 915 | 916 | case NPF_FMT_SPEC_CONV_POINTER: { 917 | cbuf_len = 918 | npf_utoa_rev((npf_uint_t)(uintptr_t)va_arg(args, void *), cbuf, 16, 'a' - 'A'); 919 | need_0x = 'x'; 920 | } break; 921 | 922 | #if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 923 | case NPF_FMT_SPEC_CONV_WRITEBACK: 924 | switch (fs.length_modifier) { 925 | NPF_WRITEBACK(NONE, int); 926 | NPF_WRITEBACK(SHORT, short); 927 | NPF_WRITEBACK(LONG, long); 928 | NPF_WRITEBACK(LONG_DOUBLE, double); 929 | NPF_WRITEBACK(CHAR, signed char); 930 | #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 931 | NPF_WRITEBACK(LARGE_LONG_LONG, long long); 932 | NPF_WRITEBACK(LARGE_INTMAX, intmax_t); 933 | NPF_WRITEBACK(LARGE_SIZET, size_t); 934 | NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); 935 | #endif 936 | default: break; 937 | } break; 938 | #endif 939 | 940 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 941 | case NPF_FMT_SPEC_CONV_FLOAT_DEC: 942 | case NPF_FMT_SPEC_CONV_FLOAT_SCI: 943 | case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: 944 | case NPF_FMT_SPEC_CONV_FLOAT_HEX: { 945 | double val; 946 | if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { 947 | val = (double)va_arg(args, long double); 948 | } else { 949 | val = va_arg(args, double); 950 | } 951 | 952 | sign_c = (val < 0.) ? '-' : fs.prepend; 953 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 954 | zero = (val == 0.); 955 | #endif 956 | cbuf_len = npf_ftoa_rev(cbuf, &fs, val); 957 | } break; 958 | #endif 959 | default: break; 960 | } 961 | 962 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 963 | // Compute the field width pad character 964 | if (fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE) { 965 | if (fs.leading_zero_pad) { // '0' flag is only legal with numeric types 966 | if ((fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) && 967 | (fs.conv_spec != NPF_FMT_SPEC_CONV_CHAR) && 968 | (fs.conv_spec != NPF_FMT_SPEC_CONV_PERCENT)) { 969 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 970 | if ((fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec && zero) { 971 | pad_c = ' '; 972 | } else 973 | #endif 974 | { pad_c = '0'; } 975 | } 976 | } else { pad_c = ' '; } 977 | } 978 | #endif 979 | 980 | // Compute the number of bytes to truncate or '0'-pad. 981 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 982 | if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) { 983 | #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 984 | // float precision is after the decimal point 985 | if (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) 986 | #endif 987 | { prec_pad = npf_max(0, fs.prec - cbuf_len); } 988 | } 989 | #endif 990 | 991 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 992 | // Given the full converted length, how many pad bytes? 993 | field_pad = fs.field_width - cbuf_len - !!sign_c; 994 | if (need_0x) { field_pad -= 2; } 995 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 996 | field_pad -= prec_pad; 997 | #endif 998 | field_pad = npf_max(0, field_pad); 999 | 1000 | // Apply right-justified field width if requested 1001 | if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first. 1002 | if (pad_c == '0') { 1003 | if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } 1004 | // Pad byte is '0', write '0x' before '0' pad chars. 1005 | if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } 1006 | } 1007 | while (field_pad-- > 0) { NPF_PUTC(pad_c); } 1008 | // Pad byte is ' ', write '0x' after ' ' pad chars but before number. 1009 | if ((pad_c != '0') && need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } 1010 | } else 1011 | #endif 1012 | { if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } } // no pad, '0x' requested. 1013 | 1014 | // Write the converted payload 1015 | if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) { 1016 | for (int i = 0; i < cbuf_len; ++i) { NPF_PUTC(cbuf[i]); } 1017 | } else { 1018 | if (sign_c) { NPF_PUTC(sign_c); } 1019 | #if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 1020 | while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads. 1021 | #endif 1022 | #if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 1023 | if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { 1024 | while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); } 1025 | } else 1026 | #endif 1027 | { while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed 1028 | } 1029 | 1030 | #if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 1031 | if (fs.left_justified && pad_c) { // Apply left-justified field width 1032 | while (field_pad-- > 0) { NPF_PUTC(pad_c); } 1033 | } 1034 | #endif 1035 | } 1036 | 1037 | return pc_cnt.n; 1038 | } 1039 | 1040 | #undef NPF_PUTC 1041 | #undef NPF_EXTRACT 1042 | #undef NPF_WRITEBACK 1043 | 1044 | int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, ...) { 1045 | va_list val; 1046 | va_start(val, format); 1047 | int const rv = npf_vpprintf(pc, pc_ctx, format, val); 1048 | va_end(val); 1049 | return rv; 1050 | } 1051 | 1052 | int npf_snprintf(char *buffer, size_t bufsz, const char *format, ...) { 1053 | va_list val; 1054 | va_start(val, format); 1055 | int const rv = npf_vsnprintf(buffer, bufsz, format, val); 1056 | va_end(val); 1057 | return rv; 1058 | } 1059 | 1060 | int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, va_list vlist) { 1061 | npf_bufputc_ctx_t bufputc_ctx; 1062 | bufputc_ctx.dst = buffer; 1063 | bufputc_ctx.len = bufsz; 1064 | bufputc_ctx.cur = 0; 1065 | 1066 | npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; 1067 | int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); 1068 | pc('\0', &bufputc_ctx); 1069 | 1070 | if (buffer && bufsz) { 1071 | #ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW 1072 | if (n >= (int)bufsz) { buffer[0] = '\0'; } 1073 | #else 1074 | buffer[bufsz - 1] = '\0'; 1075 | #endif 1076 | } 1077 | 1078 | return n; 1079 | } 1080 | 1081 | #if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1082 | #pragma GCC diagnostic pop 1083 | #endif 1084 | 1085 | #ifdef _MSC_VER 1086 | #pragma warning(pop) 1087 | #endif 1088 | 1089 | #endif // NANOPRINTF_IMPLEMENTATION_INCLUDED 1090 | #endif // NANOPRINTF_IMPLEMENTATION 1091 | 1092 | /* 1093 | nanoprintf is dual-licensed under both the "Unlicense" and the 1094 | "Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing 1095 | structure is to make nanoprintf as consumable as possible in as many 1096 | environments / countries / companies as possible without any 1097 | encumberances. 1098 | 1099 | The text of the two licenses follows below: 1100 | 1101 | ============================== UNLICENSE ============================== 1102 | 1103 | This is free and unencumbered software released into the public domain. 1104 | 1105 | Anyone is free to copy, modify, publish, use, compile, sell, or 1106 | distribute this software, either in source code form or as a compiled 1107 | binary, for any purpose, commercial or non-commercial, and by any 1108 | means. 1109 | 1110 | In jurisdictions that recognize copyright laws, the author or authors 1111 | of this software dedicate any and all copyright interest in the 1112 | software to the public domain. We make this dedication for the benefit 1113 | of the public at large and to the detriment of our heirs and 1114 | successors. We intend this dedication to be an overt act of 1115 | relinquishment in perpetuity of all present and future rights to this 1116 | software under copyright law. 1117 | 1118 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1119 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1120 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1121 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 1122 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1123 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1124 | OTHER DEALINGS IN THE SOFTWARE. 1125 | 1126 | For more information, please refer to 1127 | 1128 | ================================ 0BSD ================================= 1129 | 1130 | Copyright (C) 2019- by Charles Nicholson 1131 | 1132 | Permission to use, copy, modify, and/or distribute this software for 1133 | any purpose with or without fee is hereby granted. 1134 | 1135 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1136 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1137 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1138 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1139 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1140 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1141 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1142 | */ 1143 | -------------------------------------------------------------------------------- /source/common/libc/ndsabi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | 5 | #ifndef NDSABI_H 6 | #define NDSABI_H 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | /** 15 | * Copies n bytes from src to dest (forward) 16 | * Assumes dest and src are 2-byte aligned 17 | * @param dest Destination address 18 | * @param src Source address 19 | * @param n Number of bytes to copy 20 | */ 21 | void __ndsabi_memcpy2(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 22 | 23 | /** 24 | * Copies n bytes from src to dest (forward) 25 | * This is a slow, unaligned, byte-by-byte copy: ideal for SRAM 26 | * @param dest Destination address 27 | * @param src Source address 28 | * @param n Number of bytes to copy 29 | */ 30 | void __ndsabi_memcpy1(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 31 | 32 | /** 33 | * Copies n bytes from src to dest (backwards) 34 | * This is a slow, unaligned, byte-by-byte copy: ideal for SRAM 35 | * @param dest Destination address 36 | * @param src Source address 37 | * @param n Number of bytes to copy 38 | */ 39 | void __ndsabi_rmemcpy1(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 40 | 41 | /** 42 | * Copies n bytes from src to dest (backwards) 43 | * @param dest Destination address 44 | * @param src Source address 45 | * @param n Number of bytes to copy 46 | */ 47 | void __ndsabi_rmemcpy(void* __restrict__ dest, const void* __restrict__ src, size_t n) __attribute__((nonnull(1, 2))); 48 | 49 | /** 50 | * Fills dest with n bytes of c 51 | * Assumes dest is 4-byte aligned 52 | * Trailing copy uses the low word of c, and the low byte of c 53 | * @param dest Destination address 54 | * @param n Number of bytes to set 55 | * @param c Value to set 56 | */ 57 | void __ndsabi_lwordset4(void* dest, size_t n, long long c) __attribute__((nonnull(1))); 58 | 59 | /** 60 | * Fills dest with n bytes of c 61 | * Assumes dest is 4-byte aligned 62 | * Trailing copy uses the low byte of c 63 | * @param dest Destination address 64 | * @param n Number of bytes to set 65 | * @param c Value to set 66 | */ 67 | void __ndsabi_wordset4(void* dest, size_t n, int c) __attribute__((nonnull(1))); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | #endif /* define NDSABI_H */ 73 | -------------------------------------------------------------------------------- /source/common/libc/rmemcpy.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // Support: 6 | // __ndsabi_rmemcpy, __ndsabi_rmemcpy1 7 | 8 | #include "macros.inc" 9 | 10 | .arm 11 | .align 2 12 | 13 | .section .text.__ndsabi_rmemcpy, "ax", %progbits 14 | .global __ndsabi_rmemcpy 15 | .type __ndsabi_rmemcpy, %function 16 | __ndsabi_rmemcpy: 17 | @ >6-bytes is roughly the threshold when byte-by-byte copy is slower 18 | cmp r2, #6 19 | ble __ndsabi_rmemcpy1 20 | 21 | align_switch r0, r1, r3, __ndsabi_rmemcpy1, .Lcopy_halves 22 | 23 | @ Check if end needs word aligning 24 | add r3, r0, r2 25 | joaobapt_test r3 26 | 27 | @ Copy byte tail to align 28 | submi r2, r2, #1 29 | ldrmib r3, [r1, r2] 30 | strmib r3, [r0, r2] 31 | @ r2 is now half aligned 32 | 33 | @ Copy half tail to align 34 | subcs r2, r2, #2 35 | ldrcsh r3, [r1, r2] 36 | strcsh r3, [r0, r2] 37 | @ r2 is now word aligned 38 | 39 | cmp r2, #32 40 | blt .Lcopy_words 41 | 42 | @ Word aligned, 32-byte copy 43 | push {r0-r1, r4-r10} 44 | add r0, r0, r2 45 | add r1, r1, r2 46 | .Lloop_32: 47 | subs r2, r2, #32 48 | ldmgedb r1!, {r3-r10} 49 | stmgedb r0!, {r3-r10} 50 | bgt .Lloop_32 51 | pop {r0-r1, r4-r10} 52 | bxeq lr 53 | 54 | @ < 32 bytes remaining to be copied 55 | add r2, r2, #32 56 | 57 | .Lcopy_words: 58 | subs r2, r2, #4 59 | ldrge r3, [r1, r2] 60 | strge r3, [r0, r2] 61 | bgt .Lcopy_words 62 | bxeq lr 63 | 64 | @ Copy byte & half head 65 | joaobapt_test_into r3, r2 66 | @ Copy half 67 | addcs r2, r2, #2 68 | ldrcsh r3, [r1, r2] 69 | strcsh r3, [r0, r2] 70 | @ Copy byte 71 | ldrmib r3, [r1] 72 | strmib r3, [r0] 73 | bx lr 74 | 75 | .Lcopy_halves: 76 | @ Copy byte tail to align 77 | add r3, r0, r2 78 | tst r3, #1 79 | subne r2, r2, #1 80 | ldrneb r3, [r1, r2] 81 | strneb r3, [r0, r2] 82 | @ r2 is now half aligned 83 | 84 | .Lloop_2: 85 | subs r2, r2, #2 86 | ldrgeh r3, [r1, r2] 87 | strgeh r3, [r0, r2] 88 | bgt .Lloop_2 89 | bxeq lr 90 | 91 | @ Copy byte head 92 | ldrb r3, [r1] 93 | strb r3, [r0] 94 | bx lr 95 | 96 | .global __ndsabi_rmemcpy1 97 | .type __ndsabi_rmemcpy1, %function 98 | __ndsabi_rmemcpy1: 99 | subs r2, r2, #1 100 | ldrgeb r3, [r1, r2] 101 | strgeb r3, [r0, r2] 102 | bgt __ndsabi_rmemcpy1 103 | bx lr 104 | -------------------------------------------------------------------------------- /source/common/libc/strchr.c: -------------------------------------------------------------------------------- 1 | /* strchr( const char *, int ) 2 | 3 | This file is part of the Public Domain C Library (PDCLib). 4 | Permission is granted to use, modify, and / or redistribute at will. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef REGTEST 10 | 11 | char * strchr( const char * s, int c ) 12 | { 13 | do 14 | { 15 | if ( *s == ( char ) c ) 16 | { 17 | return ( char * ) s; 18 | } 19 | } while ( *s++ ); 20 | 21 | return NULL; 22 | } 23 | 24 | #endif 25 | 26 | #ifdef TEST 27 | 28 | #include "_PDCLIB_test.h" 29 | 30 | int main( void ) 31 | { 32 | char abccd[] = "abccd"; 33 | TESTCASE( strchr( abccd, 'x' ) == NULL ); 34 | TESTCASE( strchr( abccd, 'a' ) == &abccd[0] ); 35 | TESTCASE( strchr( abccd, 'd' ) == &abccd[4] ); 36 | TESTCASE( strchr( abccd, '\0' ) == &abccd[5] ); 37 | TESTCASE( strchr( abccd, 'c' ) == &abccd[2] ); 38 | return TEST_RESULTS; 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /source/common/libc/strlen.c: -------------------------------------------------------------------------------- 1 | /* strlen( const char * ) 2 | 3 | This file is part of the Public Domain C Library (PDCLib). 4 | Permission is granted to use, modify, and / or redistribute at will. 5 | */ 6 | 7 | #include 8 | 9 | #ifndef REGTEST 10 | 11 | size_t strlen( const char * s ) 12 | { 13 | size_t rc = 0; 14 | 15 | while ( s[rc] ) 16 | { 17 | ++rc; 18 | } 19 | 20 | return rc; 21 | } 22 | 23 | #endif 24 | 25 | #ifdef TEST 26 | 27 | #include "_PDCLIB_test.h" 28 | 29 | int main( void ) 30 | { 31 | TESTCASE( strlen( abcde ) == 5 ); 32 | TESTCASE( strlen( "" ) == 0 ); 33 | return TEST_RESULTS; 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /source/common/wonderful.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | /* wf-bin2c generated files require , so we provide a stub. */ 6 | 7 | #ifndef __WF_WONDERFUL_H__ 8 | #define __WF_WONDERFUL_H__ 9 | 10 | #ifndef __WONDERFUL__ 11 | #define __WONDERFUL__ 12 | #endif 13 | 14 | #endif /* __WF_WONDERFUL_H__ */ 15 | -------------------------------------------------------------------------------- /source/common/xor_constant.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 Adrian "asie" Siekierka 4 | 5 | .arm 6 | .syntax unified 7 | 8 | .global xor_constant 9 | .section .text.xor_constant, "ax" 10 | xor_constant: 11 | eor r0, r0, r1 12 | bx lr 13 | -------------------------------------------------------------------------------- /source/misc/r4isdhc_pad.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | MEMORY { 5 | RAM : ORIGIN = 0x02000000, LENGTH = 3840K 6 | } 7 | 8 | SECTIONS { 9 | .r4i_sdhc_pad : { 10 | KEEP(*(.r4i_sdhc_pad)) 11 | } >RAM = 0x00 12 | } 13 | -------------------------------------------------------------------------------- /source/misc/r4isdhc_pad.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (c) 2024 lifehackerhansol 4 | 5 | .syntax unified 6 | .align 4 7 | .arm 8 | .section .r4i_sdhc_pad, "ax" 9 | 10 | b 0x2000450 11 | .space 0xEC - 0x04 12 | 13 | bl 0x2000218 14 | .space 0x450 - 0xF0 15 | 16 | .end 17 | --------------------------------------------------------------------------------