├── .gitignore ├── Makefile ├── arm9code └── mpu_reset.s ├── load.ld └── source ├── arm9clear.arm.c ├── arm9mpu_reset.s ├── boot.c ├── boot.h ├── io_dldi.h ├── io_dldi.s ├── load_crt0.s ├── minifat.c ├── minifat.h ├── sdmmc.c └── sdmmc.h /.gitignore: -------------------------------------------------------------------------------- 1 | *build 2 | load.bin 3 | load.elf 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 6 | endif 7 | 8 | -include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | #--------------------------------------------------------------------------------- 15 | TARGET := load 16 | BUILD ?= build 17 | SOURCES := source source/patches 18 | INCLUDES := build 19 | SPECS := specs 20 | 21 | 22 | #--------------------------------------------------------------------------------- 23 | # options for code generation 24 | #--------------------------------------------------------------------------------- 25 | ARCH := -mthumb -mthumb-interwork -march=armv4t -mtune=arm7tdmi 26 | 27 | CFLAGS := -g -Wall -Os \ 28 | -ffunction-sections -fdata-sections \ 29 | $(ARCH) 30 | 31 | CFLAGS += $(INCLUDE) $(EXTRA_CFLAGS) -DARM7 32 | 33 | ASFLAGS := -g $(ARCH) $(EXTRA_CFLAGS) $(INCLUDE) 34 | LDFLAGS = -nostartfiles -T $(TOPDIR)/load.ld -Wl,--no-warn-rwx-segments,--nmagic,--gc-sections -g $(ARCH) -Wl,-Map,$(TARGET).map 35 | 36 | LIBS := -lnds7 -lcalico_ds7 37 | 38 | #--------------------------------------------------------------------------------- 39 | # list of directories containing libraries, this must be the top level containing 40 | # include and lib 41 | #--------------------------------------------------------------------------------- 42 | LIBDIRS := $(LIBNDS) 43 | 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export TOPDIR := $(CURDIR) 53 | export LOADBIN ?= $(CURDIR)/$(TARGET).bin 54 | export LOADELF := $(CURDIR)/$(TARGET).elf 55 | export DEPSDIR := $(CURDIR)/$(BUILD) 56 | 57 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) 58 | 59 | export CC := $(PREFIX)gcc 60 | export CXX := $(PREFIX)g++ 61 | export AR := $(PREFIX)ar 62 | export OBJCOPY := $(PREFIX)objcopy 63 | 64 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 65 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 66 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 67 | 68 | export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 69 | 70 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 72 | -I$(CURDIR)/$(BUILD) 73 | 74 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 75 | 76 | #--------------------------------------------------------------------------------- 77 | # use CC for linking standard C 78 | #--------------------------------------------------------------------------------- 79 | export LD := $(CC) 80 | #--------------------------------------------------------------------------------- 81 | 82 | .PHONY: $(BUILD) clean 83 | 84 | #--------------------------------------------------------------------------------- 85 | $(BUILD): 86 | @[ -d $@ ] || mkdir -p $@ 87 | @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile 88 | 89 | #--------------------------------------------------------------------------------- 90 | clean: 91 | @echo clean ... 92 | @rm -fr $(BUILD) *.elf *.bin 93 | 94 | 95 | #--------------------------------------------------------------------------------- 96 | else 97 | 98 | DEPENDS := $(OFILES:.o=.d) 99 | 100 | #--------------------------------------------------------------------------------- 101 | # main targets 102 | #--------------------------------------------------------------------------------- 103 | $(LOADBIN) : $(LOADELF) 104 | @$(OBJCOPY) -O binary $< $@ 105 | @echo built ... $(notdir $@) 106 | 107 | 108 | $(LOADELF) : $(OFILES) 109 | @echo linking $(notdir $@) 110 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 111 | 112 | arm9mpu_reset.o: mpu_reset.bin 113 | 114 | mpu_reset.bin: mpu_reset.elf 115 | $(OBJCOPY) -O binary $< $@ 116 | 117 | mpu_reset.elf: $(TOPDIR)/arm9code/mpu_reset.s 118 | $(CC) $(ASFLAGS) -Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $< -o $@ 119 | 120 | -include $(DEPENDS) 121 | #--------------------------------------------------------------------------------------- 122 | endif 123 | #--------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /arm9code/mpu_reset.s: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2006 - 2015 Dave Murphy (WinterMute) 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | */ 18 | 19 | #include 20 | 21 | .text 22 | .align 4 23 | 24 | .arm 25 | 26 | .arch armv5te 27 | .cpu arm946e-s 28 | 29 | @--------------------------------------------------------------------------------- 30 | .global _start 31 | .type _start STT_FUNC 32 | @--------------------------------------------------------------------------------- 33 | _start: 34 | @--------------------------------------------------------------------------------- 35 | @ Switch off MPU 36 | mrc p15, 0, r0, c1, c0, 0 37 | bic r0, r0, #PROTECT_ENABLE 38 | mcr p15, 0, r0, c1, c0, 0 39 | 40 | 41 | adr r12, mpu_initial_data 42 | ldmia r12, {r0-r10} 43 | 44 | mcr p15, 0, r0, c2, c0, 0 45 | mcr p15, 0, r0, c2, c0, 1 46 | mcr p15, 0, r1, c3, c0, 0 47 | mcr p15, 0, r2, c5, c0, 2 48 | mcr p15, 0, r3, c5, c0, 3 49 | mcr p15, 0, r4, c6, c0, 0 50 | mcr p15, 0, r5, c6, c1, 0 51 | mcr p15, 0, r6, c6, c3, 0 52 | mcr p15, 0, r7, c6, c4, 0 53 | mcr p15, 0, r8, c6, c6, 0 54 | mcr p15, 0, r9, c6, c7, 0 55 | mcr p15, 0, r10, c9, c1, 0 56 | 57 | mov r0, #0 58 | mcr p15, 0, r0, c6, c2, 0 @ PU Protection Unit Data/Unified Region 2 59 | mcr p15, 0, r0, c6, c5, 0 @ PU Protection Unit Data/Unified Region 5 60 | 61 | mrc p15, 0, r0, c9, c1, 0 @ DTCM 62 | mov r0, r0, lsr #12 @ base 63 | mov r0, r0, lsl #12 @ size 64 | add r0, r0, #0x4000 @ dtcm top 65 | 66 | sub r0, r0, #4 @ irq vector 67 | mov r1, #0 68 | str r1, [r0] 69 | sub r0, r0, #4 @ IRQ1 Check Bits 70 | str r1, [r0] 71 | 72 | sub r0, r0, #128 73 | bic r0, r0, #7 74 | 75 | msr cpsr_c, #0xd3 @ svc mode 76 | mov sp, r0 77 | sub r0, r0, #128 78 | msr cpsr_c, #0xd2 @ irq mode 79 | mov sp, r0 80 | sub r0, r0, #128 81 | msr cpsr_c, #0xdf @ system mode 82 | mov sp, r0 83 | 84 | @ enable cache & tcm 85 | mrc p15, 0, r0, c1, c0, 0 86 | ldr r1,= ITCM_ENABLE | DTCM_ENABLE | ICACHE_ENABLE | DCACHE_ENABLE 87 | orr r0,r0,r1 88 | mcr p15, 0, r0, c1, c0, 0 89 | 90 | ldr r10, =0x2FFFE04 91 | ldr r0, =0xE59FF018 92 | str r0, [r10] 93 | add r1, r10, #0x20 94 | str r10, [r1] 95 | bx r10 96 | 97 | .pool 98 | 99 | mpu_initial_data: 100 | .word 0x00000042 @ p15,0,c2,c0,0..1,r0 ;PU Cachability Bits for Data/Unified+Instruction Protection Region 101 | .word 0x00000002 @ p15,0,c3,c0,0,r1 ;PU Write-Bufferability Bits for Data Protection Regions 102 | .word 0x15111011 @ p15,0,c5,c0,2,r2 ;PU Extended Access Permission Data/Unified Protection Region 103 | .word 0x05100011 @ p15,0,c5,c0,3,r3 ;PU Extended Access Permission Instruction Protection Region 104 | .word 0x04000033 @ p15,0,c6,c0,0,r4 ;PU Protection Unit Data/Unified Region 0 105 | .word 0x0200002b @ p15,0,c6,c1,0,r5 ;PU Protection Unit Data/Unified Region 1 4MB 106 | .word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3 107 | .word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4 108 | .word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6 109 | .word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB 110 | .word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size 111 | -------------------------------------------------------------------------------- /load.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | MEMORY { 6 | wram : ORIGIN = 0x037f8000, LENGTH = 0x17000 7 | vram : ORIGIN = 0x06000000, LENGTH = 128K 8 | } 9 | 10 | PROVIDE_HIDDEN( __sys_start = ORIGIN(wram) + LENGTH(wram) ); 11 | PROVIDE_HIDDEN( __sp_usr = __sys_start + 0xe80 ); 12 | PROVIDE_HIDDEN( __sp_irq = __sp_usr + 0x100 ); 13 | PROVIDE_HIDDEN( __sp_svc = __sp_irq + 0x40 ); 14 | PROVIDE_HIDDEN( __irq_flags2 = __sp_svc + 0x00 ); 15 | PROVIDE_HIDDEN( __scfg_buf = __sp_svc + 0x04 ); 16 | PROVIDE_HIDDEN( __dma_fill = __sp_svc + 0x20 ); 17 | PROVIDE_HIDDEN( __irq_flags = __sp_svc + 0x38 ); 18 | PROVIDE_HIDDEN( __irq_vector = __sp_svc + 0x3c ); 19 | 20 | SECTIONS 21 | { 22 | .init : 23 | { 24 | __text_start = . ; 25 | KEEP (*(.init)) 26 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 27 | } >vram = 0xff 28 | 29 | .plt : 30 | { 31 | *(.plt) 32 | } >vram = 0xff 33 | 34 | .text : /* ALIGN (4): */ 35 | { 36 | 37 | *(.text*) 38 | *(.stub) 39 | /* .gnu.warning sections are handled specially by elf32.em. */ 40 | *(.gnu.warning) 41 | *(.gnu.linkonce.t*) 42 | *(.glue_7) 43 | *(.glue_7t) 44 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 45 | } >vram = 0xff 46 | 47 | .fini : 48 | { 49 | KEEP (*(.fini)) 50 | } >vram =0xff 51 | 52 | __text_end = . ; 53 | 54 | .rodata : 55 | { 56 | *(.rodata) 57 | *all.rodata*(*) 58 | *(.roda) 59 | *(.rodata.*) 60 | *(.gnu.linkonce.r*) 61 | SORT(CONSTRUCTORS) 62 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 63 | } >vram = 0xff 64 | 65 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >vram 66 | __exidx_start = .; 67 | .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >vram 68 | __exidx_end = .; 69 | 70 | /* Ensure the __preinit_array_start label is properly aligned. We 71 | could instead move the label definition inside the section, but 72 | the linker would then create the section even if it turns out to 73 | be empty, which isn't pretty. */ 74 | . = ALIGN(32 / 8); 75 | PROVIDE (__preinit_array_start = .); 76 | .preinit_array : { KEEP (*(.preinit_array)) } >vram = 0xff 77 | PROVIDE (__preinit_array_end = .); 78 | PROVIDE (__init_array_start = .); 79 | .init_array : { KEEP (*(.init_array)) } >vram = 0xff 80 | PROVIDE (__init_array_end = .); 81 | PROVIDE (__fini_array_start = .); 82 | .fini_array : { KEEP (*(.fini_array)) } >vram = 0xff 83 | PROVIDE (__fini_array_end = .); 84 | 85 | .ctors : 86 | { 87 | /* gcc uses crtbegin.o to find the start of the constructors, so 88 | we make sure it is first. Because this is a wildcard, it 89 | doesn't matter if the user does not actually link against 90 | crtbegin.o; the linker won't look for a file to match a 91 | wildcard. The wildcard also means that it doesn't matter which 92 | directory crtbegin.o is in. */ 93 | KEEP (*crtbegin.o(.ctors)) 94 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 95 | KEEP (*(SORT(.ctors.*))) 96 | KEEP (*(.ctors)) 97 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 98 | } >vram = 0xff 99 | 100 | .dtors : 101 | { 102 | KEEP (*crtbegin.o(.dtors)) 103 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 104 | KEEP (*(SORT(.dtors.*))) 105 | KEEP (*(.dtors)) 106 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 107 | } >vram = 0xff 108 | 109 | .eh_frame : 110 | { 111 | KEEP (*(.eh_frame)) 112 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 113 | } >vram = 0xff 114 | 115 | .gcc_except_table : 116 | { 117 | *(.gcc_except_table) 118 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 119 | } >vram = 0xff 120 | .jcr : { KEEP (*(.jcr)) } >vram = 0 121 | .got : { *(.got.plt) *(.got) } >vram = 0 122 | 123 | 124 | .vram ALIGN(4) : 125 | { 126 | __vram_start = ABSOLUTE(.) ; 127 | *(.vram) 128 | *vram.*(.text) 129 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 130 | __vram_end = ABSOLUTE(.) ; 131 | } >vram = 0xff 132 | 133 | 134 | .data ALIGN(4) : { 135 | __data_start = ABSOLUTE(.); 136 | *(.data) 137 | *(.data.*) 138 | *(.gnu.linkonce.d*) 139 | CONSTRUCTORS 140 | . = ALIGN(4); 141 | __data_end = ABSOLUTE(.) ; 142 | } >vram = 0xff 143 | 144 | 145 | 146 | .bss ALIGN(4) : 147 | { 148 | __bss_start = ABSOLUTE(.); 149 | __bss_start__ = ABSOLUTE(.); 150 | *(.dynbss) 151 | *(.gnu.linkonce.b*) 152 | *(.bss*) 153 | *(COMMON) 154 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 155 | } >vram 156 | 157 | __bss_end = . ; 158 | __bss_end__ = . ; 159 | 160 | _end = . ; 161 | __end__ = . ; 162 | PROVIDE (end = _end); 163 | 164 | /* Stabs debugging sections. */ 165 | .stab 0 : { *(.stab) } 166 | .stabstr 0 : { *(.stabstr) } 167 | .stab.excl 0 : { *(.stab.excl) } 168 | .stab.exclstr 0 : { *(.stab.exclstr) } 169 | .stab.index 0 : { *(.stab.index) } 170 | .stab.indexstr 0 : { *(.stab.indexstr) } 171 | .comment 0 : { *(.comment) } 172 | /* DWARF debug sections. 173 | Symbols in the DWARF debugging sections are relative to the beginning 174 | of the section so we begin them at 0. */ 175 | /* DWARF 1 */ 176 | .debug 0 : { *(.debug) } 177 | .line 0 : { *(.line) } 178 | /* GNU DWARF 1 extensions */ 179 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 180 | .debug_sfnames 0 : { *(.debug_sfnames) } 181 | /* DWARF 1.1 and DWARF 2 */ 182 | .debug_aranges 0 : { *(.debug_aranges) } 183 | .debug_pubnames 0 : { *(.debug_pubnames) } 184 | /* DWARF 2 */ 185 | .debug_info 0 : { *(.debug_info) } 186 | .debug_abbrev 0 : { *(.debug_abbrev) } 187 | .debug_line 0 : { *(.debug_line) } 188 | .debug_frame 0 : { *(.debug_frame) } 189 | .debug_str 0 : { *(.debug_str) } 190 | .debug_loc 0 : { *(.debug_loc) } 191 | .debug_macinfo 0 : { *(.debug_macinfo) } 192 | /* SGI/MIPS DWARF 2 extensions */ 193 | .debug_weaknames 0 : { *(.debug_weaknames) } 194 | .debug_funcnames 0 : { *(.debug_funcnames) } 195 | .debug_typenames 0 : { *(.debug_typenames) } 196 | .debug_varnames 0 : { *(.debug_varnames) } 197 | .stack 0x80000 : { _stack = .; *(.stack) } 198 | /* These must appear regardless of . */ 199 | } 200 | -------------------------------------------------------------------------------- /source/arm9clear.arm.c: -------------------------------------------------------------------------------- 1 | #define ARM9 2 | #undef ARM7 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "boot.h" 15 | 16 | /*------------------------------------------------------------------------- 17 | resetMemory2_ARM9 18 | Clears the ARM9's DMA channels and resets video memory 19 | Written by Darkain. 20 | Modified by Chishm: 21 | * Changed MultiNDS specific stuff 22 | --------------------------------------------------------------------------*/ 23 | void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9 (void) 24 | { 25 | register int i; 26 | 27 | //clear out ARM9 DMA channels 28 | for (i=0; i<4; i++) { 29 | DMA_CR(i) = 0; 30 | DMA_SRC(i) = 0; 31 | DMA_DEST(i) = 0; 32 | TIMER_CR(i) = 0; 33 | TIMER_DATA(i) = 0; 34 | } 35 | 36 | VRAM_CR = (VRAM_CR & 0xffff0000) | 0x00008080 ; 37 | 38 | vu16 *mainregs = (vu16*)0x04000000; 39 | vu16 *subregs = (vu16*)0x04001000; 40 | 41 | for (i=0; i<43; i++) { 42 | mainregs[i] = 0; 43 | subregs[i] = 0; 44 | } 45 | 46 | REG_DISPSTAT = 0; 47 | 48 | VRAM_A_CR = 0; 49 | VRAM_B_CR = 0; 50 | // Don't mess with the ARM7's VRAM 51 | // VRAM_C_CR = 0; 52 | VRAM_D_CR = 0; 53 | VRAM_E_CR = 0; 54 | VRAM_F_CR = 0; 55 | VRAM_G_CR = 0; 56 | VRAM_H_CR = 0; 57 | VRAM_I_CR = 0; 58 | REG_POWERCNT = 0x820F; 59 | 60 | //set shared ram to ARM7 61 | WRAM_CR = 0x03; 62 | 63 | // Return to passme loop 64 | *((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24 65 | *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address 66 | 67 | asm volatile( 68 | "\tbx %0\n" 69 | : : "r" (0x02FFFE04) 70 | ); 71 | while(1); 72 | } 73 | 74 | /*------------------------------------------------------------------------- 75 | startBinary_ARM9 76 | Jumps to the ARM9 NDS binary in sync with the display and ARM7 77 | Written by Darkain. 78 | Modified by Chishm: 79 | * Removed MultiNDS specific stuff 80 | --------------------------------------------------------------------------*/ 81 | void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (void) 82 | { 83 | REG_IME=0; 84 | REG_EXMEMCNT = 0xE880; 85 | // set ARM9 load address to 0 and wait for it to change again 86 | ARM9_START_FLAG = 0; 87 | while(REG_VCOUNT!=191); 88 | while(REG_VCOUNT==191); 89 | while ( ARM9_START_FLAG != 1 ); 90 | VoidFn arm9code = *(VoidFn*)(0x2FFFE24); 91 | arm9code(); 92 | while(1); 93 | } 94 | 95 | -------------------------------------------------------------------------------- /source/arm9mpu_reset.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .global mpu_reset, mpu_reset_end 3 | 4 | mpu_reset: 5 | .incbin "mpu_reset.bin" 6 | mpu_reset_end: 7 | -------------------------------------------------------------------------------- /source/boot.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | boot.c 3 | 4 | BootLoader 5 | Loads a file into memory and runs it 6 | 7 | All resetMemory and startBinary functions are based 8 | on the MultiNDS loader by Darkain. 9 | Original source available at: 10 | http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp 11 | 12 | License: 13 | Copyright (C) 2005 Michael "Chishm" Chisholm 14 | 15 | This program is free software; you can redistribute it and/or 16 | modify it under the terms of the GNU General Public License 17 | as published by the Free Software Foundation; either version 2 18 | of the License, or (at your option) any later version. 19 | 20 | This program is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | 25 | You should have received a copy of the GNU General Public License 26 | along with this program; if not, write to the Free Software 27 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 28 | 29 | If you use this code, please give due credit and email me about your 30 | project at chishm@hotmail.com 31 | 32 | Helpful information: 33 | This code runs from VRAM bank C on ARM7 34 | ------------------------------------------------------------------*/ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "boot.h" 46 | #include "io_dldi.h" 47 | #include "sdmmc.h" 48 | #include "minifat.h" 49 | 50 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 51 | // Important things 52 | #define TEMP_MEM MM_ENV_FREE_D000 53 | #define ARM9_START_ADDRESS (*(vu32*)&g_envAppNdsHeader->arm9_entrypoint) 54 | #define DEFAULT_BOOT_NAME "BOOT.NDS" 55 | 56 | extern volatile uptr __irq_vector; 57 | 58 | extern unsigned long _start; 59 | extern unsigned long storedFileCluster; 60 | extern unsigned long wantToPatchDLDI; 61 | extern unsigned long argStart; 62 | extern unsigned long argSize; 63 | extern unsigned long dsiSD; 64 | extern unsigned long dsiMode; 65 | 66 | static uptr temp_arm9_start_address; 67 | 68 | static char boot_nds[] = "fat:/boot.nds"; 69 | static unsigned long argbuf[4]; 70 | 71 | static MiniFat fatState; 72 | 73 | /*------------------------------------------------------------------------- 74 | passArgs_ARM7 75 | Copies the command line arguments to the end of the ARM9 binary, 76 | then sets a flag in memory for the loaded NDS to use 77 | --------------------------------------------------------------------------*/ 78 | static void passArgs_ARM7 (void) { 79 | void* argSrc; 80 | void* argDst; 81 | 82 | if (!argStart || !argSize) { 83 | char *arg = boot_nds; 84 | argSize = __builtin_strlen(boot_nds); 85 | 86 | if (dsiSD) { 87 | arg++; 88 | arg[0] = 's'; 89 | arg[1] = 'd'; 90 | } 91 | __builtin_memcpy(argbuf,arg,argSize+1); 92 | argSrc = argbuf; 93 | } else { 94 | argSrc = (void*)(argStart + (uptr)&_start); 95 | } 96 | 97 | argDst = (void*)((g_envAppNdsHeader->arm9_ram_address + g_envAppNdsHeader->arm9_size + 3) & ~3); // Word aligned 98 | 99 | if (dsiMode && (g_envAppNdsHeader->unitcode & BIT(1)) && g_envAppTwlHeader->arm9i_size) 100 | { 101 | void* argDst2 = (void*)((g_envAppTwlHeader->arm9i_ram_address + g_envAppTwlHeader->arm9i_size + 3) & ~3); // Word aligned 102 | if (argDst2 > argDst) 103 | argDst = argDst2; 104 | } 105 | 106 | armCopyMem32(argDst, argSrc, (argSize + 3) &~ 3); 107 | 108 | g_envNdsArgvHeader->magic = ENV_NDS_ARGV_MAGIC; 109 | g_envNdsArgvHeader->args_str = argDst; 110 | g_envNdsArgvHeader->args_str_size = argSize; 111 | } 112 | 113 | /*------------------------------------------------------------------------- 114 | resetMemory_ARM7 115 | Clears all of the NDS's RAM that is visible to the ARM7 116 | Written by Darkain. 117 | Modified by Chishm: 118 | * Added STMIA clear mem loop 119 | --------------------------------------------------------------------------*/ 120 | static void resetMemory_ARM7 (void) 121 | { 122 | // Reset the interrupt controller & related BIOS variables 123 | REG_IME = 0; 124 | REG_IE = 0; 125 | REG_IF = ~0; 126 | __irq_vector = 0; 127 | __irq_flags = ~0; 128 | __irq_flags2 = ~0; 129 | 130 | REG_POWCNT = POWCNT_SOUND; //turn off power to stuff 131 | 132 | for (unsigned i=0; i<16; i++) { 133 | SCHANNEL_CR(i) = 0; 134 | SCHANNEL_TIMER(i) = 0; 135 | SCHANNEL_SOURCE(i) = 0; 136 | SCHANNEL_LENGTH(i) = 0; 137 | } 138 | 139 | REG_SOUNDCNT = 0; 140 | 141 | //clear out ARM7 DMA channels and timers 142 | for (unsigned i=0; i<4; i++) { 143 | DMA_CR(i) = 0; 144 | DMA_SRC(i) = 0; 145 | DMA_DEST(i) = 0; 146 | TIMER_CR(i) = 0; 147 | TIMER_DATA(i) = 0; 148 | } 149 | 150 | // Clear most of ARM7 exclusive WRAM 151 | extern char __sys_start[]; 152 | armFillMem32((void*)MM_A7WRAM, 0, __sys_start - (char*)MM_A7WRAM); 153 | 154 | // Clear most of main RAM 155 | uptr main_ram_clr_begin = dsiMode ? MM_ENV_TWL_AUTOLOAD_EXT : MM_MAINRAM; 156 | uptr main_ram_clr_end = MM_ENV_HB_BOOTSTUB - (dsiMode ? 0 : (MM_MAINRAM_SZ_TWL-MM_MAINRAM_SZ_NTR)); 157 | armFillMem32((void*)main_ram_clr_begin, 0, main_ram_clr_end-main_ram_clr_begin); 158 | 159 | // Repair ARM7 mirror of SCFG regs if they have been previously cleared out 160 | if (dsiMode && !__scfg_buf.ext && !__scfg_buf.other) { 161 | __scfg_buf.ext = g_scfgBackup->ext; 162 | __scfg_buf.other = g_scfgBackup->other; 163 | } 164 | 165 | // XX: Previously we would read user settings from NVRAM here. However, 166 | // either the previously loaded app or the app we want to load are 167 | // guaranteed to be hbmenu, which already does this on startup by virtue 168 | // of being compiled with a modern enough version of libnds. 169 | 170 | // Repair AES keyslot used by NAND encryption 171 | if (dsiMode) { 172 | REG_AES_SLOTxY(3).data[2] = 0x202DDD1D; 173 | REG_AES_SLOTxY(3).data[3] = 0xE1A00005; 174 | aesBusyWaitReady(); 175 | } 176 | } 177 | 178 | static void loadBinary_ARM7 (u32 fileCluster) 179 | { 180 | EnvNdsHeader ndsHeader; 181 | 182 | // read NDS header 183 | minifatRead(&fatState, fileCluster, &ndsHeader, 0, sizeof(ndsHeader)); 184 | 185 | // Load binaries into memory 186 | minifatRead(&fatState, fileCluster, (void*)ndsHeader.arm9_ram_address, ndsHeader.arm9_rom_offset, ndsHeader.arm9_size); 187 | minifatRead(&fatState, fileCluster, (void*)ndsHeader.arm7_ram_address, ndsHeader.arm7_rom_offset, ndsHeader.arm7_size); 188 | 189 | // first copy the header to its proper location, excluding 190 | // the ARM9 start address, so as not to start it 191 | temp_arm9_start_address = ndsHeader.arm9_entrypoint; // Store for later 192 | ndsHeader.arm9_entrypoint = 0; 193 | dmaCopyWords(3, &ndsHeader, g_envAppNdsHeader, sizeof(EnvNdsHeader)); 194 | 195 | if (dsiMode && (ndsHeader.unitcode & BIT(1))) 196 | { 197 | // Read full TWL header 198 | minifatRead(&fatState, fileCluster, g_envAppTwlHeader, 0, sizeof(EnvTwlHeader)); 199 | 200 | // Load TWL binaries into memory 201 | if (g_envAppTwlHeader->arm9i_size) 202 | minifatRead(&fatState, fileCluster, (void*)g_envAppTwlHeader->arm9i_ram_address, g_envAppTwlHeader->arm9i_rom_offset, g_envAppTwlHeader->arm9i_size); 203 | if (g_envAppTwlHeader->arm7i_size) 204 | minifatRead(&fatState, fileCluster, (void*)g_envAppTwlHeader->arm7i_ram_address, g_envAppTwlHeader->arm7i_rom_offset, g_envAppTwlHeader->arm7i_size); 205 | } 206 | } 207 | 208 | /*------------------------------------------------------------------------- 209 | startBinary_ARM7 210 | Jumps to the ARM7 NDS binary in sync with the display and ARM9 211 | Written by Darkain. 212 | Modified by Chishm: 213 | * Removed MultiNDS specific stuff 214 | --------------------------------------------------------------------------*/ 215 | static void startBinary_ARM7 (void) { 216 | REG_IME=0; 217 | while(REG_VCOUNT!=191); 218 | while(REG_VCOUNT==191); 219 | // copy NDS ARM9 start address into the header, starting ARM9 220 | ARM9_START_ADDRESS = temp_arm9_start_address; 221 | ARM9_START_FLAG = 1; 222 | // Start ARM7 223 | VoidFn arm7code = (VoidFn)g_envAppNdsHeader->arm7_entrypoint; 224 | arm7code(); 225 | } 226 | 227 | extern const char mpu_reset[]; 228 | extern const char mpu_reset_end[]; 229 | 230 | int main (void) { 231 | #ifdef NO_DLDI 232 | dsiSD = true; 233 | dsiMode = true; 234 | #endif 235 | 236 | bool ok = false; 237 | MiniFatDiscReadFn readFn; 238 | 239 | #ifndef NO_SDMMC 240 | if (dsiSD && dsiMode) { 241 | sdmmc_controller_init(true); 242 | ok = sdmmc_sdcard_init() == 0; 243 | readFn = sdmmc_sdcard_readsectors; 244 | } 245 | #ifndef NO_DLDI 246 | else 247 | #endif 248 | #endif 249 | #ifndef NO_DLDI 250 | { 251 | ok = _io_dldi.startup(); 252 | readFn = _io_dldi.readSectors; 253 | } 254 | #endif 255 | 256 | u32 fileCluster = storedFileCluster; 257 | // Init card 258 | if(!ok || !minifatInit(&fatState, readFn, 0)) 259 | { 260 | return -1; 261 | } 262 | if (fileCluster < MINIFAT_CLUSTER_FIRST) /* Invalid file cluster specified */ 263 | { 264 | MiniFatDirEnt ent; 265 | fileCluster = minifatFind(&fatState, 0, DEFAULT_BOOT_NAME, &ent); 266 | if (fileCluster < MINIFAT_CLUSTER_FIRST || (ent.attrib & MINIFAT_ATTRIB_DIR)) 267 | { 268 | return -1; 269 | } 270 | } 271 | 272 | // ARM9 clears its memory part 2 273 | // copy ARM9 function to RAM, and make the ARM9 jump to it 274 | armCopyMem32((void*)TEMP_MEM, resetMemory2_ARM9, resetMemory2_ARM9_size); 275 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 276 | // Wait until the ARM9 has completed its task 277 | while (ARM9_START_ADDRESS == TEMP_MEM); 278 | 279 | // ARM9 sets up mpu 280 | // copy ARM9 function to RAM, and make the ARM9 jump to it 281 | armCopyMem32((void*)TEMP_MEM, mpu_reset, mpu_reset_end - mpu_reset); 282 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 283 | // Wait until the ARM9 has completed its task 284 | while (ARM9_START_ADDRESS == TEMP_MEM); 285 | 286 | // Get ARM7 to clear RAM 287 | resetMemory_ARM7(); 288 | 289 | // ARM9 enters a wait loop 290 | // copy ARM9 function to RAM, and make the ARM9 jump to it 291 | armCopyMem32((void*)TEMP_MEM, startBinary_ARM9, startBinary_ARM9_size); 292 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 293 | 294 | // Load the NDS file 295 | loadBinary_ARM7(fileCluster); 296 | 297 | #ifndef NO_DLDI 298 | // Patch with DLDI if desired 299 | if (wantToPatchDLDI) { 300 | dldiPatchBinary((void*)g_envAppNdsHeader->arm9_ram_address, g_envAppNdsHeader->arm9_size, &_dldi_start); 301 | } 302 | #endif 303 | 304 | #ifndef NO_SDMMC 305 | if (dsiSD && dsiMode) { 306 | sdmmc_controller_init(true); 307 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu; 308 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu; 309 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0; 310 | } 311 | #endif 312 | 313 | // Pass command line arguments to loaded program 314 | passArgs_ARM7(); 315 | 316 | startBinary_ARM7(); 317 | 318 | return 0; 319 | } 320 | -------------------------------------------------------------------------------- /source/boot.h: -------------------------------------------------------------------------------- 1 | #ifndef _BOOT_H_ 2 | #define _BOOT_H_ 3 | 4 | #define resetMemory2_ARM9_size 0x400 5 | void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9(); 6 | #define startBinary_ARM9_size 0x100 7 | void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (); 8 | #define ARM9_START_FLAG (*(vu8*)0x02FFFDFB) 9 | 10 | #endif // _BOOT_H_ 11 | -------------------------------------------------------------------------------- /source/io_dldi.h: -------------------------------------------------------------------------------- 1 | /* 2 | io_dldi.h 3 | 4 | Reserved space for post-compilation adding of an extra driver 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 2006-12-22 - Chishm 30 | * Original release 31 | */ 32 | 33 | #ifndef IO_DLDI_H 34 | #define IO_DLDI_H 35 | 36 | #include 37 | 38 | // export interface 39 | extern DLDI_INTERFACE _dldi_start ; 40 | extern DISC_INTERFACE _io_dldi ; 41 | 42 | #endif // define IO_DLDI_H 43 | -------------------------------------------------------------------------------- /source/io_dldi.s: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | Copyright (C) 2005 Michael "Chishm" Chisholm 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (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, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | If you use this code, please give due credit and email me about your 20 | project at chishm@hotmail.com 21 | ------------------------------------------------------------------*/ 22 | @--------------------------------------------------------------------------------- 23 | .align 4 24 | .arm 25 | .global _dldi_start 26 | .global _io_dldi 27 | @--------------------------------------------------------------------------------- 28 | .equ FEATURE_MEDIUM_CANREAD, 0x00000001 29 | .equ FEATURE_MEDIUM_CANWRITE, 0x00000002 30 | .equ FEATURE_SLOT_GBA, 0x00000010 31 | .equ FEATURE_SLOT_NDS, 0x00000020 32 | 33 | 34 | _dldi_start: 35 | #ifndef NO_DLDI 36 | 37 | @--------------------------------------------------------------------------------- 38 | @ Driver patch file standard header -- 16 bytes 39 | #ifdef STANDARD_DLDI 40 | .word 0xBF8DA5ED @ Magic number to identify this region 41 | #else 42 | .word 0xBF8DA5EE @ Magic number to identify this region 43 | #endif 44 | .asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator) 45 | .byte 0x01 @ Version number 46 | .byte 0x0e @ 16KiB @ Log [base-2] of the size of this driver in bytes. 47 | .byte 0x00 @ Sections to fix 48 | .byte 0x0e @ 16KiB @ Log [base-2] of the allocated space in bytes. 49 | 50 | @--------------------------------------------------------------------------------- 51 | @ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes 52 | .align 4 53 | .asciz "Loader (No interface)" 54 | 55 | @--------------------------------------------------------------------------------- 56 | @ Offsets to important sections within the data -- 32 bytes 57 | .align 6 58 | .word _dldi_start @ data start 59 | .word _dldi_end @ data end 60 | .word 0x00000000 @ Interworking glue start -- Needs address fixing 61 | .word 0x00000000 @ Interworking glue end 62 | .word 0x00000000 @ GOT start -- Needs address fixing 63 | .word 0x00000000 @ GOT end 64 | .word 0x00000000 @ bss start -- Needs setting to zero 65 | .word 0x00000000 @ bss end 66 | @--------------------------------------------------------------------------------- 67 | @ IO_INTERFACE data -- 32 bytes 68 | _io_dldi: 69 | .ascii "DLDI" @ ioType 70 | .word 0x00000000 @ Features 71 | .word _DLDI_startup @ 72 | .word _DLDI_isInserted @ 73 | .word _DLDI_readSectors @ Function pointers to standard device driver functions 74 | .word _DLDI_writeSectors @ 75 | .word _DLDI_clearStatus @ 76 | .word _DLDI_shutdown @ 77 | 78 | 79 | @--------------------------------------------------------------------------------- 80 | 81 | _DLDI_startup: 82 | _DLDI_isInserted: 83 | _DLDI_readSectors: 84 | _DLDI_writeSectors: 85 | _DLDI_clearStatus: 86 | _DLDI_shutdown: 87 | mov r0, #0x00 @ Return false for every function 88 | bx lr 89 | 90 | 91 | 92 | @--------------------------------------------------------------------------------- 93 | .align 94 | .pool 95 | 96 | .space (_dldi_start + 16384) - . @ Fill to 16KiB 97 | 98 | _dldi_end: 99 | .end 100 | @--------------------------------------------------------------------------------- 101 | #else 102 | @--------------------------------------------------------------------------------- 103 | @ IO_INTERFACE data -- 32 bytes 104 | _io_dldi: 105 | .ascii "DLDI" @ ioType 106 | .word 0x00000000 @ Features 107 | .word _DLDI_startup @ 108 | .word _DLDI_isInserted @ 109 | .word _DLDI_readSectors @ Function pointers to standard device driver functions 110 | .word _DLDI_writeSectors @ 111 | .word _DLDI_clearStatus @ 112 | .word _DLDI_shutdown @ 113 | 114 | _DLDI_startup: 115 | _DLDI_isInserted: 116 | _DLDI_readSectors: 117 | _DLDI_writeSectors: 118 | _DLDI_clearStatus: 119 | _DLDI_shutdown: 120 | mov r0, #0x00 @ Return false for every function 121 | bx lr 122 | 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /source/load_crt0.s: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | Copyright (C) 2005 Michael "Chishm" Chisholm 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (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, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | If you use this code, please give due credit and email me about your 20 | project at chishm@hotmail.com 21 | ------------------------------------------------------------------*/ 22 | 23 | @--------------------------------------------------------------------------------- 24 | .section ".init" 25 | .global _start 26 | .global storedFileCluster 27 | .global wantToPatchDLDI 28 | .global argStart 29 | .global argSize 30 | .global dsiSD 31 | .global dsiMode 32 | @--------------------------------------------------------------------------------- 33 | .align 4 34 | .arm 35 | @--------------------------------------------------------------------------------- 36 | _start: 37 | @--------------------------------------------------------------------------------- 38 | b startUp 39 | 40 | storedFileCluster: 41 | .word 0 @ default BOOT.NDS 42 | wantToPatchDLDI: 43 | .word 0x00000001 @ by default patch the DLDI section of the loaded NDS 44 | @ Used for passing arguments to the loaded app 45 | argStart: 46 | .word _end - _start 47 | argSize: 48 | .word 0x00000000 49 | dldiOffset: 50 | .word _dldi_start - _start 51 | dsiSD: 52 | .word 0 53 | dsiMode: 54 | .word 0 55 | 56 | startUp: 57 | mov r0, #0x04000000 58 | mov r1, #0 59 | str r1, [r0,#0x208] @ REG_IME 60 | str r1, [r0,#0x210] @ REG_IE 61 | str r1, [r0,#0x218] @ REG_AUXIE 62 | 63 | mov r0, #0x12 @ Switch to IRQ Mode 64 | msr cpsr, r0 65 | ldr sp, =__sp_irq @ Set IRQ stack 66 | 67 | mov r0, #0x13 @ Switch to SVC Mode 68 | msr cpsr, r0 69 | ldr sp, =__sp_svc @ Set SVC stack 70 | 71 | mov r0, #0x1F @ Switch to System Mode 72 | msr cpsr, r0 73 | ldr sp, =__sp_usr @ Set user stack 74 | 75 | ldr r0, =__bss_start @ Clear BSS section to 0x00 76 | ldr r1, =__bss_end 77 | sub r1, r1, r0 78 | bl ClearMem 79 | 80 | mov r0, #0 @ int argc 81 | mov r1, #0 @ char *argv[] 82 | ldr r3, =main 83 | bl _blx_r3_stub @ jump to user code 84 | 85 | @ If the user ever returns, restart 86 | b _start 87 | 88 | @--------------------------------------------------------------------------------- 89 | _blx_r3_stub: 90 | @--------------------------------------------------------------------------------- 91 | bx r3 92 | 93 | @--------------------------------------------------------------------------------- 94 | @ Clear memory to 0x00 if length != 0 95 | @ r0 = Start Address 96 | @ r1 = Length 97 | @--------------------------------------------------------------------------------- 98 | ClearMem: 99 | @--------------------------------------------------------------------------------- 100 | mov r2, #3 @ Round down to nearest word boundary 101 | add r1, r1, r2 @ Shouldn't be needed 102 | bics r1, r1, r2 @ Clear 2 LSB (and set Z) 103 | bxeq lr @ Quit if copy size is 0 104 | 105 | mov r2, #0 106 | ClrLoop: 107 | stmia r0!, {r2} 108 | subs r1, r1, #4 109 | bne ClrLoop 110 | bx lr 111 | 112 | @--------------------------------------------------------------------------------- 113 | @ Copy memory if length != 0 114 | @ r1 = Source Address 115 | @ r2 = Dest Address 116 | @ r4 = Dest Address + Length 117 | @--------------------------------------------------------------------------------- 118 | CopyMemCheck: 119 | @--------------------------------------------------------------------------------- 120 | sub r3, r4, r2 @ Is there any data to copy? 121 | @--------------------------------------------------------------------------------- 122 | @ Copy memory 123 | @ r1 = Source Address 124 | @ r2 = Dest Address 125 | @ r3 = Length 126 | @--------------------------------------------------------------------------------- 127 | CopyMem: 128 | @--------------------------------------------------------------------------------- 129 | mov r0, #3 @ These commands are used in cases where 130 | add r3, r3, r0 @ the length is not a multiple of 4, 131 | bics r3, r3, r0 @ even though it should be. 132 | bxeq lr @ Length is zero, so exit 133 | CIDLoop: 134 | ldmia r1!, {r0} 135 | stmia r2!, {r0} 136 | subs r3, r3, #4 137 | bne CIDLoop 138 | bx lr 139 | 140 | @--------------------------------------------------------------------------------- 141 | .align 142 | .pool 143 | .end 144 | @--------------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /source/minifat.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | minifat.c -- Minimal FAT filesystem driver 4 | 5 | Copyright (c) 2023 fincs 6 | Loosely based on code by Michael "Chishm" Chisholm 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any 10 | damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any 13 | purpose, including commercial applications, and to alter it and 14 | redistribute it freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you 17 | must not claim that you wrote the original software. If you use 18 | this software in a product, an acknowledgment in the product 19 | documentation would be appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and 21 | must not be misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | 25 | ------------------------------------------------------------------*/ 26 | 27 | #include "minifat.h" 28 | #include 29 | 30 | #ifdef MINIFAT_DEBUG 31 | #include 32 | #else 33 | #define dietPrint(...) ((void)0) 34 | #endif 35 | 36 | // First FAT cluster number used to indicate invalid/unusable cluster numbers 37 | #define FAT12_BAD_CLUSTER 0x0ff7 38 | #define FAT16_BAD_CLUSTER 0xfff7 39 | #define FAT32_BAD_CLUSTER 0x0ffffff7 40 | 41 | // Maximum number of clusters representable in the given FAT type 42 | #define MAX_CLUSTERS(_type) (_type##_BAD_CLUSTER-MINIFAT_CLUSTER_FIRST) 43 | 44 | // MBR partition information 45 | typedef struct { 46 | u8 status; 47 | u8 startHead; 48 | u16 startCySc; 49 | u8 type; 50 | u8 endHead; 51 | u16 endCySc; 52 | u16 lbaStartLo; 53 | u16 lbaStartHi; 54 | u16 lbaSizeLo; 55 | u16 lbaSizeHi; 56 | } PARTITION; 57 | 58 | _Static_assert(sizeof(PARTITION) == 0x10); 59 | 60 | // BIOS Parameter Block 61 | typedef struct { 62 | u16 bytesPerSector; 63 | u8 sectorsPerCluster; 64 | u16 reservedSectors; 65 | u8 numFATs; 66 | u16 rootEntries; 67 | u16 numSectorsSmall; 68 | u8 mediaDesc; 69 | u16 sectorsPerFAT; 70 | u16 sectorsPerTrk; 71 | u16 numHeads; 72 | u32 numHiddenSectors; 73 | u32 numSectors; 74 | } MK_PACKED BIOS_BPB; 75 | 76 | // Boot Sector - must be packed 77 | typedef struct { 78 | u8 jmpBoot[3]; 79 | u8 OEMName[8]; 80 | BIOS_BPB bpb; 81 | 82 | union { // Different types of extended BIOS Parameter Block for FAT16 and FAT32 83 | struct { 84 | // Ext BIOS Parameter Block for FAT16 85 | u8 driveNumber; 86 | u8 reserved1; 87 | u8 extBootSig; 88 | u32 volumeID; 89 | u8 volumeLabel[11]; 90 | u8 fileSysType[8]; 91 | // Bootcode 92 | u8 bootCode[448]; 93 | } MK_PACKED fat16; 94 | 95 | struct { 96 | // FAT32 extended block 97 | u32 sectorsPerFAT32; 98 | u16 extFlags; 99 | u16 fsVer; 100 | u32 rootClus; 101 | u16 fsInfo; 102 | u16 bkBootSec; 103 | u8 reserved[12]; 104 | // Ext BIOS Parameter Block for FAT16 105 | u8 driveNumber; 106 | u8 reserved1; 107 | u8 extBootSig; 108 | u32 volumeID; 109 | u8 volumeLabel[11]; 110 | u8 fileSysType[8]; 111 | // Bootcode 112 | u8 bootCode[420]; 113 | } MK_PACKED fat32; 114 | } extBlock; 115 | 116 | u16 bootSig; 117 | } BOOT_SEC; 118 | 119 | _Static_assert(sizeof(BOOT_SEC) == MINIFAT_SECTOR_SZ); 120 | 121 | MK_INLINE u32 _minifatClustToSec(MiniFat* fat, u32 cluster) 122 | { 123 | return fat->dataStart + ((cluster-MINIFAT_CLUSTER_FIRST) << fat->clusterShift); 124 | } 125 | 126 | MK_CONSTEXPR char _minifatToUpper(char c) 127 | { 128 | return c >= 'a' && c <= 'z' ? c + 'A' - 'a' : c; 129 | } 130 | 131 | MK_CONSTEXPR bool _minifatIsNonZeroPo2(unsigned x) 132 | { 133 | return x != 0 && (x & (x-1)) == 0; 134 | } 135 | 136 | static bool _minifatLoadBuffer(MiniFat* fat, u32 sector) 137 | { 138 | bool ok = true; 139 | if (fat->bufferPos != sector) { 140 | ok = fat->discReadFn(sector, 1, fat->buffer); 141 | if (ok) { 142 | fat->bufferPos = sector; 143 | } 144 | } 145 | return ok; 146 | } 147 | 148 | static bool _minifatInitParams(MiniFat* fat) 149 | { 150 | BOOT_SEC* buf = (BOOT_SEC*)fat->buffer; 151 | 152 | // Check for a valid boot signature 153 | if (buf->bootSig != 0xaa55) { 154 | dietPrint("Bad boot sig\n"); 155 | return false; 156 | } 157 | 158 | // Check for a valid Microsoft VBR 159 | unsigned jmp = buf->jmpBoot[0]; 160 | if (jmp != 0xeb && jmp != 0xe9 && jmp != 0xe8) { 161 | dietPrint("Bad jump opcode\n"); 162 | goto _isUnknown; 163 | } 164 | 165 | /* Not needed 166 | static const u8 fat32_ident[] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' }; 167 | if (memcmp(buf->extBlock.fat32.fileSysType, fat32_ident, 8) == 0) { 168 | //... etc 169 | } 170 | */ 171 | 172 | // Retrieve FAT size 173 | u32 sectorsPerFat = buf->bpb.sectorsPerFAT; 174 | if (!sectorsPerFat) { 175 | sectorsPerFat = buf->extBlock.fat32.sectorsPerFAT32; 176 | } 177 | 178 | // Validate basic FAT VBR parameters 179 | if ( 180 | buf->bpb.bytesPerSector != MINIFAT_SECTOR_SZ || // Only supporting 512-byte sectors 181 | !_minifatIsNonZeroPo2(buf->bpb.sectorsPerCluster) || 182 | buf->bpb.reservedSectors == 0 || 183 | (buf->bpb.numFATs != 1 && buf->bpb.numFATs != 2) || 184 | (buf->bpb.numSectorsSmall < 0x80 && buf->bpb.numSectors < 0x10000) || 185 | sectorsPerFat == 0 186 | ) { 187 | dietPrint("Bad FAT validation\n"); 188 | goto _isUnknown; 189 | } 190 | 191 | // Calculate total volume size in sectors 192 | u32 totalSectors = buf->bpb.numSectorsSmall; 193 | if (!totalSectors) { 194 | totalSectors = buf->bpb.numSectors; 195 | } 196 | 197 | // Initialize FAT parameters 198 | fat->clusterSectors = buf->bpb.sectorsPerCluster; 199 | fat->clusterShift = 0; 200 | fat->fatStart = fat->bufferPos + buf->bpb.reservedSectors; 201 | fat->rootDirStart = fat->fatStart + buf->bpb.numFATs*sectorsPerFat; 202 | fat->dataStart = fat->rootDirStart + (buf->bpb.rootEntries*sizeof(MiniFatDirEnt) + MINIFAT_SECTOR_SZ-1) / MINIFAT_SECTOR_SZ; 203 | 204 | // Calculate cluster sector size shift (log2) 205 | while ((1U << fat->clusterShift) != fat->clusterSectors) { 206 | fat->clusterShift ++; 207 | } 208 | 209 | // Calculate number of clusters 210 | u32 numClusters = (fat->bufferPos + totalSectors - fat->dataStart) >> fat->clusterShift; 211 | dietPrint("! FAT num clus 0x%lx\n", numClusters); 212 | 213 | // Discriminate FAT version according to number of clusters 214 | // XX: Note that the official FAT specification is off-by-one here. 215 | // In any case, well-behaved formatting tools will avoid volume sizes 216 | // dangerously close to the problematic decision boundaries. 217 | if (numClusters > MAX_CLUSTERS(FAT32)) { 218 | goto _isUnknown; 219 | } else if (numClusters > MAX_CLUSTERS(FAT16)) { 220 | // FAT32 - validate parameters 221 | if ( 222 | buf->bpb.rootEntries != 0 || 223 | buf->extBlock.fat32.fsVer != 0 || 224 | buf->extBlock.fat32.rootClus < MINIFAT_CLUSTER_FIRST || 225 | buf->extBlock.fat32.rootClus >= (MINIFAT_CLUSTER_FIRST + numClusters) 226 | ) { 227 | dietPrint("Bad FAT32 params\n"); 228 | goto _isUnknown; 229 | } 230 | 231 | // Select the correct FAT if mirroring is disabled 232 | // XX: FatFs does not implement this for some reason 233 | if (buf->extBlock.fat32.extFlags & 0x80) { 234 | fat->fatStart += (buf->extBlock.fat32.extFlags & 0x0f)*sectorsPerFat; 235 | } 236 | 237 | fat->fsType = MiniFatType_Fat32; 238 | fat->rootDirCluster = buf->extBlock.fat32.rootClus; 239 | fat->rootDirStart = _minifatClustToSec(fat, fat->rootDirCluster); 240 | } else { 241 | // FAT12/16 - validate parameters 242 | if (buf->bpb.rootEntries == 0) { 243 | dietPrint("Bad FAT12/16 params\n"); 244 | goto _isUnknown; 245 | } 246 | 247 | fat->fsType = numClusters > MAX_CLUSTERS(FAT12) ? MiniFatType_Fat16 : MiniFatType_Fat12; 248 | fat->rootDirCluster = MINIFAT_CLUSTER_FREE; // FAT12/16 store the root dir outside of the data area 249 | } 250 | 251 | return true; 252 | 253 | _isUnknown: 254 | fat->fsType = MiniFatType_Unknown; 255 | return true; 256 | } 257 | 258 | static bool _minifatInitPartition(MiniFat* fat, u32 sector) 259 | { 260 | return 261 | sector != 0 && 262 | _minifatLoadBuffer(fat, sector) && 263 | _minifatInitParams(fat) && 264 | fat->fsType != MiniFatType_Unknown; 265 | } 266 | 267 | bool minifatInit(MiniFat* fat, MiniFatDiscReadFn discReadFn, unsigned partitionIndex) 268 | { 269 | if (partitionIndex > 4) { 270 | return false; 271 | } 272 | 273 | fat->discReadFn = discReadFn; 274 | fat->bufferPos = UINT32_MAX; 275 | 276 | // Read and parse first sector of card 277 | if (!_minifatLoadBuffer(fat, 0) || !_minifatInitParams(fat)) { 278 | return false; 279 | } 280 | 281 | // If we have a valid boot sector but not a valid filesystem, check MBR 282 | if (fat->fsType == MiniFatType_Unknown) { 283 | // Extract partition offsets from MBR 284 | u32 partOffsets[4]; 285 | for (unsigned i = 0; i < 4; i ++) { 286 | PARTITION* part = &((PARTITION*)&fat->buffer[0x1be])[i]; 287 | 288 | // Fail if MBR is malformed 289 | if (part->status != 0x80 && part->status != 0x00) { 290 | return false; 291 | } 292 | 293 | // Ignore unpopulated/extended partitions 294 | if (part->type == 0x00 || part->type == 0x05 || part->type == 0x0f) { 295 | partOffsets[i] = 0; 296 | } else { 297 | partOffsets[i] = part->lbaStartLo | (part->lbaStartHi << 16); 298 | } 299 | } 300 | 301 | // Try to initialize the specified partition, or the first partition that succeeds 302 | bool ok = false; 303 | if (partitionIndex) { 304 | ok = _minifatInitPartition(fat, partOffsets[partitionIndex-1]); 305 | } else for (unsigned i = 0; !ok && i < 4; i ++) { 306 | ok = _minifatInitPartition(fat, partOffsets[i]); 307 | } 308 | 309 | // If above did not successfully initialize a partition, fail 310 | if (!ok) { 311 | return false; 312 | } 313 | } 314 | 315 | dietPrint("! FAT at 0x%lx type%u\n", fat->bufferPos, fat->fsType); 316 | dietPrint(" clusSect=%u clusShift=%u\n", fat->clusterSectors, fat->clusterShift); 317 | return true; 318 | } 319 | 320 | u32 minifatNextCluster(MiniFat* fat, u32 cluster) 321 | { 322 | u32 sector; 323 | u32 offset; 324 | u32 ret = MINIFAT_CLUSTER_FREE; 325 | 326 | if (cluster < MINIFAT_CLUSTER_FIRST) { 327 | return cluster; 328 | } 329 | 330 | switch (fat->fsType) { 331 | default: 332 | case MiniFatType_Unknown: 333 | break; 334 | 335 | case MiniFatType_Fat12: 336 | sector = fat->fatStart + (((cluster * 3) / 2) / MINIFAT_SECTOR_SZ); 337 | offset = ((cluster * 3) / 2) % MINIFAT_SECTOR_SZ; 338 | if (!_minifatLoadBuffer(fat, sector)) { 339 | break; 340 | } 341 | 342 | ret = fat->buffer[offset]; 343 | offset++; 344 | 345 | if (offset >= MINIFAT_SECTOR_SZ) { 346 | offset = 0; 347 | sector++; 348 | } 349 | 350 | if (!_minifatLoadBuffer(fat, sector)) { 351 | break; 352 | } 353 | 354 | ret |= fat->buffer[offset] << 8; 355 | 356 | if (cluster & 1) { 357 | ret >>= 4; 358 | } else { 359 | ret &= 0x0fff; 360 | } 361 | 362 | if (ret >= FAT12_BAD_CLUSTER) { 363 | ret = MINIFAT_CLUSTER_EOF; 364 | } 365 | break; 366 | 367 | case MiniFatType_Fat16: 368 | sector = fat->fatStart + ((cluster << 1) / MINIFAT_SECTOR_SZ); 369 | offset = cluster % (MINIFAT_SECTOR_SZ >> 1); 370 | if (!_minifatLoadBuffer(fat, sector)) { 371 | break; 372 | } 373 | 374 | // read the next cluster value 375 | ret = ((u16*)fat->buffer)[offset]; 376 | 377 | if (ret >= FAT16_BAD_CLUSTER) { 378 | ret = MINIFAT_CLUSTER_EOF; 379 | } 380 | break; 381 | 382 | case MiniFatType_Fat32: 383 | sector = fat->fatStart + ((cluster << 2) / MINIFAT_SECTOR_SZ); 384 | offset = cluster % (MINIFAT_SECTOR_SZ >> 2); 385 | if (!_minifatLoadBuffer(fat, sector)) { 386 | break; 387 | } 388 | 389 | // read the next cluster value 390 | ret = ((u32*)fat->buffer)[offset] & 0x0fffffff; 391 | 392 | if (ret >= FAT32_BAD_CLUSTER) { 393 | ret = MINIFAT_CLUSTER_EOF; 394 | } 395 | break; 396 | } 397 | 398 | return ret; 399 | } 400 | 401 | MK_INLINE void _minifatMake8dot3(char* buf, const char* name) 402 | { 403 | unsigned i = 0; 404 | 405 | // Copy filename 406 | for (; i < 8 && *name && *name != '.'; i ++, name ++) { 407 | buf[i] = _minifatToUpper(*name); 408 | } 409 | 410 | // Pad filename 411 | for (; i < 8; i ++) { 412 | buf[i] = ' '; 413 | } 414 | 415 | // Skip extension dot 416 | if (*name == '.') name++; 417 | 418 | // Copy extension 419 | for (; i < 8+3 && *name; i ++, name ++) { 420 | buf[i] = _minifatToUpper(*name); 421 | } 422 | 423 | // Pad extension 424 | for (; i < 8+3; i ++) { 425 | buf[i] = ' '; 426 | } 427 | } 428 | 429 | u32 minifatFind(MiniFat* fat, u32 dirCluster, const char* name, MiniFatDirEnt* out_entry) 430 | { 431 | char name8dot3[8+3]; 432 | _minifatMake8dot3(name8dot3, name); 433 | 434 | u32 baseSector; 435 | if (dirCluster < MINIFAT_CLUSTER_FIRST) { 436 | dirCluster = fat->rootDirCluster; 437 | baseSector = fat->rootDirStart; 438 | } else { 439 | baseSector = _minifatClustToSec(fat, dirCluster); 440 | } 441 | 442 | u32 sectorOff = 0; 443 | for (;;) { 444 | if (!_minifatLoadBuffer(fat, baseSector + sectorOff)) { 445 | return MINIFAT_CLUSTER_EOF; 446 | } 447 | 448 | MiniFatDirEnt* entries = (void*)fat->buffer; 449 | for (unsigned i = 0; i < MINIFAT_SECTOR_SZ / sizeof(MiniFatDirEnt); i ++) { 450 | MiniFatDirEnt* entry = &entries[i]; 451 | 452 | if (entry->name[0] == 0x00) { 453 | return MINIFAT_CLUSTER_EOF; 454 | } 455 | 456 | if (entry->name[0] == 0xe5 || (entry->attrib & MINIFAT_ATTRIB_VOLUME)) { 457 | continue; 458 | } 459 | 460 | if (__builtin_memcmp(entry->name_ext, name8dot3, 8+3) == 0) { 461 | if (out_entry) *out_entry = *entry; 462 | return entry->startCluster | (entry->startClusterHigh << 16); 463 | } 464 | } 465 | 466 | sectorOff ++; 467 | 468 | if (dirCluster >= MINIFAT_CLUSTER_FIRST) { 469 | if (sectorOff == fat->clusterSectors) { 470 | dirCluster = minifatNextCluster(fat, dirCluster); 471 | if (dirCluster < MINIFAT_CLUSTER_FIRST) { 472 | return MINIFAT_CLUSTER_EOF; 473 | } 474 | 475 | baseSector = _minifatClustToSec(fat, dirCluster); 476 | sectorOff = 0; 477 | } 478 | } else if (baseSector + sectorOff == fat->dataStart) { 479 | return MINIFAT_CLUSTER_EOF; 480 | } 481 | } 482 | } 483 | 484 | MK_INLINE bool _minifatLargeRead(MiniFat* fat, void** pBuffer, u32* pTotalReadLen, u32 readPos, u32 readLen) 485 | { 486 | bool ok = fat->discReadFn(readPos, readLen, *pBuffer); 487 | if (ok) { 488 | u32 readBytes = readLen * MINIFAT_SECTOR_SZ; 489 | *pBuffer = (u8*)*pBuffer + readBytes; 490 | *pTotalReadLen += readBytes; 491 | } 492 | return ok; 493 | } 494 | 495 | u32 minifatRead(MiniFat* fat, u32 objCluster, void* buffer, u32 offset, u32 length) 496 | { 497 | const u32 clusterSectors = fat->clusterSectors; 498 | const u32 clusterBytes = MINIFAT_SECTOR_SZ << fat->clusterShift; 499 | 500 | // All offsets must be word aligned 501 | if (((uptr)buffer | offset | length) & 3) { 502 | return 0; 503 | } 504 | 505 | // Skip over initial clusters 506 | while (objCluster >= MINIFAT_CLUSTER_FIRST && offset >= clusterBytes) { 507 | offset -= clusterBytes; 508 | objCluster = minifatNextCluster(fat, objCluster); 509 | } 510 | 511 | // Fail early if EOF 512 | if (objCluster < MINIFAT_CLUSTER_FIRST) { 513 | return 0; 514 | } 515 | 516 | // Initialize variables 517 | u32 totalReadLen = 0; 518 | u32 baseSector = _minifatClustToSec(fat, objCluster); 519 | u32 sectorOff = offset / MINIFAT_SECTOR_SZ; 520 | offset &= MINIFAT_SECTOR_SZ-1; 521 | 522 | // Read the head if needed 523 | if (offset || length < MINIFAT_SECTOR_SZ) { 524 | if (!_minifatLoadBuffer(fat, baseSector+sectorOff)) { 525 | return 0; 526 | } 527 | 528 | u32 maxLen = MINIFAT_SECTOR_SZ - offset; 529 | totalReadLen = length < maxLen ? length : maxLen; 530 | armCopyMem32(buffer, &fat->buffer[offset], totalReadLen); 531 | 532 | buffer = (u8*)buffer + totalReadLen; 533 | sectorOff ++; 534 | length -= totalReadLen; 535 | } 536 | 537 | // Main body read loop, iterating through clusters 538 | u32 accumReadPos = 0; 539 | u32 accumReadLen = 0; 540 | while (length >= MINIFAT_SECTOR_SZ) { 541 | u32 maxSectors = clusterSectors - sectorOff; 542 | u32 remSectors = length / MINIFAT_SECTOR_SZ; 543 | 544 | u32 readLen = remSectors < maxSectors ? remSectors : maxSectors; 545 | if_likely (readLen) { 546 | u32 readPos = baseSector+sectorOff; 547 | if_likely (accumReadLen) { 548 | u32 nextReadPos = accumReadPos + accumReadLen; 549 | if_likely (nextReadPos == readPos) { 550 | accumReadLen += readLen; 551 | } else if (!_minifatLargeRead(fat, &buffer, &totalReadLen, accumReadPos, accumReadLen)) { 552 | return totalReadLen; 553 | } else { 554 | goto _startNewRead; 555 | } 556 | } else { 557 | _startNewRead: 558 | accumReadPos = readPos; 559 | accumReadLen = readLen; 560 | } 561 | 562 | sectorOff += readLen; 563 | length -= readLen * MINIFAT_SECTOR_SZ; 564 | } 565 | 566 | if (length && sectorOff >= clusterSectors) { 567 | objCluster = minifatNextCluster(fat, objCluster); 568 | if (objCluster < MINIFAT_CLUSTER_FIRST) { 569 | return totalReadLen; 570 | } 571 | 572 | baseSector = _minifatClustToSec(fat, objCluster); 573 | sectorOff = 0; 574 | } 575 | } 576 | 577 | if (accumReadLen && !_minifatLargeRead(fat, &buffer, &totalReadLen, accumReadPos, accumReadLen)) { 578 | return totalReadLen; 579 | } 580 | 581 | // Read the tail if needed 582 | if (length) { 583 | if (!_minifatLoadBuffer(fat, baseSector+sectorOff)) { 584 | return totalReadLen; 585 | } 586 | 587 | armCopyMem32(buffer, &fat->buffer[0], length); 588 | totalReadLen += length; 589 | } 590 | 591 | return totalReadLen; 592 | } 593 | -------------------------------------------------------------------------------- /source/minifat.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | minifat.h -- Minimal FAT filesystem driver 4 | 5 | Copyright (c) 2023 fincs 6 | Loosely based on code by Michael "Chishm" Chisholm 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any 10 | damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any 13 | purpose, including commercial applications, and to alter it and 14 | redistribute it freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you 17 | must not claim that you wrote the original software. If you use 18 | this software in a product, an acknowledgment in the product 19 | documentation would be appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and 21 | must not be misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | 25 | ------------------------------------------------------------------*/ 26 | /*! \file minifat.h 27 | \brief Minimal FAT filesystem driver 28 | */ 29 | 30 | #pragma once 31 | #include 32 | 33 | #define MINIFAT_SECTOR_SZ 512 //!< Size of a sector in bytes 34 | 35 | #define MINIFAT_CLUSTER_FREE 0 //!< Free cluster 36 | #define MINIFAT_CLUSTER_EOF 1 //!< End-of-file marker 37 | #define MINIFAT_CLUSTER_FIRST 2 //!< First valid cluster 38 | 39 | #define MINIFAT_ATTRIB_ARCHIVE 0x20 //!< Archive attribute 40 | #define MINIFAT_ATTRIB_DIR 0x10 //!< Directory attribute 41 | #define MINIFAT_ATTRIB_LFN 0x0f //!< Long File Name attribute combination 42 | #define MINIFAT_ATTRIB_VOLUME 0x08 //!< Volume attribute 43 | #define MINIFAT_ATTRIB_HIDDEN 0x02 //!< Hidden attribute 44 | #define MINIFAT_ATTRIB_SYSTEM 0x04 //!< System attribute 45 | #define MINIFAT_ATTRIB_READONLY 0x01 //!< Read-only attribute 46 | 47 | //! Disc read callback function type 48 | typedef bool (*MiniFatDiscReadFn)(u32 sector, u32 numSectors, void* buffer); 49 | 50 | //! FAT filesystem type 51 | typedef enum MiniFatType { 52 | MiniFatType_Unknown, 53 | MiniFatType_Fat12, 54 | MiniFatType_Fat16, 55 | MiniFatType_Fat32, 56 | } MiniFatType; 57 | 58 | //! FAT filesystem driver state 59 | typedef struct MiniFat { 60 | alignas(4) u8 buffer[MINIFAT_SECTOR_SZ]; 61 | 62 | MiniFatDiscReadFn discReadFn; 63 | u32 bufferPos; 64 | 65 | MiniFatType fsType; 66 | u8 clusterSectors; 67 | u8 clusterShift; 68 | 69 | u32 fatStart; 70 | u32 rootDirStart; 71 | u32 rootDirCluster; 72 | u32 dataStart; 73 | } MiniFat; 74 | 75 | //! FAT directory entry 76 | typedef struct MiniFatDirEnt { 77 | union { 78 | char name_ext[8+3]; 79 | struct { 80 | char name[8]; 81 | char ext[3]; 82 | }; 83 | }; 84 | u8 attrib; 85 | u8 reserved; 86 | u8 cTime_ms; 87 | u16 cTime; 88 | u16 cDate; 89 | u16 aDate; 90 | u16 startClusterHigh; 91 | u16 mTime; 92 | u16 mDate; 93 | u16 startCluster; 94 | u32 fileSize; 95 | } MiniFatDirEnt; 96 | 97 | //! Initialize 98 | bool minifatInit(MiniFat* fat, MiniFatDiscReadFn discReadFn, unsigned partitionIndex); 99 | 100 | //! Return 101 | u32 minifatNextCluster(MiniFat* fat, u32 cluster); 102 | 103 | //! Find 104 | u32 minifatFind(MiniFat* fat, u32 dirCluster, const char* name, MiniFatDirEnt* out_entry); 105 | 106 | //! Read 107 | u32 minifatRead(MiniFat* fat, u32 objCluster, void* buffer, u32 offset, u32 length); 108 | -------------------------------------------------------------------------------- /source/sdmmc.c: -------------------------------------------------------------------------------- 1 | #ifndef NO_SDMMC 2 | #include 3 | #include 4 | #include "sdmmc.h" 5 | 6 | static struct mmcdevice deviceSD; 7 | 8 | //--------------------------------------------------------------------------------- 9 | static int geterror(struct mmcdevice *ctx) { 10 | //--------------------------------------------------------------------------------- 11 | //if(ctx->error == 0x4) return -1; 12 | //else return 0; 13 | return (ctx->error << 29) >> 31; 14 | } 15 | 16 | 17 | //--------------------------------------------------------------------------------- 18 | static void setTarget(struct mmcdevice *ctx) { 19 | //--------------------------------------------------------------------------------- 20 | sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); 21 | setckl(ctx->clk); 22 | if (ctx->SDOPT == 0) { 23 | sdmmc_mask16(REG_SDOPT, 0, 0x8000); 24 | } else { 25 | sdmmc_mask16(REG_SDOPT, 0x8000, 0); 26 | } 27 | 28 | } 29 | 30 | 31 | //--------------------------------------------------------------------------------- 32 | static void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) { 33 | //--------------------------------------------------------------------------------- 34 | int i; 35 | bool getSDRESP = (cmd << 15) >> 31; 36 | uint16_t flags = (cmd << 15) >> 31; 37 | const bool readdata = cmd & 0x20000; 38 | const bool writedata = cmd & 0x40000; 39 | 40 | if(readdata || writedata) 41 | { 42 | flags |= TMIO_STAT0_DATAEND; 43 | } 44 | 45 | ctx->error = 0; 46 | while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? 47 | sdmmc_write16(REG_SDIRMASK0,0); 48 | sdmmc_write16(REG_SDIRMASK1,0); 49 | sdmmc_write16(REG_SDSTATUS0,0); 50 | sdmmc_write16(REG_SDSTATUS1,0); 51 | sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); 52 | sdmmc_write16(REG_SDCMDARG1,args >> 16); 53 | sdmmc_write16(REG_SDCMD,cmd &0xFFFF); 54 | 55 | uint32_t size = ctx->size; 56 | uint16_t *dataPtr = (uint16_t*)ctx->data; 57 | uint32_t *dataPtr32 = (uint32_t*)ctx->data; 58 | 59 | bool useBuf = ( NULL != dataPtr ); 60 | bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr)))); 61 | 62 | uint16_t status0 = 0; 63 | 64 | while(1) { 65 | volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1); 66 | volatile uint16_t ctl32 = sdmmc_read16(REG_SDDATACTL32); 67 | if((ctl32 & 0x100)) 68 | { 69 | if(readdata) { 70 | if(useBuf) { 71 | sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); 72 | if(size > 0x1FF) { 73 | if(useBuf32) { 74 | for(i = 0; i<0x200; i+=4) { 75 | *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); 76 | } 77 | } else { 78 | for(i = 0; i<0x200; i+=2) { 79 | *dataPtr++ = sdmmc_read16(REG_SDFIFO); 80 | } 81 | } 82 | size -= 0x200; 83 | } 84 | } 85 | 86 | sdmmc_mask16(REG_SDDATACTL32, 0x800, 0); 87 | } 88 | } 89 | 90 | if(status1 & TMIO_MASK_GW) { 91 | ctx->error |= 4; 92 | break; 93 | } 94 | 95 | if(!(status1 & TMIO_STAT1_CMD_BUSY)) { 96 | status0 = sdmmc_read16(REG_SDSTATUS0); 97 | if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) { 98 | ctx->error |= 0x1; 99 | } 100 | if(status0 & TMIO_STAT0_DATAEND) { 101 | ctx->error |= 0x2; 102 | } 103 | 104 | if((status0 & flags) == flags) 105 | break; 106 | } 107 | } 108 | ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); 109 | ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); 110 | sdmmc_write16(REG_SDSTATUS0,0); 111 | sdmmc_write16(REG_SDSTATUS1,0); 112 | 113 | if(getSDRESP != 0) { 114 | ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); 115 | ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); 116 | ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); 117 | ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); 118 | } 119 | } 120 | 121 | 122 | //--------------------------------------------------------------------------------- 123 | int sdmmc_cardinserted() { 124 | //--------------------------------------------------------------------------------- 125 | return 1; //sdmmc_cardready; 126 | } 127 | 128 | //--------------------------------------------------------------------------------- 129 | void sdmmc_controller_init(bool force) { 130 | //--------------------------------------------------------------------------------- 131 | deviceSD.isSDHC = 0; 132 | deviceSD.SDOPT = 0; 133 | deviceSD.res = 0; 134 | deviceSD.initarg = 0; 135 | deviceSD.clk = 0x80; 136 | deviceSD.devicenumber = 0; 137 | 138 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu; 139 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu; 140 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u; 141 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2; 142 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu; 143 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu; 144 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512; 145 | *(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1; 146 | *(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu; 147 | *(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u; 148 | *(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL; 149 | *(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16; 150 | *(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7 151 | *(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8 152 | *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; 153 | *(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20; 154 | *(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE; 155 | *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; 156 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512; 157 | *(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0; 158 | 159 | } 160 | 161 | //--------------------------------------------------------------------------------- 162 | int sdmmc_sdcard_init() { 163 | //--------------------------------------------------------------------------------- 164 | setTarget(&deviceSD); 165 | swiDelay(0xF000); 166 | sdmmc_send_command(&deviceSD,0,0); 167 | sdmmc_send_command(&deviceSD,0x10408,0x1AA); 168 | u32 temp = (deviceSD.error & 0x1) << 0x1E; 169 | 170 | u32 temp2 = 0; 171 | do { 172 | do { 173 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 174 | sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp); 175 | temp2 = 1; 176 | } while ( !(deviceSD.error & 1) ); 177 | 178 | } while((deviceSD.ret[0] & 0x80000000) == 0); 179 | 180 | if(!((deviceSD.ret[0] >> 30) & 1) || !temp) 181 | temp2 = 0; 182 | 183 | deviceSD.isSDHC = temp2; 184 | 185 | sdmmc_send_command(&deviceSD,0x10602,0); 186 | if (deviceSD.error & 0x4) return -1; 187 | 188 | sdmmc_send_command(&deviceSD,0x10403,0); 189 | if (deviceSD.error & 0x4) return -1; 190 | deviceSD.initarg = deviceSD.ret[0] >> 0x10; 191 | 192 | sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10); 193 | if (deviceSD.error & 0x4) return -1; 194 | 195 | deviceSD.clk = 1; 196 | setckl(1); 197 | 198 | sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10); 199 | if (deviceSD.error & 0x4) return -1; 200 | 201 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 202 | if (deviceSD.error & 0x4) return -1; 203 | 204 | sdmmc_send_command(&deviceSD,0x1076A,0x0); 205 | if (deviceSD.error & 0x4) return -1; 206 | 207 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 208 | if (deviceSD.error & 0x4) return -1; 209 | 210 | deviceSD.SDOPT = 1; 211 | sdmmc_send_command(&deviceSD,0x10446,0x2); 212 | if (deviceSD.error & 0x4) return -1; 213 | 214 | sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10); 215 | if (deviceSD.error & 0x4) return -1; 216 | 217 | sdmmc_send_command(&deviceSD,0x10410,0x200); 218 | if (deviceSD.error & 0x4) return -1; 219 | deviceSD.clk |= 0x200; 220 | 221 | return 0; 222 | 223 | } 224 | 225 | //--------------------------------------------------------------------------------- 226 | bool sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out) { 227 | //--------------------------------------------------------------------------------- 228 | if (deviceSD.isSDHC == 0) 229 | sector_no <<= 9; 230 | setTarget(&deviceSD); 231 | sdmmc_write16(REG_SDSTOP,0x100); 232 | 233 | sdmmc_write16(REG_SDBLKCOUNT32,numsectors); 234 | sdmmc_write16(REG_SDBLKLEN32,0x200); 235 | 236 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 237 | deviceSD.data = out; 238 | deviceSD.size = numsectors << 9; 239 | sdmmc_send_command(&deviceSD,0x33C12,sector_no); 240 | return geterror(&deviceSD) == 0; 241 | } 242 | #endif -------------------------------------------------------------------------------- /source/sdmmc.h: -------------------------------------------------------------------------------- 1 | #ifndef __SDMMC_H__ 2 | #define __SDMMC_H__ 3 | 4 | #include 5 | 6 | #define DATA32_SUPPORT 7 | 8 | #define SDMMC_BASE 0x04004800 9 | 10 | 11 | #define REG_SDCMD 0x00 12 | #define REG_SDPORTSEL 0x02 13 | #define REG_SDCMDARG 0x04 14 | #define REG_SDCMDARG0 0x04 15 | #define REG_SDCMDARG1 0x06 16 | #define REG_SDSTOP 0x08 17 | #define REG_SDRESP 0x0c 18 | #define REG_SDBLKCOUNT 0x0a 19 | 20 | #define REG_SDRESP0 0x0c 21 | #define REG_SDRESP1 0x0e 22 | #define REG_SDRESP2 0x10 23 | #define REG_SDRESP3 0x12 24 | #define REG_SDRESP4 0x14 25 | #define REG_SDRESP5 0x16 26 | #define REG_SDRESP6 0x18 27 | #define REG_SDRESP7 0x1a 28 | 29 | #define REG_SDSTATUS0 0x1c 30 | #define REG_SDSTATUS1 0x1e 31 | 32 | #define REG_SDIRMASK0 0x20 33 | #define REG_SDIRMASK1 0x22 34 | #define REG_SDCLKCTL 0x24 35 | 36 | #define REG_SDBLKLEN 0x26 37 | #define REG_SDOPT 0x28 38 | #define REG_SDFIFO 0x30 39 | 40 | #define REG_SDDATACTL 0xd8 41 | #define REG_SDRESET 0xe0 42 | #define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? 43 | 44 | #define REG_SDDATACTL32 0x100 45 | #define REG_SDBLKLEN32 0x104 46 | #define REG_SDBLKCOUNT32 0x108 47 | #define REG_SDFIFO32 0x10C 48 | 49 | #define REG_CLK_AND_WAIT_CTL 0x138 50 | #define REG_RESET_SDIO 0x1e0 51 | //The below defines are from linux kernel drivers/mmc tmio_mmc.h. 52 | /* Definitions for values the CTRL_STATUS register can take. */ 53 | #define TMIO_STAT0_CMDRESPEND 0x0001 54 | #define TMIO_STAT0_DATAEND 0x0004 55 | #define TMIO_STAT0_CARD_REMOVE 0x0008 56 | #define TMIO_STAT0_CARD_INSERT 0x0010 57 | #define TMIO_STAT0_SIGSTATE 0x0020 58 | #define TMIO_STAT0_WRPROTECT 0x0080 59 | #define TMIO_STAT0_CARD_REMOVE_A 0x0100 60 | #define TMIO_STAT0_CARD_INSERT_A 0x0200 61 | #define TMIO_STAT0_SIGSTATE_A 0x0400 62 | 63 | #define TMIO_STAT1_CMD_IDX_ERR 0x0001 64 | #define TMIO_STAT1_CRCFAIL 0x0002 65 | #define TMIO_STAT1_STOPBIT_ERR 0x0004 66 | #define TMIO_STAT1_DATATIMEOUT 0x0008 67 | #define TMIO_STAT1_RXOVERFLOW 0x0010 68 | #define TMIO_STAT1_TXUNDERRUN 0x0020 69 | #define TMIO_STAT1_CMDTIMEOUT 0x0040 70 | #define TMIO_STAT1_RXRDY 0x0100 71 | #define TMIO_STAT1_TXRQ 0x0200 72 | #define TMIO_STAT1_ILL_FUNC 0x2000 73 | #define TMIO_STAT1_CMD_BUSY 0x4000 74 | #define TMIO_STAT1_ILL_ACCESS 0x8000 75 | 76 | #define SDMC_NORMAL 0x00000000 77 | #define SDMC_ERR_COMMAND 0x00000001 78 | #define SDMC_ERR_CRC 0x00000002 79 | #define SDMC_ERR_END 0x00000004 80 | #define SDMC_ERR_TIMEOUT 0x00000008 81 | #define SDMC_ERR_FIFO_OVF 0x00000010 82 | #define SDMC_ERR_FIFO_UDF 0x00000020 83 | #define SDMC_ERR_WP 0x00000040 84 | #define SDMC_ERR_ABORT 0x00000080 85 | #define SDMC_ERR_FPGA_TIMEOUT 0x00000100 86 | #define SDMC_ERR_PARAM 0x00000200 87 | #define SDMC_ERR_R1_STATUS 0x00000800 88 | #define SDMC_ERR_NUM_WR_SECTORS 0x00001000 89 | #define SDMC_ERR_RESET 0x00002000 90 | #define SDMC_ERR_ILA 0x00004000 91 | #define SDMC_ERR_INFO_DETECT 0x00008000 92 | 93 | #define SDMC_STAT_ERR_UNKNOWN 0x00080000 94 | #define SDMC_STAT_ERR_CC 0x00100000 95 | #define SDMC_STAT_ERR_ECC_FAILED 0x00200000 96 | #define SDMC_STAT_ERR_CRC 0x00800000 97 | #define SDMC_STAT_ERR_OTHER 0xf9c70008 98 | 99 | #define TMIO_MASK_ALL 0x837f031d 100 | 101 | #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ 102 | TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) 103 | 104 | #define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) 105 | #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) 106 | 107 | typedef struct mmcdevice { 108 | u8* data; 109 | u32 size; 110 | u32 error; 111 | u16 stat0; 112 | u16 stat1; 113 | u32 ret[4]; 114 | u32 initarg; 115 | u32 isSDHC; 116 | u32 clk; 117 | u32 SDOPT; 118 | u32 devicenumber; 119 | u32 total_size; //size in sectors of the device 120 | u32 res; 121 | } mmcdevice; 122 | 123 | enum { 124 | MMC_DEVICE_SDCARD, 125 | MMC_DEVICE_NAND, 126 | }; 127 | 128 | void sdmmc_controller_init(bool force_init); 129 | void sdmmc_initirq(); 130 | int sdmmc_cardinserted(); 131 | 132 | int sdmmc_sdcard_init(); 133 | int sdmmc_nand_init(); 134 | void sdmmc_get_cid(int devicenumber, u32 *cid); 135 | 136 | static inline void sdmmc_nand_cid( u32 *cid) { 137 | sdmmc_get_cid(MMC_DEVICE_NAND,cid); 138 | } 139 | 140 | static inline void sdmmc_sdcard_cid( u32 *cid) { 141 | sdmmc_get_cid(MMC_DEVICE_SDCARD,cid); 142 | } 143 | 144 | bool sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out); 145 | /* 146 | int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in); 147 | int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out); 148 | int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in); 149 | */ 150 | 151 | extern u32 sdmmc_cid[]; 152 | extern int sdmmc_curdevice; 153 | 154 | //--------------------------------------------------------------------------------- 155 | static inline u16 sdmmc_read16(u16 reg) { 156 | //--------------------------------------------------------------------------------- 157 | return *(vu16*)(SDMMC_BASE + reg); 158 | } 159 | 160 | //--------------------------------------------------------------------------------- 161 | static inline void sdmmc_write16(u16 reg, u16 val) { 162 | //--------------------------------------------------------------------------------- 163 | *(vu16*)(SDMMC_BASE + reg) = val; 164 | } 165 | 166 | //--------------------------------------------------------------------------------- 167 | static inline u32 sdmmc_read32(u16 reg) { 168 | //--------------------------------------------------------------------------------- 169 | return *(vu32*)(SDMMC_BASE + reg); 170 | } 171 | 172 | //--------------------------------------------------------------------------------- 173 | static inline void sdmmc_write32(u16 reg, u32 val) { 174 | //--------------------------------------------------------------------------------- 175 | *(vu32*)(SDMMC_BASE + reg) = val; 176 | } 177 | 178 | //--------------------------------------------------------------------------------- 179 | static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set) { 180 | //--------------------------------------------------------------------------------- 181 | u16 val = sdmmc_read16(reg); 182 | val &= ~clear; 183 | val |= set; 184 | sdmmc_write16(reg, val); 185 | } 186 | 187 | 188 | //--------------------------------------------------------------------------------- 189 | static inline void setckl(u32 data) { 190 | //--------------------------------------------------------------------------------- 191 | sdmmc_mask16(REG_SDCLKCTL, 0x100, 0); 192 | sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF); 193 | sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); 194 | } 195 | 196 | #endif 197 | --------------------------------------------------------------------------------