├── .gitignore ├── Makefile ├── README.md ├── arm11 ├── Makefile ├── link.ld └── source │ ├── arm │ ├── exception.c │ ├── exception.h │ ├── gic.c │ ├── gic.h │ ├── mmu.c │ ├── mmu.h │ ├── scu.c │ ├── scu.h │ ├── timer.c │ ├── timer.h │ ├── xrq.h │ └── xrqVectors.S │ ├── boot.S │ ├── hw │ ├── codec.c │ ├── codec.h │ ├── gpio.h │ ├── gpulcd.c │ ├── gpulcd.h │ ├── mcu.c │ └── mcu.h │ ├── main.c │ ├── pxi │ ├── log.c │ ├── log.h │ ├── timer.c │ └── timer.h │ ├── system │ ├── event.h │ ├── sections.h │ ├── sys.c │ └── sys.h │ └── ui │ ├── debug.c │ ├── debug.h │ ├── draw.c │ ├── draw.h │ ├── ui.c │ └── ui.h ├── arm9 ├── Makefile ├── link.ld └── source │ ├── debug.c │ ├── debug.h │ ├── exception.c │ ├── exception.h │ ├── fatfs │ ├── 00history.txt │ ├── 00readme.txt │ ├── diskio.h │ ├── ff.c │ ├── ff.h │ ├── ffconf.h │ ├── integer.h │ ├── sdmmc.c │ └── sdmmc.h │ ├── irq.h │ ├── irqHandlers.c │ ├── irqHandlers.h │ ├── log.c │ ├── log.h │ ├── main.c │ ├── smalllib.c │ ├── smalllib.h │ ├── start.S │ ├── timer.c │ └── timer.h ├── common ├── arm.h ├── bfn.S ├── bfn.h ├── common.h ├── console.c ├── console.h ├── font.h ├── hid.h ├── i2c.c ├── i2c.h ├── memtests.c ├── memtests.h ├── memtests_asm.S ├── power.c ├── power.h ├── pxi.c ├── pxi.h ├── screen.h ├── spi.c ├── spi.h └── vram.h └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.bin 3 | *.elf 4 | *.firm 5 | 6 | *.id0 7 | *.id1 8 | *.id2 9 | *.nam 10 | *.til 11 | 12 | .vscode/ -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = 3ds_hw_test 2 | 3 | ARM9_LOAD = 0x08000100 4 | ARM9_ENTRY = $(ARM9_LOAD) 5 | ARM9_ALT_LOAD = 0x01FF8000 6 | ARM9_ALT_ENTRY = $(ARM9_ALT_LOAD) 7 | ARM9_BIN = arm9/$(TARGET)_arm9.bin 8 | 9 | ARM11_LOAD = 0x18100000 10 | ARM11_ENTRY = $(ARM11_LOAD) 11 | ARM11_BIN = arm11/$(TARGET)_arm11.bin 12 | 13 | export CC = arm-none-eabi-gcc 14 | export OBJCOPY = arm-none-eabi-objcopy 15 | 16 | ifeq (, $(shell which $(CC) 2> /dev/null)) 17 | export USE_DKA = true 18 | endif 19 | ifeq (, $(shell which $(OBJCOPY) 2> /dev/null)) 20 | export USE_DKA = true 21 | endif 22 | 23 | ifeq ($(USE_DKA),true) 24 | ifeq ($(strip $(DEVKITARM)),) 25 | $(error "Please provide $(CC) and $(OBJCOPY) in PATH, or set DEVKITARM in your environment.") 26 | endif 27 | $(info Building with DKA) 28 | include $(DEVKITARM)/base_tools 29 | endif 30 | 31 | .PHONY: all clean .FORCE 32 | .FORCE: 33 | 34 | all: $(TARGET).firm $(TARGET)_direct.firm 35 | 36 | $(TARGET).firm: $(ARM9_BIN) $(ARM11_BIN) 37 | firmtool build $@ -n $(ARM9_ENTRY) -e $(ARM11_ENTRY) -D $(ARM9_BIN) $(ARM11_BIN) -A $(ARM9_LOAD) $(ARM11_LOAD) -C NDMA XDMA -b 38 | $(TARGET)_direct.firm: $(ARM9_BIN) $(ARM11_BIN) 39 | firmtool build $@ -n $(ARM9_ALT_ENTRY) -e $(ARM11_ENTRY) -D $(ARM9_BIN) $(ARM11_BIN) -A $(ARM9_ALT_LOAD) $(ARM11_LOAD) -C memcpy XDMA -b 40 | 41 | $(ARM9_BIN): .FORCE 42 | @$(MAKE) -C arm9 43 | 44 | $(ARM11_BIN): .FORCE 45 | @$(MAKE) -C arm11 46 | 47 | clean: 48 | @$(MAKE) -C arm9 clean 49 | @$(MAKE) -C arm11 clean 50 | @rm -f $(TARGET)*.firm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3ds_hw_test 2 | A (very) WIP hardware tester for the 3DS 3 | 4 | ## General Info 5 | The overall, long-term goal of this project is to comprehensively test as 6 | many of the various hardware components on Nintendo 3DS and 2DS systems as 7 | possible, while also being able to boot and run on systems which are unable to 8 | run almost any other software due to some critical hardware fault. Currently, 9 | this tool only functions a simple memory tester, implementing some of the 10 | tests from [Memtest86+](https://www.memtest.org/). 11 | 12 | ## To-Do List / Roadmap 13 | In very approximate order of priority: 14 | * Make the memory testing functionality more complete 15 | - Implement the rest of the memory tests described 16 | [here](https://www.memtest86.com/tech_individual-test-descr.html), save for 17 | those which don't really have an equivalent on the 3DS's ARM processors 18 | - Further optimize, clean up, and comment the current testing code 19 | * Totally revamp the UI, as the current one is dirt simple 20 | - Allow selecting specifc tests to run on specific memory regions 21 | - Allow selecting a number of passes to run 22 | - Display a nice-looking progess bar, estimated time remaining, current test 23 | pattern, etc. 24 | * Implement tests for other 3DS hardware components 25 | * Probably some other stuff I have in mind but forgot 26 | 27 | ## How to Build This 28 | Building requires [firmtool](https://github.com/TuxSH/firmtool), and either 29 | `arm-none-eabi-gcc` and `arm-none-eabi-objcopy` in path or DevkitARM installed. 30 | Then, just run `make`. 31 | 32 | ## License 33 | You may use this under the terms of the GNU General Public License GPL v2 or 34 | under the terms of any later revisions of the GPL. Refer to the provided 35 | `license.txt` file for further information. 36 | 37 | ## Credits 38 | * **MemTest86** and **Memtest86+** for providing very useful [conceptual 39 | descriptions](https://www.memtest86.com/tech_memory-test.html) and [example 40 | implementations](https://www.memtest.org/#downcode), respectively, of a proper 41 | memory testing methodology 42 | * **Normmatt**, for sdmmc.c / sdmmc.h and the basis for the draw code 43 | * **Cha(N)**, **Kane49**, and all other FatFS contributors for FatFS 44 | * All **[3dbrew.org](https://www.3dbrew.org/wiki/Main_Page) editors** 45 | -------------------------------------------------------------------------------- /arm11/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = 3ds_hw_test_arm11 2 | 3 | export COMMIT := $(shell git rev-parse --short HEAD) 4 | 5 | SOURCE = source 6 | BUILD = build 7 | INCLUDES = $(SOURCE) ../common 8 | SOURCES = $(INCLUDES) $(SOURCE)/* 9 | 10 | SFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.S)) 11 | CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c)) 12 | 13 | OBJS = $(subst ../common/,build/cmn.,$(subst source,build,$(SFILES:.S=.o) $(CFILES:.c=.o))) 14 | DEPS = $(OBJS:.o=.d) 15 | INCLUDE = $(foreach dir, $(INCLUDES), -I$(CURDIR)/$(dir)) 16 | 17 | ARCH = -DARM11 -march=armv6k -mtune=mpcore -mlittle-endian -marm -mtp=soft 18 | ASFLAGS = $(ARCH) $(COMMON_ARCH) $(INCLUDE) 19 | CFLAGS = -DCOMMIT="\"$(COMMIT)\"" -Wall -Wextra -Os $(ARCH) $(INCLUDE) -fno-stack-protector 20 | LDFLAGS = -T link.ld -Wl,--nmagic,--use-blx -nostartfiles 21 | 22 | ifneq (true, $(USE_DKA)) 23 | LDFLAGS += -specs=nosys.specs -specs=nano.specs 24 | endif 25 | 26 | .PHONY: all clean 27 | 28 | all: $(TARGET).bin 29 | 30 | $(TARGET).elf: $(OBJS) 31 | $(CC) $(LDFLAGS) $^ -o $@ 32 | 33 | $(BUILD)/%.o: $(SOURCE)/%.c 34 | @mkdir -p "$(@D)" 35 | $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ 36 | 37 | $(BUILD)/%.o: $(SOURCE)/%.S 38 | @mkdir -p "$(@D)" 39 | $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@ 40 | 41 | $(BUILD)/cmn.%.o: ../common/%.c 42 | @mkdir -p "$(@D)" 43 | $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ 44 | 45 | $(BUILD)/cmn.%.o: ../common/%.S 46 | @mkdir -p "$(@D)" 47 | $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@ 48 | 49 | $(TARGET).bin: $(TARGET).elf 50 | $(OBJCOPY) -S -O binary $< $@ 51 | 52 | clean: 53 | @rm -rf $(OBJS) $(TARGET).elf $(TARGET).bin $(BUILD) 54 | 55 | -include $(DEPS) 56 | -------------------------------------------------------------------------------- /arm11/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(__boot) 4 | 5 | MEMORY 6 | { 7 | /* Virtual address where bootrom will be mappped to avoid veneers */ 8 | BOOTROM (RX) : ORIGIN = 0x17000000, LENGTH = 32K 9 | VRAMLOAD (RWX) : ORIGIN = 0x18100000, LENGTH = 1M 10 | } 11 | 12 | SECTIONS 13 | { 14 | .bootrom (NOLOAD) : ALIGN(4K) 15 | { 16 | *(.bootrom*) 17 | . = ALIGN(4K); 18 | } >BOOTROM 19 | 20 | .text : ALIGN(4K) 21 | { 22 | __text_pa = LOADADDR(.text); 23 | __text_va = ABSOLUTE(.); 24 | *(.text*) 25 | . = ALIGN(4K); 26 | __text_len = . - __text_va; 27 | } >VRAMLOAD 28 | 29 | .data : ALIGN(4K) 30 | { 31 | __data_pa = LOADADDR(.data); 32 | __data_va = ABSOLUTE(.); 33 | *(.data*) 34 | . = ALIGN(4K); 35 | __data_len = . - __data_va; 36 | } >VRAMLOAD 37 | 38 | .rodata : ALIGN(4K) 39 | { 40 | __rodata_pa = LOADADDR(.rodata); 41 | __rodata_va = ABSOLUTE(.); 42 | *(.rodata*) 43 | . = ALIGN(4K); 44 | __rodata_len = . - __rodata_va; 45 | } >VRAMLOAD 46 | 47 | .shared (NOLOAD) : ALIGN(4K) 48 | { 49 | __shared_pa = LOADADDR(.shared); 50 | __shared_va = ABSOLUTE(.); 51 | *(.shared*) 52 | . = ALIGN(4K); 53 | __shared_len = . - __shared_va; 54 | } >VRAMLOAD 55 | 56 | .bss (NOLOAD) : ALIGN(4K) 57 | { 58 | __bss_pa = LOADADDR(.bss); 59 | __bss_va = ABSOLUTE(.); 60 | *(.bss*) 61 | . = ALIGN(4K); 62 | __bss_len = . - __bss_va; 63 | } >VRAMLOAD 64 | } 65 | -------------------------------------------------------------------------------- /arm11/source/arm/exception.c: -------------------------------------------------------------------------------- 1 | #include "exception.h" 2 | #include "ui/debug.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void doException(u32 type, u32* regs) { 11 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x29, 4); 12 | 13 | consoleClear(); 14 | debugPrintf("ARM11 ded %ld\n\n", type); 15 | 16 | for (int i = 0; i < 20; i += 2) { 17 | debugPrintf("%02d %08lX %02d %08lX\n", i, regs[i], i+1, regs[i+1]); 18 | } 19 | 20 | debugPrint("\nStack:\n"); 21 | u32* sp = (u32*) regs[13]; 22 | 23 | for (int i = 0; i < 32; i += 4) { 24 | debugPrintf("%08lX %08lX %08lX %08lX\n", sp[i], sp[i+1], sp[i+2], sp[i+3]); 25 | } 26 | 27 | PXI_Send(PXICMD_LEGACY_BOOT); 28 | 29 | while (1); 30 | } -------------------------------------------------------------------------------- /arm11/source/arm/exception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | void doException(u32 type, u32* regs); -------------------------------------------------------------------------------- /arm11/source/arm/gic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "arm/gic.h" 25 | 26 | #include "system/event.h" 27 | 28 | /* Generic Interrupt Controller Registers */ 29 | #define REG_GIC(cpu, o, t) REG_ARM_PMR(0x200 + ((cpu) * 0x100) + (o), t) 30 | 31 | #define REG_GIC_CONTROL(c) (*REG_GIC(c, 0x00, u32)) 32 | #define REG_GIC_PRIOMASK(c) (*REG_GIC(c, 0x04, u32)) 33 | #define REG_GIC_POI(c) (*REG_GIC(c, 0x08, u32)) 34 | #define REG_GIC_IRQACK(c) (*REG_GIC(c, 0x0C, u32)) 35 | #define REG_GIC_IRQEND(c) (*REG_GIC(c, 0x10, u32)) 36 | #define REG_GIC_LASTPRIO(c) (*REG_GIC(c, 0x14, u32)) 37 | #define REG_GIC_PENDING(c) (*REG_GIC(c, 0x18, u32)) 38 | 39 | #define GIC_THIS_CPU_ALIAS (-1) 40 | #define GIC_IRQ_SPURIOUS (1023) 41 | 42 | /* Interrupt Distributor Registers */ 43 | #define REG_DIC(off, type) REG_ARM_PMR(0x1000 + (off), type) 44 | 45 | #define REG_DIC_CONTROL (*REG_DIC(0x00, u32)) 46 | #define REG_DIC_TYPE (*REG_DIC(0x04, u32)) 47 | #define REG_DIC_SETENABLE REG_DIC(0x100, u32) // 32 intcfg per reg 48 | #define REG_DIC_CLRENABLE REG_DIC(0x180, u32) 49 | #define REG_DIC_SETPENDING REG_DIC(0x200, u32) 50 | #define REG_DIC_CLRPENDING REG_DIC(0x280, u32) 51 | #define REG_DIC_PRIORITY REG_DIC(0x400, u8) // 1 intcfg per reg (in upper 4 bits) 52 | #define REG_DIC_TARGETCPU REG_DIC(0x800, u8) // 1 intcfg per reg 53 | #define REG_DIC_CFGREG REG_DIC(0xC00, u32) // 16 intcfg per reg 54 | #define REG_DIC_SOFTINT (*REG_DIC(0xF00, u32)) 55 | 56 | // used only on reset routines 57 | #define REG_DIC_PRIORITY32 REG_DIC(0x400, u32) // 4 intcfg per reg (in upper 4 bits) 58 | #define REG_DIC_TARGETCPU32 REG_DIC(0x800, u32) // 4 intcfg per reg 59 | 60 | #define GIC_PRIO_NEVER32 \ 61 | (GIC_PRIO_NEVER | (GIC_PRIO_NEVER << 8) | \ 62 | (GIC_PRIO_NEVER << 16) | (GIC_PRIO_NEVER << 24)) 63 | #define GIC_PRIO_HIGH32 \ 64 | (GIC_PRIO_HIGHEST | (GIC_PRIO_HIGHEST << 8) | \ 65 | (GIC_PRIO_HIGHEST << 16) | (GIC_PRIO_HIGHEST << 24)) 66 | 67 | /* CPU source ID is present in Interrupt Acknowledge register? */ 68 | #define IRQN_SRC_MASK (0x7 << 10) 69 | 70 | /* Interrupt Handling */ 71 | #define LOCAL_IRQS (32) 72 | #define DIC_MAX_IRQ (LOCAL_IRQS + MAX_IRQ) 73 | 74 | #define COREMASK_VALID(x) (((x) > 0) && ((x) < BIT(MAX_CPU))) 75 | 76 | #define IRQN_IS_VALID(n) ((n) < DIC_MAX_IRQ) 77 | 78 | static gicIrqHandler gicIrqHandlers[DIC_MAX_IRQ]; 79 | static _Atomic(u32) gicIrqPending[DIC_MAX_IRQ / 32]; 80 | 81 | static struct { 82 | u8 tgt; 83 | u8 prio; 84 | } gicIrqConfig[DIC_MAX_IRQ]; 85 | 86 | // gets used whenever a NULL pointer is passed to gicEnableInterrupt 87 | static void gicDummyHandler(u32 irqn) { (void)irqn; return; } 88 | 89 | static const struct { 90 | u8 low, high, mode; 91 | } gicDefaultIrqCfg[] = { 92 | { .low = 0x00, .high = 0x1F, .mode = GIC_RISINGEDGE_NN }, 93 | { .low = 0x20, .high = 0x23, .mode = GIC_LEVELHIGH_1N }, 94 | { .low = 0x24, .high = 0x24, .mode = GIC_RISINGEDGE_1N }, 95 | { .low = 0x25, .high = 0x27, .mode = GIC_LEVELHIGH_1N }, 96 | { .low = 0x28, .high = 0x2D, .mode = GIC_RISINGEDGE_1N }, 97 | { .low = 0x30, .high = 0x3B, .mode = GIC_LEVELHIGH_1N }, 98 | { .low = 0x40, .high = 0x4E, .mode = GIC_RISINGEDGE_1N }, 99 | { .low = 0x4F, .high = 0x4F, .mode = GIC_LEVELHIGH_1N }, 100 | { .low = 0x50, .high = 0x57, .mode = GIC_RISINGEDGE_1N }, 101 | { .low = 0x58, .high = 0x58, .mode = GIC_LEVELHIGH_1N }, 102 | { .low = 0x59, .high = 0x75, .mode = GIC_RISINGEDGE_1N }, 103 | { .low = 0x76, .high = 0x77, .mode = GIC_LEVELHIGH_1N }, 104 | { .low = 0x78, .high = 0x78, .mode = GIC_RISINGEDGE_1N }, 105 | { .low = 0x79, .high = 0x7d, .mode = GIC_LEVELHIGH_1N }, 106 | }; 107 | 108 | static u8 gicGetDefaultIrqCfg(u32 irqn) { 109 | for (unsigned i = 0; i < countof(gicDefaultIrqCfg); i++) { 110 | if ((irqn >= gicDefaultIrqCfg[i].low) && (irqn <= gicDefaultIrqCfg[i].high)) 111 | return gicDefaultIrqCfg[i].mode; 112 | } 113 | // TODO: would it be considerably faster to use bsearch? 114 | return GIC_RISINGEDGE_1N; 115 | } 116 | 117 | void gicTopHandler(void) 118 | { 119 | while(1) { 120 | u32 irqn, irqsource, index, mask; 121 | 122 | /** 123 | If more than one of these CPUs reads the Interrupt Acknowledge Register at the 124 | same time, they can all acknowledge the same interrupt. The interrupt service 125 | routine must ensure that only one of them tries to process the interrupt, with the 126 | others returning after writing the ID to the End of Interrupt Register. 127 | */ 128 | irqsource = REG_GIC_IRQACK(GIC_THIS_CPU_ALIAS); 129 | 130 | if (irqsource == GIC_IRQ_SPURIOUS) // no further processing is needed 131 | break; 132 | 133 | irqn = irqsource & ~IRQN_SRC_MASK; 134 | 135 | index = irqn / 32; 136 | mask = BIT(irqn % 32); 137 | atomic_fetch_or(&gicIrqPending[index], mask); 138 | 139 | (gicIrqHandlers[irqn])(irqsource); 140 | // if the id is < 16, the source CPU can be obtained from irqn 141 | // if the handler isn't set, it'll try to branch to 0 and trigger a prefetch abort 142 | 143 | REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irqsource; 144 | } 145 | } 146 | 147 | void gicGlobalReset(void) 148 | { 149 | u32 dic_type; 150 | unsigned gicn, intn; 151 | 152 | dic_type = REG_DIC_TYPE; 153 | 154 | // number of local controllers 155 | gicn = ((dic_type >> 5) & 3) + 1; 156 | 157 | // number of interrupt lines (up to 224 external + 32 fixed internal per CPU) 158 | intn = ((dic_type & 7) + 1) * 32; 159 | 160 | // clamp it down to the amount of CPUs designed to handle 161 | if (gicn > MAX_CPU) 162 | gicn = MAX_CPU; 163 | 164 | // clear the interrupt handler and config table 165 | getEventIRQ()->reset(); 166 | memset(gicIrqHandlers, 0, sizeof(gicIrqHandlers)); 167 | memset(gicIrqConfig, 0, sizeof(gicIrqConfig)); 168 | 169 | // disable all MP11 GICs 170 | for (unsigned i = 0; i < gicn; i++) 171 | REG_GIC_CONTROL(i) = 0; 172 | 173 | // disable the main DIC 174 | REG_DIC_CONTROL = 0; 175 | 176 | // clear all external interrupts 177 | for (unsigned i = 1; i < (intn / 32); i++) { 178 | REG_DIC_CLRENABLE[i] = ~0; 179 | REG_DIC_CLRPENDING[i] = ~0; 180 | } 181 | 182 | // reset all external priorities to highest by default 183 | // clear target processor regs 184 | for (unsigned i = 4; i < (intn / 4); i++) { 185 | REG_DIC_PRIORITY32[i] = GIC_PRIO_HIGH32; 186 | REG_DIC_TARGETCPU32[i] = 0; 187 | } 188 | 189 | // set all interrupts to active level triggered in N-N model 190 | for (unsigned i = 16; i < (intn / 16); i++) 191 | REG_DIC_CFGREG[i] = 0; 192 | 193 | // re enable the main DIC 194 | REG_DIC_CONTROL = 1; 195 | 196 | for (unsigned i = 0; i < gicn; i++) { 197 | // compare all priority bits 198 | REG_GIC_POI(i) = 3; 199 | 200 | // don't mask any interrupt with low priority 201 | REG_GIC_PRIOMASK(i) = 0xF0; 202 | 203 | // enable all the MP11 GICs 204 | REG_GIC_CONTROL(i) = 1; 205 | } 206 | } 207 | 208 | void gicLocalReset(void) 209 | { 210 | u32 irq_s; 211 | 212 | // disable all local interrupts 213 | REG_DIC_CLRENABLE[0] = ~0; 214 | REG_DIC_CLRPENDING[0] = ~0; 215 | 216 | for (unsigned i = 0; i < 4; i++) { 217 | REG_DIC_PRIORITY32[i] = GIC_PRIO_HIGH32; 218 | // local IRQs are always unmasked by default 219 | 220 | // REG_DIC_TARGETCPU[i] = 0; 221 | // not needed, always read as corresponding MP11 core 222 | } 223 | 224 | // ack until it gets a spurious IRQ 225 | do { 226 | irq_s = REG_GIC_PENDING(GIC_THIS_CPU_ALIAS); 227 | REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irq_s; 228 | } while(irq_s != GIC_IRQ_SPURIOUS); 229 | } 230 | 231 | static void gicSetIrqCfg(u32 irqn) { 232 | u32 smt, cfg; 233 | 234 | smt = irqn & 15; 235 | cfg = REG_DIC_CFGREG[irqn / 16]; 236 | cfg &= ~(3 << smt); 237 | cfg |= gicGetDefaultIrqCfg(irqn) << smt; 238 | REG_DIC_CFGREG[irqn / 16] = cfg; 239 | } 240 | 241 | void gicSetInterruptConfig(u32 irqn, u32 coremask, u32 prio, gicIrqHandler handler) 242 | { 243 | if (handler == NULL) // maybe add runtime ptr checks here too? 244 | handler = gicDummyHandler; 245 | 246 | gicIrqConfig[irqn].tgt = coremask; 247 | gicIrqConfig[irqn].prio = prio; 248 | gicIrqHandlers[irqn] = handler; 249 | } 250 | 251 | void gicClearInterruptConfig(u32 irqn) 252 | { 253 | memset(&gicIrqConfig[irqn], 0, sizeof(gicIrqConfig[irqn])); 254 | gicIrqHandlers[irqn] = NULL; 255 | } 256 | 257 | void gicEnableInterrupt(u32 irqn) 258 | { 259 | REG_DIC_PRIORITY[irqn] = gicIrqConfig[irqn].prio; 260 | REG_DIC_TARGETCPU[irqn] = gicIrqConfig[irqn].tgt; 261 | gicSetIrqCfg(irqn); 262 | 263 | REG_DIC_CLRPENDING[irqn / 32] |= BIT(irqn & 0x1F); 264 | REG_DIC_SETENABLE[irqn / 32] |= BIT(irqn & 0x1F); 265 | } 266 | 267 | void gicDisableInterrupt(u32 irqn) 268 | { 269 | REG_DIC_CLRENABLE[irqn / 32] |= BIT(irqn & 0x1F); 270 | REG_DIC_CLRPENDING[irqn / 32] |= BIT(irqn & 0x1F); 271 | } 272 | 273 | void gicTriggerSoftInterrupt(u32 softirq) 274 | { 275 | REG_DIC_SOFTINT = softirq; 276 | } 277 | 278 | static void irqEvReset(void) { 279 | memset(&gicIrqPending, 0, sizeof(gicIrqPending)); 280 | } 281 | 282 | static u32 irqEvTest(u32 param, u32 clear) { 283 | u32 index, tstmask, clrmask; 284 | 285 | if (param >= DIC_MAX_IRQ) 286 | ARM_BKPT(); 287 | index = param / 32; 288 | tstmask = BIT(param % 32); 289 | clrmask = clear ? tstmask : 0; 290 | return !!(atomic_fetch_and(&gicIrqPending[index], ~clrmask) & tstmask); 291 | } 292 | 293 | static const EventInterface evIRQ = { 294 | .reset = irqEvReset, 295 | .test = irqEvTest 296 | }; 297 | 298 | const EventInterface *getEventIRQ(void) { 299 | return &evIRQ; 300 | } 301 | -------------------------------------------------------------------------------- /arm11/source/arm/gic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2020 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | typedef void (*gicIrqHandler)(u32 irqn); 25 | 26 | enum { 27 | GIC_LEVELHIGH_NN = 0, // no interrupts use level high triggers so far 28 | GIC_LEVELHIGH_1N = 1, 29 | 30 | GIC_RISINGEDGE_NN = 2, 31 | GIC_RISINGEDGE_1N = 3 32 | 33 | // With the 1-N model, an interrupt that is taken on any CPU clears the Pending 34 | // status on all CPUs. 35 | // With the N-N model, all CPUs receive the interrupt independently. The Pending 36 | // status is cleared only for the CPU that takes it, not for the other CPUs 37 | }; 38 | 39 | enum { 40 | GIC_PRIO0 = 0x00, 41 | GIC_PRIO1 = 0x10, 42 | GIC_PRIO2 = 0x20, 43 | GIC_PRIO3 = 0x30, 44 | GIC_PRIO4 = 0x40, 45 | GIC_PRIO5 = 0x50, 46 | GIC_PRIO6 = 0x60, 47 | GIC_PRIO7 = 0x70, 48 | GIC_PRIO14 = 0xE0, 49 | GIC_PRIO15 = 0xF0, 50 | }; 51 | 52 | #define GIC_PRIO_HIGHEST GIC_PRIO0 53 | #define GIC_PRIO_LOWEST GIC_PRIO14 54 | #define GIC_PRIO_NEVER GIC_PRIO15 55 | 56 | void gicGlobalReset(void); 57 | void gicLocalReset(void); 58 | 59 | /* 60 | Notes from https://static.docs.arm.com/ddi0360/f/DDI0360F_arm11_mpcore_r2p0_trm.pdf 61 | 62 | INTERRUPT ENABLE: 63 | Interrupts 0-15 fields are read as one, that is, always enabled, and write to these fields 64 | have no effect. 65 | Notpresent interrupts (depending on the Interrupt Controller Type Register and 66 | interrupt number field) related fields are read as zero and writes to these fields have no 67 | effect. 68 | 69 | INTERRUPT PRIORITY: 70 | The first four registers are aliased for each MP11 CPU, that is, the priority set for 71 | ID0-15 and ID29-31 can be different for each MP11 CPU. The priority of IPIs ID0-15 72 | depends on the receiving CPU, not the sending CPU. 73 | 74 | INTERRUPT CPU TARGET: 75 | For MP11 CPU n, CPU targets 29, 30 and 31 are read as (1 << n). Writes are ignored. 76 | For IT0-IT28, these fields are read as zero and writes are ignored. 77 | 78 | INTERRUPT CONFIGURATION: 79 | For ID0-ID15, bit 1 of the configuration pair is always read as one, that is, rising edge 80 | sensitive. 81 | For ID0-ID15, bit 0 (software model) can be configured and applies to the interrupts 82 | sent from the writing MP11 CPU. 83 | For ID29, and ID30, the configuration pair is always read as b10, that is rising edge 84 | sensitive and N-N software model because these IDs are allocated to timer and 85 | watchdog interrupts that are CPU-specific 86 | */ 87 | 88 | #define COREMASK_ALL (BIT(MAX_CPU) - 1) 89 | 90 | void gicSetInterruptConfig(u32 irqn, u32 coremask, u32 prio, gicIrqHandler handler); 91 | void gicClearInterruptConfig(u32 irqn); 92 | 93 | void gicEnableInterrupt(u32 irqn); 94 | void gicDisableInterrupt(u32 irqn); 95 | 96 | enum { 97 | GIC_SOFTIRQ_LIST = 0, 98 | GIC_SOFTIRQ_OTHERS = 1, // all except self 99 | GIC_SOFTIRQ_SELF = 2, 100 | }; 101 | 102 | #define GIC_SOFTIRQ_SOURCE(n) (((n) >> 10) & 0xF) 103 | #define GIC_SOFTIRQ_ID(n) ((n) & 0x3FF) 104 | 105 | #define GIC_SOFTIRQ_FMT(id, filter, coremask) \ 106 | ((id) | ((coremask) << 16) | ((filter) << 24)) 107 | // id & 0xf, coremask & 3, filter & 3 108 | // coremask is only used with filter == GIC_SOFTIRQ_LIST 109 | 110 | #define GIC_SOFTIRQ_SRC(x) (((x) >> 10) % MAX_CPU) 111 | 112 | void gicTriggerSoftInterrupt(u32 softirq); 113 | -------------------------------------------------------------------------------- /arm11/source/arm/mmu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2018-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "arm/mmu.h" 24 | 25 | /* Virtual Memory Mapper */ 26 | #define L1_VA_IDX(v) (((v) >> 20) & 0xFFF) 27 | #define L2_VA_IDX(v) (((v) >> 12) & 0xFF) 28 | 29 | #define SECT_ADDR_SHIFT (20) 30 | #define COARSE_ADDR_SHIFT (10) 31 | #define PAGE_ADDR_SHIFT (12) 32 | #define LPAGE_ADDR_SHIFT (16) 33 | 34 | #define SECT_SIZE (BIT(SECT_ADDR_SHIFT)) 35 | #define COARSE_SIZE (BIT(COARSE_ADDR_SHIFT)) 36 | #define PAGE_SIZE (BIT(PAGE_ADDR_SHIFT)) 37 | #define LPAGE_SIZE (BIT(LPAGE_ADDR_SHIFT)) 38 | 39 | #define SECT_MASK (~(SECT_SIZE - 1)) 40 | #define COARSE_MASK (~(COARSE_SIZE - 1)) 41 | #define PAGE_MASK (~(PAGE_SIZE - 1)) 42 | #define LPAGE_MASK (~(LPAGE_SIZE - 1)) 43 | 44 | #define DESCRIPTOR_L1_UNMAPPED (0) 45 | #define DESCRIPTOR_L1_COARSE (1) 46 | #define DESCRIPTOR_L1_SECTION (2) 47 | #define DESCRIPTOR_L1_RESERVED (3) 48 | 49 | #define DESCRIPTOR_L2_UNMAPPED (0) 50 | #define DESCRIPTOR_L2_LARGEPAGE (1) 51 | #define DESCRIPTOR_L2_PAGE_EXEC (2) 52 | #define DESCRIPTOR_L2_PAGE_NX (3) 53 | 54 | #define DESCRIPTOR_TYPE_MASK (3) 55 | 56 | enum { 57 | L1_UNMAPPED, 58 | L1_COARSE, 59 | L1_SECTION, 60 | L1_RESERVED, 61 | 62 | L2_UNMAPPED, 63 | L2_LARGEPAGE, 64 | L2_PAGE, 65 | }; 66 | 67 | typedef struct { 68 | u32 desc[4096]; 69 | } __attribute__((aligned(16384))) mmuLevel1Table; 70 | 71 | typedef struct { 72 | u32 desc[256]; 73 | } __attribute__((aligned(1024))) mmuLevel2Table; 74 | 75 | static mmuLevel1Table mmuGlobalTT; 76 | 77 | // simple watermark allocator for 2nd level page tables 78 | #define MAX_SECOND_LEVEL (8) 79 | static mmuLevel2Table mmuCoarseTables[MAX_SECOND_LEVEL]; 80 | static u32 mmuCoarseAllocated = 0; 81 | static mmuLevel2Table *mmuAllocateLevel2Table(void) 82 | { 83 | return &mmuCoarseTables[mmuCoarseAllocated++]; 84 | } 85 | 86 | 87 | // functions to convert from internal page flag format to ARM 88 | 89 | // {TEX, CB} pairs 90 | static const u8 mmuTypeLUT[MMU_MEMORY_TYPES][2] = { 91 | [MMU_STRONG_ORDER] = {0, 0}, 92 | [MMU_UNCACHEABLE] = {1, 0}, 93 | [MMU_DEV_SHARED] = {0, 1}, 94 | [MMU_DEV_NONSHARED] = {2, 0}, 95 | [MMU_CACHE_WT] = {0, 2}, 96 | [MMU_CACHE_WB] = {1, 3}, 97 | [MMU_CACHE_WBA] = {1, 3}, 98 | }; 99 | 100 | static u32 mmuGetTEX(u32 f) 101 | { return mmuTypeLUT[MMU_FLAGS_TYPE(f)][0]; } 102 | static u32 mmuGetCB(u32 f) 103 | { return mmuTypeLUT[MMU_FLAGS_TYPE(f)][1]; } 104 | static u32 mmuGetNX(u32 f) 105 | { return MMU_FLAGS_NOEXEC(f) ? 1 : 0; } 106 | static u32 mmuGetShared(u32 f) 107 | { return MMU_FLAGS_SHARED(f) ? 1 : 0; } 108 | 109 | // access permissions 110 | static const u8 mmuAccessLUT[MMU_ACCESS_TYPES] = { 111 | [MMU_NO_ACCESS] = 0, 112 | [MMU_READ_ONLY] = 0x21, 113 | [MMU_READ_WRITE] = 0x01, 114 | }; 115 | 116 | static u32 mmuGetAP(u32 f) 117 | { return mmuAccessLUT[MMU_FLAGS_ACCESS(f)]; } 118 | 119 | // other misc helper functions 120 | static unsigned mmuWalkTT(u32 va) 121 | { 122 | mmuLevel2Table *coarsepd; 123 | u32 desc = mmuGlobalTT.desc[L1_VA_IDX(va)]; 124 | 125 | switch(desc & DESCRIPTOR_TYPE_MASK) { 126 | case DESCRIPTOR_L1_UNMAPPED: 127 | return L1_UNMAPPED; 128 | 129 | case DESCRIPTOR_L1_COARSE: 130 | break; 131 | 132 | case DESCRIPTOR_L1_SECTION: 133 | return L1_SECTION; 134 | 135 | case DESCRIPTOR_L1_RESERVED: 136 | return L1_RESERVED; 137 | } 138 | 139 | coarsepd = (mmuLevel2Table*)(desc & COARSE_MASK); 140 | desc = coarsepd->desc[L2_VA_IDX(va)]; 141 | 142 | switch(desc & DESCRIPTOR_TYPE_MASK) { 143 | default: 144 | case DESCRIPTOR_L2_UNMAPPED: 145 | return L2_UNMAPPED; 146 | 147 | case DESCRIPTOR_L2_LARGEPAGE: 148 | return L2_LARGEPAGE; 149 | 150 | case DESCRIPTOR_L2_PAGE_NX: 151 | case DESCRIPTOR_L2_PAGE_EXEC: 152 | return L2_PAGE; 153 | } 154 | } 155 | 156 | static mmuLevel2Table *mmuCoarseFix(u32 va) 157 | { 158 | u32 type; 159 | mmuLevel2Table *coarsepd; 160 | 161 | type = mmuWalkTT(va); 162 | switch(type) { 163 | case L1_UNMAPPED: 164 | coarsepd = mmuAllocateLevel2Table(); 165 | mmuGlobalTT.desc[L1_VA_IDX(va)] = (u32)coarsepd | DESCRIPTOR_L1_COARSE; 166 | break; 167 | 168 | case L2_UNMAPPED: 169 | coarsepd = (mmuLevel2Table*)(mmuGlobalTT.desc[L1_VA_IDX(va)] & COARSE_MASK); 170 | break; 171 | 172 | default: 173 | coarsepd = NULL; 174 | break; 175 | } 176 | 177 | return coarsepd; 178 | } 179 | 180 | 181 | /* Sections */ 182 | static u32 mmuSectionFlags(u32 f) 183 | { // converts the internal format to the hardware L1 section format 184 | return (mmuGetShared(f) << 16) | (mmuGetTEX(f) << 12) | 185 | (mmuGetAP(f) << 10) | (mmuGetNX(f) << 4) | 186 | (mmuGetCB(f) << 2) | DESCRIPTOR_L1_SECTION; 187 | } 188 | 189 | static void mmuMapSection(u32 va, u32 pa, u32 flags) 190 | { 191 | mmuGlobalTT.desc[L1_VA_IDX(va)] = pa | mmuSectionFlags(flags); 192 | } 193 | 194 | 195 | /* Pages */ 196 | static u32 mmuPageFlags(u32 f) 197 | { 198 | return (mmuGetShared(f) << 10) | (mmuGetTEX(f) << 6) | 199 | (mmuGetAP(f) << 4) | (mmuGetCB(f) << 2) | 200 | (mmuGetNX(f) ? DESCRIPTOR_L2_PAGE_NX : DESCRIPTOR_L2_PAGE_EXEC); 201 | } 202 | 203 | static void mmuMapPage(u32 va, u32 pa, u32 flags) 204 | { 205 | mmuLevel2Table *l2 = mmuCoarseFix(va); 206 | l2->desc[L2_VA_IDX(va)] = pa | mmuPageFlags(flags); 207 | } 208 | 209 | 210 | static bool mmuMappingFits(u32 va, u32 pa, u32 sz, u32 alignment) 211 | { 212 | return !((va | pa | sz) & (alignment)); 213 | } 214 | 215 | u32 mmuMapArea(u32 va, u32 pa, u32 size, u32 flags) 216 | { 217 | static const struct { 218 | u32 size; 219 | void (*mapfn)(u32,u32,u32); 220 | } VMappers[] = { 221 | { 222 | .size = BIT(SECT_ADDR_SHIFT), 223 | .mapfn = mmuMapSection, 224 | }, 225 | { 226 | .size = BIT(PAGE_ADDR_SHIFT), 227 | .mapfn = mmuMapPage, 228 | }, 229 | }; 230 | 231 | while(size > 0) { 232 | size_t i = 0; 233 | for (i = 0; i < countof(VMappers); i++) { 234 | u32 pgsize = VMappers[i].size; 235 | 236 | if (mmuMappingFits(va, pa, size, pgsize-1)) { 237 | (VMappers[i].mapfn)(va, pa, flags); 238 | 239 | va += pgsize; 240 | pa += pgsize; 241 | size -= pgsize; 242 | break; 243 | } 244 | } 245 | /* alternatively return the unmapped remaining size: 246 | if (i == countof(VMappers)) 247 | return size; 248 | */ 249 | } 250 | 251 | return 0; 252 | } 253 | 254 | void mmuInvalidate(void) 255 | { 256 | ARM_MCR(p15, 0, 0, c8, c7, 0); 257 | } 258 | 259 | void mmuInvalidateVA(u32 addr) 260 | { 261 | ARM_MCR(p15, 0, addr, c8, c7, 2); 262 | } 263 | 264 | void mmuInitRegisters(void) 265 | { 266 | u32 ttbr0 = (u32)(&mmuGlobalTT) | 0x12; 267 | 268 | // Set up TTBR0/1 and the TTCR 269 | ARM_MCR(p15, 0, ttbr0, c2, c0, 0); 270 | ARM_MCR(p15, 0, 0, c2, c0, 1); 271 | ARM_MCR(p15, 0, 0, c2, c0, 2); 272 | 273 | // Set up the DACR 274 | ARM_MCR(p15, 0, 0x55555555, c3, c0, 0); 275 | 276 | // Invalidate the unified TLB 277 | ARM_MCR(p15, 0, 0, c8, c7, 0); 278 | } 279 | -------------------------------------------------------------------------------- /arm11/source/arm/mmu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2018-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | enum { 24 | MMU_STRONG_ORDER = 0, 25 | MMU_UNCACHEABLE, 26 | MMU_DEV_SHARED, 27 | MMU_DEV_NONSHARED, 28 | MMU_CACHE_WT, 29 | MMU_CACHE_WB, 30 | MMU_CACHE_WBA, 31 | MMU_MEMORY_TYPES, 32 | }; 33 | 34 | enum { 35 | MMU_NO_ACCESS = 0, 36 | MMU_READ_ONLY, 37 | MMU_READ_WRITE, 38 | MMU_ACCESS_TYPES, 39 | }; 40 | 41 | #define MMU_FLAGS(t, ap, nx, s) ((s) << 25 | (nx) << 24 | (ap) << 8 | (t)) 42 | 43 | #define MMU_FLAGS_TYPE(f) ((f) & 0xFF) 44 | #define MMU_FLAGS_ACCESS(f) (((f) >> 8) & 0xFF) 45 | 46 | #define MMU_FLAGS_NOEXEC(f) ((f) & BIT(24)) 47 | #define MMU_FLAGS_SHARED(f) ((f) & BIT(25)) 48 | 49 | u32 mmuMapArea(u32 va, u32 pa, u32 size, u32 flags); 50 | 51 | void mmuInvalidate(void); 52 | void mmuInvalidateVA(u32 addr); // DO NOT USE 53 | 54 | void mmuInitRegisters(void); 55 | -------------------------------------------------------------------------------- /arm11/source/arm/scu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2018-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #define REG_SCU_CNT (*REG_ARM_PMR(0x00, u32)) 23 | #define REG_SCU_CFG (*REG_ARM_PMR(0x04, u32)) 24 | #define REG_SCU_CPU (*REG_ARM_PMR(0x08, u32)) 25 | #define REG_SCU_INV (*REG_ARM_PMR(0x0C, u32)) 26 | 27 | void SCU_Init(void) 28 | { 29 | REG_SCU_CNT = 0x1FFE; 30 | REG_SCU_INV = 0xFFFF; 31 | REG_SCU_CNT = 0x3FFF; 32 | } 33 | -------------------------------------------------------------------------------- /arm11/source/arm/scu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2018-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | void SCU_Init(void); 24 | -------------------------------------------------------------------------------- /arm11/source/arm/timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "arm/gic.h" 23 | #include "arm/timer.h" 24 | 25 | #define TIMER_INTERRUPT (0x1E) 26 | 27 | #define REG_TIMER(c, n) REG_ARM_PMR(0x700 + ((c) * 0x100) + (n), u32) 28 | #define TIMER_THIS_CPU (-1) 29 | 30 | #define REG_TIMER_LOAD(c) *REG_TIMER((c), 0x00) 31 | #define REG_TIMER_COUNT(c) *REG_TIMER((c), 0x04) 32 | #define REG_TIMER_CNT(c) *REG_TIMER((c), 0x08) 33 | #define REG_TIMER_IRQ(c) *REG_TIMER((c), 0x0C) 34 | 35 | #define TIMER_CNT_SCALE(n) ((n) << 8) 36 | #define TIMER_CNT_INT_EN BIT(2) 37 | #define TIMER_CNT_RELOAD BIT(1) 38 | #define TIMER_CNT_ENABLE BIT(0) 39 | 40 | void TIMER_WaitTicks(u32 ticks) 41 | { 42 | REG_TIMER_IRQ(TIMER_THIS_CPU) = 1; 43 | REG_TIMER_CNT(TIMER_THIS_CPU) = 0; 44 | REG_TIMER_LOAD(TIMER_THIS_CPU) = ticks; 45 | REG_TIMER_CNT(TIMER_THIS_CPU) = TIMER_CNT_ENABLE; 46 | while(REG_TIMER_COUNT(TIMER_THIS_CPU)); 47 | } 48 | -------------------------------------------------------------------------------- /arm11/source/arm/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | // The timer interval is calculated using the following equation: 24 | // T = [(PRESCALER_value + 1) * (Load_value + 1) * 2] / CPU_CLK 25 | // therefore 26 | // Load_value = [(CPU_CLK / 2) * (T / (PRESCALER_value + 1))] - 1 27 | 28 | #define BASE_CLKRATE (268111856 / 2) 29 | 30 | #define CLK_MS_TO_TICKS(m) (((BASE_CLKRATE / 1000) * (m)) - 1) 31 | 32 | void TIMER_WaitTicks(u32 ticks); 33 | 34 | static inline void TIMER_WaitMS(u32 ms) { 35 | TIMER_WaitTicks(CLK_MS_TO_TICKS(ms)); 36 | } 37 | -------------------------------------------------------------------------------- /arm11/source/arm/xrq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2020 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | u32 xrqInstallVectorTable(void); 22 | -------------------------------------------------------------------------------- /arm11/source/arm/xrqVectors.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /* 20 | * This is almost the same as the ARM9 exception handler, 21 | * but with a few extra register dumps (DFSR, IFSR and FAR) 22 | */ 23 | 24 | #include 25 | 26 | .arm 27 | .align 3 28 | 29 | .macro TRAP_ENTRY xrq 30 | msr cpsr_f, #(\xrq << 29) 31 | b xrqMain 32 | .endm 33 | 34 | xrqVectorTable: 35 | ldr pc, =xrqReset 36 | ldr pc, =xrqUndefined 37 | ldr pc, =xrqSVC 38 | ldr pc, =xrqPrefetchAbort 39 | ldr pc, =xrqDataAbort 40 | b . @ ignore the reserved exception 41 | ldr pc, =xrqIRQ 42 | ldr pc, =xrqFIQ 43 | .pool 44 | xrqVectorTableEnd: 45 | 46 | xrqReset: 47 | TRAP_ENTRY 0 48 | 49 | xrqUndefined: 50 | TRAP_ENTRY 1 51 | 52 | xrqSVC: 53 | TRAP_ENTRY 2 54 | 55 | xrqPrefetchAbort: 56 | TRAP_ENTRY 3 57 | 58 | xrqDataAbort: 59 | TRAP_ENTRY 4 60 | 61 | xrqFIQ: 62 | TRAP_ENTRY 7 63 | 64 | xrqMain: 65 | clrex 66 | cpsid aif 67 | 68 | ldr sp, =(xrqStackTop - 32*4) 69 | stmia sp, {r0-r7} 70 | 71 | mrs r1, cpsr 72 | lsr r0, r1, #29 73 | 74 | mrs r2, spsr 75 | str lr, [sp, #15*4] 76 | str r2, [sp, #16*4] 77 | 78 | ands r2, r2, #SR_PMODE_MASK 79 | orreq r2, r2, #SR_SYS_MODE 80 | orr r2, r2, #(0x10 | SR_NOINT) 81 | 82 | add r3, sp, #8*4 83 | msr cpsr_c, r2 84 | stmia r3!, {r8-r14} 85 | msr cpsr_c, r1 86 | 87 | mrc p15, 0, r4, c5, c0, 0 @ data fault status register 88 | mrc p15, 0, r5, c5, c0, 1 @ instruction fault status register 89 | mrc p15, 0, r6, c6, c0, 0 @ data fault address 90 | add r3, r3, #2*4 @ skip saved PC and CPSR 91 | stmia r3!, {r4, r5, r6} 92 | 93 | mov r1, sp 94 | bl doException 95 | 96 | xrqMainDie: 97 | b xrqMainDie 98 | 99 | 100 | xrqIRQ: 101 | clrex 102 | sub lr, lr, #4 @ Fix return address 103 | srsfd sp!, #SR_SVC_MODE @ Store IRQ mode LR and SPSR on the SVC stack 104 | cps #SR_SVC_MODE @ Switch to SVC mode 105 | push {r0-r4, r12, lr} @ Preserve registers 106 | 107 | and r4, sp, #7 @ Fix SP to be 8byte aligned 108 | sub sp, sp, r4 109 | 110 | bl gicTopHandler 111 | 112 | add sp, sp, r4 113 | 114 | pop {r0-r4, r12, lr} 115 | rfeia sp! @ Return from exception 116 | 117 | @ u32 xrqInstallVectorTable(void) 118 | .global xrqInstallVectorTable 119 | .type xrqInstallVectorTable, %function 120 | xrqInstallVectorTable: 121 | ldr r0, =xrqPage 122 | ldr r1, =xrqVectorTable 123 | mov r2, #(xrqVectorTableEnd - xrqVectorTable) 124 | b memcpy 125 | 126 | .section .bss.xrqPage 127 | .align 12 128 | .global xrqPage 129 | xrqPage: 130 | .space 8192 @ reserve two 4K aligned pages for vectors and abort stack 131 | .global xrqStackTop 132 | xrqStackTop: 133 | -------------------------------------------------------------------------------- /arm11/source/boot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | .section .text.boot 20 | .align 4 21 | 22 | #include 23 | 24 | #define STACK_SZ (8192) 25 | 26 | .global __boot 27 | __boot: 28 | cpsid aif, #SR_SVC_MODE 29 | clrex 30 | 31 | @ Writeback and invalidate all DCache 32 | @ Invalidate all caches 33 | @ Data Synchronization Barrier 34 | mov r0, #0 35 | mcr p15, 0, r0, c7, c10, 0 36 | mcr p15, 0, r0, c7, c7, 0 37 | mcr p15, 0, r0, c7, c10, 4 38 | 39 | @ Reset control registers 40 | ldr r0, =0x00054078 41 | ldr r1, =0x0000000F 42 | ldr r2, =0x00F00000 43 | 44 | mcr p15, 0, r0, c1, c0, 0 45 | mcr p15, 0, r1, c1, c0, 1 46 | mcr p15, 0, r2, c1, c0, 2 47 | 48 | /*@ VFPv2 init 49 | @ https://github.com/derrekr/fastboot3DS/blob/f63c967369451b1fd0078e649cf0010fe10a62fd/source/arm11/start.s#L195 50 | mov r0, #0 51 | mov r1, #0xF00000 @ Give full access to cp10/11 in user and privileged mode 52 | mov r2, #0x40000000 @ Clear exception bits and enable VFP11 53 | mov r3, #0x3C00000 @ Round towards zero (RZ) mode, flush-to-zero mode, default NaN mode 54 | mcr p15, 0, r1, c1, c0, 2 @ Write Coprocessor Access Control Register 55 | mcr p15, 0, r0, c7, c5, 4 @ Flush Prefetch Buffer 56 | fmxr fpexc, r2 @ Write Floating-point exception register 57 | fmxr fpscr, r3 @ Write Floating-Point Status and Control Register*/ 58 | 59 | 60 | @ Get CPU ID 61 | mrc p15, 0, r12, c0, c0, 5 62 | ands r12, r12, #3 63 | 64 | @ Setup stack according to CPU ID 65 | ldr sp, =(_stack_base + STACK_SZ) 66 | ldr r0, =STACK_SZ 67 | mla sp, r0, r12, sp 68 | 69 | beq corezero_start 70 | 71 | cmp r12, #MAX_CPU 72 | blo coresmp_start 73 | 74 | 1: 75 | wfi 76 | b 1b 77 | 78 | corezero_start: 79 | @ assume __bss_len is 128 byte aligned 80 | ldr r0, =__bss_pa 81 | ldr r1, =__bss_len 82 | add r1, r0, r1 83 | mov r2, #0 84 | mov r3, #0 85 | mov r4, #0 86 | mov r5, #0 87 | .Lclearbss: 88 | cmp r0, r1 89 | .rept (128 / 16) @ 16 bytes copied per block 90 | stmloia r0!, {r2-r5} 91 | .endr 92 | blo .Lclearbss 93 | 94 | bl SYS_CoreZeroInit 95 | 96 | coresmp_start: 97 | bl SYS_CoreInit 98 | ldr lr, =MainLoop 99 | bx lr 100 | 101 | .section .bss.stack 102 | .align 12 @ make sure stack is aligned to a page boundary 103 | .global _stack_base 104 | _stack_base: 105 | .space (MAX_CPU * STACK_SZ) 106 | -------------------------------------------------------------------------------- /arm11/source/hw/codec.c: -------------------------------------------------------------------------------- 1 | // Somewhat based on xerpi's CODEC driver for Linux 2 | /* 3 | * This file is part of GodMode9 4 | * Copyright (C) 2017 Sergi Granell, Paul LaMendola 5 | * Copyright (C) 2019 Wolfvak 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | 25 | #include "hw/codec.h" 26 | #include 27 | 28 | #define CPAD_THRESH_X (750) 29 | #define CPAD_THRESH_Y (150) 30 | 31 | /* SPI stuff */ 32 | static void CODEC_WriteRead(u32 *txb, u8 txl, u32 *rxb, u8 rxl) 33 | { 34 | SPI_XferInfo xfers[2]; 35 | 36 | xfers[0].buf = txb; 37 | xfers[0].len = txl; 38 | xfers[0].read = false; 39 | 40 | xfers[1].buf = rxb; 41 | xfers[1].len = rxl; 42 | xfers[1].read = true; 43 | 44 | SPI_DoXfer(SPI_DEV_CODEC, xfers, 2, true); 45 | } 46 | 47 | static void CODEC_RegSelect(u8 reg) 48 | { 49 | SPI_XferInfo xfer; 50 | u32 cmd; 51 | 52 | cmd = reg << 8; 53 | 54 | xfer.buf = &cmd; 55 | xfer.len = 2; 56 | xfer.read = false; 57 | 58 | SPI_DoXfer(SPI_DEV_CODEC, &xfer, 1, true); 59 | } 60 | 61 | static u8 CODEC_RegRead(u8 reg) 62 | { 63 | u32 cmd, ret; 64 | cmd = (reg << 1) | 1; 65 | CODEC_WriteRead(&cmd, 1, &ret, 1); 66 | return ret; 67 | } 68 | 69 | static void CODEC_RegReadBuf(u8 reg, u32 *out, u8 size) 70 | { 71 | u32 cmd = (reg << 1) | 1; 72 | CODEC_WriteRead(&cmd, 1, out, size); 73 | } 74 | 75 | static void CODEC_RegWrite(u8 reg, u8 val) 76 | { 77 | SPI_XferInfo xfer; 78 | u32 cmd; 79 | 80 | cmd = (val << 8) | (reg << 1); 81 | 82 | xfer.buf = &cmd; 83 | xfer.len = 2; 84 | xfer.read = false; 85 | 86 | SPI_DoXfer(SPI_DEV_CODEC, &xfer, 1, true); 87 | } 88 | 89 | static void CODEC_RegMask(u8 reg, u8 mask0, u8 mask1) 90 | { 91 | CODEC_RegWrite(reg, (CODEC_RegRead(reg) & ~mask1) | (mask0 & mask1)); 92 | } 93 | 94 | // elder god magic 95 | void CODEC_Init(void) 96 | { 97 | CODEC_RegSelect(0x67); 98 | CODEC_RegWrite(0x24, 0x98); 99 | CODEC_RegWrite(0x26, 0x00); 100 | CODEC_RegWrite(0x25, 0x43); 101 | CODEC_RegWrite(0x24, 0x18); 102 | CODEC_RegWrite(0x17, 0x43); 103 | CODEC_RegWrite(0x19, 0x69); 104 | CODEC_RegWrite(0x1B, 0x80); 105 | CODEC_RegWrite(0x27, 0x11); 106 | CODEC_RegWrite(0x26, 0xEC); 107 | CODEC_RegWrite(0x24, 0x18); 108 | CODEC_RegWrite(0x25, 0x53); 109 | 110 | CODEC_RegMask(0x26, 0x80, 0x80); 111 | CODEC_RegMask(0x24, 0x00, 0x80); 112 | CODEC_RegMask(0x25, 0x10, 0x3C); 113 | } 114 | 115 | void CODEC_GetRawData(u32 *buffer) 116 | { 117 | CODEC_RegSelect(0xFB); 118 | CODEC_RegReadBuf(1, buffer, 0x34); 119 | } 120 | 121 | void CODEC_Get(CODEC_Input *input) 122 | { 123 | u32 raw_data_buf[0x34 / 4]; 124 | u8 *raw_data = (u8*)raw_data_buf; 125 | s16 cpad_x, cpad_y; 126 | bool ts_pressed; 127 | 128 | CODEC_GetRawData(raw_data_buf); 129 | 130 | cpad_x = ((raw_data[0x24] << 8 | raw_data[0x25]) & 0xFFF) - 2048; 131 | cpad_y = ((raw_data[0x14] << 8 | raw_data[0x15]) & 0xFFF) - 2048; 132 | 133 | // X axis is inverted 134 | input->cpad_x = (abs(cpad_x) > CPAD_THRESH_X) ? -cpad_x : 0; 135 | input->cpad_y = (abs(cpad_y) > CPAD_THRESH_Y) ? cpad_y : 0; 136 | 137 | ts_pressed = !(raw_data[0] & BIT(4)); 138 | if (ts_pressed) { 139 | input->ts_x = (raw_data[0] << 8) | raw_data[1]; 140 | input->ts_y = (raw_data[10] << 8) | raw_data[11]; 141 | } else { 142 | input->ts_x = 0xFFFF; 143 | input->ts_y = 0xFFFF; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /arm11/source/hw/codec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017 Sergi Granell, Paul LaMendola 4 | * Copyright (C) 2019 Wolfvak 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | typedef struct { 25 | s16 cpad_x, cpad_y; 26 | u16 ts_x, ts_y; 27 | } CODEC_Input; 28 | 29 | void CODEC_Init(void); 30 | 31 | void CODEC_GetRawData(u32 *buffer); 32 | void CODEC_Get(CODEC_Input *input); 33 | -------------------------------------------------------------------------------- /arm11/source/hw/gpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017 derrek, profi200 4 | * Copyright (C) 2019 Wolfvak 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | #define REG_GPIO ((vu16*)(0x10100000 + 0x47000)) 25 | 26 | static inline void GPIO_setBit(u16 reg, u8 bitNum) 27 | { 28 | REG_GPIO[reg] |= 1u<. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "arm/timer.h" 25 | 26 | #include "hw/mcu.h" 27 | #include "hw/gpulcd.h" 28 | 29 | #include "system/event.h" 30 | 31 | static struct 32 | { 33 | u16 lcdIds; // Bits 0-7 top screen, 8-15 bottom screen. 34 | bool lcdIdsRead; 35 | u8 lcdPower; // 1 = on. Bit 4 top light, bit 2 bottom light, bit 0 LCDs. 36 | u8 lcdLights[2]; // LCD backlight brightness. Top, bottom. 37 | u32 framebufs[2]; // For each screen 38 | u8 doubleBuf[2]; // Top, bottom, 1 = enable. 39 | u16 strides[2]; // Top, bottom 40 | u32 formats[2]; // Top, bottom 41 | } g_gfxState = {0}; 42 | 43 | static void setupDisplayController(u8 lcd); 44 | static void resetLcdsMaybe(void); 45 | static void waitLcdsReady(void); 46 | 47 | static u32 gxModeWidth(unsigned c) { 48 | switch(c) { 49 | case 0: return 4; 50 | case 1: return 3; 51 | default: return 2; 52 | } 53 | } 54 | 55 | unsigned GFX_init(GfxFbFmt mode) 56 | { 57 | unsigned err = 0; 58 | 59 | REG_CFG11_GPUPROT = 0; 60 | 61 | // Reset 62 | // PDN_GPU_CNT_RST_REGS was added to this first line to prevent a hang since we're executing from VRAM. 63 | // Unsure if this will bite me in the ass later. 64 | REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E | PDN_GPU_CNT_RST_REGS; 65 | ARM_WaitCycles(12); 66 | REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E | PDN_GPU_CNT_RST_ALL; 67 | REG_GX_GPU_CLK = 0x100; 68 | REG_GX_PSC_VRAM = 0; 69 | REG_GX_PSC_FILL0_CNT = 0; 70 | REG_GX_PSC_FILL1_CNT = 0; 71 | REG_GX_PPF_CNT = 0; 72 | 73 | // LCD framebuffer setup. 74 | 75 | g_gfxState.strides[0] = 240 * gxModeWidth(mode); 76 | g_gfxState.strides[1] = 240 * gxModeWidth(mode); 77 | 78 | g_gfxState.framebufs[0] = VRAM_TOP_LA; 79 | g_gfxState.framebufs[1] = VRAM_BOT_A; 80 | 81 | g_gfxState.formats[0] = mode | BIT(6) | BIT(9); 82 | g_gfxState.formats[1] = mode | BIT(9); 83 | 84 | setupDisplayController(0); 85 | setupDisplayController(1); 86 | REG_LCD_PDC0_SWAP = 0; // Select framebuf 0. 87 | REG_LCD_PDC1_SWAP = 0; 88 | REG_LCD_PDC0_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E; // Start 89 | REG_LCD_PDC1_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E; 90 | 91 | // LCD reg setup. 92 | REG_LCD_ABL0_FILL = 1u<<24; // Force blackscreen 93 | REG_LCD_ABL1_FILL = 1u<<24; // Force blackscreen 94 | REG_LCD_PARALLAX_CNT = 0; 95 | REG_LCD_PARALLAX_PWM = 0xA390A39; 96 | REG_LCD_RST = 0; 97 | REG_LCD_UNK00C = 0x10001; 98 | 99 | // Clear used VRAM 100 | REG_GX_PSC_FILL0_S_ADDR = VRAM_TOP_LA >> 3; 101 | REG_GX_PSC_FILL0_E_ADDR = VRAM_END >> 3; 102 | REG_GX_PSC_FILL0_VAL = 0; 103 | REG_GX_PSC_FILL0_CNT = BIT(9) | BIT(0); 104 | 105 | // Backlight and other stuff. 106 | REG_LCD_ABL0_LIGHT = 0; 107 | REG_LCD_ABL0_CNT = 0; 108 | REG_LCD_ABL0_LIGHT_PWM = 0; 109 | REG_LCD_ABL1_LIGHT = 0; 110 | REG_LCD_ABL1_CNT = 0; 111 | REG_LCD_ABL1_LIGHT_PWM = 0; 112 | 113 | REG_LCD_RST = 1; 114 | REG_LCD_UNK00C = 0; 115 | TIMER_WaitMS(10); 116 | resetLcdsMaybe(); 117 | MCU_controlLCDPower(2u); // Power on LCDs. 118 | if(eventWait(getEventMCU(), 0x3Fu<<24, 0x3Fu<<24) != 2u<<24) __builtin_trap(); 119 | 120 | waitLcdsReady(); 121 | REG_LCD_ABL0_LIGHT_PWM = 0x1023E; 122 | REG_LCD_ABL1_LIGHT_PWM = 0x1023E; 123 | MCU_controlLCDPower(0x28u); // Power on backlights. 124 | if(eventWait(getEventMCU(), 0x3Fu<<24, 0x3Fu<<24) != 0x28u<<24) __builtin_trap(); 125 | g_gfxState.lcdPower = 0x15; // All on. 126 | 127 | // Make sure the fills finished. 128 | REG_LCD_ABL0_FILL = 0; 129 | REG_LCD_ABL1_FILL = 0; 130 | 131 | // GPU stuff. 132 | REG_GX_GPU_CLK = 0x70100; 133 | *((vu32*)0x10400050) = 0x22221200; 134 | *((vu32*)0x10400054) = 0xFF2; 135 | 136 | GFX_setBrightness(0x80, 0x80); 137 | 138 | return err; 139 | } 140 | 141 | static u16 getLcdIds(void) 142 | { 143 | u16 ids; 144 | 145 | if(!g_gfxState.lcdIdsRead) 146 | { 147 | g_gfxState.lcdIdsRead = true; 148 | 149 | u16 top, bot; 150 | I2C_writeReg(I2C_DEV_LCD0, 0x40, 0xFF); 151 | I2C_readRegBuf(I2C_DEV_LCD0, 0x40, (u8*)&top, 2); 152 | I2C_writeReg(I2C_DEV_LCD1, 0x40, 0xFF); 153 | I2C_readRegBuf(I2C_DEV_LCD1, 0x40, (u8*)&bot, 2); 154 | 155 | ids = top>>8; 156 | ids |= bot & 0xFF00u; 157 | g_gfxState.lcdIds = ids; 158 | } 159 | else ids = g_gfxState.lcdIds; 160 | 161 | return ids; 162 | } 163 | 164 | static void resetLcdsMaybe(void) 165 | { 166 | const u16 ids = getLcdIds(); 167 | 168 | // Top screen 169 | if(ids & 0xFFu) I2C_writeReg(I2C_DEV_LCD0, 0xFE, 0xAA); 170 | else 171 | { 172 | I2C_writeReg(I2C_DEV_LCD0, 0x11, 0x10); 173 | I2C_writeReg(I2C_DEV_LCD0, 0x50, 1); 174 | } 175 | 176 | // Bottom screen 177 | if(ids>>8) I2C_writeReg(I2C_DEV_LCD1, 0xFE, 0xAA); 178 | else I2C_writeReg(I2C_DEV_LCD1, 0x11, 0x10); 179 | 180 | I2C_writeReg(I2C_DEV_LCD0, 0x60, 0); 181 | I2C_writeReg(I2C_DEV_LCD1, 0x60, 0); 182 | I2C_writeReg(I2C_DEV_LCD0, 1, 0x10); 183 | I2C_writeReg(I2C_DEV_LCD1, 1, 0x10); 184 | } 185 | 186 | static void waitLcdsReady(void) 187 | { 188 | const u16 ids = getLcdIds(); 189 | 190 | if((ids & 0xFFu) == 0 || (ids>>8) == 0) // Unknown LCD? 191 | { 192 | TIMER_WaitMS(150); 193 | } 194 | else 195 | { 196 | u32 i = 0; 197 | do 198 | { 199 | u16 top, bot; 200 | I2C_writeReg(I2C_DEV_LCD0, 0x40, 0x62); 201 | I2C_readRegBuf(I2C_DEV_LCD0, 0x40, (u8*)&top, 2); 202 | I2C_writeReg(I2C_DEV_LCD1, 0x40, 0x62); 203 | I2C_readRegBuf(I2C_DEV_LCD1, 0x40, (u8*)&bot, 2); 204 | 205 | if((top>>8) == 1 && (bot>>8) == 1) break; 206 | 207 | TIMER_WaitMS(33); 208 | } while(i++ < 10); 209 | } 210 | } 211 | 212 | void GFX_powerOnBacklights(GfxBlight mask) 213 | { 214 | g_gfxState.lcdPower |= mask; 215 | 216 | mask <<= 1; 217 | MCU_controlLCDPower(mask); // Power on backlights. 218 | eventWait(getEventMCU(), 0x3F<<24, 0x3F<<24); 219 | /*if(mcuEventWait(0x3Fu<<24) != (u32)mask<<24) 220 | __builtin_trap();*/ 221 | } 222 | 223 | void GFX_powerOffBacklights(GfxBlight mask) 224 | { 225 | g_gfxState.lcdPower &= ~mask; 226 | 227 | MCU_controlLCDPower(mask); // Power off backlights. 228 | eventWait(getEventMCU(), 0x3F<<24, 0x3F<<24); 229 | /*if(mcuEventWait(0x3Fu<<24) != (u32)mask<<24) 230 | __builtin_trap();*/ 231 | } 232 | 233 | u8 GFX_getBrightness(void) 234 | { 235 | return REG_LCD_ABL0_LIGHT; 236 | } 237 | 238 | void GFX_setBrightness(u8 top, u8 bot) 239 | { 240 | g_gfxState.lcdLights[0] = top; 241 | g_gfxState.lcdLights[1] = bot; 242 | REG_LCD_ABL0_LIGHT = top; 243 | REG_LCD_ABL1_LIGHT = bot; 244 | } 245 | 246 | void GFX_setForceBlack(bool top, bool bot) 247 | { 248 | REG_LCD_ABL0_FILL = (u32)top<<24; // Force blackscreen 249 | REG_LCD_ABL1_FILL = (u32)bot<<24; // Force blackscreen 250 | } 251 | 252 | static void setupDisplayController(u8 lcd) 253 | { 254 | if(lcd > 1) return; 255 | 256 | static const u32 displayCfgs[2][24] = 257 | { 258 | { 259 | // PDC0 regs 0-0x4C. 260 | 450, 209, 449, 449, 0, 207, 209, 453<<16 | 449, 261 | 1<<16 | 0, 413, 2, 402, 402, 402, 1, 2, 262 | 406<<16 | 402, 0, 0<<4 | 0, 0<<16 | 0xFF<<8 | 0, 263 | // PDC0 regs 0x5C-0x64. 264 | 400<<16 | 240, // Width and height. 265 | 449<<16 | 209, 266 | 402<<16 | 2, 267 | // PDC0 reg 0x9C. 268 | 0<<16 | 0 269 | }, 270 | { 271 | // PDC1 regs 0-0x4C. 272 | 450, 209, 449, 449, 205, 207, 209, 453<<16 | 449, 273 | 1<<16 | 0, 413, 82, 402, 402, 79, 80, 82, 274 | 408<<16 | 404, 0, 1<<4 | 1, 0<<16 | 0<<8 | 0xFF, 275 | // PDC1 regs 0x5C-0x64. 276 | 320<<16 | 240, // Width and height. 277 | 449<<16 | 209, 278 | 402<<16 | 82, 279 | // PDC1 reg 0x9C. 280 | 0<<16 | 0 281 | } 282 | }; 283 | 284 | const u32 *const cfg = displayCfgs[lcd]; 285 | vu32 *const regs = (vu32*)(GX_REGS_BASE + 0x400 + (0x100u * lcd)); 286 | 287 | for (unsigned i = 0; i < 0x50/4; i++) 288 | regs[i] = cfg[i]; 289 | 290 | for (unsigned i = 0; i < 0xC/4; i++) 291 | regs[23 + i] = cfg[20 + i]; 292 | 293 | regs[36] = g_gfxState.strides[lcd]; // PDC reg 0x90 stride. 294 | regs[39] = cfg[23]; // PDC reg 0x9C. 295 | 296 | // PDC regs 0x68, 0x6C, 0x94, 0x98 and 0x70. 297 | regs[26] = g_gfxState.framebufs[lcd]; // Framebuffer A first address. 298 | regs[27] = g_gfxState.framebufs[lcd]; // Framebuffer A second address. 299 | regs[37] = g_gfxState.framebufs[lcd]; // Framebuffer B first address. 300 | regs[38] = g_gfxState.framebufs[lcd]; // Framebuffer B second address. 301 | regs[28] = g_gfxState.formats[lcd]; // Format 302 | 303 | regs[32] = 0; // Gamma table index 0. 304 | for(u32 i = 0; i < 256; i++) regs[33] = 0x10101u * i; 305 | } 306 | -------------------------------------------------------------------------------- /arm11/source/hw/gpulcd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | #include 21 | 22 | #define VBLANK_INTERRUPT (0x2A) 23 | 24 | enum 25 | { 26 | PDN_GPU_CNT_RST_REGS = 1u, // And more? 27 | PDN_GPU_CNT_RST_PSC = 1u<<1, // ? 28 | PDN_GPU_CNT_RST_GEOSHADER = 1u<<2, // ? 29 | PDN_GPU_CNT_RST_RASTERIZER = 1u<<3, // ? 30 | PDN_GPU_CNT_RST_PPF = 1u<<4, 31 | PDN_GPU_CNT_RST_PDC = 1u<<5, // ? 32 | PDN_GPU_CNT_RST_PDC2 = 1u<<6, // Maybe pixel pipeline or so? 33 | 34 | PDN_GPU_CNT_RST_ALL = (PDN_GPU_CNT_RST_PDC2<<1) - 1 35 | }; 36 | 37 | typedef enum 38 | { 39 | GFX_RGBA8 = 0, ///< RGBA8. (4 bytes) 40 | GFX_BGR8 = 1, ///< BGR8. (3 bytes) 41 | GFX_RGB565 = 2, ///< RGB565. (2 bytes) 42 | GFX_RGB5A1 = 3, ///< RGB5A1. (2 bytes) 43 | GFX_RGBA4 = 4 ///< RGBA4. (2 bytes) 44 | } GfxFbFmt; 45 | 46 | typedef enum 47 | { 48 | GFX_EVENT_PSC0 = 0u, 49 | GFX_EVENT_PSC1 = 1u, 50 | GFX_EVENT_PDC0 = 2u, 51 | GFX_EVENT_PDC1 = 3u, 52 | GFX_EVENT_PPF = 4u, 53 | GFX_EVENT_P3D = 5u 54 | } GfxEvent; 55 | 56 | typedef enum 57 | { 58 | GFX_BLIGHT_BOT = 1u<<2, 59 | GFX_BLIGHT_TOP = 1u<<4, 60 | GFX_BLIGHT_BOTH = GFX_BLIGHT_TOP | GFX_BLIGHT_BOT 61 | } GfxBlight; 62 | 63 | #define REG_CFG11_GPUPROT *((vu16*)(0x10140140)) 64 | #define REG_PDN_GPU_CNT *((vu32*)(0x10141200)) 65 | #define PDN_GPU_CNT_CLK_E (1u<<16) 66 | #define PDN_VRAM_CNT_CLK_E (1u) 67 | 68 | 69 | 70 | #define GX_REGS_BASE (0x10400000) 71 | #define REG_GX_GPU_CLK *((vu32*)(GX_REGS_BASE + 0x0004)) // ? 72 | 73 | // PSC (memory fill) regs. 74 | #define REG_GX_PSC_FILL0_S_ADDR *((vu32*)(GX_REGS_BASE + 0x0010)) // Start address 75 | #define REG_GX_PSC_FILL0_E_ADDR *((vu32*)(GX_REGS_BASE + 0x0014)) // End address 76 | #define REG_GX_PSC_FILL0_VAL *((vu32*)(GX_REGS_BASE + 0x0018)) // Fill value 77 | #define REG_GX_PSC_FILL0_CNT *((vu32*)(GX_REGS_BASE + 0x001C)) 78 | 79 | #define REG_GX_PSC_FILL1_S_ADDR *((vu32*)(GX_REGS_BASE + 0x0020)) 80 | #define REG_GX_PSC_FILL1_E_ADDR *((vu32*)(GX_REGS_BASE + 0x0024)) 81 | #define REG_GX_PSC_FILL1_VAL *((vu32*)(GX_REGS_BASE + 0x0028)) 82 | #define REG_GX_PSC_FILL1_CNT *((vu32*)(GX_REGS_BASE + 0x002C)) 83 | 84 | #define REG_GX_PSC_VRAM *((vu32*)(GX_REGS_BASE + 0x0030)) // gsp mudule only changes bit 8-11. 85 | #define REG_GX_PSC_STAT *((vu32*)(GX_REGS_BASE + 0x0034)) 86 | 87 | // PDC0/1 regs see lcd.h. 88 | 89 | // PPF (transfer engine) regs. 90 | #define REG_GX_PPF_IN_ADDR *((vu32*)(GX_REGS_BASE + 0x0C00)) 91 | #define REG_GX_PPF_OUT_ADDR *((vu32*)(GX_REGS_BASE + 0x0C04)) 92 | #define REG_GX_PPF_DT_OUTDIM *((vu32*)(GX_REGS_BASE + 0x0C08)) // Display transfer output dimensions. 93 | #define REG_GX_PPF_DT_INDIM *((vu32*)(GX_REGS_BASE + 0x0C0C)) // Display transfer input dimensions. 94 | #define REG_GX_PPF_FlAGS *((vu32*)(GX_REGS_BASE + 0x0C10)) 95 | #define REG_GX_PPF_UNK14 *((vu32*)(GX_REGS_BASE + 0x0C14)) // Transfer interval? 96 | #define REG_GX_PPF_CNT *((vu32*)(GX_REGS_BASE + 0x0C18)) 97 | #define REG_GX_PPF_IRQ_POS *((vu32*)(GX_REGS_BASE + 0x0C1C)) // ? 98 | #define REG_GX_PPF_LEN *((vu32*)(GX_REGS_BASE + 0x0C20)) // Texture copy size in bytes. 99 | #define REG_GX_PPF_TC_INDIM *((vu32*)(GX_REGS_BASE + 0x0C24)) // Texture copy input width and gap in 16 byte units. 100 | #define REG_GX_PPF_TC_OUTDIM *((vu32*)(GX_REGS_BASE + 0x0C28)) // Texture copy output width and gap in 16 byte units. 101 | 102 | // P3D (GPU internal) regs. See gpu_regs.h. 103 | #define REG_GX_P3D(reg) *((vu32*)(GX_REGS_BASE + 0x1000 + ((reg) * 4))) 104 | 105 | // LCD/ABL regs. 106 | #define LCD_REGS_BASE (0x10202000) 107 | #define REG_LCD_PARALLAX_CNT *((vu32*)(LCD_REGS_BASE + 0x000)) // Controls PWM for the parallax barrier? 108 | #define REG_LCD_PARALLAX_PWM *((vu32*)(LCD_REGS_BASE + 0x004)) // Frequency/other PWM stuff maybe? 109 | #define REG_LCD_UNK00C *((vu32*)(LCD_REGS_BASE + 0x00C)) // Wtf is "FIX"? 110 | #define REG_LCD_RST *((vu32*)(LCD_REGS_BASE + 0x014)) // Reset active low. 111 | 112 | #define REG_LCD_ABL0_CNT *((vu32*)(LCD_REGS_BASE + 0x200)) // Bit 0 enables ABL aka power saving mode. 113 | #define REG_LCD_ABL0_FILL *((vu32*)(LCD_REGS_BASE + 0x204)) 114 | #define REG_LCD_ABL0_LIGHT *((vu32*)(LCD_REGS_BASE + 0x240)) 115 | #define REG_LCD_ABL0_LIGHT_PWM *((vu32*)(LCD_REGS_BASE + 0x244)) 116 | 117 | #define REG_LCD_ABL1_CNT *((vu32*)(LCD_REGS_BASE + 0xA00)) // Bit 0 enables ABL aka power saving mode. 118 | #define REG_LCD_ABL1_FILL *((vu32*)(LCD_REGS_BASE + 0xA04)) 119 | #define REG_LCD_ABL1_LIGHT *((vu32*)(LCD_REGS_BASE + 0xA40)) 120 | #define REG_LCD_ABL1_LIGHT_PWM *((vu32*)(LCD_REGS_BASE + 0xA44)) 121 | 122 | 123 | // Technically these regs belong in gx.h but they are used for LCD configuration so... 124 | // Pitfall warning: The 3DS LCDs are physically rotated 90° CCW. 125 | 126 | // PDC0 (top screen display controller) regs. 127 | #define REG_LCD_PDC0_HTOTAL *((vu32*)(GX_REGS_BASE + 0x400)) 128 | #define REG_LCD_PDC0_VTOTAL *((vu32*)(GX_REGS_BASE + 0x424)) 129 | #define REG_LCD_PDC0_HPOS *((const vu32*)(GX_REGS_BASE + 0x450)) 130 | #define REG_LCD_PDC0_VPOS *((const vu32*)(GX_REGS_BASE + 0x454)) 131 | #define REG_LCD_PDC0_FB_A1 *((vu32*)(GX_REGS_BASE + 0x468)) 132 | #define REG_LCD_PDC0_FB_A2 *((vu32*)(GX_REGS_BASE + 0x46C)) 133 | #define REG_LCD_PDC0_FMT *((vu32*)(GX_REGS_BASE + 0x470)) 134 | #define REG_LCD_PDC0_CNT *((vu32*)(GX_REGS_BASE + 0x474)) 135 | #define REG_LCD_PDC0_SWAP *((vu32*)(GX_REGS_BASE + 0x478)) 136 | #define REG_LCD_PDC0_STAT *((const vu32*)(GX_REGS_BASE + 0x47C)) 137 | #define REG_LCD_PDC0_GTBL_IDX *((vu32*)(GX_REGS_BASE + 0x480)) // Gamma table index. 138 | #define REG_LCD_PDC0_GTBL_FIFO *((vu32*)(GX_REGS_BASE + 0x484)) // Gamma table FIFO. 139 | #define REG_LCD_PDC0_STRIDE *((vu32*)(GX_REGS_BASE + 0x490)) 140 | #define REG_LCD_PDC0_FB_B1 *((vu32*)(GX_REGS_BASE + 0x494)) 141 | #define REG_LCD_PDC0_FB_B2 *((vu32*)(GX_REGS_BASE + 0x498)) 142 | 143 | // PDC1 (bottom screen display controller) regs. 144 | #define REG_LCD_PDC1_HTOTAL *((vu32*)(GX_REGS_BASE + 0x500)) 145 | #define REG_LCD_PDC1_VTOTAL *((vu32*)(GX_REGS_BASE + 0x524)) 146 | #define REG_LCD_PDC1_HPOS *((const vu32*)(GX_REGS_BASE + 0x550)) 147 | #define REG_LCD_PDC1_VPOS *((const vu32*)(GX_REGS_BASE + 0x554)) 148 | #define REG_LCD_PDC1_FB_A1 *((vu32*)(GX_REGS_BASE + 0x568)) 149 | #define REG_LCD_PDC1_FB_A2 *((vu32*)(GX_REGS_BASE + 0x56C)) 150 | #define REG_LCD_PDC1_FMT *((vu32*)(GX_REGS_BASE + 0x570)) 151 | #define REG_LCD_PDC1_CNT *((vu32*)(GX_REGS_BASE + 0x574)) 152 | #define REG_LCD_PDC1_SWAP *((vu32*)(GX_REGS_BASE + 0x578)) 153 | #define REG_LCD_PDC1_STAT *((const vu32*)(GX_REGS_BASE + 0x57C)) 154 | #define REG_LCD_PDC1_GTBL_IDX *((vu32*)(GX_REGS_BASE + 0x580)) // Gamma table index. 155 | #define REG_LCD_PDC1_GTBL_FIFO *((vu32*)(GX_REGS_BASE + 0x584)) // Gamma table FIFO. 156 | #define REG_LCD_PDC1_STRIDE *((vu32*)(GX_REGS_BASE + 0x590)) 157 | #define REG_LCD_PDC1_FB_B1 *((vu32*)(GX_REGS_BASE + 0x594)) 158 | #define REG_LCD_PDC1_FB_B2 *((vu32*)(GX_REGS_BASE + 0x598)) 159 | 160 | 161 | // REG_LCD_PDC_CNT 162 | #define PDC_CNT_E (1u) 163 | #define PDC_CNT_I_MASK_H (1u<<8) // Disables H(Blank?) IRQs. 164 | #define PDC_CNT_I_MASK_V (1u<<9) // Disables VBlank IRQs. 165 | #define PDC_CNT_I_MASK_ERR (1u<<10) // Disables error IRQs. What kind of errors? 166 | #define PDC_CNT_OUT_E (1u<<16) // Output enable? 167 | 168 | // REG_LCD_PDC_SWAP 169 | // Masks 170 | #define PDC_SWAP_NEXT (1u) // Next framebuffer. 171 | #define PDC_SWAP_CUR (1u<<4) // Currently displaying framebuffer? 172 | // Bits 173 | #define PDC_SWAP_RST_FIFO (1u<<8) // Which FIFO? 174 | #define PDC_SWAP_I_H (1u<<16) // H(Blank?) IRQ bit. 175 | #define PDC_SWAP_I_V (1u<<17) // VBlank IRQ bit. 176 | #define PDC_SWAP_I_ERR (1u<<18) // Error IRQ bit? 177 | #define PDC_SWAP_I_ALL (PDC_SWAP_I_ERR | PDC_SWAP_I_V | PDC_SWAP_I_H) 178 | 179 | 180 | unsigned GFX_init(GfxFbFmt mode); 181 | void GFX_setForceBlack(bool top, bool bot); 182 | void GFX_powerOnBacklights(GfxBlight mask); 183 | void GFX_powerOffBacklights(GfxBlight mask); 184 | 185 | u8 GFX_getBrightness(void); 186 | void GFX_setBrightness(u8 top, u8 bot); 187 | -------------------------------------------------------------------------------- /arm11/source/hw/mcu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "arm/timer.h" 25 | 26 | #include "hw/gpio.h" 27 | #include "hw/gpulcd.h" 28 | #include "hw/mcu.h" 29 | 30 | #include "system/event.h" 31 | 32 | #define MCUEV_HID_MASK ( \ 33 | MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD | \ 34 | MCUEV_HID_HOME_DOWN | MCUEV_HID_HOME_UP | MCUEV_HID_WIFI_SWITCH) 35 | 36 | enum { 37 | MCUREG_VOLUME_SLIDER = 0x09, 38 | 39 | MCUREG_BATTERY_LEVEL = 0x0B, 40 | MCUREG_CONSOLE_STATE = 0x0F, 41 | 42 | MCUREG_INT_MASK = 0x10, 43 | MCUREG_INT_EN = 0x18, 44 | 45 | MCUREG_LCD_STATE = 0x22, 46 | 47 | MCUREG_LED_WIFI = 0x2A, 48 | MCUREG_LED_CAMERA = 0x2B, 49 | MCUREG_LED_SLIDER = 0x2C, 50 | MCUREG_LED_STATUS = 0x2D, 51 | 52 | MCUREG_RTC = 0x30, 53 | }; 54 | 55 | typedef struct { 56 | u8 delay; 57 | u8 smoothing; 58 | u8 loop_delay; 59 | u8 unk; 60 | u32 red[8]; 61 | u32 green[8]; 62 | u32 blue[8]; 63 | } PACKED_STRUCT mcuStatusLED; 64 | 65 | static u8 volumeSliderValue; 66 | static bool shellOpen; 67 | static _Atomic(u32) pendingEvents; 68 | 69 | static void mcuEventUpdate(void) 70 | { 71 | u32 mask; 72 | 73 | // lazily update the mask on each test attempt 74 | if (!getEventIRQ()->test(MCU_INTERRUPT, true)) 75 | return; 76 | 77 | // reading the pending mask automagically acknowledges 78 | // the interrupts so all of them must be processed in one go 79 | mcuReadRegBuf(MCUREG_INT_MASK, (u8*)&mask, sizeof(mask)); 80 | 81 | if (mask & MCUEV_HID_VOLUME_SLIDER) 82 | volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER); 83 | 84 | if (mask & MCUEV_HID_SHELL_OPEN) { 85 | mcuResetLEDs(); 86 | shellOpen = true; 87 | } 88 | 89 | if (mask & MCUEV_HID_SHELL_CLOSE) { 90 | shellOpen = false; 91 | } 92 | 93 | atomic_fetch_or(&pendingEvents, mask); 94 | } 95 | 96 | u8 mcuGetVolumeSlider(void) 97 | { 98 | mcuEventUpdate(); 99 | return volumeSliderValue; 100 | } 101 | 102 | /*u32 mcuGetSpecialHID(void) 103 | { 104 | u32 ret = 0, pend = getEventMCU()->test(MCUEV_HID_MASK, MCUEV_HID_MASK); 105 | 106 | // hopefully gets unrolled 107 | if (pend & (MCUEV_HID_PWR_DOWN | MCUEV_HID_PWR_HOLD)) 108 | ret |= BUTTON_POWER; 109 | if (pend & MCUEV_HID_HOME_DOWN) 110 | ret |= BUTTON_HOME; 111 | if (pend & MCUEV_HID_HOME_UP) 112 | ret &= ~BUTTON_HOME; 113 | return ret | shellState; 114 | }*/ 115 | 116 | void mcuSetStatusLED(u32 period_ms, u32 color) 117 | { 118 | u32 r, g, b; 119 | mcuStatusLED ledState; 120 | 121 | // handle proper non-zero periods 122 | // so small the hardware can't handle it 123 | if (period_ms != 0 && period_ms < 63) 124 | period_ms = 63; 125 | 126 | ledState.delay = (period_ms * 0x10) / 1000; 127 | ledState.smoothing = 0x40; 128 | ledState.loop_delay = 0x10; 129 | ledState.unk = 0; 130 | 131 | // all colors look like 0x00ZZ00ZZ 132 | // in order to alternate between 133 | // LED "off" and the wanted color 134 | r = (color >> 16) & 0xFF; 135 | r |= r << 16; 136 | for (int i = 0; i < 8; i++) 137 | ledState.red[i] = r; 138 | 139 | g = (color >> 8) & 0xFF; 140 | g |= g << 16; 141 | for (int i = 0; i < 8; i++) 142 | ledState.green[i] = g; 143 | 144 | b = color & 0xFF; 145 | b |= b << 16; 146 | for (int i = 0; i < 8; i++) 147 | ledState.blue[i] = b; 148 | 149 | mcuWriteRegBuf(MCUREG_LED_STATUS, (const u8*)&ledState, sizeof(ledState)); 150 | } 151 | 152 | void mcuResetLEDs(void) 153 | { 154 | mcuWriteReg(MCUREG_LED_WIFI, 0); 155 | mcuWriteReg(MCUREG_LED_CAMERA, 0); 156 | mcuWriteReg(MCUREG_LED_SLIDER, 0); 157 | mcuSetStatusLED(0, 0); 158 | } 159 | 160 | void mcuReset(void) 161 | { 162 | u32 intmask = 0; 163 | 164 | atomic_init(&pendingEvents, 0); 165 | 166 | // set register mask and clear any pending registers 167 | mcuWriteRegBuf(MCUREG_INT_EN, (const u8*)&intmask, sizeof(intmask)); 168 | mcuReadRegBuf(MCUREG_INT_MASK, (u8*)&intmask, sizeof(intmask)); 169 | 170 | mcuResetLEDs(); 171 | 172 | volumeSliderValue = mcuReadReg(MCUREG_VOLUME_SLIDER); 173 | shellOpen = true; 174 | // assume the shell is always open on boot 175 | // knowing the average 3DS user, there will be plenty 176 | // of laughs when this comes back to bite us in the rear 177 | 178 | GPIO_setBit(19, 9); 179 | } 180 | 181 | static void evReset(void) { 182 | atomic_store(&pendingEvents, 0); 183 | } 184 | 185 | static u32 evTest(u32 mask, u32 clear) { 186 | mcuEventUpdate(); 187 | return atomic_fetch_and(&pendingEvents, ~clear) & mask; 188 | } 189 | 190 | static const EventInterface evMCU = { 191 | .reset = evReset, 192 | .test = evTest 193 | }; 194 | 195 | const EventInterface *getEventMCU(void) { 196 | return &evMCU; 197 | } 198 | 199 | -------------------------------------------------------------------------------- /arm11/source/hw/mcu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "arm/timer.h" 25 | 26 | #define MCU_INTERRUPT (0x71) 27 | #define I2C_MCU_DEVICE (3) 28 | 29 | enum { 30 | MCUEV_HID_PWR_DOWN = BIT(0), 31 | MCUEV_HID_PWR_HOLD = BIT(1), 32 | MCUEV_HID_HOME_DOWN = BIT(2), 33 | MCUEV_HID_HOME_UP = BIT(3), 34 | MCUEV_HID_WIFI_SWITCH = BIT(4), 35 | MCUEV_HID_SHELL_CLOSE = BIT(5), 36 | MCUEV_HID_SHELL_OPEN = BIT(6), 37 | MCUEV_HID_VOLUME_SLIDER = BIT(22), 38 | }; 39 | 40 | u8 mcuGetVolumeSlider(void); 41 | //u32 mcuGetSpecialHID(void); 42 | 43 | void mcuSetStatusLED(u32 period_ms, u32 color); 44 | void mcuResetLEDs(void); 45 | 46 | void mcuReset(void); 47 | 48 | static inline u8 mcuReadReg(u8 addr) 49 | { 50 | u8 val; 51 | I2C_readRegBuf(I2C_MCU_DEVICE, addr, &val, 1); 52 | return val; 53 | } 54 | 55 | static inline bool mcuReadRegBuf(u8 addr, u8 *buf, u32 size) 56 | { 57 | return I2C_readRegBuf(I2C_MCU_DEVICE, addr, buf, size); 58 | } 59 | 60 | static inline bool mcuWriteReg(u8 addr, u8 val) 61 | { 62 | return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, &val, 1); 63 | } 64 | 65 | static inline bool mcuWriteRegBuf(u8 addr, const u8 *buf, u32 size) 66 | { 67 | return I2C_writeRegBuf(I2C_MCU_DEVICE, addr, buf, size); 68 | } 69 | 70 | static inline void MCU_controlLCDPower(u8 bits) 71 | { 72 | mcuWriteReg(0x22u, bits); 73 | } 74 | -------------------------------------------------------------------------------- /arm11/source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "arm/gic.h" 11 | #include "arm/timer.h" 12 | 13 | #include "hw/gpulcd.h" 14 | #include "hw/mcu.h" 15 | 16 | #include "system/sys.h" 17 | #include "system/event.h" 18 | 19 | #include "ui/ui.h" 20 | #include "ui/debug.h" 21 | 22 | #include "pxi/timer.h" 23 | 24 | #include 25 | 26 | static u32 wait_any_key_pressed() 27 | { 28 | u32 pad; 29 | while (~HID_PAD & BUTTON_NONE) ARM_WFI(); 30 | while (!((pad = ~HID_PAD) & BUTTON_NONE)) ARM_WFI(); 31 | return pad; 32 | } 33 | 34 | /*static void vblankUpdate(void) 35 | { 36 | if (!getEventIRQ()->test(VBLANK_INTERRUPT, true)) 37 | return; 38 | 39 | // handle shell events 40 | static const u32 mcuEvShell = MCUEV_HID_SHELL_OPEN | MCUEV_HID_SHELL_CLOSE; 41 | u32 shell = getEventMCU()->test(mcuEvShell, mcuEvShell); 42 | if (shell & MCUEV_HID_SHELL_CLOSE) { 43 | GFX_powerOffBacklights(GFX_BLIGHT_BOTH); 44 | } else if (shell & MCUEV_HID_SHELL_OPEN) { 45 | GFX_powerOnBacklights(GFX_BLIGHT_BOTH); 46 | } 47 | 48 | //sharedMem.hidState.full = HID_GetState(); 49 | }*/ 50 | 51 | void __attribute__((noreturn)) MainLoop(void) 52 | { 53 | //bool mainLoop = true; 54 | 55 | u8 fixed_bright_lvl = 0x80; 56 | GFX_setBrightness(fixed_bright_lvl, fixed_bright_lvl); 57 | 58 | // initialize state stuff 59 | getEventIRQ()->reset(); 60 | getEventMCU()->reset(); 61 | 62 | // configure interrupts 63 | gicSetInterruptConfig(PXI_RX_INTERRUPT, BIT(0), GIC_PRIO0, NULL); 64 | gicSetInterruptConfig(VBLANK_INTERRUPT, BIT(0), GIC_PRIO0, NULL); 65 | gicSetInterruptConfig(MCU_INTERRUPT, BIT(0), GIC_PRIO0, NULL); 66 | 67 | // enable interrupts 68 | gicEnableInterrupt(MCU_INTERRUPT); 69 | 70 | // perform gpu init after initializing mcu but before 71 | // enabling the pxi system and the vblank handler 72 | GFX_init(GFX_RGB565); 73 | 74 | gicEnableInterrupt(PXI_RX_INTERRUPT); 75 | gicEnableInterrupt(VBLANK_INTERRUPT); 76 | 77 | initUI(); 78 | 79 | // ARM9 won't try anything funny until this point 80 | PXI_Barrier(PXI_BOOT_BARRIER); 81 | debugPrint("Hello from ARM11\n"); 82 | 83 | consolePrint("Press B to power off, any other button to begin\n"); 84 | if (wait_any_key_pressed() & BUTTON_B) { 85 | PXI_Send(PXICMD_LEGACY_BOOT); 86 | PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); 87 | mcuPoweroff(); 88 | } 89 | 90 | testMemory(ALL_MEMTEST_REGIONS, ALL_MEMTESTS, true); 91 | 92 | consolePrint("Press any button to power off.\n"); 93 | wait_any_key_pressed(); 94 | PXI_Send(PXICMD_LEGACY_BOOT); 95 | PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); 96 | mcuPoweroff(); 97 | 98 | // perform deinit in reverse order 99 | gicDisableInterrupt(VBLANK_INTERRUPT); 100 | gicDisableInterrupt(PXI_RX_INTERRUPT); 101 | 102 | // unconditionally reinitialize the screens 103 | // in RGB24 framebuffer mode 104 | GFX_init(GFX_BGR8); 105 | 106 | gicDisableInterrupt(MCU_INTERRUPT); 107 | 108 | // Wait for the ARM9 to do its firmlaunch setup 109 | //PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); 110 | 111 | SYS_CoreZeroShutdown(); 112 | SYS_CoreShutdown(); 113 | } 114 | -------------------------------------------------------------------------------- /arm11/source/pxi/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include "common.h" 3 | 4 | #include 5 | #include 6 | 7 | static u8 __attribute__((section(".shared"))) sharedBuffer[MAX_LOG_LEN]; 8 | 9 | bool pxiLogWrite(const void* data, unsigned int btw) { 10 | btw = min(btw, MAX_LOG_LEN); 11 | memcpy(sharedBuffer, data, btw); 12 | 13 | PXI_Send((2 << 16) | PXICMD_LOG_DATA); 14 | PXI_Send((u32) sharedBuffer); 15 | PXI_Send(btw); 16 | 17 | //consolePrint("sent pxi log data\n"); 18 | 19 | return PXI_Recv(); 20 | } 21 | 22 | bool pxiLogWriteStr(const char* str) { 23 | strncpy((char*) sharedBuffer, str, MAX_LOG_LEN - 1); 24 | 25 | PXI_Send((1 << 16) | PXICMD_LOG_DATA); 26 | PXI_Send((u32) sharedBuffer); 27 | 28 | //consolePrint("sent pxi log str\n"); 29 | 30 | return PXI_Recv(); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /arm11/source/pxi/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define MAX_LOG_LEN 1024 6 | 7 | bool pxiLogWrite(const void* data, unsigned int btw); 8 | bool pxiLogWriteStr(const char* str); -------------------------------------------------------------------------------- /arm11/source/pxi/timer.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include "common.h" 3 | 4 | #include 5 | #include 6 | 7 | u64 pxiGetTimerTicks() { 8 | PXI_Send((0 << 16) | PXICMD_GET_TIMER_TICKS); 9 | 10 | return PXI_Recv64(); 11 | } 12 | -------------------------------------------------------------------------------- /arm11/source/pxi/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | u64 pxiGetTimerTicks(); -------------------------------------------------------------------------------- /arm11/source/system/event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | void (*reset)(void); 7 | u32 (*test)(u32 param, u32 clear); 8 | } EventInterface; 9 | 10 | const EventInterface *getEventIRQ(void); 11 | const EventInterface *getEventMCU(void); 12 | 13 | static inline void eventReset(const EventInterface *ei) { 14 | ei->reset(); 15 | } 16 | 17 | static inline u32 eventTest(const EventInterface *ei, u32 param, u32 clear) { 18 | return ei->test(param, clear); 19 | } 20 | 21 | static inline u32 eventWait(const EventInterface *ei, u32 param, u32 clear) { 22 | while(1) { 23 | u32 ret = ei->test(param, clear); 24 | if (ret) return ret; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /arm11/source/system/sections.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #define DEF_SECT_(n) extern u32 __##n##_pa, __##n##_va, __##n##_len; 24 | DEF_SECT_(text) 25 | DEF_SECT_(data) 26 | DEF_SECT_(rodata) 27 | DEF_SECT_(bss) 28 | DEF_SECT_(shared) 29 | #undef DEF_SECT_ 30 | 31 | #define SECTION_VA(n) ((u32)&__##n##_va) 32 | #define SECTION_PA(n) ((u32)&__##n##_pa) 33 | #define SECTION_LEN(n) ((u32)&__##n##_len) 34 | 35 | #define SECTION_TRI(n) SECTION_VA(n), SECTION_PA(n), SECTION_LEN(n) 36 | -------------------------------------------------------------------------------- /arm11/source/system/sys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "arm/gic.h" 25 | #include "arm/mmu.h" 26 | #include "arm/scu.h" 27 | #include "arm/xrq.h" 28 | 29 | #include "hw/codec.h" 30 | #include "hw/gpulcd.h" 31 | #include "hw/mcu.h" 32 | #include 33 | #include 34 | 35 | #include "system/sections.h" 36 | 37 | #define CFG11_MPCORE_CLKCNT ((vu16*)(0x10141300)) 38 | #define CFG11_SOCINFO ((vu16*)(0x10140FFC)) 39 | 40 | #define LEGACY_BOOT_ENTRYPOINT ((vu32*)0x1FFFFFFC) 41 | #define LEGACY_BOOT_ROUTINE_SMP (0x0001004C) 42 | 43 | static bool SYS_IsNewConsole(void) 44 | { 45 | return !IS_O3DS; 46 | } 47 | 48 | static bool SYS_ClkMultEnabled(void) 49 | { 50 | return (*CFG11_MPCORE_CLKCNT & 1) != 0; 51 | } 52 | 53 | static void SYS_EnableClkMult(void) 54 | { 55 | // magic bit twiddling to enable extra FCRAM 56 | // only done on N3DS and when it hasn't been done yet 57 | // state might get a bit messed up so it has to be done 58 | // as early as possible in the initialization chain 59 | if (SYS_IsNewConsole() && !SYS_ClkMultEnabled()) { 60 | gicSetInterruptConfig(88, BIT(0), GIC_PRIO_HIGHEST, NULL); 61 | gicEnableInterrupt(88); 62 | *CFG11_MPCORE_CLKCNT = 0x8001; 63 | do { 64 | ARM_WFI(); 65 | } while(!(*CFG11_MPCORE_CLKCNT & 0x8000)); 66 | gicDisableInterrupt(88); 67 | gicClearInterruptConfig(88); 68 | } 69 | } 70 | 71 | void SYS_CoreZeroInit(void) 72 | { 73 | gicGlobalReset(); 74 | 75 | *LEGACY_BOOT_ENTRYPOINT = 0; 76 | 77 | SYS_EnableClkMult(); 78 | 79 | SCU_Init(); 80 | 81 | // Map all sections here, located in VRAM 82 | mmuMapArea(SECTION_TRI(text), MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 1)); 83 | mmuMapArea(SECTION_TRI(data), MMU_FLAGS(MMU_CACHE_WBA, MMU_READ_WRITE, 1, 1)); 84 | mmuMapArea(SECTION_TRI(rodata), MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 1, 1)); 85 | mmuMapArea(SECTION_TRI(bss), MMU_FLAGS(MMU_CACHE_WBA, MMU_READ_WRITE, 1, 1)); 86 | mmuMapArea(SECTION_TRI(shared), MMU_FLAGS(MMU_STRONG_ORDER, MMU_READ_WRITE, 1, 1)); 87 | 88 | // High exception vectors 89 | mmuMapArea(0xFFFF0000, xrqInstallVectorTable(), 4UL << 10, MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 0)); 90 | 91 | // BootROM 92 | mmuMapArea(0x17000000, 0x00010000, 32UL << 10, MMU_FLAGS(MMU_CACHE_WT, MMU_READ_ONLY, 0, 1)); 93 | 94 | // IO Registers 95 | mmuMapArea(0x10100000, 0x10100000, 4UL << 20, MMU_FLAGS(MMU_DEV_SHARED, MMU_READ_WRITE, 1, 1)); 96 | 97 | // MPCore Private Memory Region 98 | mmuMapArea(0x17E00000, 0x17E00000, 8UL << 10, MMU_FLAGS(MMU_DEV_SHARED, MMU_READ_WRITE, 1, 1)); 99 | 100 | // Start of VRAM, for framebuffers 101 | mmuMapArea(0x18000000, 0x18000000, 1UL << 20, MMU_FLAGS(MMU_CACHE_WT, MMU_READ_WRITE, 1, 1)); 102 | 103 | // DSP Memory + AXIWRAM 104 | mmuMapArea(0x1FF00000, 0x1FF00000, 1UL << 20, MMU_FLAGS(MMU_STRONG_ORDER, MMU_READ_WRITE, 1, 1)); 105 | 106 | // FCRAM 107 | if (SYS_IsNewConsole()) { 108 | mmuMapArea(0x20000000, 0x20000000, 256UL << 20, MMU_FLAGS(MMU_STRONG_ORDER, MMU_READ_WRITE, 1, 1)); 109 | } else { 110 | mmuMapArea(0x20000000, 0x20000000, 128UL << 20, MMU_FLAGS(MMU_STRONG_ORDER, MMU_READ_WRITE, 1, 1)); 111 | } 112 | 113 | // screen init magicks 114 | TIMER_WaitMS(64); 115 | 116 | // Initialize peripherals 117 | PXI_Reset(); 118 | 119 | I2C_init(); 120 | mcuReset(); 121 | 122 | SPI_Init(); 123 | CODEC_Init(); 124 | } 125 | 126 | void SYS_CoreInit(void) 127 | { 128 | // Reset local GIC registers 129 | gicLocalReset(); 130 | 131 | // Set up MMU registers 132 | mmuInitRegisters(); 133 | 134 | // Enable fancy ARM11 features 135 | ARM_SetACR(ARM_GetACR() | 136 | ACR_RETSTK | ACR_DBPRED | ACR_SBPRED | ACR_FOLDING | ACR_SMP); 137 | 138 | ARM_SetCR(ARM_GetCR() | 139 | CR_MMU | CR_CACHES | CR_FLOWPRED | CR_HIGHVEC | CR_DSUBPAGE); 140 | 141 | ARM_DSB(); 142 | 143 | ARM_EnableInterrupts(); 144 | } 145 | 146 | void SYS_CoreZeroShutdown(void) 147 | { 148 | ARM_DisableInterrupts(); 149 | gicGlobalReset(); 150 | } 151 | 152 | void __attribute__((noreturn)) SYS_CoreShutdown(void) 153 | { 154 | u32 core = ARM_CoreID(); 155 | 156 | ARM_DisableInterrupts(); 157 | 158 | gicLocalReset(); 159 | 160 | ARM_WbInvDC(); 161 | ARM_InvIC(); 162 | ARM_DSB(); 163 | 164 | ARM_SetCR(ARM_GetCR() & ~(CR_MMU | CR_CACHES | CR_FLOWPRED)); 165 | ARM_SetACR(ARM_GetACR() & 166 | ~(ACR_RETSTK | ACR_DBPRED | ACR_SBPRED | ACR_FOLDING | ACR_SMP)); 167 | 168 | SPI_Deinit(); 169 | 170 | if (!core) { 171 | while(*LEGACY_BOOT_ENTRYPOINT == 0); 172 | ((void (*)(void))(*LEGACY_BOOT_ENTRYPOINT))(); 173 | } else { 174 | // Branch to bootrom function that does SMP reinit magic 175 | // (waits for IPI + branches to word @ 0x1FFFFFDC) 176 | ((void (*)(void))LEGACY_BOOT_ROUTINE_SMP)(); 177 | } 178 | __builtin_unreachable(); 179 | } 180 | -------------------------------------------------------------------------------- /arm11/source/system/sys.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | /* 24 | how to run the SYS_Core(Zero){Init,Shutdown} functions: 25 | for init: 26 | - FIRST run CoreZeroInit ONCE 27 | - all cores must run CoreInit ONCE 28 | 29 | for shutdown: 30 | - all non-zero cores must call CoreShutdown 31 | - core zero must call CoreZeroShutdown, then CoreShutdown 32 | */ 33 | 34 | void SYS_CoreZeroInit(void); 35 | void SYS_CoreInit(void); 36 | 37 | void SYS_CoreZeroShutdown(void); 38 | void __attribute__((noreturn)) SYS_CoreShutdown(void); 39 | -------------------------------------------------------------------------------- /arm11/source/ui/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "pxi/log.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #define PREFIX_LEN (sizeof(ARM11_DEBUG_PREFIX) - 1) 11 | 12 | static char strBuf[sizeof(ARM11_DEBUG_PREFIX) + DEBUG_MAX_LEN] = ARM11_DEBUG_PREFIX; 13 | 14 | void logDebugPrintf(bool logOnly, const char* format, ...) { 15 | va_list args; 16 | va_start(args, format); 17 | int n = bfnVsnprintf(strBuf + PREFIX_LEN, DEBUG_MAX_LEN, format, args); 18 | va_end(args); 19 | 20 | if (n == 0) 21 | return; 22 | 23 | if (!logOnly) { 24 | if ((u32) n != consoleNPrint(strBuf + PREFIX_LEN, n)) 25 | ARM_BKPT(); 26 | } 27 | 28 | pxiLogWrite(strBuf, PREFIX_LEN + n); 29 | } -------------------------------------------------------------------------------- /arm11/source/ui/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define ARM11_DEBUG_PREFIX "[ARM11] " 6 | #define DEBUG_MAX_LEN 512 7 | 8 | #define debugPrint(str) debugPrintf("%s", str) 9 | #define debugPrintf(format, args...) logDebugPrintf(false, format, args) 10 | 11 | // Uses bootrom vsnprintf 12 | void PRINTF_ARGS(2) logDebugPrintf(bool logOnly, const char* format, ...); -------------------------------------------------------------------------------- /arm11/source/ui/draw.c: -------------------------------------------------------------------------------- 1 | #include "draw.h" 2 | 3 | u32 drawStringColor(u16* screen, const char* str, u32 x, u32 y, u16 color) { 4 | u32 count = 0; 5 | 6 | for (; *str != '\0'; x += CHR_WIDTH, str++, count++) { 7 | drawCharacter(screen, *str, x, y, color); 8 | } 9 | 10 | return count; 11 | } 12 | 13 | /*u32 DrawString(u16* screen, const char* str, u32 x, u32 y) { 14 | return DrawStringColor(screen, str, x, y, CON_COLOR_FONT); 15 | }*/ 16 | 17 | u32 drawStringCenteredColor(u16* screen, const char* str, u32 x, u32 y, u16 color) { 18 | return drawStringColor(screen, str, x - (strlen(str) * CHR_WIDTH / 2), y, color); 19 | } 20 | 21 | void drawProgressBar(u16* screen, u32 x, u32 y, u32 width, u32 height, u32 widthFilled, u16 fillColor, u16 unfillColor) { 22 | fillRect(screen, x, y, widthFilled, height, fillColor); 23 | fillRect(screen, x + widthFilled, y, width - widthFilled, height, unfillColor); 24 | } -------------------------------------------------------------------------------- /arm11/source/ui/draw.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | u32 drawStringColor(u16* screen, const char* str, u32 x, u32 y, u16 color); 6 | //u32 DrawString(u16* screen, const char* str, u32 x, u32 y); 7 | 8 | u32 drawStringCenteredColor(u16* screen, const char* str, u32 x, u32 y, u16 color); 9 | 10 | void drawProgressBar(u16* screen, u32 x, u32 y, u32 width, u32 height, u32 widthFilled, u16 fillColor, u16 unfillColor); -------------------------------------------------------------------------------- /arm11/source/ui/ui.c: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | 3 | #include 4 | #include "ui/debug.h" 5 | 6 | static void drawBorders() { 7 | fillRect(MAIN_SCREEN, 0, 0, BORDER_THICKNESS, SCREEN_HEIGHT, BORDER_COLOR); 8 | fillRect(MAIN_SCREEN, 0, 0, MAIN_SCREEN_WIDTH, BORDER_THICKNESS, BORDER_COLOR); 9 | fillRect(MAIN_SCREEN, MAIN_SCREEN_WIDTH - BORDER_THICKNESS, 0, BORDER_THICKNESS, SCREEN_HEIGHT, BORDER_COLOR); 10 | fillRect(MAIN_SCREEN, 0, SCREEN_HEIGHT - BORDER_THICKNESS, MAIN_SCREEN_WIDTH, BORDER_THICKNESS, BORDER_COLOR); 11 | } 12 | 13 | void initUI() { 14 | drawBorders(); 15 | 16 | drawStringCenteredColor(MAIN_SCREEN, "3ds_hw_test by aspargas2 - commit " COMMIT, MAIN_SCREEN_WIDTH / 2, BORDER_THICKNESS + 2, COLOR_LIGHT_BLUE); 17 | //drawProgressBar(MAIN_SCREEN, 50, 50, 100, 10, 50, COLOR_GREEN, COLOR_GREY); 18 | } -------------------------------------------------------------------------------- /arm11/source/ui/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "draw.h" 4 | 5 | #define BORDER_THICKNESS 2 6 | #define BORDER_COLOR COLOR_WHITE 7 | 8 | void initUI(); -------------------------------------------------------------------------------- /arm9/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = 3ds_hw_test_arm9 2 | 3 | export COMMIT := $(shell git rev-parse --short HEAD) 4 | 5 | SOURCE = source 6 | BUILD = build 7 | INCLUDES = $(SOURCE) ../common 8 | SOURCES = $(INCLUDES) $(SOURCE)/* 9 | 10 | SFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.S)) 11 | CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c)) 12 | 13 | OBJS = $(subst ../common/,build/cmn.,$(subst source,build,$(SFILES:.S=.o) $(CFILES:.c=.o))) 14 | DEPS = $(OBJS:.o=.d) 15 | INCLUDE = $(foreach dir, $(INCLUDES), -I$(CURDIR)/$(dir)) 16 | 17 | ARCH = -DARM9 -mcpu=arm946e-s -march=armv5te -mthumb -mlittle-endian -mthumb-interwork 18 | ASFLAGS = $(ARCH) $(COMMON_ARCH) $(INCLUDE) 19 | CFLAGS = -DCOMMIT="\"$(COMMIT)\"" -Wall -Wextra -Os -flto $(ARCH) $(INCLUDE) -fno-stack-protector -fno-builtin 20 | LDFLAGS = -T link.ld -flto -Wl,--nmagic,--use-blx -nostartfiles -nostdlib 21 | 22 | .PHONY: all clean 23 | 24 | all: $(TARGET).bin 25 | 26 | $(TARGET).elf: $(OBJS) 27 | $(CC) $(LDFLAGS) $^ -o $@ 28 | 29 | $(BUILD)/%.o: $(SOURCE)/%.c 30 | @mkdir -p "$(@D)" 31 | $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ 32 | 33 | $(BUILD)/%.o: $(SOURCE)/%.S 34 | @mkdir -p "$(@D)" 35 | $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@ 36 | 37 | $(BUILD)/cmn.%.o: ../common/%.c 38 | @mkdir -p "$(@D)" 39 | $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ 40 | 41 | $(BUILD)/cmn.%.o: ../common/%.S 42 | @mkdir -p "$(@D)" 43 | $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@ 44 | 45 | $(TARGET).bin: $(TARGET).elf 46 | $(OBJCOPY) -S -O binary $< $@ 47 | 48 | clean: 49 | @rm -rf $(OBJS) $(TARGET).elf $(TARGET).bin $(BUILD) 50 | 51 | -include $(DEPS) 52 | -------------------------------------------------------------------------------- /arm9/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | MEMORY 6 | { 7 | /* (-1 - 0xFFFF) == 0xFFFF0000; had to express it like this to stop getting unnecessary 8 | * veneer functions when trying to call bootrom stuff. Linker bug? */ 9 | BOOTROM (RX) : ORIGIN = (-1 - 0xFFFF), LENGTH = 32K 10 | /* ITCM length is 32K, but the bootrom will only load firms into it up to 14K */ 11 | ITCM (RWX) : ORIGIN = 0x00008000, LENGTH = 14K 12 | DTCM (RW) : ORIGIN = 0xFFF00000, LENGTH = 16K 13 | } 14 | 15 | SECTIONS 16 | { 17 | .bootrom (NOLOAD) : ALIGN(4) 18 | { 19 | *(.bootrom*) 20 | . = ALIGN(4); 21 | } >BOOTROM 22 | 23 | .text : ALIGN(4) 24 | { 25 | __loaded_start = .; 26 | __text_start = ABSOLUTE(.); 27 | *(.text.start) 28 | *(.text*) 29 | . = ALIGN(4); 30 | __text_len = . - __text_start; 31 | } >ITCM 32 | 33 | .rodata : ALIGN(4) 34 | { 35 | __rodata_start = ABSOLUTE(.); 36 | *(.rodata*) 37 | . = ALIGN(4); 38 | __rodata_len = . - __rodata_start; 39 | } >ITCM 40 | 41 | .data : ALIGN(4) 42 | { 43 | __data_start = ABSOLUTE(.); 44 | *(.data*) 45 | . = ALIGN(4); 46 | __data_len = . - __data_start; 47 | __loaded_end = .; 48 | } >ITCM 49 | 50 | .bss (NOLOAD) : ALIGN(4) 51 | { 52 | __bss_start = ABSOLUTE(.); 53 | *(.bss*) 54 | . = ALIGN(4); 55 | __bss_len = . - __bss_start; 56 | } >DTCM 57 | } 58 | 59 | __loaded_len = __loaded_end - __loaded_start; -------------------------------------------------------------------------------- /arm9/source/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "console.h" 3 | #include "smalllib.h" 4 | 5 | #include 6 | 7 | #ifdef ARM9 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | static void debugNPrint(bool logOnly, const char* str, u32 n) { 18 | // Console code returns number of chars printed, so min(n, strlen(str)) 19 | if (n == 0) 20 | return; 21 | n = logOnly ? strnlen(str, n) : 22 | consoleNPrint(str, n); 23 | if (n == 0) 24 | return; 25 | logWrite(str, n); 26 | } 27 | 28 | void debugPrint(const char* str) { 29 | if (*str == '\0') 30 | return; 31 | u32 irqState = ARM_EnterCritical(); 32 | logWrite(ARM9_DEBUG_PREFIX, sizeof(ARM9_DEBUG_PREFIX) - 1); 33 | debugNPrint(false, str, -1); 34 | ARM_LeaveCritical(irqState); 35 | } 36 | 37 | void logDebugPrintf(bool logOnly, const char* format, ...) { 38 | static char strbuf[30]; 39 | u32 i = 0; 40 | 41 | u32 irqState = ARM_EnterCritical(); 42 | va_list args; 43 | va_start(args, format); 44 | 45 | logWrite(ARM9_DEBUG_PREFIX, sizeof(ARM9_DEBUG_PREFIX) - 1); 46 | 47 | for (;;i++) { 48 | char c = format[i]; 49 | 50 | if (c == '\0') { 51 | debugNPrint(logOnly, format, i); 52 | break; 53 | } 54 | 55 | if (c == '%') { 56 | debugNPrint(logOnly, format, i); 57 | format += i; 58 | i = 0; 59 | int lcount = 0; 60 | 61 | codeswitch: 62 | switch (format[1]) { 63 | case '%': 64 | format++; 65 | continue; 66 | case 's': 67 | debugNPrint(logOnly, va_arg(args, const char*), -1); 68 | format += 2; 69 | continue; 70 | case 'd': { 71 | s32 arg; 72 | if (lcount > 1) { 73 | s64 arg64 = va_arg(args, s64); 74 | if ((arg64 < (s64) INT32_MIN) || (arg64 > (s64) INT32_MAX)) 75 | goto default_; 76 | else 77 | arg = (s32) arg64; 78 | } else 79 | arg = va_arg(args, s32); 80 | itoadec(arg, strbuf); 81 | debugNPrint(logOnly, strbuf, 30); 82 | format += 2; 83 | continue; 84 | } 85 | case 'u': { 86 | u32 arg; 87 | if (lcount > 1) { 88 | u64 arg64 = va_arg(args, u64); 89 | if (arg64 > (u64) UINT32_MAX) 90 | goto default_; 91 | else 92 | arg = (u32) arg64; 93 | } else 94 | arg = va_arg(args, u32); 95 | utoadec(arg, strbuf); 96 | debugNPrint(logOnly, strbuf, 30); 97 | format += 2; 98 | continue; 99 | } 100 | case 'x': 101 | if (lcount > 1) { 102 | u64 arg = va_arg(args, u64); 103 | utoahex(arg >> 32, strbuf); 104 | if (*strbuf != '0') 105 | debugNPrint(logOnly, strbuf, 30); 106 | utoahex((u32) arg, strbuf); 107 | } else 108 | utoahex(va_arg(args, u32), strbuf); 109 | debugNPrint(logOnly, strbuf, 30); 110 | format += 2; 111 | continue; 112 | case 'l': 113 | lcount++; 114 | format++; 115 | goto codeswitch; 116 | default_: 117 | default: 118 | format -= lcount; 119 | continue; 120 | } 121 | } 122 | } 123 | 124 | va_end(args); 125 | ARM_LeaveCritical(irqState); 126 | } -------------------------------------------------------------------------------- /arm9/source/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define ARM9_DEBUG_PREFIX "[ARM9] " 6 | 7 | #define debugPrintf(format, args...) logDebugPrintf(false, format, args) 8 | 9 | void debugPrint(const char* str); 10 | 11 | // Hand-rolled minimal printf 12 | void PRINTF_ARGS(2) logDebugPrintf(bool logOnly, const char* format, ...); -------------------------------------------------------------------------------- /arm9/source/exception.c: -------------------------------------------------------------------------------- 1 | #include "exception.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "log.h" 8 | 9 | void doException(u32 type, u32* regs) { 10 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x29, 4); 11 | 12 | consoleClear(); 13 | debugPrintf("\nARM9 ded %ld\n\n", type); 14 | 15 | for (int i = 0; i < 16; i += 2) { 16 | debugPrintf("%d %lx %d %lx\n", i, regs[i], i + 1, regs[i + 1]); 17 | } 18 | 19 | debugPrintf("CPSR %lx\n\nStack:\n", regs[16]); 20 | 21 | u32* sp = (u32*) regs[13]; 22 | 23 | for (int i = 0; i < 16; i += 4) { 24 | debugPrintf("%lx %lx %lx %lx\n", sp[i], sp[i+1], sp[i+2], sp[i+3]); 25 | } 26 | 27 | deinitLog(); 28 | 29 | while (1); 30 | } -------------------------------------------------------------------------------- /arm9/source/exception.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | void doException(u32 type, u32* regs); -------------------------------------------------------------------------------- /arm9/source/fatfs/00history.txt: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | Revision history of FatFs module 3 | ---------------------------------------------------------------------------- 4 | 5 | R0.00 (February 26, 2006) 6 | 7 | Prototype. 8 | 9 | 10 | 11 | R0.01 (April 29, 2006) 12 | 13 | The first release. 14 | 15 | 16 | 17 | R0.02 (June 01, 2006) 18 | 19 | Added FAT12 support. 20 | Removed unbuffered mode. 21 | Fixed a problem on small (<32M) partition. 22 | 23 | 24 | 25 | R0.02a (June 10, 2006) 26 | 27 | Added a configuration option (_FS_MINIMUM). 28 | 29 | 30 | 31 | R0.03 (September 22, 2006) 32 | 33 | Added f_rename(). 34 | Changed option _FS_MINIMUM to _FS_MINIMIZE. 35 | 36 | 37 | 38 | R0.03a (December 11, 2006) 39 | 40 | Improved cluster scan algorithm to write files fast. 41 | Fixed f_mkdir() creates incorrect directory on FAT32. 42 | 43 | 44 | 45 | R0.04 (February 04, 2007) 46 | 47 | Added f_mkfs(). 48 | Supported multiple drive system. 49 | Changed some interfaces for multiple drive system. 50 | Changed f_mountdrv() to f_mount(). 51 | 52 | 53 | 54 | R0.04a (April 01, 2007) 55 | 56 | Supported multiple partitions on a physical drive. 57 | Added a capability of extending file size to f_lseek(). 58 | Added minimization level 3. 59 | Fixed an endian sensitive code in f_mkfs(). 60 | 61 | 62 | 63 | R0.04b (May 05, 2007) 64 | 65 | Added a configuration option _USE_NTFLAG. 66 | Added FSINFO support. 67 | Fixed DBCS name can result FR_INVALID_NAME. 68 | Fixed short seek (<= csize) collapses the file object. 69 | 70 | 71 | 72 | R0.05 (August 25, 2007) 73 | 74 | Changed arguments of f_read(), f_write() and f_mkfs(). 75 | Fixed f_mkfs() on FAT32 creates incorrect FSINFO. 76 | Fixed f_mkdir() on FAT32 creates incorrect directory. 77 | 78 | 79 | 80 | R0.05a (February 03, 2008) 81 | 82 | Added f_truncate() and f_utime(). 83 | Fixed off by one error at FAT sub-type determination. 84 | Fixed btr in f_read() can be mistruncated. 85 | Fixed cached sector is not flushed when create and close without write. 86 | 87 | 88 | 89 | R0.06 (April 01, 2008) 90 | 91 | Added fputc(), fputs(), fprintf() and fgets(). 92 | Improved performance of f_lseek() on moving to the same or following cluster. 93 | 94 | 95 | 96 | R0.07 (April 01, 2009) 97 | 98 | Merged Tiny-FatFs as a configuration option. (_FS_TINY) 99 | Added long file name feature. (_USE_LFN) 100 | Added multiple code page feature. (_CODE_PAGE) 101 | Added re-entrancy for multitask operation. (_FS_REENTRANT) 102 | Added auto cluster size selection to f_mkfs(). 103 | Added rewind option to f_readdir(). 104 | Changed result code of critical errors. 105 | Renamed string functions to avoid name collision. 106 | 107 | 108 | 109 | R0.07a (April 14, 2009) 110 | 111 | Septemberarated out OS dependent code on reentrant cfg. 112 | Added multiple sector size feature. 113 | 114 | 115 | 116 | R0.07c (June 21, 2009) 117 | 118 | Fixed f_unlink() can return FR_OK on error. 119 | Fixed wrong cache control in f_lseek(). 120 | Added relative path feature. 121 | Added f_chdir() and f_chdrive(). 122 | Added proper case conversion to extended character. 123 | 124 | 125 | 126 | R0.07e (November 03, 2009) 127 | 128 | Septemberarated out configuration options from ff.h to ffconf.h. 129 | Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH. 130 | Fixed name matching error on the 13 character boundary. 131 | Added a configuration option, _LFN_UNICODE. 132 | Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 133 | 134 | 135 | 136 | R0.08 (May 15, 2010) 137 | 138 | Added a memory configuration option. (_USE_LFN = 3) 139 | Added file lock feature. (_FS_SHARE) 140 | Added fast seek feature. (_USE_FASTSEEK) 141 | Changed some types on the API, XCHAR->TCHAR. 142 | Changed .fname in the FILINFO structure on Unicode cfg. 143 | String functions support UTF-8 encoding files on Unicode cfg. 144 | 145 | 146 | 147 | R0.08a (August 16, 2010) 148 | 149 | Added f_getcwd(). (_FS_RPATH = 2) 150 | Added sector erase feature. (_USE_ERASE) 151 | Moved file lock semaphore table from fs object to the bss. 152 | Fixed f_mkfs() creates wrong FAT32 volume. 153 | 154 | 155 | 156 | R0.08b (January 15, 2011) 157 | 158 | Fast seek feature is also applied to f_read() and f_write(). 159 | f_lseek() reports required table size on creating CLMP. 160 | Extended format syntax of f_printf(). 161 | Ignores duplicated directory separators in given path name. 162 | 163 | 164 | 165 | R0.09 (September 06, 2011) 166 | 167 | f_mkfs() supports multiple partition to complete the multiple partition feature. 168 | Added f_fdisk(). 169 | 170 | 171 | 172 | R0.09a (August 27, 2012) 173 | 174 | Changed f_open() and f_opendir() reject null object pointer to avoid crash. 175 | Changed option name _FS_SHARE to _FS_LOCK. 176 | Fixed assertion failure due to OS/2 EA on FAT12/16 volume. 177 | 178 | 179 | 180 | R0.09b (January 24, 2013) 181 | 182 | Added f_setlabel() and f_getlabel(). 183 | 184 | 185 | 186 | R0.10 (October 02, 2013) 187 | 188 | Added selection of character encoding on the file. (_STRF_ENCODE) 189 | Added f_closedir(). 190 | Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO) 191 | Added forced mount feature with changes of f_mount(). 192 | Improved behavior of volume auto detection. 193 | Improved write throughput of f_puts() and f_printf(). 194 | Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write(). 195 | Fixed f_write() can be truncated when the file size is close to 4GB. 196 | Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. 197 | 198 | 199 | 200 | R0.10a (January 15, 2014) 201 | 202 | Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID) 203 | Added a configuration option of minimum sector size. (_MIN_SS) 204 | 2nd argument of f_rename() can have a drive number and it will be ignored. 205 | Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10) 206 | Fixed f_close() invalidates the file object without volume lock. 207 | Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10) 208 | Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07) 209 | 210 | 211 | 212 | R0.10b (May 19, 2014) 213 | 214 | Fixed a hard error in the disk I/O layer can collapse the directory entry. 215 | Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07) 216 | 217 | 218 | 219 | R0.10c (November 09, 2014) 220 | 221 | Added a configuration option for the platforms without RTC. (_FS_NORTC) 222 | Changed option name _USE_ERASE to _USE_TRIM. 223 | Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b) 224 | Fixed a potential problem of FAT access that can appear on disk error. 225 | Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08) 226 | 227 | 228 | 229 | R0.11 (February 09, 2015) 230 | 231 | Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND) 232 | Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c) 233 | Fixed _FS_NORTC option does not work properly. (appeared at R0.10c) 234 | 235 | 236 | 237 | R0.11a (September 05, 2015) 238 | 239 | Fixed wrong media change can lead a deadlock at thread-safe configuration. 240 | Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE) 241 | Removed some code pages actually not exist on the standard systems. (_CODE_PAGE) 242 | Fixed errors in the case conversion teble of code page 437 and 850 (ff.c). 243 | Fixed errors in the case conversion teble of Unicode (cc*.c). 244 | 245 | 246 | 247 | R0.12 (April 12, 2016) 248 | 249 | Added support for exFAT file system. (_FS_EXFAT) 250 | Added f_expand(). (_USE_EXPAND) 251 | Changed some members in FINFO structure and behavior of f_readdir(). 252 | Added an option _USE_CHMOD. 253 | Removed an option _WORD_ACCESS. 254 | Fixed errors in the case conversion table of Unicode (cc*.c). 255 | 256 | 257 | 258 | R0.12a (July 10, 2016) 259 | 260 | Added support for creating exFAT volume with some changes of f_mkfs(). 261 | Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. 262 | f_forward() is available regardless of _FS_TINY. 263 | Fixed f_mkfs() creates wrong volume. (appeared at R0.12) 264 | Fixed wrong memory read in create_name(). (appeared at R0.12) 265 | Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. 266 | 267 | 268 | 269 | R0.12b (September 04, 2016) 270 | 271 | Made f_rename() be able to rename objects with the same name but case. 272 | Fixed an error in the case conversion teble of code page 866. (ff.c) 273 | Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) 274 | Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) 275 | Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) 276 | Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) 277 | Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) 278 | Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) 279 | 280 | 281 | 282 | R0.12c (March 04, 2017) 283 | 284 | Improved write throughput at the fragmented file on the exFAT volume. 285 | Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN. 286 | Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12) 287 | Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c) 288 | 289 | 290 | 291 | R0.13 (May 21, 2017) 292 | 293 | Changed heading character of configuration keywords "_" to "FF_". 294 | Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead. 295 | Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0) 296 | Improved cluster allocation time on stretch a deep buried cluster chain. 297 | Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3. 298 | Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous. 299 | Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12) 300 | Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c) 301 | Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c) 302 | 303 | 304 | 305 | R0.13a (October 14, 2017) 306 | 307 | Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) 308 | Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). 309 | Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). 310 | Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) 311 | Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) 312 | Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) 313 | 314 | 315 | 316 | R0.13b (April 07, 2018) 317 | 318 | Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3) 319 | Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2) 320 | Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c) 321 | Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b) 322 | 323 | 324 | 325 | R0.13c (October 14, 2018) 326 | Supported stdint.h for C99 and later. (integer.h was included in ff.h) 327 | Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12) 328 | Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12) 329 | Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b) 330 | 331 | 332 | 333 | R0.14 (October 14, 2019) 334 | Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1) 335 | Changed some API functions, f_mkfs() and f_fdisk(). 336 | Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters. 337 | Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters. 338 | Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12) 339 | Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12) 340 | 341 | 342 | R0.14a (December 5, 2020) 343 | Limited number of recursive calls in f_findnext(). 344 | Fixed old floppy disks formatted with MS-DOS 2.x and 3.x cannot be mounted. 345 | Fixed some compiler warnings. 346 | 347 | 348 | 349 | R0.14b (April 17, 2021) 350 | Made FatFs uses standard library for copy, compare and search instead of built-in string functions. 351 | Added support for long long integer and floating point to f_printf(). (FF_STRF_LLI and FF_STRF_FP) 352 | Made path name parser ignore the terminating separator to allow "dir/". 353 | Improved the compatibility in Unix style path name feature. 354 | Fixed the file gets dead-locked when f_open() failed with some conditions. (appeared at R0.12a) 355 | Fixed f_mkfs() can create wrong exFAT volume due to a timing dependent error. (appeared at R0.12) 356 | Fixed code page 855 cannot be set by f_setcp(). 357 | Fixed some compiler warnings. 358 | 359 | 360 | -------------------------------------------------------------------------------- /arm9/source/fatfs/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.14b 2 | 3 | 4 | FILES 5 | 6 | 00readme.txt This file. 7 | 00history.txt Revision history. 8 | ff.c FatFs module. 9 | ffconf.h Configuration file of FatFs module. 10 | ff.h Common include file for FatFs and application module. 11 | diskio.h Common include file for FatFs and disk I/O module. 12 | diskio.c An example of glue function to attach existing disk I/O module to FatFs. 13 | ffunicode.c Optional Unicode utility functions. 14 | ffsystem.c An example of optional O/S related functions. 15 | 16 | 17 | Low level disk I/O module is not included in this archive because the FatFs 18 | module is only a generic file system layer and it does not depend on any specific 19 | storage device. You need to provide a low level disk I/O module written to 20 | control the storage device that attached to the target system. 21 | 22 | -------------------------------------------------------------------------------- /arm9/source/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2014 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #include "integer.h" 13 | #include "sdmmc.h" 14 | 15 | 16 | /* Status of Disk Functions */ 17 | typedef BYTE DSTATUS; 18 | 19 | /* Results of Disk Functions */ 20 | typedef enum { 21 | RES_OK = 0, /* 0: Successful */ 22 | RES_ERROR, /* 1: R/W Error */ 23 | RES_WRPRT, /* 2: Write Protected */ 24 | RES_NOTRDY, /* 3: Not Ready */ 25 | RES_PARERR /* 4: Invalid Parameter */ 26 | } DRESULT; 27 | 28 | 29 | /* Disk Status Bits (DSTATUS) */ 30 | 31 | #define STA_NOINIT 0x01 /* Drive not initialized */ 32 | #define STA_NODISK 0x02 /* No medium in the drive */ 33 | #define STA_PROTECT 0x04 /* Write protected */ 34 | 35 | 36 | /* Command code for disk_ioctrl fucntion */ 37 | 38 | /* Generic command (Used by FatFs) */ 39 | #define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ 40 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ 41 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ 42 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ 43 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ 44 | 45 | /* Generic command (Not used by FatFs) */ 46 | #define CTRL_POWER 5 /* Get/Set power status */ 47 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 48 | #define CTRL_EJECT 7 /* Eject media */ 49 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 50 | 51 | /* MMC/SDC specific ioctl command */ 52 | #define MMC_GET_TYPE 10 /* Get card type */ 53 | #define MMC_GET_CSD 11 /* Get CSD */ 54 | #define MMC_GET_CID 12 /* Get CID */ 55 | #define MMC_GET_OCR 13 /* Get OCR */ 56 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 57 | 58 | /* ATA/CF specific ioctl command */ 59 | #define ATA_GET_REV 20 /* Get F/W revision */ 60 | #define ATA_GET_MODEL 21 /* Get model name */ 61 | #define ATA_GET_SN 22 /* Get serial number */ 62 | 63 | 64 | 65 | inline DSTATUS disk_initialize ( 66 | __attribute__((unused)) 67 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 68 | ) 69 | { 70 | if (!sdmmc_sdcard_init()) 71 | return RES_PARERR; 72 | return RES_OK; 73 | } 74 | 75 | inline DSTATUS disk_status ( 76 | __attribute__((unused)) 77 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 78 | ) 79 | { 80 | return RES_OK; 81 | } 82 | 83 | inline DRESULT disk_read ( 84 | __attribute__((unused)) 85 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 86 | BYTE *buff, /* Data buffer to store read data */ 87 | DWORD sector, /* Sector address in LBA */ 88 | UINT count /* Number of sectors to read */ 89 | ) 90 | { 91 | if (sdmmc_sdcard_readsectors(sector, count, buff)) { 92 | return RES_PARERR; 93 | } 94 | 95 | return RES_OK; 96 | } 97 | 98 | inline DRESULT disk_write ( 99 | __attribute__((unused)) 100 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 101 | const BYTE *buff, /* Data to be written */ 102 | DWORD sector, /* Sector address in LBA */ 103 | UINT count /* Number of sectors to write */ 104 | ) 105 | { 106 | if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { 107 | return RES_PARERR; 108 | } 109 | 110 | return RES_OK; 111 | } 112 | 113 | inline DRESULT disk_ioctl ( 114 | __attribute__((unused)) 115 | BYTE pdrv, /* Physical drive nmuber (0..) */ 116 | BYTE cmd, /* Control code */ 117 | void *buff /* Buffer to send/receive control data */ 118 | ) 119 | { 120 | switch (cmd) { 121 | case GET_SECTOR_SIZE: 122 | *((DWORD*) buff) = 0x200; 123 | return RES_OK; 124 | case GET_SECTOR_COUNT: 125 | *((DWORD*) buff) = getMMCDevice(1)->total_size; 126 | return RES_OK; 127 | case GET_BLOCK_SIZE: 128 | *((DWORD*) buff) = 0x2000; 129 | return RES_OK; 130 | case CTRL_SYNC: 131 | // nothing to do here - the disk_write function handles that 132 | return RES_OK; 133 | } 134 | return RES_PARERR; 135 | } 136 | 137 | #ifdef __cplusplus 138 | } 139 | #endif 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /arm9/source/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs Functional Configurations 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define FFCONF_DEF 86631 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define FF_FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define FF_FS_MINIMIZE 3 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: Basic functions are fully enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define FF_USE_FIND 0 29 | /* This option switches filtered directory read functions, f_findfirst() and 30 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 31 | 32 | 33 | #define FF_USE_MKFS 0 34 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 35 | 36 | 37 | #define FF_USE_FASTSEEK 0 38 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 39 | 40 | 41 | #define FF_USE_EXPAND 0 42 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 43 | 44 | 45 | #define FF_USE_CHMOD 0 46 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 47 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 48 | 49 | 50 | #define FF_USE_LABEL 0 51 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 52 | / (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define FF_USE_FORWARD 0 56 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 57 | 58 | 59 | #define FF_USE_STRFUNC 0 60 | #define FF_PRINT_LLI 0 61 | #define FF_PRINT_FLOAT 0 62 | #define FF_STRF_ENCODE 0 63 | /* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and 64 | / f_printf(). 65 | / 66 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 67 | / 1: Enable without LF-CRLF conversion. 68 | / 2: Enable with LF-CRLF conversion. 69 | / 70 | / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 71 | makes f_printf() support floating point argument. These features want C99 or later. 72 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 73 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 74 | / to be read/written via those functions. 75 | / 76 | / 0: ANSI/OEM in current CP 77 | / 1: Unicode in UTF-16LE 78 | / 2: Unicode in UTF-16BE 79 | / 3: Unicode in UTF-8 80 | */ 81 | 82 | 83 | /*---------------------------------------------------------------------------/ 84 | / Locale and Namespace Configurations 85 | /---------------------------------------------------------------------------*/ 86 | 87 | #define FF_CODE_PAGE 437 88 | /* This option specifies the OEM code page to be used on the target system. 89 | / Incorrect code page setting can cause a file open failure. 90 | / 91 | / 437 - U.S. 92 | / 720 - Arabic 93 | / 737 - Greek 94 | / 771 - KBL 95 | / 775 - Baltic 96 | / 850 - Latin 1 97 | / 852 - Latin 2 98 | / 855 - Cyrillic 99 | / 857 - Turkish 100 | / 860 - Portuguese 101 | / 861 - Icelandic 102 | / 862 - Hebrew 103 | / 863 - Canadian French 104 | / 864 - Arabic 105 | / 865 - Nordic 106 | / 866 - Russian 107 | / 869 - Greek 2 108 | / 932 - Japanese (DBCS) 109 | / 936 - Simplified Chinese (DBCS) 110 | / 949 - Korean (DBCS) 111 | / 950 - Traditional Chinese (DBCS) 112 | / 0 - Include all code pages above and configured by f_setcp() 113 | */ 114 | 115 | 116 | #define FF_USE_LFN 0 117 | #define FF_MAX_LFN 255 118 | /* The FF_USE_LFN switches the support for LFN (long file name). 119 | / 120 | / 0: Disable LFN. FF_MAX_LFN has no effect. 121 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 122 | / 2: Enable LFN with dynamic working buffer on the STACK. 123 | / 3: Enable LFN with dynamic working buffer on the HEAP. 124 | / 125 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 126 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 127 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 128 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 129 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 130 | / specification. 131 | / When use stack for the working buffer, take care on stack overflow. When use heap 132 | / memory for the working buffer, memory management functions, ff_memalloc() and 133 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 134 | 135 | 136 | #define FF_LFN_UNICODE 0 137 | /* This option switches the character encoding on the API when LFN is enabled. 138 | / 139 | / 0: ANSI/OEM in current CP (TCHAR = char) 140 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 141 | / 2: Unicode in UTF-8 (TCHAR = char) 142 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 143 | / 144 | / Also behavior of string I/O functions will be affected by this option. 145 | / When LFN is not enabled, this option has no effect. */ 146 | 147 | 148 | #define FF_LFN_BUF 255 149 | #define FF_SFN_BUF 12 150 | /* This set of options defines size of file name members in the FILINFO structure 151 | / which is used to read out directory items. These values should be suffcient for 152 | / the file names to read. The maximum possible length of the read file name depends 153 | / on character encoding. When LFN is not enabled, these options have no effect. */ 154 | 155 | 156 | #define FF_FS_RPATH 0 157 | /* This option configures support for relative path. 158 | / 159 | / 0: Disable relative path and remove related functions. 160 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 161 | / 2: f_getcwd() function is available in addition to 1. 162 | */ 163 | 164 | 165 | /*---------------------------------------------------------------------------/ 166 | / Drive/Volume Configurations 167 | /---------------------------------------------------------------------------*/ 168 | 169 | #define FF_VOLUMES 1 170 | /* Number of volumes (logical drives) to be used. (1-10) */ 171 | 172 | 173 | #define FF_STR_VOLUME_ID 0 174 | #define FF_VOLUME_STRS "SDCARD","NAND" 175 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 176 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 177 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 178 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 179 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 180 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 181 | / not defined, a user defined volume string table needs to be defined as: 182 | / 183 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 184 | */ 185 | 186 | 187 | #define FF_MULTI_PARTITION 0 188 | /* This option switches support for multiple volumes on the physical drive. 189 | / By default (0), each logical drive number is bound to the same physical drive 190 | / number and only an FAT volume found on the physical drive will be mounted. 191 | / When this function is enabled (1), each logical drive number can be bound to 192 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 193 | / funciton will be available. */ 194 | 195 | 196 | #define FF_MIN_SS 512 197 | #define FF_MAX_SS 512 198 | /* This set of options configures the range of sector size to be supported. (512, 199 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 200 | / harddisk, but a larger value may be required for on-board flash memory and some 201 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 202 | / for variable sector size mode and disk_ioctl() function needs to implement 203 | / GET_SECTOR_SIZE command. */ 204 | 205 | 206 | #define FF_LBA64 0 207 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 208 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 209 | 210 | 211 | #define FF_MIN_GPT 0x10000000 212 | /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and 213 | / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ 214 | 215 | 216 | #define FF_USE_TRIM 0 217 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 218 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 219 | / disk_ioctl() function. */ 220 | 221 | 222 | 223 | /*---------------------------------------------------------------------------/ 224 | / System Configurations 225 | /---------------------------------------------------------------------------*/ 226 | 227 | #define FF_FS_TINY 0 228 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 229 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 230 | / Instead of private sector buffer eliminated from the file object, common sector 231 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 232 | 233 | 234 | #define FF_FS_EXFAT 0 235 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 236 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 237 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 238 | 239 | 240 | #define FF_FS_NORTC 1 241 | #define FF_NORTC_MON 1 242 | #define FF_NORTC_MDAY 1 243 | #define FF_NORTC_YEAR 2020 244 | /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have 245 | / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable 246 | / the timestamp function. Every object modified by FatFs will have a fixed timestamp 247 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 248 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 249 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 250 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 251 | / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ 252 | 253 | 254 | #define FF_FS_NOFSINFO 0 255 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 256 | / option, and f_getfree() function at first time after volume mount will force 257 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 258 | / 259 | / bit0=0: Use free cluster count in the FSINFO if available. 260 | / bit0=1: Do not trust free cluster count in the FSINFO. 261 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 262 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 263 | */ 264 | 265 | 266 | #define FF_FS_LOCK 0 267 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 268 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 269 | / is 1. 270 | / 271 | / 0: Disable file lock function. To avoid volume corruption, application program 272 | / should avoid illegal open, remove and rename to the open objects. 273 | / >0: Enable file lock function. The value defines how many files/sub-directories 274 | / can be opened simultaneously under file lock control. Note that the file 275 | / lock control is independent of re-entrancy. */ 276 | 277 | 278 | /* #include // O/S definitions */ 279 | #define FF_FS_REENTRANT 0 280 | #define FF_FS_TIMEOUT 1000 281 | #define FF_SYNC_t HANDLE 282 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 283 | / module itself. Note that regardless of this option, file access to different 284 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 285 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 286 | / to the same volume is under control of this function. 287 | / 288 | / 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. 289 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 290 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 291 | / function, must be added to the project. Samples are available in 292 | / option/syscall.c. 293 | / 294 | / The FF_FS_TIMEOUT defines timeout period in unit of time tick. 295 | / The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 296 | / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be 297 | / included somewhere in the scope of ff.h. */ 298 | 299 | 300 | 301 | /*--- End of configuration options ---*/ 302 | -------------------------------------------------------------------------------- /arm9/source/fatfs/integer.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _FF_INTEGER 6 | #define _FF_INTEGER 7 | 8 | #ifdef _WIN32 /* FatFs development platform */ 9 | 10 | #include 11 | #include 12 | typedef unsigned __int64 QWORD; 13 | 14 | 15 | #else /* Embedded platform */ 16 | 17 | /* These types MUST be 16-bit or 32-bit */ 18 | typedef int INT; 19 | typedef unsigned int UINT; 20 | 21 | /* This type MUST be 8-bit */ 22 | typedef unsigned char BYTE; 23 | 24 | /* These types MUST be 16-bit */ 25 | typedef short SHORT; 26 | typedef unsigned short WORD; 27 | typedef unsigned short WCHAR; 28 | 29 | /* These types MUST be 32-bit */ 30 | typedef long LONG; 31 | typedef unsigned long DWORD; 32 | 33 | /* This type MUST be 64-bit (Remove this for C89 compatibility) */ 34 | typedef unsigned long long QWORD; 35 | 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /arm9/source/fatfs/sdmmc.h: -------------------------------------------------------------------------------- 1 | #ifndef __SDMMC_H__ 2 | #define __SDMMC_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define SDMMC_BASE 0x10006000 8 | 9 | #define REG_SDCMD 0x00 10 | #define REG_SDPORTSEL 0x02 11 | #define REG_SDCMDARG 0x04 12 | #define REG_SDCMDARG0 0x04 13 | #define REG_SDCMDARG1 0x06 14 | #define REG_SDSTOP 0x08 15 | #define REG_SDBLKCOUNT 0x0a 16 | 17 | #define REG_SDRESP0 0x0c 18 | #define REG_SDRESP1 0x0e 19 | #define REG_SDRESP2 0x10 20 | #define REG_SDRESP3 0x12 21 | #define REG_SDRESP4 0x14 22 | #define REG_SDRESP5 0x16 23 | #define REG_SDRESP6 0x18 24 | #define REG_SDRESP7 0x1a 25 | 26 | #define REG_SDSTATUS0 0x1c 27 | #define REG_SDSTATUS1 0x1e 28 | 29 | #define REG_SDIRMASK0 0x20 30 | #define REG_SDIRMASK1 0x22 31 | #define REG_SDCLKCTL 0x24 32 | 33 | #define REG_SDBLKLEN 0x26 34 | #define REG_SDOPT 0x28 35 | #define REG_SDFIFO 0x30 36 | 37 | #define REG_DATACTL 0xd8 38 | #define REG_SDRESET 0xe0 39 | #define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? 40 | 41 | #define REG_DATACTL32 0x100 42 | #define REG_SDBLKLEN32 0x104 43 | #define REG_SDBLKCOUNT32 0x108 44 | #define REG_SDFIFO32 0x10C 45 | 46 | #define REG_CLK_AND_WAIT_CTL 0x138 47 | #define REG_RESET_SDIO 0x1e0 48 | 49 | #define TMIO_STAT0_CMDRESPEND 0x0001 50 | #define TMIO_STAT0_DATAEND 0x0004 51 | #define TMIO_STAT0_CARD_REMOVE 0x0008 52 | #define TMIO_STAT0_CARD_INSERT 0x0010 53 | #define TMIO_STAT0_SIGSTATE 0x0020 54 | #define TMIO_STAT0_WRPROTECT 0x0080 55 | #define TMIO_STAT0_CARD_REMOVE_A 0x0100 56 | #define TMIO_STAT0_CARD_INSERT_A 0x0200 57 | #define TMIO_STAT0_SIGSTATE_A 0x0400 58 | #define TMIO_STAT1_CMD_IDX_ERR 0x0001 59 | #define TMIO_STAT1_CRCFAIL 0x0002 60 | #define TMIO_STAT1_STOPBIT_ERR 0x0004 61 | #define TMIO_STAT1_DATATIMEOUT 0x0008 62 | #define TMIO_STAT1_RXOVERFLOW 0x0010 63 | #define TMIO_STAT1_TXUNDERRUN 0x0020 64 | #define TMIO_STAT1_CMDTIMEOUT 0x0040 65 | #define TMIO_STAT1_RXRDY 0x0100 66 | #define TMIO_STAT1_TXRQ 0x0200 67 | #define TMIO_STAT1_ILL_FUNC 0x2000 68 | #define TMIO_STAT1_CMD_BUSY 0x4000 69 | #define TMIO_STAT1_ILL_ACCESS 0x8000 70 | 71 | #define TMIO_MASK_ALL 0x837f031d 72 | 73 | #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ 74 | TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) 75 | 76 | #define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) 77 | #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) 78 | 79 | #ifdef __cplusplus 80 | extern "C" { 81 | #endif 82 | 83 | typedef struct mmcdevice { 84 | uint8_t* rData; 85 | const uint8_t* tData; 86 | uint32_t size; 87 | uint32_t error; 88 | uint16_t stat0; 89 | uint16_t stat1; 90 | uint32_t ret[4]; 91 | uint32_t initarg; 92 | uint32_t isSDHC; 93 | uint32_t clk; 94 | uint32_t SDOPT; 95 | uint32_t devicenumber; 96 | uint32_t total_size; //size in sectors of the device 97 | uint32_t res; 98 | } mmcdevice; 99 | 100 | int sdmmc_sdcard_init(); 101 | int sdmmc_sdcard_readsector(uint32_t sector_no, uint8_t *out); 102 | int sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out); 103 | int sdmmc_sdcard_writesector(uint32_t sector_no, const uint8_t *in); 104 | int sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, const uint8_t *in); 105 | 106 | int sdmmc_nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out); 107 | int sdmmc_nand_writesectors(uint32_t sector_no, uint32_t numsectors, const uint8_t *in); 108 | 109 | int sdmmc_get_cid(bool isNand, uint32_t *info); 110 | 111 | mmcdevice *getMMCDevice(int drive); 112 | 113 | void InitSD(); 114 | int Nand_Init(); 115 | int SD_Init(); 116 | 117 | #ifdef __cplusplus 118 | }; 119 | #endif 120 | 121 | //--------------------------------------------------------------------------------- 122 | static inline uint16_t sdmmc_read16(uint16_t reg) { 123 | //--------------------------------------------------------------------------------- 124 | return *(volatile uint16_t*)(SDMMC_BASE + reg); 125 | } 126 | 127 | //--------------------------------------------------------------------------------- 128 | static inline void sdmmc_write16(uint16_t reg, uint16_t val) { 129 | //--------------------------------------------------------------------------------- 130 | *(volatile uint16_t*)(SDMMC_BASE + reg) = val; 131 | } 132 | 133 | //--------------------------------------------------------------------------------- 134 | static inline uint32_t sdmmc_read32(uint16_t reg) { 135 | //--------------------------------------------------------------------------------- 136 | return *(volatile uint32_t*)(SDMMC_BASE + reg); 137 | } 138 | 139 | //--------------------------------------------------------------------------------- 140 | static inline void sdmmc_write32(uint16_t reg, uint32_t val) { 141 | //--------------------------------------------------------------------------------- 142 | *(volatile uint32_t*)(SDMMC_BASE + reg) = val; 143 | } 144 | 145 | //--------------------------------------------------------------------------------- 146 | static inline void sdmmc_mask16(uint16_t reg, const uint16_t clear, const uint16_t set) { 147 | //--------------------------------------------------------------------------------- 148 | uint16_t val = sdmmc_read16(reg); 149 | val &= ~clear; 150 | val |= set; 151 | sdmmc_write16(reg, val); 152 | } 153 | 154 | static inline void setckl(uint32_t data) 155 | { 156 | sdmmc_mask16(REG_SDCLKCTL,0x100,0); 157 | sdmmc_mask16(REG_SDCLKCTL,0x2FF,data&0x2FF); 158 | sdmmc_mask16(REG_SDCLKCTL,0x0,0x100); 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /arm9/source/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // https://www.3dbrew.org/wiki/IRQ_Registers 6 | 7 | #define IRQ_IE ((vu32*)0x10001000) 8 | #define IRQ_IF ((vu32*)0x10001004) 9 | 10 | static inline void disableAllInterrupts() { 11 | *IRQ_IE = 0; 12 | } 13 | 14 | static inline void enableInterrupt(u32 irqn) { 15 | *IRQ_IE |= BIT(irqn); 16 | } 17 | 18 | static inline void disableInterrupt(u32 irqn) { 19 | *IRQ_IE &= ~BIT(irqn); 20 | } 21 | 22 | static inline void clearAllInterrupts() { 23 | *IRQ_IF = -1; 24 | } 25 | 26 | static inline bool testInterrupt(u32 irqn) { 27 | return *IRQ_IF & BIT(irqn); 28 | } 29 | 30 | static inline void clearInterrupt(u32 irqn) { 31 | *IRQ_IF &= BIT(irqn); 32 | } -------------------------------------------------------------------------------- /arm9/source/irqHandlers.c: -------------------------------------------------------------------------------- 1 | #include "irqHandlers.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "irq.h" 8 | #include "log.h" 9 | #include "timer.h" 10 | 11 | #define PXICMD_QUEUE_LEN 64 12 | 13 | #define CIRC_INC(i) (((i) == (PXICMD_QUEUE_LEN - 1)) ? (0) : ((i) + 1)) 14 | #define CIRC_DEC(i) (((i) == 0) ? (PXICMD_QUEUE_LEN - 1) : ((i) - 1)) 15 | 16 | static u32 pxicmdQueue[PXICMD_QUEUE_LEN]; 17 | 18 | static u32 pxicmdQueueFront = 0; 19 | static u32 pxicmdQueueBack = 0; 20 | 21 | static void enqueuePxicmd(u32 cmd) { 22 | if (pxicmdQueueFront == CIRC_INC(pxicmdQueueBack)) 23 | ARM_BKPT(); 24 | 25 | pxicmdQueue[pxicmdQueueBack = CIRC_INC(pxicmdQueueBack)] = cmd; 26 | } 27 | 28 | u32 dequeuePxicmd() { 29 | if (pxicmdQueueFront == pxicmdQueueBack) 30 | return PXICMD_NONE; 31 | 32 | return pxicmdQueue[pxicmdQueueFront = CIRC_INC(pxicmdQueueFront)]; 33 | } 34 | 35 | void irqHandlerMain(/*u32 lr*/) { 36 | //debugPrintf("Got IRQ; %x\n", lr); 37 | 38 | if(testInterrupt(TIMER_INTERRUPT(0))) 39 | clearInterrupt(TIMER_INTERRUPT(0)); 40 | 41 | if (testInterrupt(PXI_RX_INTERRUPT)) { 42 | clearInterrupt(PXI_RX_INTERRUPT); 43 | 44 | while (!(*PXI_CNT & PXI_CNT_RECV_FIFO_EMPTY)) { 45 | u32 cmd = PXI_Recv(); 46 | u32 argc = cmd >> 16; 47 | cmd &= 0xFFFF; 48 | 49 | switch (cmd) { 50 | case PXICMD_LOG_DATA: 51 | PXI_Send(logWrite((void*) PXI_Recv(), PXI_Recv())); 52 | break; 53 | 54 | case PXICMD_LOG_STRING: 55 | PXI_Send(logWriteStr((char*) PXI_Recv())); 56 | break; 57 | 58 | case PXICMD_GET_TIMER_TICKS: 59 | PXI_Send64(getFullTimerTicks()); 60 | break; 61 | 62 | // For all other commands, place command ID and args in queue to be dealt with later 63 | default: 64 | enqueuePxicmd(cmd); 65 | for (u32 i = 0; i < argc; i++) 66 | enqueuePxicmd(PXI_Recv()); 67 | break; 68 | } 69 | 70 | //debugPrintf("Handled cmd %d\n", cmd); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /arm9/source/irqHandlers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | u32 dequeuePxicmd(); 6 | 7 | void irqHandlerMain(/*u32 lr*/); -------------------------------------------------------------------------------- /arm9/source/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | #include "fatfs/ff.h" 4 | 5 | #include 6 | 7 | static FATFS fs; 8 | static FIL logfile; 9 | static bool logready = false; 10 | 11 | bool logReady() { 12 | return logready; 13 | } 14 | 15 | bool initLog() { 16 | if (f_mount(&fs, "0:", 1) != FR_OK) 17 | return false; 18 | 19 | if (f_open(&logfile, LOG_FILE, FA_WRITE | FA_OPEN_APPEND) != FR_OK) { 20 | f_unmount("0:"); 21 | return false; 22 | } 23 | 24 | logready = true; 25 | return true; 26 | } 27 | 28 | void deinitLog() { 29 | logready = false; 30 | f_close(&logfile); 31 | f_unmount("0:"); 32 | } 33 | 34 | bool logWrite(const void* data, unsigned int btw) { 35 | if (!logready) 36 | return false; 37 | 38 | UINT bw; 39 | u32 irqState = ARM_EnterCritical(); 40 | bool ret = (f_write(&logfile, data, btw, &bw) == FR_OK) && (bw == btw); 41 | ARM_LeaveCritical(irqState); 42 | 43 | return ret; 44 | } 45 | 46 | bool logWriteStr(const char* str) { 47 | return logready && logWrite(str, strlen(str)); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /arm9/source/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define LOG_FILE "hwtest.log" 6 | 7 | bool logReady(); 8 | bool initLog(); 9 | void deinitLog(); 10 | 11 | bool logWrite(const void* data, unsigned int btw); 12 | bool logWriteStr(const char* str); -------------------------------------------------------------------------------- /arm9/source/main.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "hid.h" 3 | #include "log.h" 4 | #include "i2c.h" 5 | #include "timer.h" 6 | #include "irq.h" 7 | #include "irqHandlers.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #define TITLE_STRING ("===== 3ds_hw_test by aspargas2 - commit " COMMIT " =====\n") 19 | 20 | static u32 wait_any_key_pressed() 21 | { 22 | u32 pad; 23 | while (~HID_PAD & BUTTON_NONE); 24 | while (!((pad = ~HID_PAD) & BUTTON_NONE)); 25 | return pad; 26 | } 27 | 28 | static void wait_any_key_poweroff() 29 | { 30 | consolePrint("Press any key to poweroff.\n"); 31 | wait_any_key_pressed(); 32 | mcuPoweroff(); 33 | } 34 | 35 | static bool bootBarrierWithTimeout() { 36 | bool ret; 37 | 38 | PXI_SetRemote(PXI_BOOT_BARRIER); 39 | startFullTimerCountup(FREQ_1024); 40 | 41 | while (1) { 42 | if (PXI_GetRemote() == PXI_BOOT_BARRIER) { 43 | ret = true; 44 | break; 45 | } else if (TIMER_VAL(1) > 3) { 46 | ret = false; 47 | break; 48 | } 49 | } 50 | 51 | return ret; 52 | } 53 | 54 | int main(/*int argc, char *argv[]*/) { 55 | bool arm11dead, mainLoop = true; 56 | 57 | PXI_Reset(); 58 | resetAllTimers(); 59 | disableAllInterrupts(); 60 | clearAllInterrupts(); 61 | 62 | // If bootroms are unlocked, lock them to unlock FCRAM 63 | bool locked = false; 64 | u8* sysprot = (void*) 0x10000000; // CFG9_SYSPROT9 65 | if (*sysprot == 0) { 66 | locked = true; 67 | *sysprot = 1; 68 | sysprot++; // CFG9_SYSPROT11 69 | *sysprot = 1; 70 | } 71 | 72 | // Enable Extended ARM9 Memory 73 | if (!IS_O3DS) 74 | (*(u8*)0x10000200) = 1; 75 | 76 | if ((arm11dead = !bootBarrierWithTimeout())) 77 | // Flash power LED red, as the screens are probably off 78 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x29, 6); 79 | 80 | // Gives an interrupt around once a second, ensuring we can WFI without getting stuck forever 81 | enableInterrupt(TIMER_INTERRUPT(0)); 82 | 83 | consolePrintColor(TITLE_STRING, COLOR_LIGHT_BLUE); 84 | consolePrint("Hello from ARM9\nInitializing SD log... "); 85 | 86 | if (initLog()) { 87 | consolePrint("success\n"); 88 | } else { 89 | consolePrint("failed. SD log will not be written.\n"); 90 | } 91 | 92 | logWriteStr("\n\n\n"); 93 | logWriteStr(TITLE_STRING); 94 | 95 | if (locked) 96 | debugPrint("Note: launched with bootroms unlocked\n"); 97 | 98 | if (!arm11dead) { 99 | enableInterrupt(PXI_RX_INTERRUPT); 100 | ARM_EnableInterrupts(); 101 | 102 | //debugPrint("Entering main loop\n"); 103 | 104 | while (mainLoop) { 105 | u32 pxiCmd = dequeuePxicmd(); 106 | 107 | switch(pxiCmd) { 108 | case PXICMD_NONE: 109 | ARM_WFI(); 110 | break; 111 | 112 | case PXICMD_RUN_MEMTEST: { 113 | u64* totalErrorsAddr = (u64*) dequeuePxicmd(); 114 | *totalErrorsAddr = testMemory(dequeuePxicmd(), dequeuePxicmd(), false); 115 | //ARM_BKPT(); 116 | break; 117 | } 118 | 119 | case PXICMD_LEGACY_BOOT: 120 | mainLoop = false; 121 | debugPrint("Deinitializing SD log... "); 122 | deinitLog(); 123 | consolePrint("done\n"); 124 | break; 125 | 126 | default: 127 | debugPrintf("Got unknown PXI cmd from queue: %lx\n", pxiCmd); 128 | break; 129 | } 130 | 131 | //debugPrintf("looped %x %x %x\n", *IRQ_IE, *IRQ_IF, *PXI_CNT); 132 | } 133 | 134 | ARM_DisableInterrupts(); 135 | PXI_Barrier(PXI_FIRMLAUNCH_BARRIER); 136 | } else { 137 | debugPrint("\nARM11 failed to boot, continuing independently\n\n"); 138 | 139 | consolePrint("Press B to power off, anything else to test memory\n"); 140 | if (wait_any_key_pressed() & BUTTON_B) { 141 | deinitLog(); 142 | mcuPoweroff(); 143 | } 144 | 145 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x29, 1); 146 | testMemory(ALL_MEMTEST_REGIONS, ALL_MEMTESTS, true); 147 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x29, 6); 148 | 149 | debugPrint("Deinitializing SD log... "); 150 | deinitLog(); 151 | consolePrint("done\n"); 152 | 153 | wait_any_key_poweroff(); 154 | } 155 | 156 | while (1); 157 | } 158 | -------------------------------------------------------------------------------- /arm9/source/smalllib.c: -------------------------------------------------------------------------------- 1 | #include "smalllib.h" 2 | 3 | #include 4 | 5 | size_t strlen(const char* str) { 6 | return strnlen(str, INT_MAX); 7 | } 8 | 9 | size_t strnlen(const char* str, size_t maxlen) { 10 | size_t len = 0; 11 | 12 | while (len < maxlen && str[len] != '\0') len++; 13 | 14 | return len; 15 | } 16 | 17 | char* strchr(const char* str, int c) { 18 | for (char ch = *str; ch != '\0'; ch = *(++str)) 19 | if (ch == c) 20 | return (char*) str; 21 | 22 | return NULL; 23 | } 24 | 25 | int memcmp(const void* ptr1, const void* ptr2, size_t num) { 26 | const u8* p1 = ptr1; 27 | const u8* p2 = ptr2; 28 | 29 | for (; num != 0; num--) { 30 | u8 diff = *p1++ - *p2++; 31 | 32 | if (diff != 0) 33 | return diff; 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | void utoahex(u32 x, char* buf) { 40 | bool started = false; 41 | 42 | for (size_t i = 0; i < sizeof(x) * 2; i++) { 43 | const u8 dig = x >> 28; 44 | 45 | if (started || dig != 0) { 46 | *buf++ = ((dig < 10) ? '0' : ('a' - 0xA)) + dig; 47 | started = true; 48 | } 49 | 50 | x <<= 4; 51 | } 52 | 53 | if (!started) 54 | *buf++ = '0'; 55 | 56 | *buf = '\0'; 57 | } 58 | 59 | // Compile for arm so __aeabi_uidiv_whatever can get inlined and optimized 60 | void __attribute__((noinline, target("arm"))) utoadec(u32 x, char* buf) { 61 | bool started = false; 62 | u32 pow_10 = 1000000000UL; 63 | 64 | while (pow_10 != 0) { 65 | char dig = '0'; 66 | 67 | while (true) { 68 | const u32 temp = x - pow_10; 69 | if (temp > x) 70 | break; 71 | x = temp; 72 | dig++; 73 | } 74 | 75 | if (started || dig != '0') { 76 | *buf++ = dig; 77 | started = true; 78 | } 79 | 80 | pow_10 /= 10; 81 | } 82 | 83 | if (!started) 84 | *buf++ = '0'; 85 | 86 | *buf = '\0'; 87 | } 88 | 89 | void itoadec(s32 x, char* buf) { 90 | if (x < 0) { 91 | *buf++ = '-'; 92 | x = -x; 93 | } 94 | 95 | utoadec(x, buf); 96 | } 97 | 98 | int __attribute__((naked, target("arm"))) clz(u32 __attribute__((unused)) x) { 99 | asm("clz r0, r0"); 100 | asm("bx lr"); 101 | } -------------------------------------------------------------------------------- /arm9/source/smalllib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "bfn.h" 5 | 6 | #include 7 | 8 | #define memset(dest, c, num) ((void) bfnMemset(dest, c, num)) 9 | #define memcpy(dest, src, num) ((void) bfnMemcpy(src, dest, num)) 10 | 11 | int clz(u32 x); 12 | 13 | void utoahex(u32 x, char* buf); 14 | void utoadec(u32 x, char* buf); 15 | void itoadec(s32 x, char* buf); -------------------------------------------------------------------------------- /arm9/source/start.S: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | .section .text.start 5 | .arm 6 | 7 | .macro TRAP_ENTRY xrq 8 | msr cpsr_f, #(\xrq << 29) 9 | b xrqMain 10 | .endm 11 | 12 | xrqVectorTable: 13 | b _start @ reset, which will never happen 14 | b (xrqUndefined + 0x8000) 15 | b (xrqSVC + 0x8000) 16 | b (xrqPrefetchAbort + 0x8000) 17 | b (xrqDataAbort + 0x8000) 18 | b . @ ignore the reserved exception 19 | b (xrqIRQ + 0x8000) 20 | b (xrqFIQ + 0x8000) 21 | xrqVectorTableEnd: 22 | 23 | @xrqReset: 24 | @ TRAP_ENTRY 0 25 | 26 | xrqUndefined: 27 | TRAP_ENTRY 1 28 | 29 | xrqSVC: 30 | TRAP_ENTRY 2 31 | 32 | xrqPrefetchAbort: 33 | TRAP_ENTRY 3 34 | 35 | xrqDataAbort: 36 | sub lr, lr, #4 37 | TRAP_ENTRY 4 38 | 39 | xrqFIQ: 40 | TRAP_ENTRY 7 41 | 42 | xrqMain: 43 | sub lr, lr, #4 44 | 45 | ldr sp, =(xrqStackTop - 32*4) 46 | stmia sp, {r0-r7} 47 | 48 | mrs r1, cpsr 49 | orr r0, r1, #SR_NOINT 50 | lsr r0, r1, #29 51 | 52 | mrs r2, spsr 53 | str lr, [sp, #15*4] 54 | str r2, [sp, #16*4] 55 | 56 | ands r2, r2, #SR_PMODE_MASK 57 | orreq r2, r2, #SR_SYS_MODE 58 | orr r2, r2, #(0x10 | SR_NOINT) 59 | 60 | add r3, sp, #8*4 61 | msr cpsr_c, r2 62 | stmia r3!, {r8-r14} 63 | msr cpsr_c, r1 64 | 65 | mov r1, sp 66 | bl doException 67 | 68 | 69 | xrqIRQ: 70 | sub lr, lr, #4 71 | stmfd sp!, {r0-r3, r12, lr} 72 | @mov r0, lr 73 | bl irqHandlerMain 74 | 75 | ldmfd sp!, {r0-r3, r12, pc}^ 76 | 77 | 78 | asm_func _start 79 | mov r9, r0 @ argc 80 | mov r10, r1 @ argv 81 | 82 | @ Setup IRQ mode stack 83 | msr cpsr_c, #(SR_IRQ_MODE | SR_NOINT) 84 | ldr sp, =xrqStackTop 85 | 86 | @ Switch to system mode, disable interrupts 87 | msr cpsr_c, #(SR_SYS_MODE | SR_NOINT) 88 | 89 | @ Change the stack pointer to the end of DTCM 90 | ldr sp, =(0xFFF00000 + 0x4000) 91 | 92 | @ Configure TCMs 93 | ldr r0, =0xFFF0000A 94 | ldr r1, =0x00000024 95 | mcr p15, 0, r0, c9, c1, 0 @ DTCM 96 | mcr p15, 0, r1, c9, c1, 1 @ ITCM 97 | 98 | @ Disable caches / mpu, enable TCMs, set exception vector location 99 | mrc p15, 0, r0, c1, c0, 0 @ read control register 100 | orr r0, #(CR_ITCM | CR_DTCM) 101 | bic r0, #(CR_ICACHE | CR_ALT_VECTORS) 102 | bic r0, #(CR_MPU | CR_DCACHE) 103 | mcr p15, 0, r0, c1, c0, 0 @ write control register 104 | 105 | @ Check if we're already executing from ITCM 106 | cmp pc, #ARM9MEM_BASE 107 | ldrlo pc, =relocate_done 108 | 109 | @ Copy entire binary to ITCM 110 | adr r0, xrqVectorTable 111 | mov r1, #xrqVectorTable 112 | ldr r2, =__loaded_len 113 | ldr lr, =relocate_done 114 | ldr pc, =bfnMemcpy32 115 | 116 | relocate_done: 117 | 118 | adr lr, mpu_regions 119 | ldm lr, {r0-r8} 120 | mcr p15, 0, r0, c6, c0, 0 121 | mcr p15, 0, r1, c6, c1, 0 122 | mcr p15, 0, r2, c6, c2, 0 123 | mcr p15, 0, r3, c6, c3, 0 124 | mcr p15, 0, r4, c6, c4, 0 125 | mcr p15, 0, r5, c6, c5, 0 126 | mcr p15, 0, r6, c6, c6, 0 127 | 128 | mcr p15, 0, r7, c5, c0, 2 129 | mcr p15, 0, r8, c5, c0, 3 130 | 131 | mov r0, #0 132 | mcr p15, 0, r0, c6, c7, 0 133 | mcr p15, 0, r0, c3, c0, 0 @ Write bufferable 134 | mcr p15, 0, r0, c2, c0, 0 @ Data cacheable 135 | mcr p15, 0, r0, c2, c0, 1 @ Inst cacheable 136 | 137 | bl bfnEnableMPU 138 | 139 | @ Fixes mounting of SDMC 140 | ldr r0, =0x10000020 141 | mov r1, #0x340 142 | str r1, [r0] 143 | 144 | @ Clear BSS 145 | mov r0, #0 146 | ldr r1, =__bss_start 147 | ldr r2, =__bss_len 148 | bl bfnMemset32 149 | 150 | mov r0, r9 151 | mov r1, r10 152 | blx main 153 | 154 | b . 155 | 156 | .ltorg 157 | 158 | #define REGION_4KiB (0b01011) 159 | #define REGION_8KiB (0b01100) 160 | #define REGION_16KiB (0b01101) 161 | #define REGION_32KiB (0b01110) 162 | #define REGION_64KiB (0b01111) 163 | #define REGION_128KiB (0b10000) 164 | #define REGION_256KiB (0b10001) 165 | #define REGION_512KiB (0b10010) 166 | #define REGION_1MiB (0b10011) 167 | #define REGION_2MiB (0b10100) 168 | #define REGION_4MiB (0b10101) 169 | #define REGION_8MiB (0b10110) 170 | #define REGION_16MiB (0b10111) 171 | #define REGION_32MiB (0b11000) 172 | #define REGION_64MiB (0b11001) 173 | #define REGION_128MiB (0b11010) 174 | #define REGION_256MiB (0b11011) 175 | #define REGION_512MiB (0b11100) 176 | #define REGION_1GiB (0b11101) 177 | #define REGION_2GiB (0b11110) 178 | #define REGION_4GiB (0b11111) 179 | #define MAKE_REGION(adr, size) ((adr) | ((size)<<1) | 1) 180 | 181 | mpu_regions: 182 | @ Execute-only region for exception table, so null derefs will still abort 183 | .word MAKE_REGION(0, REGION_4KiB) 184 | @ Next ITCM mirror for main excecution 185 | .word MAKE_REGION(0x8000, REGION_32KiB) 186 | @ Bootrom 187 | .word MAKE_REGION(0xFFFF0000, REGION_64KiB) 188 | @ DTCM 189 | .word MAKE_REGION(0xFFF00000, REGION_16KiB) 190 | @ IO mem + VRAM + DSPMEM + AXIWRAM 191 | .word MAKE_REGION(0x10000000, REGION_256MiB) 192 | @ FCRAM 193 | .word MAKE_REGION(0x20000000, REGION_256MiB) 194 | @ ARM9 Memory 195 | .word MAKE_REGION(0x08000000, REGION_2MiB) 196 | mpu_permissions: 197 | .word 0x01111510 198 | .word 0x00000555 199 | 200 | .section .bss 201 | .align 4 202 | .global xrqStackBottom 203 | xrqStackBottom: 204 | .space 512 @ hopefully this is enough room for the abort stack 205 | .global xrqStackTop 206 | xrqStackTop: -------------------------------------------------------------------------------- /arm9/source/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | #include 4 | 5 | void resetAllTimers() { 6 | for (int i = 0; i < N_TIMERS; i++) 7 | resetTimer(i); 8 | } 9 | 10 | void startFullTimerCountup(TimerPrescaler prescaler) { 11 | resetAllTimers(); 12 | 13 | TIMER_CNT(0) = TIMER_ACTIVE | TIMER_ENABLE_IRQ | prescaler; 14 | TIMER_CNT(1) = TIMER_CNT(2) = TIMER_CNT(3) = TIMER_ACTIVE | TIMER_COUNT_UP; 15 | } 16 | 17 | u64 getFullTimerTicks() { 18 | u64 ticks = 0; 19 | u32 irqState = ARM_EnterCritical(); 20 | 21 | for (int i = N_TIMERS - 1; i >= 0; i--) { 22 | ticks <<= 16; 23 | ticks |= (u64) TIMER_VAL(i); 24 | } 25 | 26 | ARM_LeaveCritical(irqState); 27 | return ticks; 28 | } -------------------------------------------------------------------------------- /arm9/source/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // https://www.3dbrew.org/wiki/TIMER_Registers 6 | 7 | #define N_TIMERS 4 8 | #define TIMER_FREQ 67027964 9 | 10 | #define TIMER_VAL(n) (*(vu16*)(0x10003000 + 4*(n))) 11 | #define TIMER_CNT(n) (*(u16*)(0x10003002 + 4*(n))) 12 | #define TIMER_INTERRUPT(n) (8 + n) 13 | 14 | typedef enum { // timer will run at TIMER_FREQ / one of these 15 | FREQ_1, 16 | FREQ_64, 17 | FREQ_256, 18 | FREQ_1024, 19 | } TimerPrescaler; 20 | 21 | #define TIMER_COUNT_UP 0x0004 22 | #define TIMER_ENABLE_IRQ 0x0040 23 | #define TIMER_ACTIVE 0x0080 24 | 25 | static inline void resetTimer(int n) { 26 | TIMER_CNT(n) = 0; 27 | TIMER_VAL(n) = 0; 28 | } 29 | 30 | void resetAllTimers(); 31 | void startFullTimerCountup(TimerPrescaler prescaler); 32 | u64 getFullTimerTicks(); -------------------------------------------------------------------------------- /common/arm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | /* Status Register flags */ 6 | #define SR_USR_MODE (0x10) 7 | #define SR_FIQ_MODE (0x11) 8 | #define SR_IRQ_MODE (0x12) 9 | #define SR_SVC_MODE (0x13) 10 | #define SR_ABT_MODE (0x17) 11 | #define SR_UND_MODE (0x1B) 12 | #define SR_SYS_MODE (0x1F) 13 | #define SR_PMODE_MASK (0x0F) 14 | 15 | #define SR_THUMB BIT(5) 16 | #define SR_NOFIQ BIT(6) 17 | #define SR_NOIRQ BIT(7) 18 | #define SR_NOINT (SR_NOFIQ | SR_NOIRQ) 19 | 20 | #ifdef ARM9 21 | #define CPU_FREQ (134055928) 22 | #define CR_MPU BIT(0) 23 | #define CR_DCACHE BIT(2) 24 | #define CR_ICACHE BIT(12) 25 | #define CR_DTCM BIT(16) 26 | #define CR_ITCM BIT(18) 27 | #define CR_V4TLD BIT(15) 28 | 29 | #define CR_ALT_VECTORS BIT(13) 30 | #define CR_CACHE_RROBIN BIT(14) 31 | #define CR_DTCM_LOAD BIT(17) 32 | #define CR_ITCM_LOAD BIT(19) 33 | 34 | #define CR_TCM_LOAD (CR_DTCM_LOAD | CR_ITCM_LOAD) 35 | 36 | #define ICACHE_SZ (4096) 37 | #define DCACHE_SZ (4096) 38 | 39 | #define MAX_IRQ (32) 40 | #define MAX_CPU (1) 41 | #else // ARM11 42 | #define CPU_FREQ (268111856) 43 | #define CR_MMU BIT(0) 44 | #define CR_ALIGN BIT(1) 45 | #define CR_DCACHE BIT(2) 46 | #define CR_ICACHE BIT(12) 47 | #define CR_FLOWPRED BIT(11) 48 | #define CR_HIGHVEC BIT(13) 49 | #define CR_V4TLD BIT(15) 50 | #define CR_UNALIGN BIT(22) 51 | #define CR_DSUBPAGE BIT(23) 52 | 53 | #define ACR_RETSTK BIT(0) 54 | #define ACR_DBPRED BIT(1) 55 | #define ACR_SBPRED BIT(2) 56 | #define ACR_FOLDING BIT(3) 57 | #define ACR_EXCL BIT(4) 58 | #define ACR_SMP BIT(5) 59 | 60 | #define ICACHE_SZ (16384) 61 | #define DCACHE_SZ (16384) 62 | 63 | #define MAX_IRQ (224) 64 | #define MAX_CPU (1) 65 | #endif 66 | 67 | #define CR_CACHES (CR_DCACHE | CR_ICACHE) 68 | 69 | 70 | #ifndef __ASSEMBLER__ 71 | 72 | #include "bfn.h" 73 | 74 | // only accessible from ARM mode 75 | #define ARM_MCR(cp, op1, reg, crn, crm, op2) asm_v( \ 76 | "MCR " #cp ", " #op1 ", %[R], " #crn ", " #crm ", " #op2 "\n\t" \ 77 | :: [R] "r"(reg) : "memory","cc") 78 | 79 | #define ARM_MRC(cp, op1, reg, crn, crm, op2) asm_v( \ 80 | "MRC " #cp ", " #op1 ", %[R], " #crn ", " #crm ", " #op2 "\n\t" \ 81 | : [R] "=r"(reg) :: "memory","cc") 82 | 83 | #define ARM_MSR(cp, reg) asm_v( \ 84 | "MSR " #cp ", %[R]\n\t" \ 85 | :: [R] "r"(reg) : "memory","cc") 86 | 87 | #define ARM_MRS(reg, cp) asm_v( \ 88 | "MRS %[R], " #cp "\n\t" \ 89 | : [R] "=r"(reg) :: "memory","cc") 90 | 91 | 92 | /* ARM Private Memory Region */ 93 | #ifdef ARM11 94 | #define REG_ARM_PMR(off, type) ((volatile type*)(0x17E00000 + (off))) 95 | 96 | static inline void ARM_ISB(void) { 97 | bfnInstructionSynchronizationBarrier(); 98 | } 99 | 100 | static inline void ARM_DMB(void) { 101 | bfnDataMemoryBarrier(); 102 | } 103 | 104 | static inline void ARM_WFI(void) { 105 | // replace with a bootrom call if 106 | // switching to thumb is necessary 107 | asm_v("wfi\n\t":::"memory"); 108 | } 109 | 110 | static inline void ARM_WFE(void) { 111 | asm_v("wfe\n\t":::"memory"); // same as above 112 | } 113 | 114 | static inline void ARM_SEV(void) { 115 | asm_v("sev\n\t":::"memory"); // same as above 116 | } 117 | 118 | /* Control Registers */ 119 | static inline u32 ARM_GetCR(void) { 120 | u32 cr; 121 | ARM_MRC(p15, 0, cr, c1, c0, 0); 122 | return cr; 123 | } 124 | 125 | static inline void ARM_SetCR(u32 cr) { 126 | ARM_MCR(p15, 0, cr, c1, c0, 0); 127 | } 128 | 129 | static inline u32 ARM_GetACR(void) { 130 | u32 acr; 131 | ARM_MRC(p15, 0, acr, c1, c0, 1); 132 | return acr; 133 | } 134 | 135 | static inline void ARM_SetACR(u32 acr) { 136 | ARM_MCR(p15, 0, acr, c1, c0, 1); 137 | } 138 | 139 | #else 140 | 141 | static inline void ARM_WFI(void) { 142 | bfnWFI(); 143 | } 144 | 145 | #endif 146 | 147 | /* 148 | * A Data Synchronization Barrier (DSB) completes when all 149 | * instructions before this instruction complete. 150 | */ 151 | static inline void ARM_DSB(void) { 152 | bfnDataSynchronizationBarrier(); 153 | } 154 | 155 | /* CPU ID */ 156 | static inline u32 ARM_CoreID(void) { 157 | u32 id; 158 | #ifdef ARM9 159 | id = 0; 160 | #else 161 | ARM_MRC(p15, 0, id, c0, c0, 5); 162 | #endif 163 | return id & 3; 164 | } 165 | 166 | /* Status register management */ 167 | static inline u32 ARM_EnterCritical(void) { 168 | return bfnEnterCriticalSection(); 169 | } 170 | 171 | static inline void ARM_LeaveCritical(u32 stat) { 172 | bfnLeaveCriticalSection(stat); 173 | } 174 | 175 | static inline void ARM_DisableInterrupts(void) { 176 | ARM_LeaveCritical(SR_NOINT); 177 | } 178 | 179 | static inline void ARM_EnableInterrupts(void) { 180 | ARM_LeaveCritical(0x00); 181 | } 182 | 183 | /* Cache functions */ 184 | static inline void ARM_InvIC(void) { 185 | bfnInvalidateICache(); 186 | } 187 | 188 | static inline void ARM_InvIC_Range(void *base, u32 len) { 189 | bfnInvalidateICacheRange(base, len); 190 | #ifdef ARM11 // make sure to also invalidate the branch target cache 191 | bfnInvalidateBranchTargetCacheRange(base, len); 192 | #endif 193 | } 194 | 195 | static inline void ARM_InvDC(void) { 196 | bfnInvalidateDCache(); 197 | } 198 | 199 | static inline void ARM_InvDC_Range(void *base, u32 len) { 200 | bfnInvalidateDCacheRange(base, len); 201 | } 202 | 203 | static inline void ARM_WbDC(void) { 204 | bfnWritebackDCache(); 205 | } 206 | 207 | static inline void ARM_WbDC_Range(void *base, u32 len) { 208 | bfnWritebackDCacheRange(base, len); 209 | } 210 | 211 | static inline void ARM_WbInvDC(void) { 212 | bfnWritebackInvalidateDCache(); 213 | } 214 | 215 | static inline void ARM_WbInvDC_Range(void *base, u32 len) { 216 | bfnWritebackInvalidateDCacheRange(base, len); 217 | } 218 | 219 | static inline void ARM_WaitCycles(u32 cycles) { 220 | bfnWaitCycles(cycles); 221 | } 222 | 223 | static inline void ARM_BKPT(void) { 224 | __builtin_trap(); 225 | } 226 | 227 | #endif // __ASSEMBLER__ 228 | -------------------------------------------------------------------------------- /common/bfn.S: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | .section .bootrom 4 | 5 | #ifdef ARM9 // .bootrom linked at 0xFFFF0000 6 | 7 | .org 0x0198; asm_func bfnWaitCycles 8 | .org 0x01A4; asm_func bfnWFI 9 | .org 0x03A4; asm_func bfnMemset32 10 | .org 0x03F0; asm_func bfnMemcpy32 11 | .org 0x056C; asm_func bfnMemset 12 | .org 0x05E0; asm_func bfnMemcpy 13 | .org 0x06EC; asm_func bfnEnterCriticalSection 14 | .org 0x0700; asm_func bfnLeaveCriticalSection 15 | .org 0x0798; asm_func bfnEnableDCache 16 | .org 0x07B0; asm_func bfnDisableDCache 17 | .org 0x07C8; asm_func bfnSetDCache 18 | .org 0x07F0; asm_func bfnInvalidateDCache 19 | .org 0x07FC; asm_func bfnWritebackDCache 20 | .org 0x0830; asm_func bfnWritebackInvalidateDCache 21 | .org 0x0868; asm_func bfnInvalidateDCacheRange 22 | .org 0x0884; asm_func bfnWritebackDCacheRange 23 | .org 0x08A8; asm_func bfnWritebackInvalidateDCacheRange 24 | .org 0x096C; asm_func bfnDataSynchronizationBarrier 25 | .org 0x0A5C; asm_func bfnEnableICache 26 | .org 0x0A74; asm_func bfnDisableICache 27 | .org 0x0A8C; asm_func bfnSetICache 28 | .org 0x0AB4; asm_func bfnInvalidateICache 29 | .org 0x0AC0; asm_func bfnInvalidateICacheRange 30 | .org 0x0C38; asm_func bfnEnableMPU 31 | .org 0x0C48; asm_func bfnDisableMPU 32 | .org 0x0C58; asm_func bfnResetControlRegisters 33 | 34 | #else // ARM11; .bootrom linked at 0x10000 35 | 36 | .org 0x1288; asm_func bfnEnableDCache 37 | .org 0x12A0; asm_func bfnDisableDCache 38 | .org 0x12B8; asm_func bfnSetDCache 39 | .org 0x12E0; asm_func bfnInvalidateDCache 40 | .org 0x12EC; asm_func bfnWritebackDCache 41 | .org 0x1320; asm_func bfnWritebackInvalidateDCache 42 | .org 0x1358; asm_func bfnInvalidateDCacheRange 43 | .org 0x1374; asm_func bfnWritebackDCacheRange 44 | .org 0x1398; asm_func bfnWritebackInvalidateDCacheRange 45 | .org 0x13C0; asm_func bfnDataSynchronizationBarrier 46 | .org 0x13E8; asm_func bfnDataMemoryBarrier 47 | .org 0x13F4; asm_func bfnEnableICache 48 | .org 0x140C; asm_func bfnDisableICache 49 | .org 0x1424; asm_func bfnSetICache 50 | .org 0x144C; asm_func bfnInvalidateICache 51 | .org 0x1458; asm_func bfnInvalidateICacheRange 52 | .org 0x1490; asm_func bfnInstructionSynchronizationBarrier 53 | .org 0x14E8; asm_func bfnInvalidateBranchTargetCache 54 | .org 0x14F4; asm_func bfnInvalidateBranchTargetCacheRange 55 | .org 0x16E4; asm_func bfnMemset32 56 | .org 0x1730; asm_func bfnMemcpy32 57 | .org 0x18AC; asm_func bfnMemset 58 | .org 0x1920; asm_func bfnMemcpy 59 | .org 0x1A38; asm_func bfnWaitCycles 60 | .org 0x1AC4; asm_func bfnEnterCriticalSection 61 | .org 0x1AD8; asm_func bfnLeaveCriticalSection 62 | .org 0x28F8; .thumb_func; asm_func bfnVsnprintf 63 | 64 | #endif -------------------------------------------------------------------------------- /common/bfn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* 6 | Declarations of preexisting BootROM functions; addresses are in bfn.S 7 | All of these functions should follow the standard AAPCS convention 8 | */ 9 | 10 | #ifdef ARM9 11 | 12 | // mcr instruction to wait for interrupt 13 | void bfnWFI(void); 14 | 15 | void bfnEnableMPU(void); 16 | void bfnDisableMPU(void); 17 | 18 | // sets CR0 to its reset state (MPU & caches disabled, high vectors, TCMs enabled) 19 | // invalidates both instruction and data caches (without previously writing back!!) 20 | void bfnResetControlRegisters(void); 21 | 22 | #else 23 | 24 | void bfnInstructionSynchronizationBarrier(void); 25 | 26 | void bfnDataMemoryBarrier(void); 27 | 28 | void bfnInvalidateBranchTargetCache(void); 29 | void bfnInvalidateBranchTargetCacheRange(void* start, u32 len); 30 | 31 | // seems to be perfectly compatible with regular library vsnprintf 32 | int bfnVsnprintf(char* s, int n, const char* format, va_list arg); 33 | 34 | #endif 35 | 36 | // delays execution time by cycles 37 | void bfnWaitCycles(u32 cycles); 38 | 39 | u32* bfnMemset32(u32 val, u32* dest, u32 count); 40 | u32* bfnMemcpy32(const u32* src, u32* dest, u32 count); 41 | 42 | void bfnMemset(void* dest, u8 val, u32 count); 43 | void bfnMemcpy(const void* src, void* dest, u32 count); 44 | 45 | // disables interrupts and returns the old irq state 46 | u32 bfnEnterCriticalSection(void); 47 | // restores the old irq state 48 | void bfnLeaveCriticalSection(u32 irqState); 49 | 50 | // enables the data cache and returns the old dcache bit 51 | bool bfnEnableDCache(void); 52 | // disables the data cache and returns the old dcache bit 53 | bool bfnDisableDCache(void); 54 | // sets the data cache bit and returns the old one 55 | bool bfnSetDCache(bool enable); 56 | 57 | // invalidates all data cache entries 58 | void bfnInvalidateDCache(void); 59 | // writes back all data cache entries 60 | void bfnWritebackDCache(void); 61 | // writes back and invalidates all data cache entries 62 | void bfnWritebackInvalidateDCache(void); 63 | 64 | void bfnInvalidateDCacheRange(void* start, u32 len); 65 | void bfnWritebackDCacheRange(void* start, u32 len); 66 | void bfnWritebackInvalidateDCacheRange(void* start, u32 len); 67 | 68 | void bfnDataSynchronizationBarrier(void); 69 | 70 | bool bfnEnableICache(void); 71 | bool bfnDisableICache(void); 72 | bool bfnSetICache(bool enable); 73 | 74 | // also invalidates the branch target cache on ARM11 75 | void bfnInvalidateICache(void); 76 | 77 | // WARNING: DOES NOT INVALIDATE THE BRANCH TARGET CACHE ON ARM11 78 | // NEEDS TO INVALIDATE IT AND FLUSH THE PREFETCH BUFFER 79 | void bfnInvalidateICacheRange(void* start, u32 len); 80 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef ARM9 4 | #ifndef ARM11 5 | #error "Unknown processor" 6 | #endif 7 | #endif 8 | 9 | #ifdef ARM9 10 | #ifdef ARM11 11 | #error "Something is very wrong" 12 | #endif 13 | #endif 14 | 15 | #define BIT(x) (1 << (x)) 16 | 17 | #ifdef __ASSEMBLER__ 18 | 19 | .macro asm_func name 20 | .global \name 21 | .type \name, %function 22 | \name: 23 | .endm 24 | 25 | #else 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #define u8 uint8_t 32 | #define u16 uint16_t 33 | #define u32 uint32_t 34 | #define u64 uint64_t 35 | 36 | #define vu8 volatile u8 37 | #define vu16 volatile u16 38 | #define vu32 volatile u32 39 | #define vu64 volatile u64 40 | 41 | #define s8 int8_t 42 | #define s16 int16_t 43 | #define s32 int32_t 44 | #define s64 int64_t 45 | 46 | #define ALIGN(n) __attribute__((aligned(n))) 47 | #define PACKED_ALIGN(n) __attribute__((packed, aligned(n))) 48 | #define PACKED_STRUCT PACKED_ALIGN(4) 49 | 50 | #define asm_v asm __volatile__ 51 | 52 | #define PRINTF_ARGS(n) __attribute__((format (printf, (n), (n) + 1))) 53 | 54 | #define max(a,b) \ 55 | (((a) > (b)) ? (a) : (b)) 56 | #define min(a,b) \ 57 | (((a) < (b)) ? (a) : (b)) 58 | #define getbe16(d) \ 59 | (((d)[0]<<8) | (d)[1]) 60 | #define getbe32(d) \ 61 | ((((u32) getbe16(d))<<16) | ((u32) getbe16(d+2))) 62 | #define getbe64(d) \ 63 | ((((u64) getbe32(d))<<32) | ((u64) getbe32(d+4))) 64 | #define getle16(d) (*((u16*) (d))) 65 | #define getle32(d) (*((u32*) (d))) 66 | #define getle64(d) (*((u64*) (d))) 67 | #define align(v,a) \ 68 | (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) 69 | #define countof(x) \ 70 | (sizeof(x) / sizeof(*(x))) 71 | 72 | #define IS_O3DS (((*(vu16*) 0x10140FFC) & 2) == 0) 73 | 74 | #endif //__ASSEMBLER__ 75 | 76 | // https://www.3dbrew.org/wiki/Memory_layout 77 | #define ARM9MEM_BASE (0x08000000) 78 | #define ARM9MEM_SIZE (0x00100000) 79 | #define NEW_ARM9MEM_BASE (0x08100000) 80 | #define NEW_ARM9MEM_SIZE (0x00080000) 81 | #define VRAM_BASE (0x18000000) 82 | #define VRAM_SIZE (0x00600000) 83 | #define DSPMEM_BASE (0x1FF00000) 84 | #define DSPMEM_SIZE (0x00080000) 85 | #define AXIWRAM_BASE (0x1FF80000) 86 | #define AXIWRAM_SIZE (0x00080000) 87 | #define FCRAM_BASE (0x20000000) 88 | #define FCRAM_SIZE (0x08000000) 89 | #define NEW_FCRAM_BASE (0x28000000) 90 | #define NEW_FCRAM_SIZE (0x08000000) 91 | -------------------------------------------------------------------------------- /common/console.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "font.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static u32 consoleXPos = CON_START_X; 10 | static u32 consoleYPos = CON_START_Y; 11 | 12 | void fillRect(u16* screen, int x, int y, int width, int height, u16 color) { 13 | for (int xx = 1; xx <= width; xx++) 14 | for (int yy = 1; yy <= height; yy++) 15 | screen[((x + xx) * SCREEN_HEIGHT) - y - yy] = color; 16 | } 17 | 18 | static void shiftRect(u16* screen, int x, int y, int width, int height, int shift, u16 bgcolor) { 19 | for (int xx = 1; xx <= width; xx++) 20 | for (int yy = 1; yy <= height - shift; yy++) 21 | screen[((x + xx) * SCREEN_HEIGHT) - y - yy] = screen[((x + xx) * SCREEN_HEIGHT) - y - yy - shift]; 22 | 23 | fillRect(screen, x, y + height - shift, width, shift, bgcolor); 24 | } 25 | 26 | void drawCharacter(u16* screen, char character, int x, int y, u16 color) { 27 | // char - 0x20 because non-essential parts of the font are trimmed off 28 | u32* charData = (u32*) (font + character - 0x20); 29 | 30 | for (int xx = 0; xx < CHR_WIDTH; xx++) { 31 | int xDisplacement = (x + xx) * SCREEN_HEIGHT; 32 | for (int yy = 0; yy < CHR_HEIGHT; yy++) { 33 | int yDisplacement = SCREEN_HEIGHT - (y + yy) - 1; 34 | u16 *screenPos = screen + xDisplacement + yDisplacement; 35 | int bit = (yy * CHR_WIDTH) + xx; 36 | if (charData[bit / 32] & (1 << (bit % 32))) 37 | *screenPos = color; 38 | } 39 | } 40 | } 41 | 42 | void consoleClear() { 43 | consoleXPos = CON_START_X; 44 | consoleYPos = CON_START_Y; 45 | 46 | fillRect(MAIN_SCREEN, CON_START_X, CON_START_Y, CON_WIDTH, CON_HEIGHT, CON_COLOR_BG); 47 | } 48 | 49 | u32 consoleNPrintColor(const char* str, u16 color, u32 n) { 50 | u32 i = 0; 51 | u32 irqState = ARM_EnterCritical(); 52 | 53 | for (; str[i] != '\0' && i < n; i++) { 54 | if (str[i] == '\n' || consoleXPos >= CON_END_X) { 55 | consoleXPos = CON_START_X; 56 | consoleYPos += CON_STEP_Y; 57 | if (consoleYPos > (CON_END_Y - CON_STEP_Y)) { 58 | consoleYPos = (CON_END_Y - CON_STEP_Y); 59 | shiftRect(MAIN_SCREEN, CON_START_X, CON_START_Y, CON_WIDTH, CON_HEIGHT, CON_STEP_Y, CON_COLOR_BG); 60 | } 61 | } 62 | 63 | if (str[i] != '\n') { 64 | drawCharacter(MAIN_SCREEN, str[i], consoleXPos, consoleYPos, color); 65 | consoleXPos += CON_STEP_X; 66 | } 67 | } 68 | 69 | ARM_LeaveCritical(irqState); 70 | return i; 71 | } 72 | -------------------------------------------------------------------------------- /common/console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "screen.h" 5 | #include "font.h" 6 | 7 | #define CON_COLOR_BG COLOR_BLACK 8 | #define CON_COLOR_FONT COLOR_WHITE 9 | 10 | #ifdef ARM9 11 | #define CON_START_Y 0 12 | #define CON_START_X 0 13 | #define CON_END_Y (SCREEN_HEIGHT) 14 | #define CON_END_X (MAIN_SCREEN_WIDTH - 2) 15 | #else 16 | #define CON_START_Y (5 + CHR_HEIGHT) 17 | #define CON_START_X 4 18 | #define CON_END_Y (SCREEN_HEIGHT - 5) 19 | #define CON_END_X (MAIN_SCREEN_WIDTH - 6) 20 | #endif 21 | 22 | #define CON_HEIGHT (CON_END_Y - CON_START_Y) 23 | #define CON_WIDTH (CON_END_X - CON_START_X) 24 | #define CON_STEP_Y CHR_HEIGHT 25 | #define CON_STEP_X CHR_WIDTH 26 | #define CON_N_CHARS_Y (CON_HEIGHT / CON_STEP_Y) 27 | #define CON_N_CHARS_X (CON_WIDTH / CON_STEP_X) 28 | 29 | void fillRect(u16* screen, int x, int y, int width, int height, u16 color); 30 | void drawCharacter(u16* screen, char character, int x, int y, u16 color); 31 | 32 | void consoleClear(); 33 | u32 consoleNPrintColor(const char* str, u16 color, u32 n); 34 | 35 | static inline u32 consolePrintColor(const char* str, u16 color) { 36 | return consoleNPrintColor(str, color, -1); 37 | } 38 | 39 | static inline u32 consoleNPrint(const char* str, u32 n) { 40 | return consoleNPrintColor(str, CON_COLOR_FONT, n); 41 | } 42 | 43 | static inline u32 consolePrint(const char* str) { 44 | return consolePrintColor(str, CON_COLOR_FONT); 45 | } 46 | -------------------------------------------------------------------------------- /common/font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define CHR_HEIGHT 10 6 | #define CHR_WIDTH 6 7 | 8 | // Generated with an ugly python script; each u64 is a character's font data 9 | 10 | static const u64 font[] = { 11 | /*0x0, 0x7adce1ce1780, 0x7b3b7fb7f780, 0x873ef94000, 0x873e708000, 0x708db671c000, 0x708fbe708000, 0x31e300000, 12 | 0xffffffce1cfffff, 0xc4a148c000, 0xfffff3b5eb73fff, 0x72289e128f00, 0x21c21c8a2700, 0x18e208a28600, 0xdb6924f24f00, 0x22a73672a200, 13 | 0x8639e386080, 0x830e3ce30800, 0x21ca88a9c200, 0x480492492480, 0xa28a3caaaabc, 0x72242891228489c, 0x7df7c0000000, 0x3e21ca88a9c200, 14 | 0x208208a9c200, 0x21ca88208200, 0x843e408000, 0x813e108000, 0x782082000000, 0x1287f852000, 0xfbe71c208000, 0x20871cfbe000,*/ 15 | 0x0, 0x200208208200, 0x14514, 0x53e514f94000, 0x872aa1c2aa708, 0xcb2108426980, 0xb12a84292300, 0x8208, 16 | 0x10204104104210, 0x4210410410204, 0x22a72a200, 0x823e208000, 0x10830c000000000, 0x3e000000, 0x618000000000, 0x82104208410820, 17 | 0x7229aaca2700, 0xf8820828c200, 0xf84210822700, 0x722818822700, 0x410f92518400, 0x722820782f80, 0x72289e084600, 0x208210820f80, 18 | 0x72289c8a2700, 0x31083c8a2700, 0x618018600000, 0x10830c00c300000, 0x810204210800, 0xf80f80000, 0x108420408100, 0x200210822700, 19 | 0x702eaaea2700, 0x8a2fa2894200, 0x7a491c924780, 0x7220820a2700, 0x7a4924924780, 0xf8209e082f80, 0x8209e082f80, 0x7228ba0a2700, 20 | 0x8a28be8a2880, 0x708208208700, 0x312490410e00, 0x892286292880, 0xf82082082080, 0x8a28aaab6880, 0x8a28b2aa6880, 0x7228a28a2700, 21 | 0x8209e8a2780, 0xb12aa28a2700, 0x89229e8a2780, 0x72281c0a2700, 0x208208208f80, 0x7228a28a2880, 0x2148a28a2880, 0x8b6aaa8a2880, 22 | 0x8a2508522880, 0x2082148a2880, 0xf82108420f80, 0x18208208208218, 0x820410208104082, 0xc20820820820c, 0x894200, 0x3e000000000000, 23 | 0x10204, 0xf22f20700000, 0x6a68a6682080, 0x7220a2700000, 0xb328b2b20800, 0xf02fa2700000, 0x20821c208c00, 0x720b328b2b00000, 24 | 0x8a28a2782080, 0x708208300200, 0x188208208300200, 0x89238a482080, 0x708208208300, 0xaaaaaa580000, 0x8a28a6680000, 0x7228a2700000, 25 | 0x826a68a6680000, 0x820b328b2b00000, 0x820a6680000, 0x7a0702f00000, 0xc08208708200, 0xb328a2880000, 0x2148a2880000, 0x52aaaaa80000, 26 | 0x894214880000, 0x720f228a2880000, 0xf84210f80000, 0x10208204208210, 0x8208200208208, 0x4208210208204, 0x10a84000000, 0xfa2894200000, 27 | /*0x1087220820a2700, 0xb328a2880500, 0xf02fa2700210, 0xf22f20700508, 0xf22f20700500, 0xf22f20700408, 0xf22f20718918, 0x1087220a2700000, 28 | 0xf02fa2700508, 0xf02fa2700500, 0xf02fa2700204, 0x708208300500, 0x708208300508, 0x708208300204, 0x8a2fa2894222, 0x8a2fa289c48c, 29 | 0xf8209e082f88, 0xf0afa8780000, 0xe8a29e28af00, 0x7228a2700508, 0x7228a2700500, 0x7228a2700204, 0xb328a2880508, 0xb328a2880204, 30 | 0x720f228a2880500, 0x7228a28a2721, 0x7228a28a2891, 0x872a2aa708000, 0x7a208238248c, 0x208f88f94880, 0x892e92392380, 0x18821c208c00, 31 | 0xf22f20700210, 0x708208300210, 0x7228a2700210, 0xb328a2880210, 0x8a28a66806ac, 0x8a28b2aa689a, 0xf80f22f20700, 0xf807228a2700, 32 | 0x722084200200, 0x20be000000, 0x2083e000000, 0x38422508524184, 0x10f1a508524184, 0x208208200200, 0x912252900000, 0x252912240000, 33 | 0x888888888888888, 0xa95a95a95a95a95, 0xbbbbbbbbbbbbbbb, 0x208208208208208, 0x20820820f208208, 0x2082083c83c8208, 0x514514517514514, 0x51451451f000000, 34 | 0x2082083c83c0000, 0x5145145d05d4514, 0x514514514514514, 0x5145145d07c0000, 0x7d05d4514, 0x1f514514, 0x3c83c8208, 0x20820820f000000, 35 | 0x38208208, 0x3f208208, 0x20820823f000000, 0x208208238208208, 0x3f000000, 0x20820823f208208, 0x208208e08e08208, 0x514514534514514, 36 | 0xf04d14514, 0x514514d04f00000, 0xfc0dd4514, 0x514514dc0fc0000, 0x514514d04d14514, 0xfc0fc0000, 0x514514dc0dd4514, 0xfc0fc8208, 37 | 0x3f514514, 0x208208fc0fc0000, 0x51451453f000000, 0x3c514514, 0xe08e08208, 0x208208e08e00000, 0x51451453c000000, 0x51451453f514514, 38 | 0x208208fc8fc8208, 0xf208208, 0x208208238000000, 0xfffffffffffffff, 0xfffffffc0000000, 0x1c71c71c71c71c7, 0xe38e38e38e38e38, 0x3fffffff, 39 | 0xb12492b00000, 0x26a28924a2918, 0x820828a2f80, 0x514514f80000, 0xfa4210224f80, 0x312492f00000, 0x82b92492480000, 0xc08208f80000, 40 | 0x70872289c21c, 0x7228be8a2700, 0xd948a28a2700, 0x624924604600, 0x72aaaa700000, 0x9caaa720000, 0xf0209c082f00, 0x8a28a28a2700, 41 | 0x3f03f03f000, 0xf80208f88200, 0x700108408100, 0x700408108400, 0x208208208208c00, 0x6208208208208, 0x803e008000, 0x42a110a84000, 42 | 0x31248c, 0x21c200000, 0x8000000, 0x10428a410800, 0x28a286, 0x384206, 0x71c71c71c000, 0x0,*/ 43 | }; -------------------------------------------------------------------------------- /common/hid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #define HID_PAD (*(vu32 *)0x10146000) 6 | 7 | #define BUTTON_NONE (0xFFF) 8 | #define BUTTON_A (1<<0) 9 | #define BUTTON_B (1<<1) 10 | #define BUTTON_SELECT (1<<2) 11 | #define BUTTON_START (1<<3) 12 | #define BUTTON_RIGHT (1<<4) 13 | #define BUTTON_LEFT (1<<5) 14 | #define BUTTON_UP (1<<6) 15 | #define BUTTON_DOWN (1<<7) 16 | #define BUTTON_R (1<<8) 17 | #define BUTTON_L (1<<9) 18 | #define BUTTON_X (1<<10) 19 | #define BUTTON_Y (1<<11) 20 | 21 | #define BUTTON_HELD(b, m) (~(b) & (m)) 22 | #define BUTTON_PRESSED(b, o, m) ((~(b) & (o)) & (m)) 23 | #define BUTTON_CHANGED(b, o, m) (((b) ^ (o)) & (m)) 24 | -------------------------------------------------------------------------------- /common/i2c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fastboot 3DS 3 | * Copyright (C) 2017 derrek, profi200 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "i2c.h" 22 | 23 | 24 | #define I2C1_REGS_BASE (0x10161000) 25 | 26 | #define I2C2_REGS_BASE (0x10144000) 27 | 28 | #define I2C3_REGS_BASE (0x10148000) 29 | 30 | 31 | typedef struct 32 | { 33 | vu8 REG_I2C_DATA; 34 | vu8 REG_I2C_CNT; 35 | vu16 REG_I2C_CNTEX; 36 | vu16 REG_I2C_SCL; 37 | } I2cRegs; 38 | 39 | static const struct 40 | { 41 | u8 busId; 42 | u8 devAddr; 43 | } i2cDevTable[] = 44 | { 45 | {0, 0x4A}, 46 | {0, 0x7A}, 47 | {0, 0x78}, 48 | {1, 0x4A}, 49 | {1, 0x78}, 50 | {1, 0x2C}, 51 | {1, 0x2E}, 52 | {1, 0x40}, 53 | {1, 0x44}, 54 | {2, 0xD6}, 55 | {2, 0xD0}, 56 | {2, 0xD2}, 57 | {2, 0xA4}, 58 | {2, 0x9A}, 59 | {2, 0xA0}, 60 | {1, 0xEE}, 61 | {0, 0x40}, 62 | {2, 0x54} 63 | }; 64 | 65 | 66 | 67 | static void i2cWaitBusy(I2cRegs *const regs) 68 | { 69 | while(regs->REG_I2C_CNT & I2C_ENABLE); 70 | } 71 | 72 | static I2cRegs* i2cGetBusRegsBase(u8 busId) 73 | { 74 | I2cRegs *base; 75 | switch(busId) 76 | { 77 | case 0: 78 | base = (I2cRegs*)I2C1_REGS_BASE; 79 | break; 80 | case 1: 81 | base = (I2cRegs*)I2C2_REGS_BASE; 82 | break; 83 | case 2: 84 | base = (I2cRegs*)I2C3_REGS_BASE; 85 | break; 86 | default: 87 | base = NULL; 88 | } 89 | 90 | return base; 91 | } 92 | 93 | void I2C_init(void) 94 | { 95 | I2cRegs *regs = i2cGetBusRegsBase(0); // Bus 1 96 | i2cWaitBusy(regs); 97 | regs->REG_I2C_CNTEX = 2; // ? 98 | regs->REG_I2C_SCL = 1280; // ? 99 | 100 | regs = i2cGetBusRegsBase(1); // Bus 2 101 | i2cWaitBusy(regs); 102 | regs->REG_I2C_CNTEX = 2; // ? 103 | regs->REG_I2C_SCL = 1280; // ? 104 | 105 | regs = i2cGetBusRegsBase(2); // Bus 3 106 | i2cWaitBusy(regs); 107 | regs->REG_I2C_CNTEX = 2; // ? 108 | regs->REG_I2C_SCL = 1280; // ? 109 | } 110 | 111 | static bool i2cStartTransfer(int devId, u8 regAddr, bool read, I2cRegs *const regs) 112 | { 113 | const u8 devAddr = i2cDevTable[devId].devAddr; 114 | 115 | 116 | u32 i = 0; 117 | for(; i < 8; i++) 118 | { 119 | i2cWaitBusy(regs); 120 | 121 | // Select device and start. 122 | regs->REG_I2C_DATA = devAddr; 123 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START; 124 | i2cWaitBusy(regs); 125 | if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. 126 | { 127 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; 128 | continue; 129 | } 130 | 131 | // Select register and change direction to write. 132 | regs->REG_I2C_DATA = regAddr; 133 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE; 134 | i2cWaitBusy(regs); 135 | if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. 136 | { 137 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; 138 | continue; 139 | } 140 | 141 | // Select device in read mode for read transfer. 142 | if(read) 143 | { 144 | regs->REG_I2C_DATA = devAddr | 1u; // Set bit 0 for read. 145 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_START; 146 | i2cWaitBusy(regs); 147 | if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. 148 | { 149 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; 150 | continue; 151 | } 152 | } 153 | 154 | break; 155 | } 156 | 157 | if(i < 8) return true; 158 | else return false; 159 | } 160 | 161 | bool I2C_readRegBuf(int devId, u8 regAddr, u8 *out, u32 size) 162 | { 163 | const u8 busId = i2cDevTable[devId].busId; 164 | I2cRegs *const regs = i2cGetBusRegsBase(busId); 165 | 166 | 167 | if(!i2cStartTransfer(devId, regAddr, true, regs)) return false; 168 | 169 | while(--size) 170 | { 171 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_ACK; 172 | i2cWaitBusy(regs); 173 | *out++ = regs->REG_I2C_DATA; 174 | } 175 | 176 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_READ | I2C_STOP; 177 | i2cWaitBusy(regs); 178 | *out = regs->REG_I2C_DATA; // Last byte 179 | 180 | return true; 181 | } 182 | 183 | bool I2C_writeRegBuf(int devId, u8 regAddr, const u8 *in, u32 size) 184 | { 185 | const u8 busId = i2cDevTable[devId].busId; 186 | I2cRegs *const regs = i2cGetBusRegsBase(busId); 187 | 188 | 189 | if(!i2cStartTransfer(devId, regAddr, false, regs)) return false; 190 | 191 | while(--size) 192 | { 193 | regs->REG_I2C_DATA = *in++; 194 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE; 195 | i2cWaitBusy(regs); 196 | if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. 197 | { 198 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; 199 | return false; 200 | } 201 | } 202 | 203 | regs->REG_I2C_DATA = *in; 204 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIRE_WRITE | I2C_STOP; 205 | i2cWaitBusy(regs); 206 | if(!I2C_GET_ACK(regs->REG_I2C_CNT)) // If ack flag is 0 it failed. 207 | { 208 | regs->REG_I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP; 209 | return false; 210 | } 211 | 212 | return true; 213 | } 214 | -------------------------------------------------------------------------------- /common/i2c.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of fastboot 3DS 3 | * Copyright (C) 2017 derrek, profi200 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | 25 | #define I2C_STOP (1u) 26 | #define I2C_START (1u<<1) 27 | #define I2C_ERROR (1u<<2) 28 | #define I2C_ACK (1u<<4) 29 | #define I2C_DIRE_WRITE (0u) 30 | #define I2C_DIRE_READ (1u<<5) 31 | #define I2C_IRQ_ENABLE (1u<<6) 32 | #define I2C_ENABLE (1u<<7) 33 | 34 | typedef enum 35 | { 36 | I2C_DEV_TWL_MCU = 0u, // DSi mode MCU 37 | I2C_DEV_CAMERA1 = 1u, // Internal self-facing camera 38 | I2C_DEV_CAMERA2 = 2u, // External right camera 39 | I2C_DEV_CTR_MCU = 3u, 40 | I2C_DEV_CAMERA3 = 4u, // External left camera 41 | I2C_DEV_LCD0 = 5u, // Upper LCD 42 | I2C_DEV_LCD1 = 6u, // Lower LCD 43 | I2C_DEV_UNK7 = 7u, // Debug? 44 | I2C_DEV_UNK8 = 8u, // Debug? 45 | I2C_DEV_UNK9 = 9u, // HID debug? 46 | I2C_DEV_GYRO_OLD = 10u, // Old 3DS only? 47 | I2C_DEV_GYRO_NEW = 11u, // New 3DS only? 48 | I2C_DEV_UNK12 = 12u, // HID "DebugPad"? 49 | I2C_DEV_IR = 13u, // Infrared (IrDA) 50 | I2C_DEV_EEPROM = 14u, // Dev unit only? 51 | I2C_DEV_NFC = 15u, 52 | I2C_DEV_QTM = 16u, // IO expander chip (New 3DS only) 53 | I2C_DEV_N3DS_HID = 17u // C-Stick and ZL/ZR buttons 54 | } I2cDevice; 55 | 56 | #define I2C_GET_ACK(reg) ((bool)((reg)>>4 & 1u)) 57 | 58 | 59 | /** 60 | * @brief Initializes the I2C buses. Call this only once. 61 | */ 62 | void I2C_init(void); 63 | 64 | /** 65 | * @brief Reads data from a I2C register to a buffer. 66 | * 67 | * @param[in] devId The device ID. Use the enum above. 68 | * @param[in] regAddr The register address. 69 | * @param out The output buffer pointer. 70 | * @param[in] size The read size. 71 | * 72 | * @return Returns true on success and false on failure. 73 | */ 74 | bool I2C_readRegBuf(int devId, u8 regAddr, u8 *out, u32 size); 75 | 76 | /** 77 | * @brief Writes a buffer to a I2C register. 78 | * 79 | * @param[in] devId The device ID. Use the enum above. 80 | * @param[in] regAddr The register address. 81 | * @param[in] in The input buffer pointer. 82 | * @param[in] size The write size. 83 | * 84 | * @return Returns true on success and false on failure. 85 | */ 86 | bool I2C_writeRegBuf(int devId, u8 regAddr, const u8 *in, u32 size); 87 | 88 | static inline u8 I2C_readReg(int devId, u8 regAddr) { 89 | u8 v; 90 | I2C_readRegBuf(devId, regAddr, &v, 1); 91 | return v; 92 | } 93 | 94 | static inline void I2C_writeReg(int devId, u8 regAddr, u8 v) { 95 | I2C_writeRegBuf(devId, regAddr, &v, 1); 96 | } 97 | -------------------------------------------------------------------------------- /common/memtests.c: -------------------------------------------------------------------------------- 1 | #include "memtests.h" 2 | 3 | #include "pxi.h" 4 | #include "arm.h" 5 | 6 | #ifdef ARM9 7 | #include 8 | #include 9 | #else 10 | #include 11 | #include 12 | 13 | // TODO: use the mpcore timer stuff for this on ARM11 14 | // ... and find a nice way to not have totally different bit fade code on ARM9 15 | #define getFullTimerTicks pxiGetTimerTicks 16 | 17 | #endif //ARM9 18 | 19 | #include 20 | 21 | extern MemtestError errors_buffer[MAX_ERRORS]; 22 | 23 | u32 memtest_walking_ones(u32 start_addr, u32 end_addr); 24 | u32 memtest_own_address(u32 start_addr, u32 end_addr); 25 | u32 memtest_moving_inversions_ones_zeros(u32 start_addr, u32 end_addr); 26 | u32 memtest_moving_inversions_8bit_walking(u32 start_addr, u32 end_addr); 27 | u32 memtest_bit_fade_check(u32 start_addr, u32 end_addr, u32 pattern); 28 | 29 | const MemestRegionInfo memtestRegions[] = { 30 | { ARM9MEM_BASE, ARM9MEM_SIZE, "ARM9 Internal Memory" }, 31 | { NEW_ARM9MEM_BASE, NEW_ARM9MEM_SIZE, "New3DS Extended ARM9 Memory" }, 32 | { DSPMEM_BASE, DSPMEM_SIZE, "DSP Memory" }, 33 | { AXIWRAM_BASE, AXIWRAM_SIZE, "AXI Work RAM" }, 34 | { FCRAM_BASE, FCRAM_SIZE, "FCRAM" }, 35 | { NEW_FCRAM_BASE, NEW_FCRAM_SIZE, "New3DS Extended FCRAM" }, 36 | }; 37 | 38 | const MemtestInfo memtests[] = { 39 | { memtest_walking_ones, "Walking Ones Test" }, 40 | { memtest_own_address, "Own Address Test" }, 41 | { memtest_moving_inversions_ones_zeros, "Moving Inversions Test, Ones & Zeros" }, 42 | { memtest_moving_inversions_8bit_walking, "Moving inversions Test, 8 Bit Patterns" }, 43 | { NULL, "Bit Fade Test" }, 44 | }; 45 | 46 | _Static_assert(N_MEMTEST_REGIONS == countof(memtestRegions)); 47 | _Static_assert(N_MEMTESTS == countof(memtests)); 48 | 49 | #ifndef ARM9 50 | static u64 __attribute__((section(".shared"))) errorsFromArm9; 51 | #endif 52 | 53 | static u64 totalErrors = 0; 54 | static u64 bitFadeTimers[N_MEMTEST_REGIONS] = { 0 }; 55 | 56 | static void logErrors(u32 nErrors) { 57 | for (u32 i = 0; i < min(nErrors, MAX_ERRORS); i++) { 58 | MemtestError* e = errors_buffer + i; 59 | logDebugPrintf(true, "%lx: %lx -> %lx\n", e->address, e->original, e->observed); 60 | } 61 | } 62 | 63 | static bool checkBitFade(u32 maxRegion) { 64 | bool ret = false; 65 | 66 | for (u32 regionIndex = 0; regionIndex < maxRegion; regionIndex++) { 67 | u64 timer = bitFadeTimers[regionIndex]; 68 | if (timer != 0) { 69 | if (timer < getFullTimerTicks()) { 70 | const u32 startAddr = memtestRegions[regionIndex].base, 71 | size = memtestRegions[regionIndex].size, 72 | endAddr = startAddr + size, 73 | pattern = (timer & 1) ? 0 : ~0; 74 | 75 | debugPrintf("Processing bit fade pattern %lx for %s...\n", pattern, memtestRegions[regionIndex].name); 76 | 77 | u32 nErrors = memtest_bit_fade_check(startAddr, endAddr, pattern); 78 | 79 | if (nErrors) { 80 | logErrors(nErrors); 81 | totalErrors += nErrors; 82 | } 83 | 84 | if (pattern == 0) { 85 | bfnMemset32(~0, (u32*) startAddr, size); 86 | bitFadeTimers[regionIndex] = (getFullTimerTicks() + BIT_FADE_DELAY) & ~1ULL; 87 | ret = true; 88 | } else { 89 | bitFadeTimers[regionIndex] = 0; 90 | } 91 | } else { 92 | ret = true; 93 | } 94 | } 95 | } 96 | 97 | return ret; 98 | } 99 | 100 | u64 testMemory(u32 regions, u32 tests, bool showFinal) { 101 | totalErrors = 0; 102 | 103 | #ifndef ARM9 104 | bool watingForArm9 = false; 105 | 106 | // If we have any ARM9-only regions, tell ARM9 to test them 107 | if (regions & MEMTEST_REGIONS_ARM9_ONLY) { 108 | errorsFromArm9 = ~0ULL; 109 | 110 | PXI_Send((3 << 16) | PXICMD_RUN_MEMTEST); 111 | PXI_Send((u32) &errorsFromArm9); 112 | PXI_Send(regions & MEMTEST_REGIONS_ARM9_ONLY); 113 | PXI_Send(tests); 114 | 115 | watingForArm9 = true; 116 | regions &= ~MEMTEST_REGIONS_ARM9_ONLY; 117 | } 118 | #endif 119 | 120 | for (u32 regionIndex = 0; regionIndex < N_MEMTEST_REGIONS; regionIndex++) { 121 | if (!(regions & BIT(regionIndex))) 122 | continue; 123 | 124 | if (IS_O3DS && (BIT(regionIndex) & MEMTEST_REGIONS_NEW3DS_ONLY)) { 125 | // TODO UI 126 | debugPrintf("Skipping %s\n", memtestRegions[regionIndex].name); 127 | 128 | continue; 129 | } 130 | 131 | // TODO: proper UI stuff 132 | debugPrintf("Testing %s ...\n", memtestRegions[regionIndex].name); 133 | 134 | const u32 startAddr = memtestRegions[regionIndex].base, 135 | size = memtestRegions[regionIndex].size, 136 | endAddr = startAddr + size; 137 | 138 | for (u32 testIndex = 0; testIndex < N_MEMTESTS; testIndex++) { 139 | if (!(tests & BIT(testIndex))) 140 | continue; 141 | 142 | // Check bit fade timers on previous regions 143 | checkBitFade(regionIndex); 144 | 145 | if (testIndex == MEMTEST_BIT_FADE) { 146 | debugPrintf("Setting up %s...\n", memtests[MEMTEST_BIT_FADE].name); 147 | bfnMemset32(0, (u32*) startAddr, size); 148 | bitFadeTimers[regionIndex] = (getFullTimerTicks() + BIT_FADE_DELAY) | 1ULL; 149 | break; 150 | } 151 | 152 | // TODO UI 153 | debugPrintf("Running %s...\n", memtests[testIndex].name); 154 | 155 | // TODO split this up to expose pattern etc 156 | u32 nErrors = memtests[testIndex].func(startAddr, endAddr); 157 | 158 | if (nErrors) { 159 | logErrors(nErrors); 160 | totalErrors += nErrors; 161 | } 162 | } 163 | 164 | debugPrint("\n"); 165 | } 166 | 167 | debugPrint("Waiting on remaining bit fades to finish ...\n"); 168 | while (checkBitFade(N_MEMTEST_REGIONS)) 169 | ARM_WFI(); 170 | 171 | #ifndef ARM9 172 | if (watingForArm9) { 173 | debugPrint("Waiting on ARM9-side tests to finish ...\n"); 174 | while (errorsFromArm9 == ~0ULL) 175 | ARM_WFI(); 176 | 177 | totalErrors += errorsFromArm9; 178 | watingForArm9 = false; 179 | } 180 | #endif 181 | 182 | if (showFinal) 183 | debugPrintf("Done. %llu total errors detected.\n", totalErrors); 184 | 185 | return totalErrors; 186 | } -------------------------------------------------------------------------------- /common/memtests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | // Must have enough space in BSS to store MAX_ERRORS * ERROR_STRUCT_SIZE (struct defined below) 6 | #define MAX_ERRORS 128 7 | #define ERROR_STRUCT_SIZE 12 8 | 9 | // 3 minutes 10 | #define BIT_FADE_DELAY (3ULL * 60ULL * (67027964ULL / 1024ULL)) 11 | 12 | #ifndef __ASSEMBLER__ 13 | 14 | enum { 15 | MEMTEST_REGION_ARM9MEM = 0, 16 | MEMTEST_REGION_NEW_ARM9MEM, 17 | MEMTEST_REGION_DSPMEM, 18 | MEMTEST_REGION_AXIWRAM, 19 | MEMTEST_REGION_FCRAM, 20 | MEMTEST_REGION_NEW_FCRAM, 21 | 22 | N_MEMTEST_REGIONS 23 | }; 24 | 25 | enum { 26 | MEMTEST_WALKING_ONES = 0, 27 | MEMTEST_OWN_ADDRESS, 28 | MEMTEST_MOV_INV_1_0, 29 | MEMTEST_MOV_INV_8_BIT, 30 | MEMTEST_BIT_FADE, 31 | 32 | N_MEMTESTS 33 | }; 34 | 35 | // Bit fade must be done last for each region so other regions may begin testing while waiting 36 | _Static_assert(MEMTEST_BIT_FADE + 1 == N_MEMTESTS); 37 | 38 | typedef struct { 39 | u32 base; 40 | u32 size; 41 | const char* name; 42 | } MemestRegionInfo; 43 | 44 | typedef struct { 45 | u32 (*func)(u32, u32); 46 | const char* name; 47 | } MemtestInfo; 48 | 49 | extern const MemestRegionInfo memtestRegions[]; 50 | extern const char* memtestNames[]; 51 | 52 | #define ALL_MEMTEST_REGIONS (BIT(N_MEMTEST_REGIONS) - 1) 53 | #define ALL_MEMTESTS (BIT(N_MEMTESTS) - 1) 54 | 55 | #define MEMTEST_REGIONS_NEW3DS_ONLY \ 56 | (BIT(MEMTEST_REGION_NEW_ARM9MEM) | BIT(MEMTEST_REGION_NEW_FCRAM)) 57 | #define MEMTEST_REGIONS_ARM9_ONLY \ 58 | (BIT(MEMTEST_REGION_ARM9MEM) | BIT(MEMTEST_REGION_NEW_ARM9MEM)) 59 | 60 | typedef struct { 61 | u32 address; 62 | u32 original; 63 | u32 observed; 64 | } PACKED_STRUCT MemtestError; 65 | _Static_assert(ERROR_STRUCT_SIZE == sizeof(MemtestError)); 66 | 67 | // Returns total errors detected 68 | u64 testMemory(u32 regions, u32 tests, bool showFinal); 69 | 70 | #endif //__ASSEMBLER__ 71 | -------------------------------------------------------------------------------- /common/memtests_asm.S: -------------------------------------------------------------------------------- 1 | #include "memtests.h" 2 | #include 3 | 4 | #define UNROLL_COUNT 4 5 | 6 | .section .text 7 | .arm 8 | 9 | memtest_fail_own_address_thunk: 10 | mov r3, r2 11 | memtest_fail: 12 | cmp r1, #MAX_ERRORS 13 | bhs memtest_fail_return 14 | 15 | push {r5,r6} 16 | mov r5, #ERROR_STRUCT_SIZE 17 | mov r6, r1 18 | mul r6, r5 19 | ldr r5, =errors_buffer 20 | add r5, r6 21 | stm r5!, {r2-r4} 22 | 23 | pop {r5,r6} 24 | memtest_fail_return: 25 | add r1, #1 26 | bx lr 27 | 28 | asm_func memtest_walking_ones 29 | push {r4-r7,lr} 30 | mov r12, r1 31 | mov r1, #0 32 | mov r7, r0 33 | @ Not sure of the specfic reasoning for this pattern, but it's what memtest86+ uses 34 | ldr r3, =0x5555AAAA 35 | walking_ones_outer_loop: 36 | str r3, [r7] 37 | mvn r5, r3 38 | 39 | mov r6, #4 40 | walking_ones_forward_loop: 41 | add r2, r7, r6 42 | cmp r2, r12 43 | bhs walking_ones_forward_break 44 | str r5, [r2] 45 | ldr r4, [r7] 46 | cmp r3, r4 47 | blne memtest_fail 48 | lsl r6, #1 49 | b walking_ones_forward_loop 50 | walking_ones_forward_break: 51 | 52 | mov r6, #4 53 | walking_ones_backward_loop: 54 | sub r2, r7, r6 55 | cmp r2, r0 56 | blo walking_ones_backward_break 57 | str r5, [r2] 58 | ldr r4, [r7] 59 | cmp r3, r4 60 | blne memtest_fail 61 | lsl r6, #1 62 | b walking_ones_backward_loop 63 | walking_ones_backward_break: 64 | 65 | @ Advance the initial position by 8KB. memtest86+ uses 1MB steps here, but some entire 66 | @ memory regions on the 3DS are smaller than that. 8KB is probably much smaller than 67 | @ this needs to be, but the test still finishes within seconds on FCRAM so it's fine. 68 | add r7, #(1<<13) 69 | cmp r7, r12 70 | blo walking_ones_outer_loop 71 | 72 | @ Rerun the entire test with the inverse of the initial pattern 73 | mov r7, r0 74 | mvns r3, r3 75 | bmi walking_ones_outer_loop 76 | 77 | mov r0, r1 78 | pop {r4-r7,pc} 79 | 80 | asm_func memtest_own_address 81 | push {r4,lr} 82 | mov r12, r1 83 | mov r2, r0 84 | 85 | own_address_fill_loop: 86 | .rept UNROLL_COUNT 87 | @ This gives an assembler warning which I have no idea how to suppress 88 | str r2, [r2], #4 89 | .endr 90 | cmp r2, r12 91 | blo own_address_fill_loop 92 | 93 | mov r2, r0 94 | mov r1, #0 95 | 96 | own_address_check_loop: 97 | .rept UNROLL_COUNT 98 | ldr r4, [r2] 99 | cmp r2, r4 100 | blne memtest_fail_own_address_thunk 101 | add r2, #4 102 | .endr 103 | cmp r2, r12 104 | blo own_address_check_loop 105 | 106 | mov r0, r1 107 | pop {r4,pc} 108 | 109 | moving_inversions_fill: 110 | mov r2, r0 111 | moving_inversions_fill_loop: 112 | .rept UNROLL_COUNT 113 | str r3, [r2], #4 114 | .endr 115 | cmp r2, r12 116 | blo moving_inversions_fill_loop 117 | bx lr 118 | 119 | moving_inversions_bottom_up: 120 | push {lr} 121 | mov r2, r0 122 | moving_inversions_bottom_up_loop: 123 | .rept UNROLL_COUNT 124 | ldr r4, [r2] 125 | cmp r3, r4 126 | blne memtest_fail 127 | str r5, [r2], #4 128 | .endr 129 | cmp r2, r12 130 | blo moving_inversions_bottom_up_loop 131 | pop {pc} 132 | 133 | moving_inversions_top_down: 134 | push {lr} 135 | mov r2, r12 136 | moving_inversions_top_down_loop: 137 | .rept UNROLL_COUNT 138 | sub r2, #4 139 | ldr r4, [r2] 140 | cmp r3, r4 141 | blne memtest_fail 142 | str r5, [r2] 143 | .endr 144 | cmp r2, r0 145 | bhi moving_inversions_top_down_loop 146 | pop {pc} 147 | 148 | asm_func memtest_moving_inversions_ones_zeros 149 | push {r4-r6,lr} 150 | mov r12, r1 151 | mov r1, #0 152 | mov r3, #0 153 | mvn r5, r3 154 | 155 | moving_inversions_ones_zeros_repeat: 156 | mov r6, #6 157 | bl moving_inversions_fill 158 | moving_inversions_ones_zeros_loop: 159 | bl moving_inversions_bottom_up 160 | mov r3, r5 161 | mvn r5, r3 162 | bl moving_inversions_top_down 163 | mov r3, r5 164 | mvn r5, r3 165 | subs r6, #1 166 | bne moving_inversions_ones_zeros_loop 167 | mov r3, r5 168 | mvns r5, r3 169 | bpl moving_inversions_ones_zeros_repeat 170 | 171 | mov r0, r1 172 | pop {r4-r6,pc} 173 | 174 | asm_func memtest_moving_inversions_8bit_walking 175 | push {r4-r7,lr} 176 | ldr r3, =0x01010101 177 | mov r12, r1 178 | mov r1, #0 179 | 180 | moving_inversions_8bit_walking_outer_loop: 181 | mov r7, #2 182 | mvn r5, r3 183 | 184 | moving_inversions_8bit_walking_repeat: 185 | @mov r6, #6 186 | mov r6, #3 187 | bl moving_inversions_fill 188 | moving_inversions_8bit_walking_loop: 189 | bl moving_inversions_bottom_up 190 | mov r3, r5 191 | mvn r5, r3 192 | bl moving_inversions_top_down 193 | mov r3, r5 194 | mvn r5, r3 195 | subs r6, #1 196 | bne moving_inversions_8bit_walking_loop 197 | mov r3, r5 198 | mvn r5, r3 199 | subs r7, #1 200 | bne moving_inversions_8bit_walking_repeat 201 | 202 | lsl r3, #1 203 | lsls r4, r3, #24 204 | bne moving_inversions_8bit_walking_outer_loop 205 | 206 | mov r0, r1 207 | pop {r4-r7,pc} 208 | 209 | asm_func memtest_bit_fade_check 210 | push {r4,lr} 211 | 212 | mov r3, r2 213 | mov r2, r0 214 | mov r12, r1 215 | mov r1, #0 216 | 217 | bit_fade_check_loop: 218 | .rept UNROLL_COUNT 219 | ldr r4, [r2] 220 | cmp r3, r4 221 | blne memtest_fail 222 | add r2, #4 223 | .endr 224 | cmp r2, r12 225 | blo bit_fade_check_loop 226 | 227 | mov r0, r1 228 | pop {r4,pc} 229 | 230 | .ltorg 231 | 232 | .section .bss 233 | 234 | .global errors_buffer 235 | errors_buffer: 236 | .space (MAX_ERRORS * ERROR_STRUCT_SIZE) 237 | -------------------------------------------------------------------------------- /common/power.c: -------------------------------------------------------------------------------- 1 | #include "power.h" 2 | #include "arm.h" 3 | #include "i2c.h" 4 | 5 | void mcuReboot() { 6 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x22, 1 << 0); // poweroff LCD to prevent MCU hangs 7 | ARM_WbDC(); 8 | ARM_DSB(); 9 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x20, 1 << 2); 10 | while(true); 11 | } 12 | 13 | void mcuPoweroff() 14 | { 15 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x22, 1 << 0); // poweroff LCD to prevent MCU hangs 16 | ARM_WbDC(); 17 | ARM_DSB(); 18 | I2C_writeReg(I2C_DEV_CTR_MCU, 0x20, 1 << 0); 19 | while(true); 20 | } 21 | -------------------------------------------------------------------------------- /common/power.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | void mcuReboot(); 6 | void mcuPoweroff(); 7 | -------------------------------------------------------------------------------- /common/pxi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 d0k3, Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | void PXI_Send64(u64 dw) 22 | { 23 | PXI_Send((u32) (dw & (u32) -1)); 24 | PXI_Send(dw >> 32); 25 | } 26 | 27 | u64 PXI_Recv64(void) 28 | { 29 | u64 ret = PXI_Recv(); 30 | return ret | (((u64) PXI_Recv()) << 32); 31 | } 32 | 33 | void PXI_Barrier(u8 barrier_id) 34 | { 35 | PXI_SetRemote(barrier_id); 36 | PXI_WaitRemote(barrier_id); 37 | } 38 | 39 | void PXI_Reset(void) 40 | { 41 | *PXI_SYNC_IRQ = 0; 42 | *PXI_CNT = PXI_CNT_SEND_FIFO_FLUSH | PXI_CNT_ENABLE_FIFO; 43 | for (int i = 0; i < PXI_FIFO_LEN; i++) 44 | *PXI_RECV; 45 | 46 | *PXI_CNT = 0; 47 | *PXI_CNT = PXI_CNT_RECV_FIFO_AVAIL_IRQ | PXI_CNT_ENABLE_FIFO | 48 | PXI_CNT_ACKNOWLEDGE_ERROR; 49 | 50 | PXI_SetRemote(0xFF); 51 | } 52 | 53 | void PXI_SendArray(const u32 *w, u32 c) 54 | { 55 | while(c--) 56 | PXI_Send(*(w++)); 57 | } 58 | 59 | void PXI_RecvArray(u32 *w, u32 c) 60 | { 61 | while(c--) 62 | *(w++) = PXI_Recv(); 63 | } 64 | 65 | /*u32 PXI_DoCMD(u32 cmd, const u32 *args, u32 argc) 66 | { 67 | PXI_Send((argc << 16) | cmd); 68 | PXI_SendArray(args, argc); 69 | return PXI_Recv(); 70 | }*/ 71 | -------------------------------------------------------------------------------- /common/pxi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2017-2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | #include "common.h" 21 | 22 | #ifdef ARM9 23 | #define PXI_BASE (0x10008000) 24 | #define PXI_SYNC_INTERRUPT (12) 25 | #define PXI_RX_INTERRUPT (14) 26 | #else 27 | #define PXI_BASE (0x10163000) 28 | #define PXI_SYNC_INTERRUPT (80) 29 | #define PXI_RX_INTERRUPT (83) 30 | #endif 31 | 32 | enum { 33 | PXICMD_LEGACY_BOOT = 0, 34 | 35 | PXICMD_RUN_MEMTEST, 36 | PXICMD_LOG_DATA, 37 | PXICMD_LOG_STRING, 38 | PXICMD_GET_TIMER_TICKS, 39 | 40 | PXICMD_NONE, 41 | }; 42 | 43 | /* 44 | * These should be somewhat "unusual" 45 | * IDs and shouldnt be similar to 46 | * those used by any other software 47 | */ 48 | enum { 49 | PXI_BOOT_BARRIER = 21, 50 | PXI_FIRMLAUNCH_BARRIER = 154, 51 | }; 52 | 53 | #define PXI_FIFO_LEN (16) 54 | //#define PXI_MAX_ARGS (32) 55 | 56 | #define PXI_SYNC_RECV ((vu8*)(PXI_BASE + 0x00)) 57 | #define PXI_SYNC_SEND ((vu8*)(PXI_BASE + 0x01)) 58 | #define PXI_SYNC_IRQ ((vu8*)(PXI_BASE + 0x03)) 59 | #define PXI_CNT ((vu16*)(PXI_BASE + 0x04)) 60 | #define PXI_SEND ((vu32*)(PXI_BASE + 0x08)) 61 | #define PXI_RECV ((vu32*)(PXI_BASE + 0x0C)) 62 | 63 | #define PXI_CNT_SEND_FIFO_EMPTY (BIT(0)) 64 | #define PXI_CNT_SEND_FIFO_FULL (BIT(1)) 65 | #define PXI_CNT_SEND_FIFO_EMPTY_IRQ (BIT(2)) 66 | #define PXI_CNT_SEND_FIFO_FLUSH (BIT(3)) 67 | #define PXI_CNT_RECV_FIFO_EMPTY (BIT(8)) 68 | #define PXI_CNT_RECV_FIFO_FULL (BIT(9)) 69 | #define PXI_CNT_RECV_FIFO_AVAIL_IRQ (BIT(10)) 70 | #define PXI_CNT_ACKNOWLEDGE_ERROR (BIT(14)) 71 | #define PXI_CNT_ENABLE_FIFO (BIT(15)) 72 | 73 | static inline void PXI_SetRemote(u8 msg) 74 | { 75 | *PXI_SYNC_SEND = msg; 76 | } 77 | 78 | static inline u8 PXI_GetRemote(void) 79 | { 80 | return *PXI_SYNC_RECV; 81 | } 82 | 83 | static inline void PXI_WaitRemote(u8 msg) 84 | { 85 | while(PXI_GetRemote() != msg); 86 | } 87 | 88 | static inline void PXI_Send(u32 w) 89 | { 90 | while(*PXI_CNT & PXI_CNT_SEND_FIFO_FULL); 91 | *PXI_SEND = w; 92 | } 93 | 94 | static inline u32 PXI_Recv(void) 95 | { 96 | while(*PXI_CNT & PXI_CNT_RECV_FIFO_EMPTY); 97 | return *PXI_RECV; 98 | } 99 | 100 | void PXI_Send64(u64 dw); 101 | u64 PXI_Recv64(void); 102 | 103 | void PXI_Barrier(u8 barrier_id); 104 | void PXI_Reset(void); 105 | 106 | void PXI_SendArray(const u32 *w, u32 c); 107 | void PXI_RecvArray(u32 *w, u32 c); 108 | 109 | //u32 PXI_DoCMD(u32 cmd, const u32 *args, u32 argc); 110 | -------------------------------------------------------------------------------- /common/screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define BYTES_PER_PIXEL 2 6 | #define SCREEN_HEIGHT 240 7 | #define SCREEN_WIDTH_TOP 400 8 | #define SCREEN_WIDTH_BOT 320 9 | 10 | _Static_assert(TOP_VRAM == SCREEN_HEIGHT * SCREEN_WIDTH_TOP * BYTES_PER_PIXEL); 11 | 12 | #define TOP_SCREEN ((u16*) VRAM_TOP_LA) 13 | #define BOT_SCREEN ((u16*) VRAM_BOT_A) 14 | 15 | #ifdef ARM9 16 | #define MAIN_SCREEN BOT_SCREEN 17 | #define MAIN_SCREEN_WIDTH SCREEN_WIDTH_BOT 18 | #else 19 | #define MAIN_SCREEN TOP_SCREEN 20 | #define MAIN_SCREEN_WIDTH SCREEN_WIDTH_TOP 21 | #endif 22 | 23 | #define RGB(r,g,b) ((u16)(r>>3<<11|g>>2<<5|b>>3)) // converts from 888 to 565 24 | 25 | #define COLOR_BLACK RGB(0x00, 0x00, 0x00) 26 | #define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF) 27 | #define COLOR_RED RGB(0xFF, 0x00, 0x00) 28 | #define COLOR_GREEN RGB(0x00, 0xFF, 0x00) 29 | #define COLOR_BLUE RGB(0x00, 0x00, 0xFF) 30 | #define COLOR_LIGHT_BLUE RGB(0x00, 0xA0, 0xE8) 31 | #define COLOR_CYAN RGB(0x00, 0xFF, 0xFF) 32 | #define COLOR_MAGENTA RGB(0xFF, 0x00, 0xFF) 33 | #define COLOR_YELLOW RGB(0xFF, 0xFF, 0x00) 34 | #define COLOR_GREY RGB(0x77, 0x77, 0x77) 35 | #define COLOR_LIGHT_GREY RGB(0xBB, 0xBB, 0xBB) 36 | //#define COLOR_TRANSPARENT RGB(0xFF, 0x00, 0xEF) // otherwise known as 'super fuchsia' -------------------------------------------------------------------------------- /common/spi.c: -------------------------------------------------------------------------------- 1 | // Somewhat based on xerpi's SPI driver for Linux 2 | /* 3 | * This file is part of GodMode9 4 | * Copyright (C) 2016 Sergi Granell 5 | * Copyright (C) 2019 Wolfvak 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "spi.h" 24 | 25 | #define REG_CFG_SPI_CNT ((vu16*)0x101401C0) 26 | 27 | #define REG_SPI(bus, reg) (*((vu32*)((bus) + (reg)))) 28 | 29 | #define REG_SPI_BUS0 (0x10160800) 30 | #define REG_SPI_BUS1 (0x10142800) 31 | #define REG_SPI_BUS2 (0x10143800) 32 | #define REG_SPI_CARD (0x1000D800) 33 | 34 | #define REG_SPI_CONTROL 0x00 35 | #define REG_SPI_DONE 0x04 36 | #define REG_SPI_BLKLEN 0x08 37 | #define REG_SPI_FIFO 0x0C 38 | #define REG_SPI_STAT 0x10 39 | 40 | #define SPI_CONTROL_RATE(n) (n) 41 | #define SPI_CONTROL_CS(n) ((n) << 6) 42 | #define SPI_DIRECTION_READ (0) 43 | #define SPI_DIRECTION_WRITE BIT(13) 44 | #define SPI_CONTROL_BUSY BIT(15) 45 | #define SPI_CONTROL_START BIT(15) 46 | 47 | #define SPI_STAT_BUSY BIT(0) 48 | 49 | #define SPI_FIFO_WIDTH (32) 50 | 51 | static struct { 52 | u32 bus; 53 | u32 reg; 54 | } SPI_Devices[] = { 55 | {REG_SPI_BUS0, SPI_CONTROL_RATE(2) | SPI_CONTROL_CS(0)}, 56 | {REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(1)}, // NVRAM 57 | {REG_SPI_BUS0, SPI_CONTROL_RATE(0) | SPI_CONTROL_CS(2)}, 58 | {REG_SPI_BUS1, SPI_CONTROL_RATE(5) | SPI_CONTROL_CS(0)}, // CODEC 59 | {REG_SPI_CARD, SPI_CONTROL_RATE(3) | SPI_CONTROL_CS(0)}, // Gamecard flash chip 60 | {REG_SPI_CARD, SPI_CONTROL_RATE(1) | SPI_CONTROL_CS(0)}, // Gamecard IR chip 61 | // TODO: complete this table 62 | }; 63 | 64 | static void SPI_WaitBusy(u32 bus) 65 | { 66 | while(REG_SPI(bus, REG_SPI_CONTROL) & SPI_CONTROL_BUSY); 67 | } 68 | 69 | static void SPI_WaitFIFO(u32 bus) 70 | { 71 | while(REG_SPI(bus, REG_SPI_STAT) & SPI_STAT_BUSY); 72 | } 73 | 74 | static void SPI_Done(u32 bus) 75 | { 76 | REG_SPI(bus, REG_SPI_DONE) = 0; 77 | } 78 | 79 | static void SPI_SingleXfer(u32 reg, u32 bus, void *buffer, u32 len, bool read) 80 | { 81 | u32 pos = 0; 82 | bool aligned = ((u32)buffer % 4 == 0) && (len % 4 == 0); 83 | 84 | REG_SPI(bus, REG_SPI_BLKLEN) = len; 85 | REG_SPI(bus, REG_SPI_CONTROL) = reg | 86 | (read ? SPI_DIRECTION_READ : SPI_DIRECTION_WRITE) | SPI_CONTROL_START; 87 | 88 | SPI_WaitFIFO(bus); 89 | 90 | do { 91 | if ((pos % SPI_FIFO_WIDTH) == 0) 92 | SPI_WaitFIFO(bus); 93 | 94 | if(aligned) { 95 | if (read) { 96 | ((u32*)buffer)[pos / 4] = REG_SPI(bus, REG_SPI_FIFO); 97 | } else { 98 | REG_SPI(bus, REG_SPI_FIFO) = ((u32*)buffer)[pos / 4]; 99 | } 100 | } else { 101 | if (read) { 102 | u32 tmp = REG_SPI(bus, REG_SPI_FIFO); 103 | memcpy((u8 *) buffer + pos, &tmp, min(4, len - pos)); 104 | } else { 105 | u32 tmp = 0; 106 | memcpy(&tmp, (u8 *) buffer + pos, min(4, len - pos)); 107 | REG_SPI(bus, REG_SPI_FIFO) = tmp; 108 | } 109 | } 110 | 111 | pos += 4; 112 | } while(pos < len); 113 | } 114 | 115 | int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfers, u32 xfer_cnt, bool done) 116 | { 117 | u32 bus, reg; 118 | 119 | bus = SPI_Devices[dev].bus; 120 | reg = SPI_Devices[dev].reg; 121 | 122 | for (u32 i = 0; i < xfer_cnt; i++) { 123 | const SPI_XferInfo *xfer = &xfers[i]; 124 | 125 | if (!xfer->buf || !xfer->len) 126 | continue; 127 | 128 | SPI_WaitBusy(bus); 129 | SPI_SingleXfer(reg, bus, xfer->buf, xfer->len, xfer->read); 130 | } 131 | 132 | SPI_WaitBusy(bus); 133 | if(done) { 134 | SPI_Done(bus); 135 | } 136 | return 0; 137 | } 138 | 139 | void SPI_Init(void) 140 | { 141 | // This cuts off access to the old NDS SPI interface 142 | *REG_CFG_SPI_CNT = 7; 143 | } 144 | 145 | void SPI_Deinit(void) 146 | { 147 | // Keep backwards compatibility with software that 148 | // assumes all bus interfaces will be set to old 149 | *REG_CFG_SPI_CNT = 0; 150 | } 151 | -------------------------------------------------------------------------------- /common/spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of GodMode9 3 | * Copyright (C) 2019 Wolfvak 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #define SPI_DEV_NVRAM 1 24 | #define SPI_DEV_CODEC 3 25 | #define SPI_DEV_CART_FLASH 4 26 | #define SPI_DEV_CART_IR 5 27 | 28 | typedef struct { 29 | void *buf; 30 | u32 len; 31 | bool read; 32 | } SPI_XferInfo; 33 | 34 | int SPI_DoXfer(u32 dev, const SPI_XferInfo *xfer, u32 xfer_cnt, bool done); 35 | 36 | void SPI_Init(void); 37 | void SPI_Deinit(void); 38 | -------------------------------------------------------------------------------- /common/vram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TOP_VRAM (400*240*2) 4 | #define BOTTOM_VRAM (320*240*2) 5 | 6 | #define VRAM_START (0x18000000) 7 | 8 | #define VRAM_TOP_LA (VRAM_START) 9 | #define VRAM_BOT_A (VRAM_TOP_LA + TOP_VRAM) 10 | 11 | #define VRAM_END (VRAM_BOT_A + BOTTOM_VRAM) 12 | --------------------------------------------------------------------------------