Compare commits
22 Commits
master
...
alpha_2020
Author | SHA1 | Date |
---|---|---|
![]() |
e63ec1404f | |
![]() |
a719aa2864 | |
![]() |
29413979fa | |
![]() |
48def49f8b | |
![]() |
31bbcbfe25 | |
![]() |
96eebb01db | |
![]() |
dd68d381ac | |
![]() |
5f25739286 | |
![]() |
5316c8938d | |
![]() |
c87769a06e | |
![]() |
ffb32cca9c | |
![]() |
dac935f66d | |
![]() |
ac33d98ad5 | |
![]() |
1caafaf5b3 | |
![]() |
179ea504d0 | |
![]() |
07f6f01d92 | |
![]() |
8fdc74a3c4 | |
![]() |
ef79b4343d | |
![]() |
b8f4d5cd53 | |
![]() |
6826d5d660 | |
![]() |
02e9fa9830 | |
![]() |
f028745f52 |
|
@ -49,7 +49,8 @@ You may use this under the terms of the GNU General Public License GPL v3 or und
|
|||
* **ctrulib devs**
|
||||
* **Luma 3DS devs**
|
||||
* **devkitPro**
|
||||
* **ChaN**
|
||||
* **ChaN** (fatfs)
|
||||
* **benhoyt** (inih)
|
||||
* **fastboot3DS project**
|
||||
* **Wolfvak, Sono and all the other people in #GodMode9 on IRC/Discord**
|
||||
* **endrift, Extrems and all the other people in #mgba on Freenode**
|
||||
|
|
|
@ -18,11 +18,11 @@ include $(DEVKITARM)/base_rules
|
|||
#---------------------------------------------------------------------------------
|
||||
#TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := ../source ../source/hardware ../source/arm11 \
|
||||
SOURCES := ../kernel/source ../source ../source/hardware ../source/arm11 \
|
||||
../source/arm11/allocator ../source/arm11/hardware \
|
||||
../source/arm11/util/rbtree
|
||||
../source/arm11/util/rbtree ../thirdparty/inih
|
||||
DATA :=
|
||||
INCLUDES := ../include
|
||||
INCLUDES := ../kernel/include ../include ../thirdparty
|
||||
DEFINES := -DARM11 -D_3DS -DVERS_STRING=\"$(VERS_STRING)\" \
|
||||
-DVERS_MAJOR=$(VERS_MAJOR) -DVERS_MINOR=$(VERS_MINOR)
|
||||
ASSETS :=
|
||||
|
@ -36,18 +36,24 @@ endif
|
|||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv6k+vfpv2 -mtune=mpcore -mfloat-abi=hard -mtp=soft -marm -mthumb-interwork
|
||||
|
||||
CFLAGS := $(ARCH) -std=gnu17 -O2 -g -flto -mword-relocations -ffunction-sections \
|
||||
-fno-math-errno -Wall -Wextra -Wno-unused-function
|
||||
CFLAGS := $(ARCH) -std=c17 -O2 -g -flto -mword-relocations \
|
||||
-ffunction-sections -fno-math-errno -Wall -Wextra
|
||||
CFLAGS += $(INCLUDE) $(DEFINES)
|
||||
|
||||
CXXFLAGS := $(ARCH) -std=c++17 -O2 -g -flto -fno-rtti -fno-exceptions \
|
||||
-mword-relocations -ffunction-sections -fno-math-errno -Wall -Wextra \
|
||||
-Wno-unused-function
|
||||
-mword-relocations -ffunction-sections -fno-math-errno -Wall -Wextra
|
||||
CXXFLAGS += $(INCLUDE) $(DEFINES)
|
||||
|
||||
ASFLAGS := $(ARCH) -g -flto $(INCLUDE) $(DEFINES)
|
||||
LDFLAGS = $(ARCH) -g -flto -specs=../arm11.specs -Wl,-Map,$(notdir $*.map) -nostartfiles
|
||||
|
||||
ifeq ($(strip $(NO_DEBUG)),)
|
||||
CFLAGS := $(subst -flto,,$(CFLAGS)) -fstack-protector-strong -fno-inline
|
||||
CXXFLAGS := $(subst -flto,,$(CXXFLAGS)) -fstack-protector-strong -fno-inline
|
||||
ASFLAGS := $(subst -flto,,$(ASFLAGS))
|
||||
LDFLAGS := $(subst -flto,,$(LDFLAGS)) -fstack-protector-strong -fno-inline -Wl,-wrap=malloc,-wrap=calloc,-wrap=free
|
||||
endif
|
||||
|
||||
LIBS := -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -188,4 +194,3 @@ endef
|
|||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
294
include/arm.h
294
include/arm.h
|
@ -50,45 +50,59 @@
|
|||
|
||||
#if !__ASSEMBLER__
|
||||
|
||||
#define MAKE_STANDALONE(name) \
|
||||
static inline void __##name(void) \
|
||||
{ \
|
||||
__asm__ volatile(#name : : : "memory"); \
|
||||
}
|
||||
|
||||
#define MAKE_GET_REG(name, inst) \
|
||||
static inline u32 __##name(void) \
|
||||
{ \
|
||||
u32 reg; \
|
||||
__asm__ volatile(inst : "=r" (reg) : ); \
|
||||
return reg; \
|
||||
}
|
||||
|
||||
#define MAKE_SET_REG_ZERO(name, inst) \
|
||||
static inline void __##name(void) \
|
||||
#define MAKE_INTR_NO_INOUT(isVolatile, name, ...) \
|
||||
ALWAYS_INLINE void __##name(void) \
|
||||
{ \
|
||||
__asm__ volatile(inst : : "r" (0) : "memory"); \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#name : : : __VA_ARGS__); \
|
||||
else \
|
||||
__asm__(#name : : : __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define MAKE_SET_REG(name, inst) \
|
||||
static inline void __##name(u32 reg) \
|
||||
{ \
|
||||
__asm__ volatile(inst : : "r" (reg) : "memory"); \
|
||||
#define MAKE_INTR_GET_REG(isVolatile, name, inst) \
|
||||
ALWAYS_INLINE u32 __##name(void) \
|
||||
{ \
|
||||
u32 reg; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(inst : "=r" (reg) : : ); \
|
||||
else \
|
||||
__asm__(inst : "=r" (reg) : : ); \
|
||||
return reg; \
|
||||
}
|
||||
|
||||
#define MAKE_INTR_SET_REG_ZERO(isVolatile, name, inst, ...) \
|
||||
ALWAYS_INLINE void __##name(void) \
|
||||
{ \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(inst : : "r" (0) : __VA_ARGS__); \
|
||||
else \
|
||||
__asm__(inst : : "r" (0) : __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define MAKE_INTR_SET_REG(isVolatile, name, inst, ...) \
|
||||
ALWAYS_INLINE void __##name(u32 reg) \
|
||||
{ \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(inst : : "r" (reg) : __VA_ARGS__); \
|
||||
else \
|
||||
__asm__(inst : : "r" (reg) : __VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
||||
#define __bkpt(val) __asm__ volatile("bkpt #" #val : : : )
|
||||
|
||||
#if !__thumb__
|
||||
// Program status register
|
||||
MAKE_GET_REG(getCpsr, "mrs %0, cpsr")
|
||||
MAKE_SET_REG(setCpsr_c, "msr cpsr_c, %0")
|
||||
MAKE_SET_REG(setCpsr, "msr cpsr_cxsf, %0")
|
||||
MAKE_GET_REG(getSpsr, "mrs %0, spsr")
|
||||
MAKE_SET_REG(setSpsr_c, "msr spsr_c, %0")
|
||||
MAKE_SET_REG(setSpsr, "msr spsr_cxsf, %0")
|
||||
MAKE_INTR_GET_REG(1, getCpsr, "mrs %0, cpsr")
|
||||
MAKE_INTR_SET_REG(1, setCpsr_c, "msr cpsr_c, %0", "memory")
|
||||
MAKE_INTR_SET_REG(1, setCpsr, "msr cpsr_cxsf, %0", "memory")
|
||||
MAKE_INTR_GET_REG(1, getSpsr, "mrs %0, spsr")
|
||||
MAKE_INTR_SET_REG(1, setSpsr_c, "msr spsr_c, %0", "memory")
|
||||
MAKE_INTR_SET_REG(1, setSpsr, "msr spsr_cxsf, %0", "memory")
|
||||
|
||||
// Control Register
|
||||
MAKE_GET_REG(getCr, "mrc p15, 0, %0, c1, c0, 0")
|
||||
MAKE_SET_REG(setCr, "mcr p15, 0, %0, c1, c0, 0")
|
||||
MAKE_INTR_GET_REG(1, getCr, "mrc p15, 0, %0, c1, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setCr, "mcr p15, 0, %0, c1, c0, 0", "memory")
|
||||
#endif // if !__thumb__
|
||||
|
||||
|
||||
|
@ -97,64 +111,214 @@ MAKE_SET_REG(setCr, "mcr p15, 0, %0, c1, c0, 0")
|
|||
#define __cpsie(flags) __asm__ volatile("cpsie " #flags : : : "memory")
|
||||
#define __setend(end) __asm__ volatile("setend " #end : : : "memory")
|
||||
|
||||
MAKE_STANDALONE(wfi)
|
||||
MAKE_STANDALONE(wfe)
|
||||
MAKE_STANDALONE(sev)
|
||||
MAKE_INTR_NO_INOUT(1, nop)
|
||||
MAKE_INTR_NO_INOUT(1, wfi, "memory")
|
||||
MAKE_INTR_NO_INOUT(1, wfe, "memory")
|
||||
MAKE_INTR_NO_INOUT(1, sev)
|
||||
|
||||
#if !__thumb__
|
||||
static inline u32 __getCpuId(void)
|
||||
ALWAYS_INLINE u8 __ldrexb(vu8 *addr)
|
||||
{
|
||||
u8 res;
|
||||
__asm__ volatile("ldrexb %0, %1" : "=r" (res) : "Q" (*addr) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u16 __ldrexh(vu16 *addr)
|
||||
{
|
||||
u16 res;
|
||||
__asm__ volatile("ldrexh %0, %1" : "=r" (res) : "Q" (*addr) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 __ldrex(vu32 *addr)
|
||||
{
|
||||
u32 res;
|
||||
__asm__ volatile("ldrex %0, %1" : "=r" (res) : "Q" (*addr) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
/*ALWAYS_INLINE u64 __ldrexd(vu64 *addr)
|
||||
{
|
||||
union
|
||||
{
|
||||
u32 r32[2];
|
||||
u64 r64;
|
||||
} r;
|
||||
|
||||
// TODO: "Error: even register required -- `ldrexd r3,r2,[r0]'"
|
||||
#ifndef __ARMEB__ // Little endian
|
||||
__asm__ volatile("ldrexd %0, %1, %2" : "=r" (r.r32[0]), "=r" (r.r32[1]) : "Q" (*addr) : );
|
||||
#else // Big endian
|
||||
__asm__ volatile("ldrexd %0, %1, %2" : "=r" (r.r32[1]), "=r" (r.r32[0]) : "Q" (*addr) : );
|
||||
#endif
|
||||
return r.r64;
|
||||
}*/
|
||||
|
||||
ALWAYS_INLINE u32 __strexb(vu8 *addr, u8 val)
|
||||
{
|
||||
u32 res;
|
||||
__asm__ volatile("strexb %0, %2, %1" : "=&r" (res), "=Q" (*addr) : "r" (val) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 __strexh(vu16 *addr, u16 val)
|
||||
{
|
||||
u32 res;
|
||||
__asm__ volatile("strexh %0, %2, %1" : "=&r" (res), "=Q" (*addr) : "r" (val) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 __strex(vu32 *addr, u32 val)
|
||||
{
|
||||
u32 res;
|
||||
__asm__ volatile("strex %0, %2, %1" : "=&r" (res), "=Q" (*addr) : "r" (val) : );
|
||||
return res;
|
||||
}
|
||||
|
||||
/*ALWAYS_INLINE u32 __strexd(vu64 *addr, u64 val)
|
||||
{
|
||||
union
|
||||
{
|
||||
u32 r32[2];
|
||||
u64 r64;
|
||||
} r;
|
||||
r.r64 = val;
|
||||
|
||||
// TODO: "Error: even register required -- `strexd r0,r3,r2,[r1]'"
|
||||
u32 res;
|
||||
#ifndef __ARMEB__ // Little endian
|
||||
__asm__ volatile("strexd %0, %2, %3, %1" : "=&r" (res), "=Q" (*addr) : "r" (r.r32[0]), "r" (r.r32[1]) : );
|
||||
#else // Big endian
|
||||
__asm__ volatile("strexd %0, %2, %3, %1" : "=&r" (res), "=Q" (*addr) : "r" (r.r32[1]), "r" (r.r32[0]) : );
|
||||
#endif
|
||||
return res;
|
||||
}*/
|
||||
|
||||
MAKE_INTR_NO_INOUT(1, clrex, "memory")
|
||||
|
||||
// Debug ID Register
|
||||
MAKE_INTR_GET_REG(0, getDidr, "mrc p14, 0, %0, c0, c0, 0")
|
||||
|
||||
// Debug Status and Control Register
|
||||
MAKE_INTR_GET_REG(1, getDscr, "mrc p14, 0, %0, c0, c1, 0")
|
||||
MAKE_INTR_SET_REG(1, setDscr, "mcr p14, 0, %0, c0, c1, 0", "memory")
|
||||
|
||||
// Data Transfer Register
|
||||
|
||||
// Vector Catch Register
|
||||
MAKE_INTR_GET_REG(1, getVcr, "mrc p14, 0, %0, c0, c7, 0")
|
||||
MAKE_INTR_SET_REG(1, setVcr, "mcr p14, 0, %0, c0, c7, 0", "memory")
|
||||
|
||||
// Breakpoint Value Register 0-5
|
||||
MAKE_INTR_GET_REG(1, getBvr0, "mrc p14, 0, %0, c0, c0, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr0, "mcr p14, 0, %0, c0, c0, 4", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBvr1, "mrc p14, 0, %0, c0, c1, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr1, "mcr p14, 0, %0, c0, c1, 4", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBvr2, "mrc p14, 0, %0, c0, c2, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr2, "mcr p14, 0, %0, c0, c2, 4", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBvr3, "mrc p14, 0, %0, c0, c3, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr3, "mcr p14, 0, %0, c0, c3, 4", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBvr4, "mrc p14, 0, %0, c0, c4, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr4, "mcr p14, 0, %0, c0, c4, 4", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBvr5, "mrc p14, 0, %0, c0, c5, 4")
|
||||
MAKE_INTR_SET_REG(1, setBvr5, "mcr p14, 0, %0, c0, c5, 4", "memory")
|
||||
|
||||
// Breakpoint Control Register 0-5
|
||||
MAKE_INTR_GET_REG(1, getBcr0, "mrc p14, 0, %0, c0, c0, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr0, "mcr p14, 0, %0, c0, c0, 5", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBcr1, "mrc p14, 0, %0, c0, c1, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr1, "mcr p14, 0, %0, c0, c1, 5", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBcr2, "mrc p14, 0, %0, c0, c2, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr2, "mcr p14, 0, %0, c0, c2, 5", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBcr3, "mrc p14, 0, %0, c0, c3, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr3, "mcr p14, 0, %0, c0, c3, 5", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBcr4, "mrc p14, 0, %0, c0, c4, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr4, "mcr p14, 0, %0, c0, c4, 5", "memory")
|
||||
MAKE_INTR_GET_REG(1, getBcr5, "mrc p14, 0, %0, c0, c5, 5")
|
||||
MAKE_INTR_SET_REG(1, setBcr5, "mcr p14, 0, %0, c0, c5, 5", "memory")
|
||||
|
||||
// Watchpoint Value Register 0-1
|
||||
MAKE_INTR_GET_REG(1, getWvr0, "mrc p14, 0, %0, c0, c0, 6")
|
||||
MAKE_INTR_SET_REG(1, setWvr0, "mcr p14, 0, %0, c0, c0, 6", "memory")
|
||||
MAKE_INTR_GET_REG(1, getWvr1, "mrc p14, 0, %0, c0, c1, 6")
|
||||
MAKE_INTR_SET_REG(1, setWvr1, "mcr p14, 0, %0, c0, c1, 6", "memory")
|
||||
|
||||
// Watchpoint Control Register 0-1
|
||||
MAKE_INTR_GET_REG(1, getWcr0, "mrc p14, 0, %0, c0, c0, 7")
|
||||
MAKE_INTR_SET_REG(1, setWcr0, "mcr p14, 0, %0, c0, c0, 7", "memory")
|
||||
MAKE_INTR_GET_REG(1, getWcr1, "mrc p14, 0, %0, c0, c1, 7")
|
||||
MAKE_INTR_SET_REG(1, setWcr1, "mcr p14, 0, %0, c0, c1, 7", "memory")
|
||||
|
||||
ALWAYS_INLINE u32 __getCpuId(void)
|
||||
{
|
||||
u32 cpuId;
|
||||
__asm__("mrc p15, 0, %0, c0, c0, 5" : "=r" (cpuId) : );
|
||||
return cpuId & 3;
|
||||
}
|
||||
|
||||
// Auxiliary Control Register
|
||||
MAKE_INTR_GET_REG(1, getAcr, "mrc p15, 0, %0, c1, c0, 1")
|
||||
MAKE_INTR_SET_REG(1, setAcr, "mcr p15, 0, %0, c1, c0, 1", "memory")
|
||||
|
||||
// Translation Table Base Register 0
|
||||
MAKE_INTR_GET_REG(1, getTtbr0, "mrc p15, 0, %0, c2, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setTtbr0, "mcr p15, 0, %0, c2, c0, 0", "memory")
|
||||
|
||||
// Translation Table Base Register 1
|
||||
MAKE_INTR_GET_REG(1, getTtbr1, "mrc p15, 0, %0, c2, c0, 1")
|
||||
MAKE_INTR_SET_REG(1, setTtbr1, "mcr p15, 0, %0, c2, c0, 1", "memory")
|
||||
|
||||
// Translation Table Base Control Register
|
||||
MAKE_INTR_GET_REG(1, getTtbcr, "mrc p15, 0, %0, c2, c0, 2")
|
||||
MAKE_INTR_SET_REG(1, setTtbcr, "mcr p15, 0, %0, c2, c0, 2", "memory")
|
||||
|
||||
// Domain Access Control Register
|
||||
MAKE_INTR_GET_REG(1, getDacr, "mrc p15, 0, %0, c3, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setDacr, "mcr p15, 0, %0, c3, c0, 0", "memory")
|
||||
|
||||
// Data Fault Status Register
|
||||
MAKE_INTR_GET_REG(1, getDfsr, "mrc p15, 0, %0, c5, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setDfsr, "mcr p15, 0, %0, c5, c0, 0", "memory")
|
||||
|
||||
// Instruction Fault Status Register
|
||||
MAKE_INTR_GET_REG(1, getIfsr, "mrc p15, 0, %0, c5, c0, 1")
|
||||
MAKE_INTR_SET_REG(1, setIfsr, "mcr p15, 0, %0, c5, c0, 1", "memory")
|
||||
|
||||
// Fault Address Register
|
||||
MAKE_INTR_GET_REG(1, getFar, "mrc p15, 0, %0, c6, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setFar, "mcr p15, 0, %0, c6, c0, 0", "memory")
|
||||
|
||||
// Watchpoint Fault Address Register
|
||||
MAKE_INTR_GET_REG(1, getWfar, "mrc p15, 0, %0, c6, c0, 1")
|
||||
MAKE_INTR_SET_REG(1, setWfar, "mcr p15, 0, %0, c6, c0, 1", "memory")
|
||||
|
||||
// Flush Prefetch Buffer
|
||||
// Data Synchronization Barrier
|
||||
// Data Memory Barrier
|
||||
MAKE_SET_REG_ZERO(isb, "mcr p15, 0, %0, c7, c5, 4")
|
||||
MAKE_SET_REG_ZERO(dsb, "mcr p15, 0, %0, c7, c10, 4")
|
||||
MAKE_SET_REG_ZERO(dmb, "mcr p15, 0, %0, c7, c10, 5")
|
||||
|
||||
// Auxiliary Control Register
|
||||
MAKE_GET_REG(getAcr, "mrc p15, 0, %0, c1, c0, 1")
|
||||
MAKE_SET_REG(setAcr, "mcr p15, 0, %0, c1, c0, 1")
|
||||
|
||||
// Translation Table Base Register 0
|
||||
MAKE_GET_REG(getTtbr0, "mrc p15, 0, %0, c2, c0, 0")
|
||||
MAKE_SET_REG(setTtbr0, "mcr p15, 0, %0, c2, c0, 0")
|
||||
|
||||
// Translation Table Base Register 1
|
||||
MAKE_GET_REG(getTtbr1, "mrc p15, 0, %0, c2, c0, 1")
|
||||
MAKE_SET_REG(setTtbr1, "mcr p15, 0, %0, c2, c0, 1")
|
||||
|
||||
// Translation Table Base Control Register
|
||||
MAKE_GET_REG(getTtbcr, "mrc p15, 0, %0, c2, c0, 2")
|
||||
MAKE_SET_REG(setTtbcr, "mcr p15, 0, %0, c2, c0, 2")
|
||||
|
||||
// Domain Access Control Register
|
||||
MAKE_GET_REG(getDacr, "mrc p15, 0, %0, c3, c0, 0")
|
||||
MAKE_SET_REG(setDacr, "mcr p15, 0, %0, c3, c0, 0")
|
||||
MAKE_INTR_SET_REG_ZERO(1, isb, "mcr p15, 0, %0, c7, c5, 4", "memory")
|
||||
MAKE_INTR_SET_REG_ZERO(1, dsb, "mcr p15, 0, %0, c7, c10, 4", "memory")
|
||||
MAKE_INTR_SET_REG_ZERO(1, dmb, "mcr p15, 0, %0, c7, c10, 5", "memory")
|
||||
|
||||
// FCSE PID Register
|
||||
MAKE_GET_REG(getFcsepidr, "mrc p15, 0, %0, c13, c0, 0")
|
||||
MAKE_SET_REG(setFcsepidr, "mcr p15, 0, %0, c13, c0, 0")
|
||||
MAKE_INTR_GET_REG(1, getFcsepidr, "mrc p15, 0, %0, c13, c0, 0")
|
||||
MAKE_INTR_SET_REG(1, setFcsepidr, "mcr p15, 0, %0, c13, c0, 0", "memory")
|
||||
|
||||
// Context ID Register
|
||||
MAKE_GET_REG(getCidr, "mrc p15, 0, %0, c13, c0, 1")
|
||||
MAKE_SET_REG(setCidr, "mcr p15, 0, %0, c13, c0, 1")
|
||||
MAKE_INTR_GET_REG(1, getCidr, "mrc p15, 0, %0, c13, c0, 1")
|
||||
MAKE_INTR_SET_REG(1, setCidr, "mcr p15, 0, %0, c13, c0, 1", "memory")
|
||||
#endif // if !__thumb__
|
||||
|
||||
#elif ARM9
|
||||
|
||||
#if !__thumb__
|
||||
MAKE_SET_REG_ZERO(wfi, "mcr p15, 0, %0, c7, c0, 4")
|
||||
MAKE_INTR_SET_REG_ZERO(1, wfi, "mcr p15, 0, %0, c7, c0, 4", "memory")
|
||||
#endif // if !__thumb__
|
||||
#endif // ifdef ARM11
|
||||
|
||||
#undef MAKE_STANDALONE
|
||||
#undef MAKE_GET_REG
|
||||
#undef MAKE_SET_REG_ZERO
|
||||
#undef MAKE_SET_REG
|
||||
#undef MAKE_INTR_NO_INOUT
|
||||
#undef MAKE_INTR_GET_REG
|
||||
#undef MAKE_INTR_SET_REG_ZERO
|
||||
#undef MAKE_INTR_SET_REG
|
||||
|
||||
#endif // if !__ASSEMBLER__
|
||||
|
|
|
@ -22,9 +22,8 @@
|
|||
|
||||
|
||||
|
||||
noreturn void panic();
|
||||
noreturn void panic(void);
|
||||
noreturn void panicMsg(const char *msg);
|
||||
//void debugMemdump(const char *filepath, void *mem, size_t size);
|
||||
|
||||
// Exception tests
|
||||
/*static inline regTest(void)
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
|
||||
// 360x240, no filter.
|
||||
alignas(16) static const u8 gbaGpuInitList[1136] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x44, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xCB, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xCC, 0x02, 0x7F, 0x00,
|
||||
0x01, 0xF0, 0x07, 0x4E, 0x02, 0x08, 0x02, 0x08, 0x03, 0x18, 0x02, 0x08,
|
||||
0x04, 0x28, 0x02, 0x08, 0x05, 0x38, 0x02, 0x08, 0x06, 0x10, 0x20, 0x4C,
|
||||
0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0xBF, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x02, 0x0F, 0x00,
|
||||
0x6E, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x6F, 0x00, 0xA1, 0x0A, 0x00, 0x00,
|
||||
0x68, 0xC3, 0x06, 0x00, 0x64, 0xC3, 0x06, 0x00, 0x62, 0xC3, 0x06, 0x00,
|
||||
0x61, 0xC3, 0x06, 0x00, 0x6F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F,
|
||||
0xBA, 0x02, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, 0xBD, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x4A, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x51, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x01, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x7F, 0x80, 0x00, 0x01, 0x02, 0x03,
|
||||
0x0C, 0x0D, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x0F, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x54, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0,
|
||||
0x89, 0x02, 0x0F, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x01, 0x02, 0x1F, 0x80,
|
||||
0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xA0,
|
||||
0xB9, 0x02, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x02, 0x0F, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x0F, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xBF, 0x00,
|
||||
0x4D, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x04, 0x01, 0x3F, 0x80, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x76, 0x76, 0x01, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0F, 0x00, 0x00, 0x01, 0xE4, 0x00,
|
||||
0x00, 0x01, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x80, 0x30, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x81, 0x00, 0x4F, 0x80, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, // Last 4 bytes: Texture format.
|
||||
0x8E, 0x00, 0x0F, 0x00, 0x01, 0x10, 0x01, 0x00, 0x80, 0x00, 0x0B, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE1, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xC8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x3F, 0x80,
|
||||
0xBF, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xD7, 0xA3, 0xBB,
|
||||
0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0x7F, 0xB0, 0x02, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x5E, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x00, 0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00,
|
||||
0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x68, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x31, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x63, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00
|
||||
};
|
||||
// 240x160 with bilinear scaling, no filter.
|
||||
/*alignas(16) static const u8 gbaGpuInitList[1136] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x44, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xCB, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xCC, 0x02, 0x7F, 0x00,
|
||||
0x01, 0xF0, 0x07, 0x4E, 0x02, 0x08, 0x02, 0x08, 0x03, 0x18, 0x02, 0x08,
|
||||
0x04, 0x28, 0x02, 0x08, 0x05, 0x38, 0x02, 0x08, 0x06, 0x10, 0x20, 0x4C,
|
||||
0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0xBF, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x02, 0x0F, 0x00,
|
||||
0x6E, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x6F, 0x00, 0xA1, 0x0A, 0x00, 0x00,
|
||||
0x68, 0xC3, 0x06, 0x00, 0x64, 0xC3, 0x06, 0x00, 0x62, 0xC3, 0x06, 0x00,
|
||||
0x61, 0xC3, 0x06, 0x00, 0x6F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F,
|
||||
0xBA, 0x02, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, 0xBD, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x4A, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x51, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x01, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x7F, 0x80, 0x00, 0x01, 0x02, 0x03,
|
||||
0x0C, 0x0D, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x0F, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x54, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0,
|
||||
0x89, 0x02, 0x0F, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x01, 0x02, 0x1F, 0x80,
|
||||
0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xA0,
|
||||
0xB9, 0x02, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x02, 0x0F, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x0F, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xBF, 0x00,
|
||||
0x4D, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x04, 0x01, 0x3F, 0x80, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x76, 0x76, 0x01, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0F, 0x00, 0x00, 0x01, 0xE4, 0x00,
|
||||
0x00, 0x01, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x80, 0x30, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x81, 0x00, 0x4F, 0x80, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8E, 0x00, 0x0F, 0x00, 0x01, 0x10, 0x01, 0x00, 0x80, 0x00, 0x0B, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE1, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xC8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x3F, 0x80,
|
||||
0xBF, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xD7, 0xA3, 0xBB,
|
||||
0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0x7F, 0xB0, 0x02, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x5E, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x00, 0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00,
|
||||
0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0xE0, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x31, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x63, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00
|
||||
};*/
|
||||
|
||||
// 360x240, no filter.
|
||||
alignas(16) static const u8 gbaGpuList2[448] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xC0, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0xD7, 0xA3, 0xBB, 0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5E, 0x02, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x10, 0x3E, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12,
|
||||
0x10, 0x00, 0x0F, 0x00
|
||||
};
|
||||
// 240x160 with bilinear scaling, no filter.
|
||||
/*alignas(16) static const u8 gbaGpuList2[448] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xC0, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0xD7, 0xA3, 0xBB, 0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5E, 0x02, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x80, 0x3D, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12,
|
||||
0x10, 0x00, 0x0F, 0x00
|
||||
};*/
|
|
@ -67,6 +67,7 @@
|
|||
#define PDC_CNT_I_MASK_H (1u<<8) // Disables H(Blank?) IRQs.
|
||||
#define PDC_CNT_I_MASK_V (1u<<9) // Disables VBlank IRQs.
|
||||
#define PDC_CNT_I_MASK_ERR (1u<<10) // Disables error IRQs. What kind of errors?
|
||||
#define PDC_CNT_I_MASK_ALL (PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_V | PDC_CNT_I_MASK_H)
|
||||
#define PDC_CNT_OUT_E (1u<<16) // Output enable?
|
||||
|
||||
// REG_LCD_PDC_SWAP
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "kevent.h"
|
||||
|
||||
|
||||
// REG_LGYFB_CNT
|
||||
#define LGYFB_ENABLE (1u)
|
||||
|
@ -31,8 +33,7 @@
|
|||
|
||||
|
||||
|
||||
void LGYFB_init(void);
|
||||
void LGYFB_processFrame(void);
|
||||
void LGYFB_init(KEvent *frameReadyEvent);
|
||||
void LGYFB_deinit(void);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "error_codes.h"
|
||||
|
||||
|
||||
|
||||
Result oafInitAndRun(void);
|
||||
void oafUpdate(void);
|
||||
void oafFinish(void);
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
|
||||
|
||||
extern const u32 _arm7_stub_start[];
|
||||
extern const u32 _arm7_stub_swi[]; // Final ARM9 mem location.
|
||||
extern const u32 _arm7_stub_size[];
|
||||
noreturn void _a7_overlay_stub(void);
|
||||
extern const u32 _a7_overlay_stub_size[];
|
||||
|
||||
noreturn void _a7_stub_start(void);
|
||||
extern u16 _a7_stub9_swi[]; // Final ARM9 mem location.
|
||||
extern const u32 _a7_stub_size[];
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
#pragma once
|
||||
|
||||
// Based on: https://github.com/ARM-software/CMSIS_5/blob/master/CMSIS/Core/Include/cmsis_gcc.h
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
// u32 result, u32 op1.
|
||||
#define MAKE_INTR_U32_1OP(isVolatile, inst) \
|
||||
ALWAYS_INLINE u32 __##inst(u32 op1) \
|
||||
{ \
|
||||
u32 res; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1" : "=r" (res) : "r" (op1) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1" : "=r" (res) : "r" (op1) : ); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
// u32 result, u32 op1, u32 op2.
|
||||
#define MAKE_INTR_U32_2OPS(isVolatile, inst) \
|
||||
ALWAYS_INLINE u32 __##inst(u32 op1, u32 op2) \
|
||||
{ \
|
||||
u32 res; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2" : "=r" (res) : "r" (op1), "r" (op2) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2" : "=r" (res) : "r" (op1), "r" (op2) : ); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
// s32 result, s32 op1, s32 op2.
|
||||
#define MAKE_INTR_S32_2OPS(isVolatile, inst) \
|
||||
ALWAYS_INLINE s32 __##inst(s32 op1, s32 op2) \
|
||||
{ \
|
||||
s32 res; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2" : "=r" (res) : "r" (op1), "r" (op2) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2" : "=r" (res) : "r" (op1), "r" (op2) : ); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
// u32 result, u32 op1, u32 op2, u32 op3.
|
||||
#define MAKE_INTR_U32_3OPS(isVolatile, inst) \
|
||||
ALWAYS_INLINE u32 __##inst(u32 op1, u32 op2, u32 op3) \
|
||||
{ \
|
||||
u32 res; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2, %3" : "=r" (res) : "r" (op1), "r" (op2), "r" (op3) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2, %3" : "=r" (res) : "r" (op1), "r" (op2), "r" (op3) : ); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
// s32 result, s32 op1, s32 op2, s32 op3.
|
||||
#define MAKE_INTR_S32_3OPS(isVolatile, inst) \
|
||||
ALWAYS_INLINE s32 __##inst(s32 op1, s32 op2, s32 op3) \
|
||||
{ \
|
||||
s32 res; \
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2, %3" : "=r" (res) : "r" (op1), "r" (op2), "r" (op3) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2, %3" : "=r" (res) : "r" (op1), "r" (op2), "r" (op3) : ); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#ifndef __ARMEB__ // Little endian
|
||||
// Special instruction with shared u64 input and output.
|
||||
// u64 result, u32 op1, u32 op2, u64 acc.
|
||||
#define MAKE_INTR_U64_U32_U32_U64(isVolatile, inst) \
|
||||
ALWAYS_INLINE u64 __##inst(u32 op1, u32 op2, u64 acc) \
|
||||
{ \
|
||||
union \
|
||||
{ \
|
||||
u32 r32[2]; \
|
||||
u64 r64; \
|
||||
} r; \
|
||||
r.r64 = acc; \
|
||||
\
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2, %3" : "=r" (r.r32[0]), "=r" (r.r32[1]) : "r" (op1), "r" (op2), "0" (r.r32[0]), "1" (r.r32[1]) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2, %3" : "=r" (r.r32[0]), "=r" (r.r32[1]) : "r" (op1), "r" (op2), "0" (r.r32[0]), "1" (r.r32[1]) : ); \
|
||||
\
|
||||
return r.r64; \
|
||||
}
|
||||
|
||||
#else // Big endian
|
||||
|
||||
// Special instruction with shared u64 input and output.
|
||||
// u64 result, u32 op1, u32 op2, u64 acc.
|
||||
#define MAKE_INTR_U64_U32_U32_U64(isVolatile, inst) \
|
||||
ALWAYS_INLINE u64 __##inst(u32 op1, u32 op2, u64 acc) \
|
||||
{ \
|
||||
union \
|
||||
{ \
|
||||
u32 r32[2]; \
|
||||
u64 r64; \
|
||||
} r; \
|
||||
r.r64 = acc; \
|
||||
\
|
||||
if(isVolatile == 1) \
|
||||
__asm__ volatile(#inst " %0, %1, %2, %3" : "=r" (r.r32[1]), "=r" (r.r32[0]) : "r" (op1), "r" (op2), "0" (r.r32[1]), "1" (r.r32[0]) : ); \
|
||||
else \
|
||||
__asm__(#inst " %0, %1, %2, %3" : "=r" (r.r32[1]), "=r" (r.r32[0]) : "r" (op1), "r" (op2), "0" (r.r32[1]), "1" (r.r32[0]) : ); \
|
||||
\
|
||||
return r.r64; \
|
||||
}
|
||||
#endif // #ifndef __ARMEB__
|
||||
|
||||
|
||||
|
||||
// Pack Halfword Bottom Top.
|
||||
#define __pkhbt(op1, op2, sh) \
|
||||
({ \
|
||||
u32 __res; \
|
||||
__asm__("pkhbt %0, %1, %2, lsl %3" : "=r" (__res) : "r" (op1), "r" (op2), "I" (sh) : ); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
// Pack Halfword Top Bottom.
|
||||
#define __pkhtb(op1, op2, sh) \
|
||||
({ \
|
||||
u32 __res, __sh = (sh); \
|
||||
if(__sh == 0) \
|
||||
__asm__("pkhtb %0, %1, %2" : "=r" (__res) : "r" (op1), "r" (op2) : ); \
|
||||
else \
|
||||
__asm__("pkhtb %0, %1, %2, asr %3" : "=r" (__res) : "r" (op1), "r" (op2), "I" (__sh) : ); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
MAKE_INTR_S32_2OPS(1, qadd) // Signed saturating addition.
|
||||
MAKE_INTR_U32_2OPS(0, qadd8) // Signed saturating parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, qadd16) // Signed saturating parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, qasx) // Signed saturating parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_S32_2OPS(1, qdadd) // Signed saturating Double and Add.
|
||||
MAKE_INTR_S32_2OPS(1, qdsub) // Signed saturating Double and Subtract.
|
||||
MAKE_INTR_U32_2OPS(0, qsax) // Signed saturating parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_S32_2OPS(1, qsub) // Signed saturating Subtract.
|
||||
MAKE_INTR_U32_2OPS(0, qsub8) // Signed saturating parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, qsub16) // Signed saturating parallel halfword-wise subtraction.
|
||||
MAKE_INTR_U32_1OP(0, rev) // Reverse the byte order in a word.
|
||||
MAKE_INTR_U32_1OP(0, rev16) // Reverse the byte order in each halfword independently.
|
||||
MAKE_INTR_U32_1OP(0, revsh) // Reverse the byte order in the bottom halfword, and sign extend to 32 bits.
|
||||
MAKE_INTR_U32_2OPS(1, sadd8) // Signed parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(1, sadd16) // Signed parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(1, sasx) // Signed parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(1, sel) // Select bytes from each operand according to the state of the APSR GE flags.
|
||||
MAKE_INTR_U32_2OPS(0, shadd8) // Signed halving parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, shadd16) // Signed halving parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, shasx) // Signed halving parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, shsax) // Signed halving parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, shsub8) // Signed halving parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, shsub16) // Signed halving parallel halfword-wise subtraction.
|
||||
MAKE_INTR_U32_3OPS(1, smlabb) // Signed Multiply Accumulate, with 16-bit operands (bottom, bottom) and a 32-bit result and accumulator.
|
||||
MAKE_INTR_U32_3OPS(1, smlabt) // Signed Multiply Accumulate, with 16-bit operands (bottom, top) and a 32-bit result and accumulator.
|
||||
MAKE_INTR_U32_3OPS(1, smlatb) // Signed Multiply Accumulate, with 16-bit operands (top, bottom) and a 32-bit result and accumulator.
|
||||
MAKE_INTR_U32_3OPS(1, smlatt) // Signed Multiply Accumulate, with 16-bit operands (top, top) and a 32-bit result and accumulator.
|
||||
MAKE_INTR_U32_3OPS(0, smlad) // Dual 16-bit Signed Multiply with Addition of products and 32-bit accumulation.
|
||||
MAKE_INTR_U32_3OPS(0, smladx) // Dual 16-bit Signed exchange Multiply with Addition of products and 32-bit accumulation.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlal) // Signed Long Multiply, with optional Accumulate, with 32-bit operands, and 64-bit result and accumulator.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlald) // Dual 16-bit Signed Multiply with Addition of products and 64-bit Accumulation.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlaldx) // Dual 16-bit Signed exchange Multiply with Addition of products and 64-bit Accumulation.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlalbb) // Signed Multiply-Accumulate with 16-bit operands (bottom, bottom) and a 64-bit accumulator.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlalbt) // Signed Multiply-Accumulate with 16-bit operands (bottom, top) and a 64-bit accumulator.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlaltb) // Signed Multiply-Accumulate with 16-bit operands (top, bottom) and a 64-bit accumulator.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlaltt) // Signed Multiply-Accumulate with 16-bit operands (top, top) and a 64-bit accumulator.
|
||||
MAKE_INTR_U32_3OPS(1, smlawb) // Signed Multiply-Accumulate Wide, with one 32-bit operand and one 16-bit operand (bottom half), and a 32-bit accumulate value, providing the top 32 bits of the result.
|
||||
MAKE_INTR_U32_3OPS(1, smlawt) // Signed Multiply-Accumulate Wide, with one 32-bit operand and one 16-bit operand (top half), and a 32-bit accumulate value, providing the top 32 bits of the result.
|
||||
MAKE_INTR_U32_3OPS(0, smlsd) // Dual 16-bit Signed Multiply with Subtraction of products and 32-bit accumulation.
|
||||
MAKE_INTR_U32_3OPS(0, smlsdx) // Dual 16-bit Signed exchange Multiply with Subtraction of products and 32-bit accumulation.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlsld) // Dual 16-bit Signed Multiply with Subtraction of products and 64-bit Accumulation.
|
||||
MAKE_INTR_U64_U32_U32_U64(0, smlsldx) // Dual 16-bit Signed exchange Multiply with Subtraction of products and 64-bit Accumulation.
|
||||
MAKE_INTR_S32_3OPS(0, smmla) // Signed Most significant word Multiply with Accumulation.
|
||||
MAKE_INTR_S32_3OPS(0, smmlar) // Signed Most significant word Multiply with Accumulation and rounding.
|
||||
MAKE_INTR_S32_3OPS(0, smmls) // Signed Most significant word Multiply with Subtraction.
|
||||
MAKE_INTR_S32_2OPS(0, smmlsr) // Signed Most significant word Multiply with Subtraction and rounding.
|
||||
MAKE_INTR_S32_2OPS(0, smmul) // Signed Most significant word Multiply.
|
||||
MAKE_INTR_S32_2OPS(0, smmulr) // Signed Most significant word Multiply and round.
|
||||
MAKE_INTR_U32_2OPS(1, smuad) // Dual 16-bit Signed Multiply with Addition of products.
|
||||
MAKE_INTR_U32_2OPS(1, smuadx) // Dual 16-bit Signed Multiply with Addition of products with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, smulbb) // Signed Multiply, with 16-bit operands (bottom, bottom) and a 32-bit result.
|
||||
MAKE_INTR_U32_2OPS(0, smulbt) // Signed Multiply, with 16-bit operands (bottom, top) and a 32-bit result.
|
||||
MAKE_INTR_U32_2OPS(0, smultb) // Signed Multiply, with 16-bit operands (top, bottom) and a 32-bit result.
|
||||
MAKE_INTR_U32_2OPS(0, smultt) // Signed Multiply, with 16-bit operands (top, top) and a 32-bit result.
|
||||
// TODO: smull // Signed Long Multiply, with 32-bit operands and 64-bit result.
|
||||
MAKE_INTR_U32_2OPS(0, smulwb) // Signed Multiply Wide, with one 32-bit and one 16-bit operand (bottom half), providing the top 32 bits of the result.
|
||||
MAKE_INTR_U32_2OPS(0, smulwt) // Signed Multiply Wide, with one 32-bit and one 16-bit operand (top half), providing the top 32 bits of the result.
|
||||
/* Doesn't affect any flags? */ MAKE_INTR_U32_2OPS(0, smusd) // Dual 16-bit Signed Multiply with Subtraction of products.
|
||||
/* Doesn't affect any flags? */ MAKE_INTR_U32_2OPS(0, smusdx) // Dual 16-bit Signed Multiply with Subtraction of products with exchange.
|
||||
|
||||
// Signed Saturate to any bit position, with optional shift before saturating.
|
||||
#define __ssat(op1, op2) \
|
||||
({ \
|
||||
s32 __res; \
|
||||
__asm__ volatile("ssat %0, %1, %2" : "=r" (__res) : "I" (op1), "r" (op2) : "cc"); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
// Parallel halfword Saturate.
|
||||
#define __ssat16(op1, op2) \
|
||||
({ \
|
||||
u32 __res; \
|
||||
__asm__ volatile("ssat16 %0, %1, %2" : "=r" (__res) : "I" (op1), "r" (op2) : "cc"); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
MAKE_INTR_U32_2OPS(1, ssax) // Signed parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(1, ssub8) // Signed parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(1, ssub16) // Signed parallel halfword-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, sxtab) // Sign extend Byte with Add, to extend an 8-bit value to a 32-bit value.
|
||||
MAKE_INTR_U32_2OPS(0, sxtab16) // Sign extend two Bytes with Add, to extend two 8-bit values to two 16-bit values.
|
||||
MAKE_INTR_U32_2OPS(0, sxtah) // Sign extend Halfword with Add, to extend a 16-bit value to a 32-bit value.
|
||||
MAKE_INTR_U32_1OP(0, sxtb) // Sign extend Byte, to extend an 8-bit value to a 32-bit value.
|
||||
MAKE_INTR_U32_1OP(0, sxtb16) // Sign extend two bytes.
|
||||
MAKE_INTR_U32_1OP(0, sxth) // Sign extend Halfword.
|
||||
MAKE_INTR_U32_2OPS(1, uadd8) // Unsigned parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(1, uadd16) // Unsigned parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(1, uasx) // Unsigned parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, uhadd8) // Unsigned halving parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, uhadd16) // Unsigned halving parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, uhasx) // Unsigned halving parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, uhsax) // Unsigned halving parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, uhsub8) // Unsigned halving parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, uhsub16) // Unsigned halving parallel halfword-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, uqadd8) // Unsigned saturating parallel byte-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, uqadd16) // Unsigned saturating parallel halfword-wise addition.
|
||||
MAKE_INTR_U32_2OPS(0, uqasx) // Unsigned saturating parallel add and subtract halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, uqsax) // Unsigned saturating parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(0, uqsub8) // Unsigned saturating parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, uqsub16) // Unsigned saturating parallel halfword-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(0, usad8) // Unsigned Sum of Absolute Differences.
|
||||
MAKE_INTR_U32_3OPS(0, usada8) // Unsigned Sum of Absolute Differences and Accumulate.
|
||||
|
||||
// Unsigned Saturate to any bit position, with optional shift before saturating.
|
||||
#define __usat(op1, op2) \
|
||||
({ \
|
||||
u32 __res; \
|
||||
__asm__ volatile("usat %0, %1, %2" : "=r" (__res) : "I" (op1), "r" (op2) : "cc"); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
// Parallel halfword Saturate.
|
||||
#define __usat16(op1, op2) \
|
||||
({ \
|
||||
u32 __res; \
|
||||
__asm__ volatile("usat16 %0, %1, %2" : "=r" (__res) : "I" (op1), "r" (op2) : "cc"); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
MAKE_INTR_U32_2OPS(1, usax) // Unsigned parallel subtract and add halfwords with exchange.
|
||||
MAKE_INTR_U32_2OPS(1, usub8) // Unsigned parallel byte-wise subtraction.
|
||||
MAKE_INTR_U32_2OPS(1, usub16) // Unsigned parallel halfword-wise subtraction.
|
||||
//MAKE_INTR_U32_2OPS(0, uxtab) // Zero extend Byte and Add.
|
||||
MAKE_INTR_U32_2OPS(0, uxtab16) // Zero extend two Bytes and Add.
|
||||
//MAKE_INTR_U32_2OPS(0, uxtah) // Zero extend Halfword and Add.
|
||||
//MAKE_INTR_U32_1OP(0, uxtb) // Zero extend Byte.
|
||||
MAKE_INTR_U32_1OP(0, uxtb16) // Zero extend two Bytes.
|
||||
//MAKE_INTR_U32_1OP(0, uxth) // Zero extend Halfword.
|
||||
|
||||
|
||||
#undef MAKE_INTR_U32_1OP
|
||||
#undef MAKE_INTR_U32_2OPS
|
||||
#undef MAKE_INTR_U32_3OPS
|
|
@ -14,30 +14,33 @@ enum
|
|||
// Common errors.
|
||||
RES_OK = 0u,
|
||||
RES_SD_CARD_REMOVED = 1u,
|
||||
RES_INVALID_ARG = 2u,
|
||||
RES_OUT_OF_MEM = 3u,
|
||||
RES_DISK_FULL = 2u,
|
||||
RES_INVALID_ARG = 3u,
|
||||
RES_OUT_OF_MEM = 4u,
|
||||
RES_OUT_OF_RANGE = 5u,
|
||||
RES_NOT_FOUND = 6u,
|
||||
|
||||
// fatfs errors.
|
||||
// Caution: Update fres2Res() in fs.c on ARM9 if this changes!
|
||||
RES_FR_DISK_ERR = 4u, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
RES_FR_INT_ERR = 5u, /* (2) Assertion failed */
|
||||
RES_FR_NOT_READY = 6u, /* (3) The physical drive cannot work */
|
||||
RES_FR_NO_FILE = 7u, /* (4) Could not find the file */
|
||||
RES_FR_NO_PATH = 8u, /* (5) Could not find the path */
|
||||
RES_FR_INVALID_NAME = 9u, /* (6) The path name format is invalid */
|
||||
RES_FR_DENIED = 10u, /* (7) Access denied due to prohibited access or directory full */
|
||||
RES_FR_EXIST = 11u, /* (8) Access denied due to prohibited access */
|
||||
RES_FR_INVALID_OBJECT = 12u, /* (9) The file/directory object is invalid */
|
||||
RES_FR_WRITE_PROTECTED = 13u, /* (10) The physical drive is write protected */
|
||||
RES_FR_INVALID_DRIVE = 14u, /* (11) The logical drive number is invalid */
|
||||
RES_FR_NOT_ENABLED = 15u, /* (12) The volume has no work area */
|
||||
RES_FR_NO_FILESYSTEM = 16u, /* (13) There is no valid FAT volume */
|
||||
RES_FR_MKFS_ABORTED = 17u, /* (14) The f_mkfs() aborted due to any problem */
|
||||
RES_FR_TIMEOUT = 18u, /* (15) Could not get a grant to access the volume within defined period */
|
||||
RES_FR_LOCKED = 19u, /* (16) The operation is rejected according to the file sharing policy */
|
||||
RES_FR_NOT_ENOUGH_CORE = 20u, /* (17) LFN working buffer could not be allocated */
|
||||
RES_FR_TOO_MANY_OPEN_FILES = 21u, /* (18) Number of open files > FF_FS_LOCK */
|
||||
RES_FR_INVALID_PARAMETER = 22u, /* (19) Given parameter is invalid */
|
||||
RES_FR_DISK_ERR = 7u, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
RES_FR_INT_ERR = 8u, /* (2) Assertion failed */
|
||||
RES_FR_NOT_READY = 9u, /* (3) The physical drive cannot work */
|
||||
RES_FR_NO_FILE = 10u, /* (4) Could not find the file */
|
||||
RES_FR_NO_PATH = 11u, /* (5) Could not find the path */
|
||||
RES_FR_INVALID_NAME = 12u, /* (6) The path name format is invalid */
|
||||
RES_FR_DENIED = 13u, /* (7) Access denied due to prohibited access or directory full */
|
||||
RES_FR_EXIST = 14u, /* (8) Access denied due to prohibited access */
|
||||
RES_FR_INVALID_OBJECT = 15u, /* (9) The file/directory object is invalid */
|
||||
RES_FR_WRITE_PROTECTED = 16u, /* (10) The physical drive is write protected */
|
||||
RES_FR_INVALID_DRIVE = 17u, /* (11) The logical drive number is invalid */
|
||||
RES_FR_NOT_ENABLED = 18u, /* (12) The volume has no work area */
|
||||
RES_FR_NO_FILESYSTEM = 19u, /* (13) There is no valid FAT volume */
|
||||
RES_FR_MKFS_ABORTED = 20u, /* (14) The f_mkfs() aborted due to any problem */
|
||||
RES_FR_TIMEOUT = 21u, /* (15) Could not get a grant to access the volume within defined period */
|
||||
RES_FR_LOCKED = 22u, /* (16) The operation is rejected according to the file sharing policy */
|
||||
RES_FR_NOT_ENOUGH_CORE = 23u, /* (17) LFN working buffer could not be allocated */
|
||||
RES_FR_TOO_MANY_OPEN_FILES = 24u, /* (18) Number of open files > FF_FS_LOCK */
|
||||
RES_FR_INVALID_PARAMETER = 25u, /* (19) Given parameter is invalid */
|
||||
|
||||
// Custom errors.
|
||||
RES_ROM_TOO_BIG = MAKE_CUSTOM_ERR(0),
|
||||
|
|
|
@ -20,11 +20,7 @@
|
|||
|
||||
#include "types.h"
|
||||
#include "error_codes.h"
|
||||
#ifdef ARM11
|
||||
#include "../thirdparty/fatfs/ff.h"
|
||||
#else
|
||||
#include "fatfs/ff.h"
|
||||
#endif // ifdef ARM11
|
||||
|
||||
|
||||
#define FS_MAX_DRIVES (FF_VOLUMES)
|
||||
|
@ -55,6 +51,7 @@ u32 fTell(FHandle h);
|
|||
u32 fSize(FHandle h);
|
||||
Result fClose(FHandle h);
|
||||
Result fStat(const char *const path, FILINFO *const fi);
|
||||
Result fChdir(const char *const path);
|
||||
Result fOpenDir(DHandle *const hOut, const char *const path);
|
||||
Result fReadDir(DHandle h, FILINFO *const fi, u32 num, u32 *const entriesRead);
|
||||
Result fCloseDir(DHandle h);
|
||||
|
|
|
@ -23,5 +23,6 @@
|
|||
|
||||
|
||||
|
||||
Result fsQuickRead(void *const buf, const char *const path, u32 size);
|
||||
Result fsQuickWrite(void *const buf, const char *const path, u32 size);
|
||||
Result fsQuickRead(const char *const path, void *const buf, u32 size);
|
||||
Result fsQuickWrite(const char *const path, const void *const buf, u32 size);
|
||||
Result fsMakePath(const char *const path);
|
||||
|
|
|
@ -103,14 +103,12 @@ typedef struct
|
|||
|
||||
|
||||
|
||||
Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePath);
|
||||
Result LGY_setGbaRtc(const GbaRtc rtc);
|
||||
Result LGY_getGbaRtc(GbaRtc *const out);
|
||||
Result LGY_backupGbaSave(void);
|
||||
#ifdef ARM11
|
||||
Result LGY_prepareGbaMode(bool biosIntro, char *const romPath);
|
||||
void LGY_switchMode(void);
|
||||
void LGY_handleEvents(void);
|
||||
void LGY_handleOverrides(void);
|
||||
void LGY_deinit(void);
|
||||
#elif ARM9
|
||||
Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePath);
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,7 @@ typedef enum
|
|||
IPC_CMD9_FSIZE = MAKE_CMD9(0, 0, 1),
|
||||
IPC_CMD9_FCLOSE = MAKE_CMD9(0, 0, 1),
|
||||
IPC_CMD9_FSTAT = MAKE_CMD9(1, 1, 0),
|
||||
IPC_CMD9_FCHDIR = MAKE_CMD9(1, 0, 0),
|
||||
IPC_CMD9_FOPEN_DIR = MAKE_CMD9(1, 1, 0),
|
||||
IPC_CMD9_FREAD_DIR = MAKE_CMD9(0, 2, 2),
|
||||
IPC_CMD9_FCLOSE_DIR = MAKE_CMD9(0, 0, 1),
|
||||
|
@ -64,7 +65,7 @@ typedef enum
|
|||
IPC_CMD9_BACKUP_GBA_SAVE = MAKE_CMD9(0, 0, 0),
|
||||
|
||||
// Miscellaneous API.
|
||||
IPC_CMD9_PREPARE_POWER = MAKE_CMD9(0, 0, 0)
|
||||
IPC_CMD9_PREPARE_POWER = MAKE_CMD9(0, 0, 0) // Also used for panic() and guruMeditation().
|
||||
} IpcCmd9;
|
||||
|
||||
enum {_CMD11_C_BASE = __COUNTER__ + 1}; // Start at 0.
|
||||
|
|
|
@ -28,15 +28,16 @@
|
|||
|
||||
|
||||
|
||||
#define ALIGN(a) __attribute__((aligned(a))) // Use alignas() instead.
|
||||
#define NAKED __attribute__((naked))
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#define PACKED __attribute__((packed))
|
||||
#define TARGET_ARM __attribute__((target("arm")))
|
||||
#define TARGET_THUMB __attribute__((target("thumb")))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define USED __attribute__((used))
|
||||
#define WEAK __attribute__((weak))
|
||||
#define ALIGN(a) __attribute__((aligned(a))) // Use alignas() instead.
|
||||
#define NAKED __attribute__((naked))
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#define ALWAYS_INLINE __attribute__((always_inline)) static inline
|
||||
#define PACKED __attribute__((packed))
|
||||
#define TARGET_ARM __attribute__((target("arm")))
|
||||
#define TARGET_THUMB __attribute__((target("thumb")))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define USED __attribute__((used))
|
||||
#define WEAK __attribute__((weak))
|
||||
|
||||
|
||||
typedef uint8_t u8;
|
||||
|
|
|
@ -26,7 +26,36 @@
|
|||
|
||||
|
||||
|
||||
NAKED void wait(u32 cycles);
|
||||
/**
|
||||
* @brief Waits at least the specified amount of CPU cycles.
|
||||
*
|
||||
* @param[in] cycles The cycles to wait.
|
||||
*/
|
||||
NAKED void wait_cycles(u32 cycles);
|
||||
|
||||
/**
|
||||
* @brief Safer strcpy with checks.
|
||||
* The dst string always gets terminated except when num is 0.
|
||||
* If the src string is too long nothing is copied and dst will be terminated.
|
||||
* This function is not safe against race conditions!
|
||||
*
|
||||
* @param dst The destination pointer.
|
||||
* @param[in] src The source pointer.
|
||||
* @param[in] num Maximum number of chars to copy including null terminator.
|
||||
*
|
||||
* @return The length of the copied string in bytes including null terminator.
|
||||
*/
|
||||
size_t safeStrcpy(char *const dst, const char *const src, size_t num);
|
||||
|
||||
/**
|
||||
* @brief Basic string to float conversion. Limited to 6 decimal places.
|
||||
* Doesn't support exponents.
|
||||
*
|
||||
* @param[in] str The string.
|
||||
*
|
||||
* @return The floatingpoint number represented by str.
|
||||
*/
|
||||
float str2float(const char *str);
|
||||
|
||||
// case insensitive string compare function
|
||||
int strnicmp(const char *str1, const char *str2, u32 len);
|
||||
|
@ -46,3 +75,23 @@ static inline u32 intLog2(u32 val)
|
|||
// The result is undefined if __builtin_clz() is called with 0.
|
||||
return (val ? 31u - __builtin_clz(val) : 0u);
|
||||
}
|
||||
|
||||
// Round up to the next power of 2.
|
||||
static inline u32 nextPow2(u32 val)
|
||||
{
|
||||
// Portable variant:
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
/*val--;
|
||||
val |= val>>1;
|
||||
val |= val>>2;
|
||||
val |= val>>4;
|
||||
val |= val>>8;
|
||||
val |= val>>16;
|
||||
val++;
|
||||
|
||||
return val;*/
|
||||
|
||||
// Warning: Allowed range is 2 - 2147483648.
|
||||
// Everything else is undefined behavior.
|
||||
return 1u<<(32u - __builtin_clz(val - 1));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// MAX_PRIO_BITS The number of available priorities. Minimum 3. Maximum 32.
|
||||
#define MAX_PRIO_BITS (4)
|
||||
|
||||
/*
|
||||
* Maximum number of objects we can create (Slabheap).
|
||||
*/
|
||||
#define MAX_TASKS (3) // Including main and idle task.
|
||||
#define MAX_EVENTS (10)
|
||||
#define MAX_MUTEXES (3)
|
||||
#define MAX_SEMAPHORES (0)
|
||||
#define MAX_TIMERS (0)
|
||||
|
||||
#define IDLE_STACK_SIZE (0x1000) // Keep in mind this stack is used in interrupt contex! TODO: Change this.
|
||||
|
||||
|
||||
|
||||
// TODO: More checks. For example slabheap.
|
||||
#if (MAX_PRIO_BITS < 3 || MAX_PRIO_BITS > 32)
|
||||
#error "Invalid number of maximum task priorities!"
|
||||
#endif
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 r4;
|
||||
u32 r5;
|
||||
u32 r6;
|
||||
u32 r7;
|
||||
u32 r8;
|
||||
u32 r9;
|
||||
u32 r10;
|
||||
u32 r11;
|
||||
u32 lr; // pc
|
||||
} cpuRegs;
|
||||
|
||||
|
||||
|
||||
KRes switchContext(KRes res, uintptr_t *oldSp, uintptr_t newSp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "types.h"
|
||||
#include "internal/list.h"
|
||||
#include "kernel.h"
|
||||
#include "arm.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TASK_STATE_DEAD = 0,
|
||||
//TASK_STATE_READY = 1,
|
||||
TASK_STATE_RUNNING = 1,
|
||||
TASK_STATE_BLOCKED = 2,
|
||||
TASK_STATE_RUNNING_SHORT = 3 // Continue task as soon as the woken ones are finished.
|
||||
} TaskState;
|
||||
|
||||
struct TaskCb
|
||||
{
|
||||
ListNode node;
|
||||
u8 core; // TODO: Multicore
|
||||
u8 prio;
|
||||
u8 id;
|
||||
KRes res; // Last error code. Also abused for taskArg.
|
||||
uintptr_t savedSp;
|
||||
void *stack;
|
||||
// Name?
|
||||
// Exit code?
|
||||
}; // Task context
|
||||
typedef struct TaskCb TaskCb;
|
||||
static_assert(offsetof(TaskCb, node) == 0, "Error: Member node of TaskCb is not at offset 0!");
|
||||
|
||||
|
||||
|
||||
const TaskCb* getCurrentTask(void);
|
||||
KRes waitQueueBlock(ListNode *waitQueue);
|
||||
bool waitQueueWakeN(ListNode *waitQueue, u32 wakeCount, KRes res, bool reschedule);
|
||||
|
||||
|
||||
static inline void kernelLock(void)
|
||||
{
|
||||
__cpsid(i);
|
||||
//spinlockLock(&g_lock);
|
||||
}
|
||||
static inline void kernelUnlock(void)
|
||||
{
|
||||
__cpsie(i);
|
||||
//spinlockUnlock(&g_lock);
|
||||
}
|
||||
|
||||
|
||||
// These functions belong in other headers however we
|
||||
// don't want to make them accessible in the public API.
|
||||
void _eventSlabInit(void);
|
||||
void _mutexSlabInit(void);
|
||||
void _semaphoreSlabInit(void);
|
||||
void _timerInit(void);
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2019 Aurora Wright, TuxSH, derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Based on https://github.com/AuroraWright/Luma3DS/blob/master/arm9/source/alignedseqmemcpy.s
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
|
||||
void kmemcpy(u32 *restrict dst, const u32 *restrict src, u32 size);
|
||||
|
||||
// Alias of kmemcpy() with volatile arguments.
|
||||
void iokmemcpy(vu32 *restrict dst, const vu32 *restrict src, u32 size);
|
||||
|
||||
void kmemset(u32 *ptr, u32 value, u32 size);
|
||||
|
||||
// Alias of kmemset() with volatile arguments.
|
||||
void iokmemset(vu32 *ptr, u32 value, u32 size);
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
// Based on https://github.com/torvalds/linux/blob/master/include/linux/list.h
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#define LIST_INIT_VAL(name) ((ListNode){&(name), &(name)})
|
||||
|
||||
#define LIST_ENTRY(ptr, type, member) \
|
||||
({ \
|
||||
void *__mptr = (void*)(ptr); \
|
||||
(type*)(__mptr - (size_t)&((type*)0)->member); \
|
||||
})
|
||||
|
||||
#define LIST_FIRST_ENTRY(ptr, type, member) \
|
||||
LIST_ENTRY((ptr)->next, type, member)
|
||||
|
||||
#define LIST_NEXT_ENTRY(pos, member) \
|
||||
LIST_ENTRY((pos)->member.next, typeof(*(pos)), member)
|
||||
|
||||
#define LIST_FOR_EACH_ENTRY(pos, start, member) \
|
||||
for(pos = LIST_FIRST_ENTRY(start, typeof(*pos), member); \
|
||||
&pos->member != (start); \
|
||||
pos = LIST_NEXT_ENTRY(pos, member))
|
||||
|
||||
|
||||
typedef struct ListNode ListNode;
|
||||
struct ListNode
|
||||
{
|
||||
ListNode *next;
|
||||
ListNode *prev;
|
||||
};
|
||||
//static_assert(offsetof(ListNode, next) == 0, "Error: Member next of ListNode is not at offset 0!");
|
||||
|
||||
|
||||
|
||||
static inline void listInit(ListNode *start)
|
||||
{
|
||||
*start = LIST_INIT_VAL(*start);
|
||||
}
|
||||
|
||||
static inline bool listEmpty(const ListNode *start)
|
||||
{
|
||||
return start->next == start;
|
||||
}
|
||||
|
||||
// Internal function. Don't use unless you know what you are doing!
|
||||
static inline void _listAdd(ListNode *node, ListNode *next, ListNode *prev)
|
||||
{
|
||||
node->next = next;
|
||||
node->prev = prev;
|
||||
next->prev = node;
|
||||
prev->next = node;
|
||||
}
|
||||
|
||||
static inline void listAddBefore(ListNode *entry, ListNode *node)
|
||||
{
|
||||
_listAdd(node, entry, entry->prev);
|
||||
}
|
||||
|
||||
static inline void listAddAfter(ListNode *entry, ListNode *node)
|
||||
{
|
||||
_listAdd(node, entry->next, entry);
|
||||
}
|
||||
|
||||
// Internal function. Don't use unless you know what you are doing!
|
||||
static inline void _listDelete(ListNode *next, ListNode *prev)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline void listDelete(ListNode *entry)
|
||||
{
|
||||
_listDelete(entry->next, entry->prev);
|
||||
}
|
||||
|
||||
static inline ListNode* listRemoveTail(ListNode *start)
|
||||
{
|
||||
ListNode *const node = start->next;
|
||||
|
||||
listDelete(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline ListNode* listRemoveHead(ListNode *start)
|
||||
{
|
||||
ListNode *const node = start->prev;
|
||||
|
||||
listDelete(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Some function aliases for queues.
|
||||
#define listPush(start, node) listAddBefore((start), (node))
|
||||
#define listPop(start) listRemoveTail((start))
|
||||
#define listPushTail(start, node) listAddAfter((start), (node))
|
||||
#define listPopHead(start) listRemoveHead((start))
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include "internal/list.h"
|
||||
|
||||
|
||||
typedef ListNode SlabHeap;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the slabheap.
|
||||
*
|
||||
* @param slab SlabHeap object pointer.
|
||||
* @param[in] objSize The size of the object slots.
|
||||
* @param[in] num The maximum number of object slots.
|
||||
*/
|
||||
void slabInit(SlabHeap *slab, size_t objSize, size_t num);
|
||||
|
||||
/**
|
||||
* @brief Allocates an object slot from the slabheap.
|
||||
*
|
||||
* @param slab SlabHeap object pointer.
|
||||
*
|
||||
* @return Returns a pointer to the object slot.
|
||||
*/
|
||||
void* slabAlloc(SlabHeap *slab);
|
||||
|
||||
/**
|
||||
* @brief Same as slabAlloc() but clears slots.
|
||||
*
|
||||
* @param slab SlabHeap object pointer.
|
||||
* @param[in] clrSize The clear size (passed to memset()).
|
||||
*
|
||||
* @return Returns a pointer to the object slot.
|
||||
*/
|
||||
void* slabCalloc(SlabHeap *slab, size_t clrSize);
|
||||
|
||||
/**
|
||||
* @brief Deallocates an object slot.
|
||||
*
|
||||
* @param slab SlabHeap object pointer.
|
||||
* @param ptr The object slot pointer.
|
||||
*/
|
||||
void slabFree(SlabHeap *slab, void *ptr);
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
|
||||
static inline void spinlockLock(u32 *lock)
|
||||
{
|
||||
u32 tmp;
|
||||
__asm__ volatile("1: ldrex %0, [%1]\n"
|
||||
" teq %0, #0\n"
|
||||
" wfene\n"
|
||||
" strexeq %0, %2, [%1]\n"
|
||||
" teqeq %0, #0\n"
|
||||
" bne 1b\n"
|
||||
" mcr p15, 0, %0, c7, c10, 5" // DMB
|
||||
: "=&r" (tmp) : "r" (lock), "r" (1) : "cc", "memory");
|
||||
}
|
||||
|
||||
static inline void spinlockUnlock(u32 *lock)
|
||||
{
|
||||
__asm__ volatile("mcr p15, 0, %0, c7, c10, 5\n" // DMB
|
||||
"str %0, [%1]\n"
|
||||
"mcr p15, 0, %0, c7, c10, 4\n" // DSB
|
||||
"sev"
|
||||
: : "r" (0), "r" (lock) : "memory");
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#define LIKELY(expr) __builtin_expect((expr), true)
|
||||
#define UNLIKELY(expr) __builtin_expect((expr), false)
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
KRES_OK = 0, // No error.
|
||||
KRES_INVALID_HANDLE = 1, // The handle or object doesn't exist.
|
||||
KRES_HANDLE_DELETED = 2, // The handle has been deleted externally.
|
||||
//KRES_WAIT_QUEUE_FULL = 3, // The wait queue is full. We can't block on it.
|
||||
KRES_WOULD_BLOCK = 3, // The function would block. For non-blocking APIs.
|
||||
KRES_NO_PERMISSIONS = 4 // You have no permissions. Example unlocking a mutex on a different task.
|
||||
};
|
||||
|
||||
typedef uintptr_t KRes; // See createTask()
|
||||
typedef struct TaskCb KTask;
|
||||
typedef void (*TaskFunc)(void*);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the kernel. Only call this once.
|
||||
*
|
||||
* @param[in] priority The priority of the main task.
|
||||
*/
|
||||
void kernelInit(uint8_t priority);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates a new kernel task.
|
||||
*
|
||||
* @param[in] stackSize The stack size.
|
||||
* @param[in] priority The priority.
|
||||
* @param[in] entry The entry function.
|
||||
* @param taskArg The task entry function argument.
|
||||
*
|
||||
* @return Returns a KTask handle.
|
||||
*/
|
||||
KTask* createTask(size_t stackSize, uint8_t priority, TaskFunc entry, void *taskArg);
|
||||
|
||||
/**
|
||||
* @brief Switches to the next task. Use with care.
|
||||
*/
|
||||
void yieldTask(void);
|
||||
|
||||
/**
|
||||
* @brief Task exit function. Must be called from the task that exits.
|
||||
*/
|
||||
void taskExit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct KEvent KEvent;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates a new KEvent.
|
||||
*
|
||||
* @param[in] oneShot Event fires only once and auto clears if true.
|
||||
*
|
||||
* @return The KEvent pointer or NULL when out of memory.
|
||||
*/
|
||||
KEvent* createEvent(bool oneShot);
|
||||
|
||||
/**
|
||||
* @brief Deletes a KEvent.
|
||||
*
|
||||
* @param[in] kevent The KEvent pointer.
|
||||
*/
|
||||
void deleteEvent(KEvent *const kevent);
|
||||
|
||||
/**
|
||||
* @brief Binds the given KEvent to an interrupt.
|
||||
*
|
||||
* @param[in] kevent The KEvent pointer.
|
||||
* @param[in] id The interrupt id.
|
||||
* @param[in] prio The interrupt priority.
|
||||
*/
|
||||
void bindInterruptToEvent(KEvent *const kevent, uint8_t id, uint8_t prio);
|
||||
|
||||
void unbindInterruptEvent(uint8_t id);
|
||||
|
||||
/**
|
||||
* @brief Waits for the given KEvent to be signaled.
|
||||
*
|
||||
* @param[in] kevent The KEvent pointer.
|
||||
*
|
||||
* @return Returns the result. See Kres above.
|
||||
*/
|
||||
KRes waitForEvent(KEvent *const kevent);
|
||||
|
||||
/**
|
||||
* @brief Signals a KEvent.
|
||||
*
|
||||
* @param[in] kevent The KEvent pointer.
|
||||
* @param[in] reschedule Set to true to immediately reschedule.
|
||||
*/
|
||||
void signalEvent(KEvent *const kevent, bool reschedule);
|
||||
|
||||
/**
|
||||
* @brief Clears a KEvent.
|
||||
*
|
||||
* @param[in] kevent The KEvent pointer.
|
||||
*/
|
||||
void clearEvent(KEvent *const kevent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct KMutex KMutex; // TODO: Implement this using semaphores?
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates a new KMutex.
|
||||
*
|
||||
* @return The KMutex pointer or NULL when out of memory.
|
||||
*/
|
||||
KMutex* createMutex(void);
|
||||
|
||||
/**
|
||||
* @brief Deletes a KMutex.
|
||||
*
|
||||
* @param[in] kmutex The KMutex pointer.
|
||||
*/
|
||||
void deleteMutex(KMutex *const kmutex);
|
||||
|
||||
/**
|
||||
* @brief Locks a KMutex.
|
||||
*
|
||||
* @param[in] kmutex The KMutex pointer.
|
||||
*
|
||||
* @return Returns the result. See Kres.
|
||||
*/
|
||||
KRes lockMutex(KMutex *const kmutex);
|
||||
|
||||
/**
|
||||
* @brief Unlocks a KMutex.
|
||||
*
|
||||
* @param[in] kmutex The KMutex pointer.
|
||||
*
|
||||
* @return Returns KRES_NO_PERMISSIONS if the current task
|
||||
* @return is not the owner. Otherwise KRES_OK.
|
||||
*/
|
||||
KRes unlockMutex(KMutex *const kmutex);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct KSema KSema;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates a new KSema.
|
||||
*
|
||||
* @param[in] count The initial count of the semaphore.
|
||||
*
|
||||
* @return The KSema pointer or NULL when out of memory.
|
||||
*/
|
||||
KSema* createSemaphore(int32_t count);
|
||||
|
||||
/**
|
||||
* @brief Deletes a KSema.
|
||||
*
|
||||
* @param[in] ksema The KSema handle.
|
||||
*/
|
||||
void deleteSemaphore(KSema *const ksema);
|
||||
|
||||
/**
|
||||
* @brief Polls a KSema.
|
||||
*
|
||||
* @param[in] ksema The KSema pointer.
|
||||
*
|
||||
* @return Returns KRES_OK or KRES_WOULD_BLOCK.
|
||||
*/
|
||||
KRes pollSemaphore(KSema *const ksema);
|
||||
|
||||
/**
|
||||
* @brief Decreases the semaphore and blocks if <=0.
|
||||
*
|
||||
* @param[in] ksema The KSema pointer.
|
||||
*
|
||||
* @return Returns the result. See Kres above.
|
||||
*/
|
||||
KRes waitForSemaphore(KSema *const ksema);
|
||||
|
||||
/**
|
||||
* @brief Increases the semaphore and wakes up signalCount waiting tasks if any.
|
||||
*
|
||||
* @param[in] ksema The KSema pointer.
|
||||
* @param[in] signalCount The number to increase the semaphore by.
|
||||
* @param[in] reschedule Set to true to immediately reschedule.
|
||||
*/
|
||||
void signalSemaphore(KSema *const ksema, uint32_t signalCount, bool reschedule);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct KTimer KTimer;
|
||||
|
||||
|
||||
|
||||
KTimer* createTimer(bool pulse);
|
||||
|
||||
void deleteTimer(KTimer *const ktimer);
|
||||
|
||||
void startTimer(KTimer *const ktimer, uint32_t usec);
|
||||
|
||||
void stopTimer(KTimer *const ktimer);
|
||||
|
||||
KRes waitForTimer(KTimer *const ktimer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "asm_macros.h"
|
||||
|
||||
.arm
|
||||
.cpu mpcore
|
||||
.fpu vfpv2
|
||||
|
||||
|
||||
|
||||
@ void switchContextNoScratchRegs(u32 *curRegs, const u32 *newRegs)
|
||||
/*ASM_FUNC switchContextNoScratchRegs
|
||||
stmia r0!, {r4-r11, sp, lr}
|
||||
add r0, r0, #20 @ Skip r0-r3, r12
|
||||
adr r2, switchContextNoScratchRegs_end
|
||||
mrs r3, cpsr
|
||||
stmia r0, {r2, r3}
|
||||
ldmia r1!, {r4-r11, sp, lr}
|
||||
add r1, r1, #20 @ Skip r0-r3, r12
|
||||
rfeia r1
|
||||
switchContextNoScratchRegs_end:
|
||||
cpsie i
|
||||
bx lr
|
||||
|
||||
@ void switchContextAllRegs(u32 *curRegs, const u32 *newRegs)
|
||||
ASM_FUNC switchContextAllRegs
|
||||
stmia r0!, {r4-r11, sp, lr}
|
||||
add r0, r0, #20 @ Skip r0-r3, r12
|
||||
adr r2, switchContextAllRegs_end
|
||||
mrs r3, cpsr
|
||||
stmia r0, {r2, r3}
|
||||
ldmia r1!, {r4-r11, sp, lr}
|
||||
ldr r3, [r1, #24] @ cpsr
|
||||
cps #19 @ SVC mode
|
||||
msr spsr_fsxc, r3
|
||||
ldmia r1, {r0-r3, r12, pc}^
|
||||
switchContextAllRegs_end:
|
||||
cpsie i
|
||||
bx lr*/
|
||||
|
||||
@ KRes switchContext(KRes res, uintptr_t *oldSp, uintptr_t newSp);
|
||||
BEGIN_ASM_FUNC switchContext
|
||||
stmfd sp!, {r4-r11, lr}
|
||||
str sp, [r1]
|
||||
mov sp, r2
|
||||
ldmfd sp!, {r4-r11, lr}
|
||||
bx lr
|
||||
END_ASM_FUNC
|
||||
|
||||
|
||||
.pool
|
|
@ -0,0 +1,258 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "kernel.h"
|
||||
#include "internal/config.h"
|
||||
#include "internal/kernel_private.h"
|
||||
#include "internal/kmemcpy_set.h"
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/util.h"
|
||||
#include "internal/list.h"
|
||||
#include "internal/contextswitch.h"
|
||||
#include "arm.h"
|
||||
|
||||
|
||||
static TaskCb *g_curTask = NULL;
|
||||
static u32 g_readyBitmap = 0;
|
||||
static ListNode g_runQueues[MAX_PRIO_BITS] = {0};
|
||||
static SlabHeap g_taskSlab = {0};
|
||||
static u32 g_numTasks = 0;
|
||||
static TaskCb *g_curDeadTask = NULL; // TODO: Improve dead task handling.
|
||||
|
||||
|
||||
|
||||
static KRes scheduler(TaskState curTaskState);
|
||||
noreturn static void kernelIdleTask(void);
|
||||
|
||||
static void initKernelState(void)
|
||||
{
|
||||
for(int i = 0; i < MAX_PRIO_BITS; i++) listInit(&g_runQueues[i]);
|
||||
slabInit(&g_taskSlab, sizeof(TaskCb), MAX_TASKS);
|
||||
_eventSlabInit();
|
||||
_mutexSlabInit();
|
||||
_semaphoreSlabInit();
|
||||
//_timerInit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Public kernel API.
|
||||
*/
|
||||
// TODO: Are KTask handles needed? (for the main task)
|
||||
// TODO: Thread local storage. Needed?
|
||||
void kernelInit(uint8_t priority)
|
||||
{
|
||||
if(priority > MAX_PRIO_BITS - 1u) return;
|
||||
|
||||
// TODO: Split this mess into helper functions.
|
||||
initKernelState();
|
||||
|
||||
TaskCb *const idleT = (TaskCb*)slabAlloc(&g_taskSlab);
|
||||
void *const iStack = malloc(IDLE_STACK_SIZE);
|
||||
TaskCb *const mainT = (TaskCb*)slabCalloc(&g_taskSlab, sizeof(TaskCb));
|
||||
if(idleT == NULL || iStack == NULL || mainT == NULL)
|
||||
{
|
||||
slabFree(&g_taskSlab, idleT);
|
||||
free(iStack);
|
||||
slabFree(&g_taskSlab, mainT);
|
||||
return;
|
||||
}
|
||||
|
||||
cpuRegs *const regs = (cpuRegs*)(iStack + IDLE_STACK_SIZE - sizeof(cpuRegs));
|
||||
regs->lr = (u32)kernelIdleTask;
|
||||
idleT->prio = 1;
|
||||
// id is already set to 0.
|
||||
idleT->savedSp = (uintptr_t)regs;
|
||||
idleT->stack = iStack;
|
||||
|
||||
// Main task already running. Nothing more to setup.
|
||||
mainT->id = 1;
|
||||
mainT->prio = priority;
|
||||
|
||||
g_curTask = mainT;
|
||||
g_readyBitmap = 1u<<1; // The idle task has priority 1 and is always ready.
|
||||
listPush(&g_runQueues[1], &idleT->node);
|
||||
g_numTasks = 2;
|
||||
}
|
||||
|
||||
KTask* createTask(size_t stackSize, uint8_t priority, TaskFunc entry, void *taskArg)
|
||||
{
|
||||
if(priority > MAX_PRIO_BITS - 1u) return NULL;
|
||||
|
||||
// Make sure the stack is aligned to 8 bytes
|
||||
stackSize = (stackSize + 7u) & ~7u;
|
||||
|
||||
SlabHeap *const taskSlabPtr = &g_taskSlab;
|
||||
TaskCb *const newT = (TaskCb*)slabAlloc(taskSlabPtr);
|
||||
void *const stack = malloc(stackSize);
|
||||
if(newT == NULL || stack == NULL)
|
||||
{
|
||||
slabFree(taskSlabPtr, newT);
|
||||
free(stack);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpuRegs *const regs = (cpuRegs*)(stack + stackSize - sizeof(cpuRegs));
|
||||
kmemset((u32*)regs, 0, sizeof(cpuRegs));
|
||||
regs->lr = (u32)entry;
|
||||
newT->prio = priority;
|
||||
newT->id = g_numTasks; // TODO: Make this more sophisticated.
|
||||
// TODO: This is kinda hacky abusing the result member to pass the task arg.
|
||||
// Pass args and stuff on the stack?
|
||||
newT->res = (KRes)taskArg;
|
||||
newT->savedSp = (uintptr_t)regs;
|
||||
newT->stack = stack;
|
||||
|
||||
kernelLock();
|
||||
listPush(&g_runQueues[priority], &newT->node);
|
||||
g_readyBitmap |= 1u<<priority;
|
||||
g_numTasks++;
|
||||
kernelUnlock();
|
||||
|
||||
return newT;
|
||||
}
|
||||
|
||||
// TODO: setTaskPriority().
|
||||
|
||||
void yieldTask(void)
|
||||
{
|
||||
kernelLock();
|
||||
scheduler(TASK_STATE_RUNNING);
|
||||
}
|
||||
|
||||
void taskExit(void)
|
||||
{
|
||||
kernelLock();
|
||||
scheduler(TASK_STATE_DEAD);
|
||||
while(1); // TODO: panic?
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Internal functions.
|
||||
*/
|
||||
const TaskCb* getCurrentTask(void)
|
||||
{
|
||||
return g_curTask;
|
||||
}
|
||||
|
||||
// The wait queue and scheduler functions automatically unlock the kernel lock
|
||||
// and expect to be called with locked lock.
|
||||
KRes waitQueueBlock(ListNode *waitQueue)
|
||||
{
|
||||
listPush(waitQueue, &g_curTask->node);
|
||||
return scheduler(TASK_STATE_BLOCKED);
|
||||
}
|
||||
|
||||
bool waitQueueWakeN(ListNode *waitQueue, u32 wakeCount, KRes res, bool reschedule)
|
||||
{
|
||||
if(listEmpty(waitQueue) || !wakeCount)
|
||||
{
|
||||
kernelUnlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 readyBitmap = 0;
|
||||
ListNode *const runQueues = g_runQueues;
|
||||
if(LIKELY(reschedule))
|
||||
{
|
||||
// Put ourself on top of the list first so we run immediately
|
||||
// after the woken tasks to finish the work we were doing.
|
||||
// TODO: Verify if this is a good strategy.
|
||||
TaskCb *const curTask = g_curTask;
|
||||
const u8 curPrio = curTask->prio;
|
||||
listPushTail(&runQueues[curPrio], &curTask->node);
|
||||
readyBitmap = 1u<<curPrio;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/*
|
||||
* Edge case:
|
||||
* 2 tasks, 1 single shot event. Task 2 waits first and then task 1.
|
||||
* When signaled (by an IRQ) only task 1 will ever run instead of
|
||||
* alternating between both because task 1 always lands on the
|
||||
* head (as intended) but on wakeup we take N tasks from the head
|
||||
* to preserve order.
|
||||
*
|
||||
* Workaround:
|
||||
* Take tasks from the tail instead. This will however punish
|
||||
* the longest waiting tasks unnecessarily.
|
||||
*/
|
||||
//TaskCb *task = LIST_ENTRY(listPopHead(waitQueue), TaskCb, node);
|
||||
TaskCb *task = LIST_ENTRY(listPop(waitQueue), TaskCb, node);
|
||||
readyBitmap |= 1u<<task->prio;
|
||||
task->res = res;
|
||||
listPushTail(&runQueues[task->prio], &task->node);
|
||||
} while(!listEmpty(waitQueue) && --wakeCount);
|
||||
g_readyBitmap |= readyBitmap;
|
||||
|
||||
if(LIKELY(reschedule)) scheduler(TASK_STATE_RUNNING_SHORT);
|
||||
else kernelUnlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static KRes scheduler(TaskState curTaskState)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if((__getCpsr() & PSR_MODE_MASK) != PSR_SYS_MODE) panic();
|
||||
#endif
|
||||
|
||||
TaskCb *const curDeadTask = g_curDeadTask;
|
||||
// TODO: Get rid of this and find a better way.
|
||||
if(UNLIKELY(curDeadTask != NULL))
|
||||
{
|
||||
free(curDeadTask->stack);
|
||||
slabFree(&g_taskSlab, curDeadTask);
|
||||
g_curDeadTask = NULL;
|
||||
}
|
||||
|
||||
TaskCb *const curTask = g_curTask;
|
||||
u32 readyBitmap = g_readyBitmap;
|
||||
ListNode *const runQueues = g_runQueues;
|
||||
// Warning. The result is undefined if the input of this builtin is 0!
|
||||
// Edge case: All tasks are sleeping except the (curently running) idle task.
|
||||
// g_readyBitmap is 0 in this case.
|
||||
const unsigned int readyPrio = (readyBitmap ? 31u - __builtin_clz(readyBitmap) : 0u);
|
||||
if(LIKELY(curTaskState == TASK_STATE_RUNNING))
|
||||
{
|
||||
const u8 curPrio = curTask->prio;
|
||||
|
||||
if(readyPrio < curPrio)
|
||||
{
|
||||
kernelUnlock();
|
||||
return KRES_OK;
|
||||
}
|
||||
|
||||
listPush(&runQueues[curPrio], &curTask->node);
|
||||
readyBitmap |= 1u<<curPrio;
|
||||
}
|
||||
else if(UNLIKELY(curTaskState == TASK_STATE_DEAD))
|
||||
{
|
||||
g_curDeadTask = curTask;
|
||||
g_numTasks--;
|
||||
}
|
||||
|
||||
TaskCb *newTask = LIST_ENTRY(listPop(&runQueues[readyPrio]), TaskCb, node);
|
||||
if(listEmpty(&runQueues[readyPrio])) readyBitmap &= ~(1u<<readyPrio);
|
||||
g_readyBitmap = readyBitmap;
|
||||
|
||||
TaskCb *oldTask = curTask;
|
||||
g_curTask = newTask;
|
||||
const KRes res = newTask->res;
|
||||
kernelUnlock();
|
||||
|
||||
return switchContext(res, &oldTask->savedSp, newTask->savedSp);
|
||||
}
|
||||
|
||||
// TODO: Cleanup deleted tasks in here? Or create a worker task?
|
||||
noreturn static void kernelIdleTask(void)
|
||||
{
|
||||
do
|
||||
{
|
||||
__wfi();
|
||||
kernelLock();
|
||||
scheduler(TASK_STATE_RUNNING);
|
||||
} while(1);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "kevent.h"
|
||||
#include "internal/list.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
#include "internal/kernel_private.h"
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/config.h"
|
||||
|
||||
|
||||
struct KEvent
|
||||
{
|
||||
bool signaled;
|
||||
const bool oneShot;
|
||||
ListNode waitQueue;
|
||||
};
|
||||
|
||||
|
||||
static SlabHeap g_eventSlab = {0};
|
||||
static KEvent *g_irqEventTable[128 - 32] = {0}; // 128 - 32 private interrupts.
|
||||
|
||||
|
||||
|
||||
void _eventSlabInit(void)
|
||||
{
|
||||
slabInit(&g_eventSlab, sizeof(KEvent), MAX_EVENTS);
|
||||
}
|
||||
|
||||
static void eventIrqHandler(u32 intSource)
|
||||
{
|
||||
signalEvent(g_irqEventTable[intSource - 32], false);
|
||||
}
|
||||
|
||||
KEvent* createEvent(bool oneShot)
|
||||
{
|
||||
KEvent *const kevent = (KEvent*)slabAlloc(&g_eventSlab);
|
||||
|
||||
kevent->signaled = false;
|
||||
*(bool*)&kevent->oneShot = oneShot;
|
||||
listInit(&kevent->waitQueue);
|
||||
|
||||
return kevent;
|
||||
}
|
||||
|
||||
void deleteEvent(KEvent *const kevent)
|
||||
{
|
||||
kernelLock();
|
||||
waitQueueWakeN(&kevent->waitQueue, (u32)-1, KRES_HANDLE_DELETED, true);
|
||||
|
||||
slabFree(&g_eventSlab, kevent);
|
||||
}
|
||||
|
||||
// TODO: Critical sections needed for bind/unbind?
|
||||
void bindInterruptToEvent(KEvent *const kevent, uint8_t id, uint8_t prio)
|
||||
{
|
||||
if(id < 32 || id > 127) return;
|
||||
|
||||
g_irqEventTable[id - 32] = kevent;
|
||||
IRQ_registerIsr(id, prio, 0, eventIrqHandler);
|
||||
}
|
||||
|
||||
void unbindInterruptEvent(uint8_t id)
|
||||
{
|
||||
if(id < 32 || id > 127) return;
|
||||
|
||||
g_irqEventTable[id - 32] = NULL;
|
||||
IRQ_unregisterIsr(id);
|
||||
}
|
||||
|
||||
// TODO: Timeout.
|
||||
KRes waitForEvent(KEvent *const kevent)
|
||||
{
|
||||
KRes res;
|
||||
|
||||
kernelLock();
|
||||
if(kevent->signaled)
|
||||
{
|
||||
if(kevent->oneShot) kevent->signaled = false;
|
||||
kernelUnlock();
|
||||
res = KRES_OK;
|
||||
}
|
||||
else res = waitQueueBlock(&kevent->waitQueue);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void signalEvent(KEvent *const kevent, bool reschedule)
|
||||
{
|
||||
kernelLock();
|
||||
if(!kevent->signaled)
|
||||
{
|
||||
if(kevent->oneShot)
|
||||
{
|
||||
if(!waitQueueWakeN(&kevent->waitQueue, 1, KRES_OK, reschedule))
|
||||
kevent->signaled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
kevent->signaled = true;
|
||||
waitQueueWakeN(&kevent->waitQueue, (u32)-1, KRES_OK, reschedule);
|
||||
}
|
||||
}
|
||||
else kernelUnlock();
|
||||
}
|
||||
|
||||
void clearEvent(KEvent *const kevent)
|
||||
{
|
||||
kernelLock(); // TODO: Can we do this without locks?
|
||||
kevent->signaled = false;
|
||||
kernelUnlock();
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2019 Aurora Wright, TuxSH, derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@ Based on https://github.com/AuroraWright/Luma3DS/blob/master/arm9/source/alignedseqmemcpy.s
|
||||
|
||||
.arm
|
||||
.cpu arm946e-s
|
||||
.fpu softvfp
|
||||
|
||||
|
||||
|
||||
@ void kmemcpy(u32 *restrict dst, const u32 *restrict src, u32 size);
|
||||
@ void iokmemcpy(vu32 *restrict dst, const vu32 *restrict src, u32 size);
|
||||
.section .text.kmemcpy, "ax", %progbits
|
||||
.global kmemcpy
|
||||
.global iokmemcpy
|
||||
.type kmemcpy %function
|
||||
.type iokmemcpy %function
|
||||
.align 2
|
||||
kmemcpy:
|
||||
iokmemcpy:
|
||||
bics r12, r2, #31
|
||||
beq kmemcpy_test_words
|
||||
stmfd sp!, {r4-r10}
|
||||
kmemcpy_blocks_lp:
|
||||
ldmia r1!, {r3-r10}
|
||||
subs r12, #32
|
||||
stmia r0!, {r3-r10}
|
||||
bne kmemcpy_blocks_lp
|
||||
ldmfd sp!, {r4-r10}
|
||||
kmemcpy_test_words:
|
||||
ands r12, r2, #28
|
||||
beq kmemcpy_halfword_byte
|
||||
kmemcpy_words_lp:
|
||||
ldr r3, [r1], #4
|
||||
subs r12, #4
|
||||
str r3, [r0], #4
|
||||
bne kmemcpy_words_lp
|
||||
kmemcpy_halfword_byte:
|
||||
tst r2, #2
|
||||
ldrneh r3, [r1], #2
|
||||
strneh r3, [r0], #2
|
||||
tst r2, #1
|
||||
ldrneb r3, [r1]
|
||||
strneb r3, [r0]
|
||||
bx lr
|
||||
|
||||
|
||||
@ void kmemset(u32 *ptr, u32 value, u32 size);
|
||||
@ void iokmemset(vu32 *ptr, u32 value, u32 size);
|
||||
.section .text.kmemset, "ax", %progbits
|
||||
.global kmemset
|
||||
.global iokmemset
|
||||
.type kmemset %function
|
||||
.type iokmemset %function
|
||||
.align 2
|
||||
kmemset:
|
||||
iokmemset:
|
||||
bics r12, r2, #31
|
||||
beq kmemset_test_words
|
||||
stmfd sp!, {r4-r9}
|
||||
mov r3, r1
|
||||
mov r4, r1
|
||||
mov r5, r1
|
||||
mov r6, r1
|
||||
mov r7, r1
|
||||
mov r8, r1
|
||||
mov r9, r1
|
||||
kmemset_blocks_lp:
|
||||
stmia r0!, {r1, r3-r9}
|
||||
subs r12, #32
|
||||
bne kmemset_blocks_lp
|
||||
ldmfd sp!, {r4-r9}
|
||||
kmemset_test_words:
|
||||
ands r12, r2, #28
|
||||
beq kmemset_halfword_byte
|
||||
kmemset_words_lp:
|
||||
str r1, [r0], #4
|
||||
subs r12, #4
|
||||
bne kmemset_words_lp
|
||||
kmemset_halfword_byte:
|
||||
tst r2, #2
|
||||
strneh r1, [r0], #2
|
||||
tst r2, #1
|
||||
strneb r1, [r0]
|
||||
bx lr
|
|
@ -0,0 +1,88 @@
|
|||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "kmutex.h"
|
||||
#include "internal/list.h"
|
||||
#include "internal/kernel_private.h"
|
||||
#include "internal/util.h"
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/config.h"
|
||||
|
||||
|
||||
struct KMutex
|
||||
{
|
||||
const TaskCb *owner;
|
||||
ListNode waitQueue;
|
||||
};
|
||||
|
||||
|
||||
static SlabHeap g_mutexSlab = {0};
|
||||
|
||||
|
||||
|
||||
void _mutexSlabInit(void)
|
||||
{
|
||||
slabInit(&g_mutexSlab, sizeof(KMutex), MAX_MUTEXES);
|
||||
}
|
||||
|
||||
// TODO: Test mutex with multiple cores.
|
||||
KMutex* createMutex(void)
|
||||
{
|
||||
KMutex *const kmutex = (KMutex*)slabAlloc(&g_mutexSlab);
|
||||
|
||||
kmutex->owner = NULL;
|
||||
listInit(&kmutex->waitQueue);
|
||||
|
||||
return kmutex;
|
||||
}
|
||||
|
||||
void deleteMutex(KMutex *const kmutex)
|
||||
{
|
||||
kernelLock();
|
||||
waitQueueWakeN(&kmutex->waitQueue, (u32)-1, KRES_HANDLE_DELETED, true);
|
||||
|
||||
slabFree(&g_mutexSlab, kmutex);
|
||||
}
|
||||
|
||||
KRes lockMutex(KMutex *const kmutex)
|
||||
{
|
||||
KRes res;
|
||||
|
||||
do
|
||||
{
|
||||
kernelLock();
|
||||
if(UNLIKELY(kmutex->owner != NULL))
|
||||
{
|
||||
res = waitQueueBlock(&kmutex->waitQueue);
|
||||
if(UNLIKELY(res != KRES_OK)) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
kmutex->owner = getCurrentTask();
|
||||
kernelUnlock();
|
||||
res = KRES_OK;
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO: Test if it works and only unlocks if current task == owner.
|
||||
KRes unlockMutex(KMutex *const kmutex)
|
||||
{
|
||||
KRes res = KRES_OK;
|
||||
|
||||
kernelLock();
|
||||
if(LIKELY(kmutex->owner != NULL))
|
||||
{
|
||||
if(LIKELY(kmutex->owner == getCurrentTask()))
|
||||
{
|
||||
kmutex->owner = NULL;
|
||||
waitQueueWakeN(&kmutex->waitQueue, 1, KRES_OK, true);
|
||||
}
|
||||
else res = KRES_NO_PERMISSIONS;
|
||||
}
|
||||
else kernelUnlock();
|
||||
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "ksemaphore.h"
|
||||
#include "internal/list.h"
|
||||
#include "internal/kernel_private.h"
|
||||
#include "internal/util.h"
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/config.h"
|
||||
|
||||
|
||||
struct KSema
|
||||
{
|
||||
s32 count;
|
||||
ListNode waitQueue;
|
||||
};
|
||||
|
||||
|
||||
static SlabHeap g_semaSlab = {0};
|
||||
|
||||
|
||||
|
||||
void _semaphoreSlabInit(void)
|
||||
{
|
||||
slabInit(&g_semaSlab, sizeof(KSema), MAX_SEMAPHORES);
|
||||
}
|
||||
|
||||
// TODO: Test semaphore with multiple cores.
|
||||
KSema* createSemaphore(int32_t count)
|
||||
{
|
||||
KSema *const ksema = (KSema*)slabAlloc(&g_semaSlab);
|
||||
|
||||
ksema->count = count;
|
||||
listInit(&ksema->waitQueue);
|
||||
|
||||
return ksema;
|
||||
}
|
||||
|
||||
void deleteSemaphore(KSema *const ksema)
|
||||
{
|
||||
kernelLock();
|
||||
waitQueueWakeN(&ksema->waitQueue, (u32)-1, KRES_HANDLE_DELETED, true);
|
||||
|
||||
slabFree(&g_semaSlab, ksema);
|
||||
}
|
||||
|
||||
KRes pollSemaphore(KSema *const ksema)
|
||||
{
|
||||
KRes res;
|
||||
|
||||
// TODO: Plain spinlocks instead?
|
||||
kernelLock();
|
||||
if(UNLIKELY(ksema->count <= 0)) res = KRES_WOULD_BLOCK;
|
||||
else {ksema->count--; res = KRES_OK;}
|
||||
kernelUnlock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
KRes waitForSemaphore(KSema *const ksema)
|
||||
{
|
||||
KRes res;
|
||||
|
||||
kernelLock();
|
||||
if(UNLIKELY(--ksema->count < 0)) res = waitQueueBlock(&ksema->waitQueue);
|
||||
else {kernelUnlock(); res = KRES_OK;}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void signalSemaphore(KSema *const ksema, uint32_t signalCount, bool reschedule)
|
||||
{
|
||||
kernelLock();
|
||||
//if(UNLIKELY(++ksema->count <= 0))
|
||||
if(UNLIKELY((ksema->count += signalCount) <= 0))
|
||||
waitQueueWakeN(&ksema->waitQueue, signalCount, KRES_OK, reschedule);
|
||||
else kernelUnlock();
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "ktimer.h"
|
||||
#include "internal/list.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
#include "arm11/hardware/timer.h"
|
||||
#include "internal/kernel_private.h"
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/config.h"
|
||||
//#include "arm11/fmt.h"
|
||||
|
||||
|
||||
/*struct KTimer
|
||||
{
|
||||
ListNode node;
|
||||
u32 delta;
|
||||
u32 ticks;
|
||||
const bool pulse;
|
||||
ListNode waitQueue;
|
||||
};
|
||||
|
||||
|
||||
static SlabHeap g_timerSlab = {0};
|
||||
static ListNode g_deltaQueue = {0};
|
||||
|
||||
|
||||
|
||||
static void timerIsr(UNUSED u32 intSource);
|
||||
static void addToDeltaQueue(KTimer *const timer, u32 ticks);
|
||||
|
||||
void _timerInit(void)
|
||||
{
|
||||
slabInit(&g_timerSlab, sizeof(KTimer), MAX_TIMERS);
|
||||
listInit(&g_deltaQueue);
|
||||
IRQ_registerIsr(IRQ_TIMER, 12, 0, timerIsr);
|
||||
}
|
||||
|
||||
KTimer* createTimer(bool pulse)
|
||||
{
|
||||
KTimer *const ktimer = (KTimer*)slabAlloc(&g_timerSlab);
|
||||
|
||||
*(bool*)&ktimer->pulse = pulse;
|
||||
listInit(&ktimer->waitQueue);
|
||||
|
||||
return ktimer;
|
||||
}
|
||||
|
||||
void deleteTimer(KTimer *const ktimer)
|
||||
{
|
||||
kernelLock();
|
||||
waitQueueWakeN(&ktimer->waitQueue, (u32)-1, KRES_HANDLE_DELETED, true);
|
||||
|
||||
slabFree(&g_timerSlab, ktimer);
|
||||
}
|
||||
|
||||
static void timerIsr(UNUSED u32 intSource)
|
||||
{
|
||||
kernelLock();
|
||||
//if(listEmpty(&g_deltaQueue)) *((vu32*)4) = 4; // This should never happen
|
||||
KTimer *ktimer = LIST_ENTRY(listPop(&g_deltaQueue), KTimer, node);
|
||||
if(ktimer->pulse) addToDeltaQueue(ktimer, ktimer->ticks);
|
||||
if(!listEmpty(&g_deltaQueue))
|
||||
{
|
||||
// Don't use fp math in ISRs.
|
||||
TIMER_start(1, LIST_FIRST_ENTRY(&g_deltaQueue, KTimer, node)->delta, false, true);
|
||||
}
|
||||
waitQueueWakeN(&ktimer->waitQueue, (u32)-1, KRES_OK, false);
|
||||
}
|
||||
|
||||
static void addToDeltaQueue(KTimer *const ktimer, u32 ticks)
|
||||
{
|
||||
KTimer *pos;
|
||||
u32 deltaSum = 0;
|
||||
LIST_FOR_EACH_ENTRY(pos, &g_deltaQueue, node)
|
||||
{
|
||||
deltaSum += pos->delta;
|
||||
if(deltaSum > ticks)
|
||||
{
|
||||
ktimer->delta = ticks - (deltaSum - pos->delta);
|
||||
listAddBefore(&pos->node, &ktimer->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ktimer->delta = ticks;
|
||||
listPush(&g_deltaQueue, &ktimer->node);
|
||||
}
|
||||
|
||||
void startTimer(KTimer *const ktimer, uint32_t usec)
|
||||
{
|
||||
const u32 ticks = TIMER_FREQ(1, 1000000) * usec;
|
||||
ktimer->ticks = ticks;
|
||||
|
||||
kernelLock();
|
||||
const bool firstTimer = listEmpty(&g_deltaQueue);
|
||||
addToDeltaQueue(ktimer, ticks);
|
||||
kernelUnlock();
|
||||
if(firstTimer) TIMER_start(1, ticks, false, true);
|
||||
}
|
||||
|
||||
void stopTimer(KTimer *const ktimer)
|
||||
{
|
||||
}
|
||||
|
||||
KRes waitForTimer(KTimer *const ktimer)
|
||||
{
|
||||
kernelLock();
|
||||
return waitQueueBlock(&ktimer->waitQueue);
|
||||
}*/
|
|
@ -0,0 +1,44 @@
|
|||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "internal/slabheap.h"
|
||||
#include "internal/kmemcpy_set.h"
|
||||
|
||||
|
||||
|
||||
void slabInit(SlabHeap *slab, size_t objSize, size_t num)
|
||||
{
|
||||
if(objSize < sizeof(SlabHeap) || !num) return;
|
||||
|
||||
listInit(slab);
|
||||
|
||||
void *pool = malloc(objSize * num);
|
||||
if(!pool) return;
|
||||
do
|
||||
{
|
||||
listPush(slab, (SlabHeap*)pool);
|
||||
pool += objSize;
|
||||
} while(--num);
|
||||
}
|
||||
|
||||
void* slabAlloc(SlabHeap *slab)
|
||||
{
|
||||
if(!slab || listEmpty(slab)) return NULL;
|
||||
|
||||
return listPop(slab);
|
||||
}
|
||||
|
||||
void* slabCalloc(SlabHeap *slab, size_t clrSize)
|
||||
{
|
||||
void *const ptr = slabAlloc(slab);
|
||||
if(ptr) kmemset(ptr, 0, clrSize);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void slabFree(SlabHeap *slab, void *ptr)
|
||||
{
|
||||
if(!slab || !ptr) return;
|
||||
|
||||
// Keep gaps filled by allocating the same mem
|
||||
// again next time an object is allocated.
|
||||
listPushTail(slab, (SlabHeap*)ptr);
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "mem_map.h"
|
||||
|
@ -24,7 +25,6 @@
|
|||
#include "arm11/fmt.h"
|
||||
#include "hardware/pxi.h"
|
||||
#include "ipc_handler.h"
|
||||
#include "hardware/gfx.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
#include "arm.h"
|
||||
#include "arm11/hardware/mcu.h"
|
||||
|
@ -32,26 +32,23 @@
|
|||
|
||||
|
||||
|
||||
noreturn void panic()
|
||||
NOINLINE noreturn void panic(void)
|
||||
{
|
||||
enterCriticalSection();
|
||||
|
||||
consoleInit(SCREEN_BOT, NULL);
|
||||
ee_printf("\x1b[41m\x1b[0J\x1b[15C****PANIC!!!****\n");
|
||||
|
||||
//PXI_sendPanicCmd(IPC_CMD9_PANIC);
|
||||
PXI_sendPanicCmd(IPC_CMD9_PREPARE_POWER);
|
||||
|
||||
// Wait for A/B/X or Y
|
||||
do
|
||||
{
|
||||
hidScanInput();
|
||||
} while(!(hidKeysDown() & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
while(!(REG_HID_PAD & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
|
||||
MCU_powerOffSys();
|
||||
while(1) __wfi();
|
||||
}
|
||||
|
||||
noreturn void panicMsg(const char *msg)
|
||||
NOINLINE noreturn void panicMsg(const char *msg)
|
||||
{
|
||||
enterCriticalSection();
|
||||
|
||||
|
@ -59,32 +56,21 @@ noreturn void panicMsg(const char *msg)
|
|||
ee_printf("\x1b[41m\x1b[0J\x1b[15C****PANIC!!!****\n\n");
|
||||
ee_printf("\nERROR MESSAGE:\n%s\n", msg);
|
||||
|
||||
//PXI_sendPanicCmd(IPC_CMD9_PANIC);
|
||||
PXI_sendPanicCmd(IPC_CMD9_PREPARE_POWER);
|
||||
|
||||
// Wait for A/B/X or Y
|
||||
do
|
||||
{
|
||||
hidScanInput();
|
||||
} while(!(hidKeysDown() & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
while(!(REG_HID_PAD & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
|
||||
MCU_powerOffSys();
|
||||
while(1) __wfi();
|
||||
}
|
||||
|
||||
// Expects the registers in the exception stack to be in the following order:
|
||||
// r0-r14, pc (unmodified), cpsr
|
||||
noreturn void guruMeditation(u8 type, const u32 *excStack)
|
||||
// r0-r14, pc (unmodified), CPSR, DFSR, IFSR, FAR, WFAR
|
||||
NOINLINE noreturn void guruMeditation(u8 type, const u32 *excStack)
|
||||
{
|
||||
const char *const typeStr[3] = {"Undefined instruction", "Prefetch abort", "Data abort"};
|
||||
u32 realPc, instSize = 4;
|
||||
//bool codeChanged = false;
|
||||
|
||||
|
||||
// verify text and rodata
|
||||
/*u32 prevHash = debugHash;
|
||||
debugHashCodeRoData();
|
||||
if(prevHash != debugHash)
|
||||
codeChanged = true;*/
|
||||
|
||||
consoleInit(SCREEN_BOT, NULL);
|
||||
|
||||
|
@ -123,32 +109,72 @@ noreturn void guruMeditation(u8 type, const u32 *excStack)
|
|||
}
|
||||
}
|
||||
|
||||
//if(codeChanged) ee_printf("Attention: RO section data changed!!");
|
||||
|
||||
//PXI_sendPanicCmd(IPC_CMD9_EXCEPTION);
|
||||
PXI_sendPanicCmd(IPC_CMD9_PREPARE_POWER);
|
||||
|
||||
// Wait for A/B/X or Y
|
||||
do
|
||||
{
|
||||
hidScanInput();
|
||||
} while(!(hidKeysDown() & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
while(!(REG_HID_PAD & (KEY_A | KEY_B | KEY_X | KEY_Y)));
|
||||
|
||||
MCU_powerOffSys();
|
||||
while(1) __wfi();
|
||||
}
|
||||
|
||||
/*void debugMemdump(const char *filepath, void *mem, size_t size)
|
||||
#ifndef NDEBUG
|
||||
// Needs to be marked as used to work with LTO.
|
||||
// The used attribute also overrides the newlib symbol.
|
||||
// This is for debugging purposes only. For security this value needs to be random!
|
||||
__attribute__((used)) uintptr_t __stack_chk_guard = 0xC724B66D;
|
||||
|
||||
// Needs to be marked as noinline and used to work with LTO.
|
||||
// The used attribute also overrides the newlib symbol.
|
||||
// Combine -fstack-protector-all with -fno-inline to get the most effective detection.
|
||||
__attribute__((noinline, used)) noreturn void __stack_chk_fail(void)
|
||||
{
|
||||
s32 file;
|
||||
panicMsg("Stack smash!");
|
||||
}
|
||||
|
||||
if((file = fOpen(filepath, FS_CREATE_ALWAYS | FS_OPEN_WRITE)) < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fWrite(file, mem, size);
|
||||
|
||||
fSync(file);
|
||||
|
||||
fClose(file);
|
||||
}*/
|
||||
// Add "-Wl,-wrap=malloc,-wrap=calloc,-wrap=free" to LDFLAGS to enable the heap check.
|
||||
static const u32 __heap_chk_guard[4] = {0x9240A724, 0x6A6594A0, 0x976F0392, 0xB3A669AB};
|
||||
|
||||
void* __real_malloc(size_t size);
|
||||
void __real_free(void *ptr);
|
||||
|
||||
void* __wrap_malloc(size_t size)
|
||||
{
|
||||
void *const buf = __real_malloc(size + 32);
|
||||
if(buf == NULL) return NULL;
|
||||
|
||||
memcpy(buf, &size, sizeof(size_t));
|
||||
memcpy(buf + sizeof(size_t), (u8*)__heap_chk_guard + sizeof(size_t), 16 - sizeof(size_t));
|
||||
memcpy(buf + 16 + size, __heap_chk_guard, 16);
|
||||
|
||||
return buf + 16;
|
||||
}
|
||||
|
||||
void* __wrap_calloc(size_t num, size_t size)
|
||||
{
|
||||
void *const buf = __wrap_malloc(num * size);
|
||||
if(buf == NULL) return NULL;
|
||||
|
||||
memset(buf, 0, num * size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void __wrap_free(void *ptr)
|
||||
{
|
||||
if(ptr == NULL) return;
|
||||
|
||||
if(memcmp(ptr - (16 - sizeof(size_t)), (u8*)__heap_chk_guard + sizeof(size_t), 16 - sizeof(size_t)) != 0)
|
||||
panicMsg("Heap underflow!");
|
||||
size_t size;
|
||||
memcpy(&size, ptr - 16, sizeof(size_t));
|
||||
|
||||
// Important! Adjust the size check if needed.
|
||||
// 1024u * 512 is roughly ok for AXIWRAM.
|
||||
if(size > (1024u * 512) || memcmp(ptr + size, __heap_chk_guard, 16) != 0)
|
||||
panicMsg("Heap overflow!");
|
||||
|
||||
__real_free(ptr - 16);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "types.h"
|
||||
#include "error_codes.h"
|
||||
#include "fs.h"
|
||||
#include "util.h"
|
||||
#include "arm11/hardware/hid.h"
|
||||
#include "arm11/fmt.h"
|
||||
#include "hardware/gfx.h"
|
||||
|
@ -14,30 +15,34 @@
|
|||
#define SCREEN_ROWS (24u)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 type; // 0 = file, 1 = dir
|
||||
char str[256];
|
||||
} DirListEnt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 num;
|
||||
const char *strPtrs[MAX_DIR_ENTRIES];
|
||||
u8 entTypes[MAX_DIR_ENTRIES]; // 0 = file, 1 = dir
|
||||
char strBufs[MAX_DIR_ENTRIES][256];
|
||||
DirListEnt entries[MAX_DIR_ENTRIES];
|
||||
DirListEnt *ptrs[MAX_DIR_ENTRIES];
|
||||
} DirList;
|
||||
|
||||
|
||||
|
||||
// num including null terminator.
|
||||
static size_t safeStrcpy(char *const dst, const char *const src, size_t num)
|
||||
int dlistCompare(const void *a, const void *b)
|
||||
{
|
||||
if(num == 0) return 0;
|
||||
const DirListEnt *const entA = *(DirListEnt**)a;
|
||||
const DirListEnt *const entB = *(DirListEnt**)b;
|
||||
|
||||
const size_t len = strlen(src) + 1;
|
||||
if(len > num)
|
||||
{
|
||||
*dst = '\0';
|
||||
return 1;
|
||||
}
|
||||
if(entA->type != entB->type) return (int)entB->type - entA->type;
|
||||
|
||||
strcpy(dst, src);
|
||||
return len;
|
||||
const char *strA = entA->str;
|
||||
const char *strB = entB->str;
|
||||
int res = *strA - *strB;
|
||||
while(*strA != '\0' && *strB != '\0' && res == 0) res = *++strA - *++strB;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result scanDir(const char *const path, DirList *const dList, const char *const filter)
|
||||
|
@ -69,9 +74,9 @@ static Result scanDir(const char *const path, DirList *const dList, const char *
|
|||
continue;
|
||||
}
|
||||
|
||||
dList->strPtrs[dListPos] = dList->strBufs[dListPos];
|
||||
dList->entTypes[dListPos] = isDir;
|
||||
safeStrcpy(dList->strBufs[dListPos], fi[i].fname, 256);
|
||||
dList->entries[dListPos].type = isDir;
|
||||
safeStrcpy(dList->entries[dListPos].str, fi[i].fname, 256);
|
||||
dList->ptrs[dListPos] = &dList->entries[dListPos];
|
||||
dListPos++;
|
||||
}
|
||||
} while(read == DIR_READ_BLOCKS);
|
||||
|
@ -82,8 +87,7 @@ static Result scanDir(const char *const path, DirList *const dList, const char *
|
|||
|
||||
free(fi);
|
||||
|
||||
// Hacky casting of function pointers. But they are compatible.
|
||||
qsort(dList->strPtrs, dList->num, sizeof(char*), (int (*)(const void *, const void *))strcmp);
|
||||
qsort(dList->ptrs, dList->num, sizeof(DirListEnt*), dlistCompare);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -97,9 +101,9 @@ static void showDirList(const DirList *const dList, u32 start)
|
|||
for(u32 i = start; i < listLength; i++)
|
||||
{
|
||||
const char *const printStr =
|
||||
(dList->entTypes[i] == 0 ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[33m %.51s");
|
||||
(dList->ptrs[i]->type == 0 ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[33m %.51s");
|
||||
|
||||
ee_printf(printStr, i - start, dList->strPtrs[i]);
|
||||
ee_printf(printStr, i - start, dList->ptrs[i]->str);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,13 +141,14 @@ Result browseFiles(const char *const basePath, char selected[512])
|
|||
kDown = hidKeysDown();
|
||||
} while(kDown == 0);
|
||||
|
||||
if(dList->num != 0)
|
||||
const u32 num = dList->num;
|
||||
if(num != 0)
|
||||
{
|
||||
oldCursorPos = cursorPos;
|
||||
if(kDown & KEY_DRIGHT)
|
||||
{
|
||||
cursorPos += SCREEN_ROWS;
|
||||
if((u32)cursorPos > dList->num) cursorPos = dList->num - 1;
|
||||
if((u32)cursorPos > num) cursorPos = num - 1;
|
||||
}
|
||||
if(kDown & KEY_DLEFT)
|
||||
{
|
||||
|
@ -154,8 +159,8 @@ Result browseFiles(const char *const basePath, char selected[512])
|
|||
if(kDown & KEY_DDOWN) cursorPos += 1;
|
||||
}
|
||||
|
||||
if(cursorPos < 0) cursorPos = dList->num - 1; // Wrap to end of list.
|
||||
if((u32)cursorPos > (dList->num - 1)) cursorPos = 0; // Wrap to start of list.
|
||||
if(cursorPos < 0) cursorPos = num - 1; // Wrap to end of list.
|
||||
if((u32)cursorPos > (num - 1)) cursorPos = 0; // Wrap to start of list.
|
||||
|
||||
if((u32)cursorPos < windowPos)
|
||||
{
|
||||
|
@ -172,13 +177,13 @@ Result browseFiles(const char *const basePath, char selected[512])
|
|||
{
|
||||
u32 pathLen = strlen(curDir);
|
||||
|
||||
if(kDown & KEY_A && dList->num != 0)
|
||||
if(kDown & KEY_A && num != 0)
|
||||
{
|
||||
// TODO: !!! Insecure !!!
|
||||
if(curDir[pathLen - 1] != '/') curDir[pathLen++] = '/';
|
||||
safeStrcpy(curDir + pathLen, dList->strPtrs[cursorPos], 256);
|
||||
safeStrcpy(curDir + pathLen, dList->ptrs[cursorPos]->str, 256);
|
||||
|
||||
if(dList->entTypes[cursorPos] == 0)
|
||||
if(dList->ptrs[cursorPos]->type == 0)
|
||||
{
|
||||
safeStrcpy(selected, curDir, 512);
|
||||
break;
|
||||
|
|
|
@ -127,6 +127,15 @@ Result fStat(const char *const path, FILINFO *const fi)
|
|||
return PXI_sendCmd(IPC_CMD9_FSTAT, cmdBuf, 4);
|
||||
}
|
||||
|
||||
Result fChdir(const char *const path)
|
||||
{
|
||||
u32 cmdBuf[2];
|
||||
cmdBuf[0] = (u32)path;
|
||||
cmdBuf[1] = strlen(path) + 1;
|
||||
|
||||
return PXI_sendCmd(IPC_CMD9_FCHDIR, cmdBuf, 2);
|
||||
}
|
||||
|
||||
Result fOpenDir(DHandle *const hOut, const char *const path)
|
||||
{
|
||||
u32 cmdBuf[4];
|
||||
|
|
|
@ -36,7 +36,7 @@ EXCEPTION_ENTRY undefInstrHandler, 0<<29
|
|||
EXCEPTION_ENTRY prefetchAbortHandler, 1<<29
|
||||
EXCEPTION_ENTRY dataAbortHandler, 2<<29
|
||||
BEGIN_ASM_FUNC exceptionHandler
|
||||
sub sp, #68
|
||||
sub sp, #84
|
||||
stmia sp, {r0-r14}^ @ Save all user/system mode regs except pc
|
||||
mrs r2, spsr @ Get saved cpsr
|
||||
mrs r3, cpsr
|
||||
|
@ -51,13 +51,21 @@ BEGIN_ASM_FUNC exceptionHandler
|
|||
exceptionHandler_skip_other_mode:
|
||||
str lr, [sp, #60] @ Save lr (pc) on exception stack
|
||||
str r2, [sp, #64] @ Save spsr (cpsr) on exception stack
|
||||
mrc p15, 0, r3, c5, c0, 0
|
||||
str r3, [sp, #68] @ DFSR
|
||||
mrc p15, 0, r3, c5, c0, 1
|
||||
str r3, [sp, #72] @ IFSR
|
||||
mrc p15, 0, r3, c6, c0, 0
|
||||
str r3, [sp, #76] @ FAR
|
||||
mrc p15, 0, r3, c6, c0, 1
|
||||
str r3, [sp, #80] @ WFAR
|
||||
mov r4, r0
|
||||
mov r5, sp
|
||||
bl deinitCpu
|
||||
mov r0, r4
|
||||
mov sp, r5
|
||||
mov r1, r5
|
||||
b guruMeditation @ r0 = exception type, r1 = reg dump ptr {r0-r14, pc (unmodified), cpsr}
|
||||
b guruMeditation @ r0 = exception type, r1 = reg dump ptr {r0-r14, pc (unmodified), CPSR, DFSR, IFSR, FAR, WFAR}
|
||||
END_ASM_FUNC
|
||||
|
||||
|
||||
|
|
|
@ -36,13 +36,14 @@
|
|||
#include "arm.h"
|
||||
#include "util.h"
|
||||
#include "arm11/allocator/vram.h"
|
||||
#include "kevent.h"
|
||||
|
||||
|
||||
static struct
|
||||
{
|
||||
u8 lcdPower; // 1 = on. Bit 4 top light, bit 2 bottom light, bit 0 LCDs.
|
||||
u8 lcdLights[2]; // LCD backlight brightness. Top, bottom.
|
||||
bool events[6];
|
||||
KEvent *events[6];
|
||||
u32 swap; // Currently active framebuffer.
|
||||
void *framebufs[2][4]; // For each screen A1, A2, B1, B2
|
||||
u8 doubleBuf[2]; // Top, bottom, 1 = enable.
|
||||
|
@ -56,19 +57,23 @@ static u8 fmt2PixSize(GfxFbFmt fmt);
|
|||
static void setupFramebufs(GfxFbFmt fmtTop, GfxFbFmt fmtBot);
|
||||
static void deallocFramebufs(void);
|
||||
static void setupDislayController(u8 lcd);
|
||||
static void gfxIrqHandler(u32 intSource);
|
||||
|
||||
void GFX_init(GfxFbFmt fmtTop, GfxFbFmt fmtBot)
|
||||
{
|
||||
g_gfxState.lcdPower = 0x15; // All on.
|
||||
setupFramebufs(fmtTop, fmtBot);
|
||||
g_gfxState.doubleBuf[0] = 1;
|
||||
g_gfxState.doubleBuf[1] = 1;
|
||||
|
||||
// FIXME: Temporary workaround for screen init compatibility (Luma/fb3DS 1.2).
|
||||
TIMER_sleepMs(50);
|
||||
(void)MCU_getEvents(0x3Fu<<24); // Discard any screen init events.
|
||||
|
||||
REG_CFG11_GPUPROT = 0;
|
||||
|
||||
// Reset
|
||||
REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E;
|
||||
wait(12);
|
||||
wait_cycles(12);
|
||||
REG_PDN_GPU_CNT = PDN_GPU_CNT_CLK_E | PDN_GPU_CNT_RST_ALL;
|
||||
REG_GX_GPU_CLK = 0x100;
|
||||
REG_GX_PSC_VRAM = 0;
|
||||
|
@ -82,7 +87,7 @@ void GFX_init(GfxFbFmt fmtTop, GfxFbFmt fmtBot)
|
|||
REG_LCD_PDC0_SWAP = 0; // Select framebuf 0.
|
||||
REG_LCD_PDC1_SWAP = 0;
|
||||
REG_LCD_PDC0_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E; // Start
|
||||
REG_LCD_PDC1_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ERR | PDC_CNT_I_MASK_H | PDC_CNT_E;
|
||||
REG_LCD_PDC1_CNT = PDC_CNT_OUT_E | PDC_CNT_I_MASK_ALL | PDC_CNT_E;
|
||||
|
||||
// LCD reg setup.
|
||||
REG_LCD_ABL0_FILL = 1u<<24; // Force blackscreen
|
||||
|
@ -92,13 +97,14 @@ void GFX_init(GfxFbFmt fmtTop, GfxFbFmt fmtBot)
|
|||
REG_LCD_RST = 0;
|
||||
REG_LCD_UNK00C = 0x10001;
|
||||
|
||||
// Register IRQ handlers.
|
||||
IRQ_registerIsr(IRQ_PSC0, 14, 0, gfxIrqHandler);
|
||||
IRQ_registerIsr(IRQ_PSC1, 14, 0, gfxIrqHandler);
|
||||
IRQ_registerIsr(IRQ_PDC0, 14, 0, gfxIrqHandler);
|
||||
//IRQ_registerIsr(IRQ_PDC1, 14, 0, gfxIrqHandler);
|
||||
IRQ_registerIsr(IRQ_PPF, 14, 0, gfxIrqHandler);
|
||||
IRQ_registerIsr(IRQ_P3D, 14, 0, gfxIrqHandler);
|
||||
// Create IRQ events.
|
||||
// PSC0, PSC1, PDC0, PDC1, PPF, P3D
|
||||
for(u8 i = 0; i < 6; i++)
|
||||
{
|
||||
KEvent *tmp = createEvent(false);
|
||||
bindInterruptToEvent(tmp, IRQ_PSC0 + i, 14);
|
||||
g_gfxState.events[i] = tmp;
|
||||
}
|
||||
|
||||
// Clear entire VRAM.
|
||||
GX_memoryFill((u32*)VRAM_BANK0, 1u<<9, VRAM_SIZE / 2, 0,
|
||||
|
@ -129,7 +135,6 @@ void GFX_init(GfxFbFmt fmtTop, GfxFbFmt fmtBot)
|
|||
REG_LCD_ABL1_LIGHT_PWM = 0x1023E;
|
||||
MCU_controlLCDPower(0x28u); // Power on backlights.
|
||||
if(MCU_waitEvents(0x3Fu<<24) != 0x28u<<24) panic();
|
||||
g_gfxState.lcdPower = 0x15; // All on.
|
||||
|
||||
// Make sure the fills finished.
|
||||
GFX_waitForPSC0();
|
||||
|
@ -176,12 +181,13 @@ void GFX_deinit(void)
|
|||
|
||||
deallocFramebufs();
|
||||
|
||||
IRQ_unregisterIsr(IRQ_PSC0);
|
||||
IRQ_unregisterIsr(IRQ_PSC1);
|
||||
IRQ_unregisterIsr(IRQ_PDC0);
|
||||
//IRQ_unregisterIsr(IRQ_PDC1);
|
||||
IRQ_unregisterIsr(IRQ_PPF);
|
||||
IRQ_unregisterIsr(IRQ_P3D);
|
||||
// PSC0, PSC1, PDC0, PDC1, PPF, P3D
|
||||
for(u8 i = 0; i < 6; i++)
|
||||
{
|
||||
unbindInterruptEvent(IRQ_PSC0 + i);
|
||||
deleteEvent(g_gfxState.events[i]);
|
||||
g_gfxState.events[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GFX_setFramebufFmt(GfxFbFmt fmtTop, GfxFbFmt fmtBot)
|
||||
|
@ -386,18 +392,11 @@ void GFX_swapFramebufs(void)
|
|||
|
||||
void GFX_waitForEvent(GfxEvent event, bool discard)
|
||||
{
|
||||
bool *const events = g_gfxState.events;
|
||||
KEvent *kevent = g_gfxState.events[event];
|
||||
|
||||
if(discard) atomic_store_explicit(&events[event], false, memory_order_relaxed);
|
||||
while(!atomic_load_explicit(&events[event], memory_order_relaxed)) __wfe();
|
||||
atomic_store_explicit(&events[event], false, memory_order_relaxed);
|
||||
}
|
||||
|
||||
static void gfxIrqHandler(u32 intSource)
|
||||
{
|
||||
bool *const events = g_gfxState.events;
|
||||
|
||||
atomic_store_explicit(&events[intSource - IRQ_PSC0], true, memory_order_relaxed);
|
||||
if(discard) clearEvent(kevent);
|
||||
waitForEvent(kevent);
|
||||
clearEvent(kevent);
|
||||
}
|
||||
|
||||
void GX_memoryFill(u32 *buf0a, u32 buf0v, u32 buf0Sz, u32 val0, u32 *buf1a, u32 buf1v, u32 buf1Sz, u32 val1)
|
||||
|
@ -452,7 +451,7 @@ void GX_textureCopy(const u32 *const in, u32 indim, u32 *out, u32 outdim, u32 si
|
|||
void GX_processCommandList(u32 size, const u32 *const cmdList)
|
||||
{
|
||||
REG_GX_P3D(GPUREG_IRQ_ACK) = 0; // Acknowledge last P3D.
|
||||
while(REG_GX_PSC_STAT & 1u<<31) wait(0x30);
|
||||
while(REG_GX_PSC_STAT & 1u<<31) wait_cycles(0x30);
|
||||
|
||||
REG_GX_P3D(GPUREG_CMDBUF_SIZE0) = size>>3;
|
||||
REG_GX_P3D(GPUREG_CMDBUF_ADDR0) = (u32)cmdList>>3;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "types.h"
|
||||
#include "mem_map.h"
|
||||
#include "arm11/hardware/i2c.h"
|
||||
#include "kevent.h"
|
||||
#include "kmutex.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
|
||||
|
||||
|
@ -63,8 +65,189 @@ static const struct
|
|||
{I2C_BUS3, 0x54}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
I2cRegs *const regs;
|
||||
KEvent *event;
|
||||
KMutex *mutex;
|
||||
} I2cState;
|
||||
static I2cState g_i2cState[3] = {{(I2cRegs*)I2C1_REGS_BASE, NULL, NULL},
|
||||
{(I2cRegs*)I2C2_REGS_BASE, NULL, NULL},
|
||||
{(I2cRegs*)I2C3_REGS_BASE, NULL, NULL}};
|
||||
|
||||
|
||||
|
||||
static bool checkAck(I2cRegs *const regs)
|
||||
{
|
||||
// If we received a NACK stop the transfer.
|
||||
if((regs->I2C_CNT & I2C_ACK) == 0u)
|
||||
{
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sendByte(I2cRegs *const regs, u8 data, u8 params, KEvent *const event)
|
||||
{
|
||||
regs->I2C_DATA = data;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE | params;
|
||||
waitForEvent(event);
|
||||
}
|
||||
|
||||
static u8 recvByte(I2cRegs *const regs, u8 params, KEvent *const event)
|
||||
{
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_READ | params;
|
||||
waitForEvent(event);
|
||||
return regs->I2C_DATA;
|
||||
}
|
||||
|
||||
void I2C_init(void)
|
||||
{
|
||||
static bool inited = false;
|
||||
if(inited) return;
|
||||
inited = true;
|
||||
|
||||
|
||||
KEvent *tmp = createEvent(true);
|
||||
bindInterruptToEvent(tmp, IRQ_I2C1, 14);
|
||||
g_i2cState[0].event = tmp;
|
||||
tmp = createEvent(true);
|
||||
bindInterruptToEvent(tmp, IRQ_I2C2, 14);
|
||||
g_i2cState[1].event = tmp;
|
||||
tmp = createEvent(true);
|
||||
bindInterruptToEvent(tmp, IRQ_I2C3, 14);
|
||||
g_i2cState[2].event = tmp;
|
||||
|
||||
for(u32 i = 0; i < 3; i++)
|
||||
{
|
||||
g_i2cState[i].mutex = createMutex();
|
||||
}
|
||||
|
||||
while(REG_I2C1_CNT & I2C_ENABLE);
|
||||
REG_I2C1_CNTEX = I2C_CLK_STRETCH;
|
||||
REG_I2C1_SCL = I2C_DELAYS(5u, 0u);
|
||||
|
||||
while(REG_I2C2_CNT & I2C_ENABLE);
|
||||
REG_I2C2_CNTEX = I2C_CLK_STRETCH;
|
||||
REG_I2C2_SCL = I2C_DELAYS(5u, 0u);
|
||||
|
||||
while(REG_I2C3_CNT & I2C_ENABLE);
|
||||
REG_I2C3_CNTEX = I2C_CLK_STRETCH;
|
||||
REG_I2C3_SCL = I2C_DELAYS(5u, 0u);
|
||||
}
|
||||
|
||||
static bool startTransfer(u8 devAddr, u8 regAddr, bool read, const I2cState *const state)
|
||||
{
|
||||
u32 tries = 8;
|
||||
do
|
||||
{
|
||||
I2cRegs *const regs = state->regs;
|
||||
KEvent *const event = state->event;
|
||||
|
||||
// Edge case on previous transfer error (NACK).
|
||||
// This is a special case where we can't predict when or if
|
||||
// the IRQ has fired. If it fires after checking but
|
||||
// before a wfi this would hang.
|
||||
if(regs->I2C_CNT & I2C_ENABLE) waitForEvent(event);
|
||||
clearEvent(event);
|
||||
|
||||
// Select device and start.
|
||||
sendByte(regs, devAddr, I2C_START, event);
|
||||
if(!checkAck(regs)) continue;
|
||||
|
||||
// Select register.
|
||||
sendByte(regs, regAddr, 0, event);
|
||||
if(!checkAck(regs)) continue;
|
||||
|
||||
// Select device in read mode for read transfer.
|
||||
if(read)
|
||||
{
|
||||
sendByte(regs, devAddr | 1u, I2C_START, event);
|
||||
if(!checkAck(regs)) continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while(--tries > 0);
|
||||
|
||||
return tries > 0;
|
||||
}
|
||||
|
||||
bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
|
||||
{
|
||||
const u8 devAddr = i2cDevTable[devId].devAddr;
|
||||
const I2cState *const state = &g_i2cState[i2cDevTable[devId].busId];
|
||||
I2cRegs *const regs = state->regs;
|
||||
KEvent *const event = state->event;
|
||||
KMutex *const mutex = state->mutex;
|
||||
|
||||
|
||||
bool res = true;
|
||||
lockMutex(mutex);
|
||||
if(startTransfer(devAddr, regAddr, true, state))
|
||||
{
|
||||
while(--size) *out++ = recvByte(regs, I2C_ACK, event);
|
||||
|
||||
// Last byte transfer.
|
||||
*out = recvByte(regs, I2C_STOP, event);
|
||||
}
|
||||
else res = false;
|
||||
unlockMutex(mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size)
|
||||
{
|
||||
const u8 devAddr = i2cDevTable[devId].devAddr;
|
||||
const I2cState *const state = &g_i2cState[i2cDevTable[devId].busId];
|
||||
I2cRegs *const regs = state->regs;
|
||||
KEvent *const event = state->event;
|
||||
KMutex *const mutex = state->mutex;
|
||||
|
||||
|
||||
lockMutex(mutex);
|
||||
if(!startTransfer(devAddr, regAddr, false, state))
|
||||
{
|
||||
unlockMutex(mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
while(--size)
|
||||
{
|
||||
sendByte(regs, *in++, 0, event);
|
||||
if(!checkAck(regs))
|
||||
{
|
||||
unlockMutex(mutex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Last byte transfer.
|
||||
sendByte(regs, *in, I2C_STOP, event);
|
||||
if(!checkAck(regs))
|
||||
{
|
||||
unlockMutex(mutex);
|
||||
return false;
|
||||
}
|
||||
unlockMutex(mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 I2C_readReg(I2cDevice devId, u8 regAddr)
|
||||
{
|
||||
u8 data;
|
||||
if(!I2C_readRegBuf(devId, regAddr, &data, 1)) return 0xFF;
|
||||
return data;
|
||||
}
|
||||
|
||||
bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data)
|
||||
{
|
||||
return I2C_writeRegBuf(devId, regAddr, &data, 1);
|
||||
}
|
||||
|
||||
static I2cRegs* i2cGetBusRegsBase(u8 busId)
|
||||
{
|
||||
I2cRegs *base;
|
||||
|
@ -86,150 +269,6 @@ static I2cRegs* i2cGetBusRegsBase(u8 busId)
|
|||
return base;
|
||||
}
|
||||
|
||||
static inline void i2cWaitBusyIrq(const I2cRegs *const regs)
|
||||
{
|
||||
do
|
||||
{
|
||||
__wfi();
|
||||
} while(regs->I2C_CNT & I2C_ENABLE);
|
||||
}
|
||||
|
||||
static bool i2cCheckAck(I2cRegs *const regs)
|
||||
{
|
||||
// If we received a NACK stop the transfer.
|
||||
if((regs->I2C_CNT & I2C_ACK) == 0u)
|
||||
{
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_ERROR | I2C_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2C_init(void)
|
||||
{
|
||||
static bool inited = false;
|
||||
if(inited) return;
|
||||
inited = true;
|
||||
|
||||
I2cRegs *regs = i2cGetBusRegsBase(I2C_BUS1);
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
regs->I2C_CNTEX = I2C_CLK_STRETCH;
|
||||
regs->I2C_SCL = I2C_DELAYS(5u, 0u);
|
||||
|
||||
regs = i2cGetBusRegsBase(I2C_BUS2);
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
regs->I2C_CNTEX = I2C_CLK_STRETCH;
|
||||
regs->I2C_SCL = I2C_DELAYS(5u, 0u);
|
||||
|
||||
regs = i2cGetBusRegsBase(I2C_BUS3);
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
regs->I2C_CNTEX = I2C_CLK_STRETCH;
|
||||
regs->I2C_SCL = I2C_DELAYS(5u, 0u);
|
||||
|
||||
IRQ_registerIsr(IRQ_I2C1, 14, 0, NULL);
|
||||
IRQ_registerIsr(IRQ_I2C2, 14, 0, NULL);
|
||||
IRQ_registerIsr(IRQ_I2C3, 14, 0, NULL);
|
||||
}
|
||||
|
||||
static bool i2cStartTransfer(u8 devAddr, u8 regAddr, bool read, I2cRegs *const regs)
|
||||
{
|
||||
u32 tries = 8;
|
||||
do
|
||||
{
|
||||
// Edge case on previous transfer error.
|
||||
// This is a special case where we can't predict when or if
|
||||
// the IRQ has already fired. If it fires after checking but
|
||||
// before a wfi this would hang.
|
||||
while(regs->I2C_CNT & I2C_ENABLE) __wfe();
|
||||
|
||||
// Select device and start.
|
||||
regs->I2C_DATA = devAddr;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE | I2C_START;
|
||||
i2cWaitBusyIrq(regs);
|
||||
if(!i2cCheckAck(regs)) continue;
|
||||
|
||||
// Select register.
|
||||
regs->I2C_DATA = regAddr;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE;
|
||||
i2cWaitBusyIrq(regs);
|
||||
if(!i2cCheckAck(regs)) continue;
|
||||
|
||||
// Select device in read mode for read transfer.
|
||||
if(read)
|
||||
{
|
||||
regs->I2C_DATA = devAddr | 1u; // Set bit 0 for read.
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE | I2C_START;
|
||||
i2cWaitBusyIrq(regs);
|
||||
if(!i2cCheckAck(regs)) continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while(--tries > 0);
|
||||
|
||||
return tries > 0;
|
||||
}
|
||||
|
||||
bool I2C_readRegBuf(I2cDevice devId, u8 regAddr, u8 *out, u32 size)
|
||||
{
|
||||
const u8 devAddr = i2cDevTable[devId].devAddr;
|
||||
I2cRegs *const regs = i2cGetBusRegsBase(i2cDevTable[devId].busId);
|
||||
|
||||
|
||||
if(!i2cStartTransfer(devAddr, regAddr, true, regs)) return false;
|
||||
|
||||
while(--size)
|
||||
{
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_READ | I2C_ACK;
|
||||
i2cWaitBusyIrq(regs);
|
||||
*out++ = regs->I2C_DATA;
|
||||
}
|
||||
|
||||
// Last byte transfer.
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_READ | I2C_STOP;
|
||||
i2cWaitBusyIrq(regs);
|
||||
*out = regs->I2C_DATA;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2C_writeRegBuf(I2cDevice devId, u8 regAddr, const u8 *in, u32 size)
|
||||
{
|
||||
const u8 devAddr = i2cDevTable[devId].devAddr;
|
||||
I2cRegs *const regs = i2cGetBusRegsBase(i2cDevTable[devId].busId);
|
||||
|
||||
|
||||
if(!i2cStartTransfer(devAddr, regAddr, false, regs)) return false;
|
||||
|
||||
while(--size)
|
||||
{
|
||||
regs->I2C_DATA = *in++;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE;
|
||||
i2cWaitBusyIrq(regs);
|
||||
if(!i2cCheckAck(regs)) return false;
|
||||
}
|
||||
|
||||
// Last byte transfer.
|
||||
regs->I2C_DATA = *in;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_IRQ_ENABLE | I2C_DIR_WRITE | I2C_STOP;
|
||||
i2cWaitBusyIrq(regs);
|
||||
if(!i2cCheckAck(regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 I2C_readReg(I2cDevice devId, u8 regAddr)
|
||||
{
|
||||
u8 data;
|
||||
if(!I2C_readRegBuf(devId, regAddr, &data, 1)) return 0xFF;
|
||||
return data;
|
||||
}
|
||||
|
||||
bool I2C_writeReg(I2cDevice devId, u8 regAddr, u8 data)
|
||||
{
|
||||
return I2C_writeRegBuf(devId, regAddr, &data, 1);
|
||||
}
|
||||
|
||||
bool I2C_writeRegIntSafe(I2cDevice devId, u8 regAddr, u8 data)
|
||||
{
|
||||
const u8 devAddr = i2cDevTable[devId].devAddr;
|
||||
|
@ -245,13 +284,13 @@ bool I2C_writeRegIntSafe(I2cDevice devId, u8 regAddr, u8 data)
|
|||
regs->I2C_DATA = devAddr;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_DIR_WRITE | I2C_START;
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
if(!i2cCheckAck(regs)) continue;
|
||||
if(!checkAck(regs)) continue;
|
||||
|
||||
// Select register.
|
||||
regs->I2C_DATA = regAddr;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_DIR_WRITE;
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
if(!i2cCheckAck(regs)) continue;
|
||||
if(!checkAck(regs)) continue;
|
||||
|
||||
break;
|
||||
} while(--tries > 0);
|
||||
|
@ -261,7 +300,7 @@ bool I2C_writeRegIntSafe(I2cDevice devId, u8 regAddr, u8 data)
|
|||
regs->I2C_DATA = data;
|
||||
regs->I2C_CNT = I2C_ENABLE | I2C_DIR_WRITE | I2C_STOP;
|
||||
while(regs->I2C_CNT & I2C_ENABLE);
|
||||
if(!i2cCheckAck(regs)) return false;
|
||||
if(!checkAck(regs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,23 +5,19 @@
|
|||
#include "ipc_handler.h"
|
||||
#include "arm11/hardware/hid.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
#include "fs.h"
|
||||
#include "hardware/cache.h"
|
||||
#include "arm11/hardware/pdn.h"
|
||||
#include "arm11/hardware/mcu.h"
|
||||
#include "arm11/hardware/lgyfb.h"
|
||||
#include "arm11/fmt.h"
|
||||
//#include "arm11/gba_save_type_db.h"
|
||||
|
||||
|
||||
#define LGY_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x41100)
|
||||
#define REG_LGY_MODE *(( vu16*)(LGY_REGS_BASE + 0x00))
|
||||
#define REG_LGY_SLEEP *(( vu16*)(LGY_REGS_BASE + 0x04))
|
||||
#define REG_LGY_UNK *((const vu16*)(LGY_REGS_BASE + 0x08)) // IRQ related?
|
||||
#define REG_LGY_PADCNT *((const vu16*)(LGY_REGS_BASE + 0x0A)) // ARM7 "KEYCNT"
|
||||
#define REG_LGY_PAD_SEL *(( vu16*)(LGY_REGS_BASE + 0x10)) // Select which keys to override.
|
||||
#define REG_LGY_PAD_VAL *(( vu16*)(LGY_REGS_BASE + 0x12)) // Override value.
|
||||
#define REG_LGY_GPIO_SEL *(( vu16*)(LGY_REGS_BASE + 0x14)) // Select which GPIOs to override.
|
||||
#define REG_LGY_PADCNT *((const vu16*)(LGY_REGS_BASE + 0x0A)) // Read-only mirror of ARM7 "KEYCNT".
|
||||
#define REG_LGY_PAD_SEL *(( vu16*)(LGY_REGS_BASE + 0x10)) // Select which keys to override. 1 = selected.
|
||||
#define REG_LGY_PAD_VAL *(( vu16*)(LGY_REGS_BASE + 0x12)) // Override value. Each bit 0 = pressed.
|
||||
#define REG_LGY_GPIO_SEL *(( vu16*)(LGY_REGS_BASE + 0x14)) // Select which GPIOs to override. 1 = selected.
|
||||
#define REG_LGY_GPIO_VAL *(( vu16*)(LGY_REGS_BASE + 0x16)) // Override value.
|
||||
#define REG_LGY_UNK2 *(( vu8*)(LGY_REGS_BASE + 0x18)) // DSi gamecard detection select?
|
||||
#define REG_LGY_UNK3 *(( vu8*)(LGY_REGS_BASE + 0x19)) // DSi gamecard detection value?
|
||||
|
@ -29,11 +25,14 @@
|
|||
|
||||
|
||||
|
||||
static void lgySleepIrqHandler(u32 intSource)
|
||||
static void lgySleepIsr(u32 intSource)
|
||||
{
|
||||
if(intSource == IRQ_LGY_SLEEP)
|
||||
{
|
||||
REG_HID_PADCNT = REG_LGY_PADCNT;
|
||||
// Workaround for The Legend of Zelda - A Link to the Past.
|
||||
// This game doesn't set the IRQ enable bit so we force it
|
||||
// on the 3DS side. Unknown if other games have this bug.
|
||||
REG_HID_PADCNT = REG_LGY_PADCNT | 1u<<14;
|
||||
}
|
||||
else // IRQ_HID_PADCNT
|
||||
{
|
||||
|
@ -43,225 +42,46 @@ static void lgySleepIrqHandler(u32 intSource)
|
|||
}
|
||||
}
|
||||
|
||||
static Result loadGbaRom(const char *const path, u32 *const rsOut)
|
||||
static void powerDownFcramForLegacy(u8 mode)
|
||||
{
|
||||
Result res;
|
||||
FHandle f;
|
||||
if((res = fOpen(&f, path, FA_OPEN_EXISTING | FA_READ)) == RES_OK)
|
||||
{
|
||||
u32 romSize;
|
||||
if((romSize = fSize(f)) <= MAX_ROM_SIZE)
|
||||
{
|
||||
u8 *ptr = (u8*)ROM_LOC;
|
||||
u32 read;
|
||||
while((res = fRead(f, ptr, 0x100000u, &read)) == RES_OK && read == 0x100000u)
|
||||
ptr += 0x100000u;
|
||||
|
||||
if(res == RES_OK)
|
||||
{
|
||||
*rsOut = romSize;
|
||||
// Pad ROM area with "open bus" value.
|
||||
memset((void*)(ROM_LOC + romSize), 0xFFFFFFFFu, MAX_ROM_SIZE - romSize);
|
||||
}
|
||||
}
|
||||
else res = RES_ROM_TOO_BIG;
|
||||
|
||||
fClose(f);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static u16 checkSaveOverride(u32 gameCode)
|
||||
{
|
||||
if((gameCode & 0xFFu) == 'F') // Classic NES Series.
|
||||
{
|
||||
return SAVE_TYPE_EEPROM_8k;
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
alignas(4) char gameCode[4];
|
||||
u16 saveType;
|
||||
} overrideLut[] =
|
||||
{
|
||||
{"\0\0\0\0", SAVE_TYPE_SRAM_256k}, // Homebrew. TODO: Set WAITCNT to 0x4014?
|
||||
{"GMB\0", SAVE_TYPE_SRAM_256k}, // Goomba Color (Homebrew).
|
||||
{"AA2\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 2.
|
||||
{"A3A\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 3.
|
||||
};
|
||||
|
||||
for(u32 i = 0; i < sizeof(overrideLut) / sizeof(*overrideLut); i++)
|
||||
{
|
||||
// Compare Game Code without region.
|
||||
if((gameCode & 0xFFFFFFu) == *((u32*)overrideLut[i].gameCode))
|
||||
{
|
||||
return overrideLut[i].saveType;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// Code based on: https://github.com/Gericom/GBARunner2/blob/master/arm9/source/save/Save.vram.cpp
|
||||
static u16 tryDetectSaveType(u32 romSize)
|
||||
{
|
||||
const u32 *romPtr = (u32*)ROM_LOC;
|
||||
u16 saveType;
|
||||
if((saveType = checkSaveOverride(romPtr[0xAC / 4])) != 0xFF)
|
||||
{
|
||||
debug_printf("Game Code in override list. Using save type %" PRIu16 ".\n", saveType);
|
||||
return saveType;
|
||||
}
|
||||
|
||||
romPtr += 0xE4 / 4; // Skip headers.
|
||||
saveType = SAVE_TYPE_NONE;
|
||||
for(; romPtr < (u32*)(ROM_LOC + romSize); romPtr++)
|
||||
{
|
||||
u32 tmp = *romPtr;
|
||||
|
||||
// "EEPR" "FLAS" "SRAM"
|
||||
if(tmp == 0x52504545u || tmp == 0x53414C46u || tmp == 0x4D415253u)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const char *str;
|
||||
u16 saveType;
|
||||
} saveTypeLut[25] =
|
||||
{
|
||||
// EEPROM
|
||||
{"EEPROM_V111", SAVE_TYPE_EEPROM_8k}, // Actually EEPROM 4k.
|
||||
{"EEPROM_V120", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
{"EEPROM_V121", SAVE_TYPE_EEPROM_64k}, // Confirmed.
|
||||
{"EEPROM_V122", SAVE_TYPE_EEPROM_8k}, // Confirmed. Except Super Mario Advance 2/3.
|
||||
{"EEPROM_V124", SAVE_TYPE_EEPROM_64k}, // Confirmed.
|
||||
{"EEPROM_V125", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
{"EEPROM_V126", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
|
||||
// FLASH
|
||||
// Assume they all have RTC.
|
||||
{"FLASH_V120", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V121", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V123", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V124", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V125", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V126", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V130", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V131", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V133", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH1M_V102", SAVE_TYPE_FLASH_1m_MRX_RTC},
|
||||
{"FLASH1M_V103", SAVE_TYPE_FLASH_1m_MRX_RTC},
|
||||
|
||||
// FRAM & SRAM
|
||||
{"SRAM_F_V100", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_F_V102", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_F_V103", SAVE_TYPE_SRAM_256k},
|
||||
|
||||
{"SRAM_V110", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V111", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V112", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V113", SAVE_TYPE_SRAM_256k}
|
||||
};
|
||||
|
||||
for(u32 i = 0; i < 25; i++)
|
||||
{
|
||||
const char *const str = saveTypeLut[i].str;
|
||||
u16 tmpSaveType = saveTypeLut[i].saveType;
|
||||
|
||||
if(memcmp(romPtr, str, strlen(str)) == 0)
|
||||
{
|
||||
if(tmpSaveType == SAVE_TYPE_EEPROM_8k || tmpSaveType == SAVE_TYPE_EEPROM_64k)
|
||||
{
|
||||
// If ROM bigger than 16 MiB --> SAVE_TYPE_EEPROM_8k_2 or SAVE_TYPE_EEPROM_64k_2.
|
||||
if(romSize > 0x1000000) tmpSaveType++;
|
||||
}
|
||||
saveType = tmpSaveType;
|
||||
debug_printf("Detected SDK save type '%s'.\n", str);
|
||||
goto saveTypeFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveTypeFound:
|
||||
|
||||
return saveType;
|
||||
}
|
||||
|
||||
/*static u16 getSaveTypeFromTable(void)
|
||||
{
|
||||
const u32 gameCode = *(u32*)(ROM_LOC + 0xAC) & ~0xFF000000u;
|
||||
|
||||
u16 saveType = SAVE_TYPE_NONE;
|
||||
for(u32 i = 0; i < sizeof(saveTypeLut) / sizeof(*saveTypeLut); i++)
|
||||
{
|
||||
// Save type in last byte.
|
||||
const u32 entry = *((u32*)&saveTypeLut[i]);
|
||||
if((entry & ~0xFF000000u) == gameCode)
|
||||
{
|
||||
saveType = entry>>24;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug_printf("Using save type 0x%" PRIX16 ".\n", saveType);
|
||||
|
||||
return saveType;
|
||||
}*/
|
||||
|
||||
static void setupFcramForGbaMode(void)
|
||||
{
|
||||
// FCRAM reset and clock disable.
|
||||
flushDCache();
|
||||
// TODO: Unmap FCRAM.
|
||||
while(!REG_LGY_MODE); // Wait until legacy mode is ready.
|
||||
*((vu32*)0x10201000) &= ~1u; // Some kind of bug fix for the GBA cart emu?
|
||||
REG_PDN_FCRAM_CNT = 0; // Set reset low (active).
|
||||
REG_PDN_FCRAM_CNT = PDN_FCRAM_CNT_RST; // Take it out of reset.
|
||||
|
||||
// For GBA mode we need to additionally apply a bug fix and reset FCRAM.
|
||||
if(mode == LGY_MODE_AGB)
|
||||
{
|
||||
*((vu32*)0x10201000) &= ~1u; // Bug fix for the GBA cart emu?
|
||||
REG_PDN_FCRAM_CNT = PDN_FCRAM_CNT_CLK_E; // Set reset low (active) but keep clock on.
|
||||
}
|
||||
REG_PDN_FCRAM_CNT = PDN_FCRAM_CNT_RST; // Take it out of reset but disable clock.
|
||||
while(REG_PDN_FCRAM_CNT & PDN_FCRAM_CNT_CLK_E_ACK); // Wait until clock is disabled.
|
||||
}
|
||||
|
||||
Result LGY_prepareGbaMode(bool biosIntro, char *const romPath)
|
||||
Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePath)
|
||||
{
|
||||
// Load the ROM image.
|
||||
u32 romSize;
|
||||
Result res = loadGbaRom(romPath, &romSize);
|
||||
if(res != RES_OK) return res;
|
||||
|
||||
// Try to detect the save type.
|
||||
const u16 saveType = tryDetectSaveType(romSize);
|
||||
//const u16 saveType = getSaveTypeFromTable();
|
||||
|
||||
// Prepare ARM9 for GBA mode + settings and save loading.
|
||||
const u32 romPathLen = strlen(romPath);
|
||||
strcpy(romPath + romPathLen - 4, ".sav");
|
||||
|
||||
u32 cmdBuf[4];
|
||||
cmdBuf[0] = (u32)romPath;
|
||||
cmdBuf[1] = romPathLen + 1;
|
||||
cmdBuf[0] = (u32)savePath;
|
||||
cmdBuf[1] = strlen(savePath) + 1;
|
||||
cmdBuf[2] = biosIntro;
|
||||
cmdBuf[3] = saveType;
|
||||
res = PXI_sendCmd(IPC_CMD9_PREPARE_GBA, cmdBuf, 4);
|
||||
Result res = PXI_sendCmd(IPC_CMD9_PREPARE_GBA, cmdBuf, 4);
|
||||
if(res != RES_OK) return res;
|
||||
|
||||
// Setup GBA Real-Time Clock.
|
||||
GbaRtc rtc;
|
||||
MCU_getRTCTime((u8*)&rtc);
|
||||
rtc.time = __builtin_bswap32(rtc.time)>>8;
|
||||
rtc.date = __builtin_bswap32(rtc.date)>>8;
|
||||
// TODO: Do we need to set day of week?
|
||||
rtc.date = __builtin_bswap32(rtc.date)>>8; // TODO: Do we need to set day of week?
|
||||
LGY_setGbaRtc(rtc);
|
||||
|
||||
// Setup Legacy Framebuffer.
|
||||
LGYFB_init();
|
||||
|
||||
// Setup FCRAM for GBA mode.
|
||||
setupFcramForGbaMode();
|
||||
powerDownFcramForLegacy(LGY_MODE_AGB);
|
||||
|
||||
// Setup IRQ handlers and sleep mode handling.
|
||||
REG_LGY_SLEEP = 1u<<15;
|
||||
IRQ_registerIsr(IRQ_LGY_SLEEP, 14, 0, lgySleepIrqHandler);
|
||||
IRQ_registerIsr(IRQ_HID_PADCNT, 14, 0, lgySleepIrqHandler);
|
||||
IRQ_registerIsr(IRQ_LGY_SLEEP, 14, 0, lgySleepIsr);
|
||||
IRQ_registerIsr(IRQ_HID_PADCNT, 14, 0, lgySleepIsr);
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
@ -282,53 +102,7 @@ void LGY_switchMode(void)
|
|||
REG_LGY_MODE = LGY_MODE_START;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "arm11/hardware/gx.h"
|
||||
#include "arm11/hardware/gpu_regs.h"
|
||||
void debugTests(void)
|
||||
{
|
||||
const u32 kDown = hidKeysDown();
|
||||
|
||||
// Print GBA RTC date/time.
|
||||
if(kDown & KEY_X)
|
||||
{
|
||||
GbaRtc rtc; LGY_getGbaRtc(&rtc);
|
||||
ee_printf("RTC: %02X.%02X.%04X %02X:%02X:%02X\n", rtc.d, rtc.mon, rtc.y + 0x2000u, rtc.h, rtc.min, rtc.s);
|
||||
|
||||
/*static u8 filter = 1;
|
||||
filter ^= 1;
|
||||
u32 texEnvSource = 0x000F000F;
|
||||
u32 texEnvCombiner = 0x00000000;
|
||||
if(filter == 1)
|
||||
{
|
||||
texEnvSource = 0x00FF00FFu;
|
||||
texEnvCombiner = 0x00010001u;
|
||||
}
|
||||
REG_GX_P3D(GPUREG_TEXENV1_SOURCE) = texEnvSource;
|
||||
REG_GX_P3D(GPUREG_TEXENV1_COMBINER) = texEnvCombiner;*/
|
||||
|
||||
// Trigger Game Boy Player enhancements.
|
||||
// Needs to be done on the Game Boy Player logo screen.
|
||||
// 2 frames nothing pressed and 1 frame all D-Pad buttons pressed.
|
||||
/*REG_LGY_PAD_SEL = 0x1FFF; // Override all buttons.
|
||||
static u8 gbp = 2;
|
||||
if(gbp > 0)
|
||||
{
|
||||
REG_LGY_PAD_VAL = 0x1FFF; // Force all buttons not pressed.
|
||||
gbp--;
|
||||
}
|
||||
else
|
||||
{
|
||||
REG_LGY_PAD_VAL = 0x1F0F; // All D-Pad buttons pressed.
|
||||
gbp = 2;
|
||||
}*/
|
||||
}
|
||||
//else REG_LGY_PAD_SEL = 0; // Stop overriding buttons.
|
||||
if(kDown & KEY_Y) LGYFB_dbgDumpFrame();
|
||||
}
|
||||
#endif
|
||||
|
||||
void LGY_handleEvents(void)
|
||||
void LGY_handleOverrides(void)
|
||||
{
|
||||
// Override D-Pad if Circle-Pad is used.
|
||||
const u32 kHeld = hidKeysHeld();
|
||||
|
@ -340,15 +114,6 @@ void LGY_handleEvents(void)
|
|||
}
|
||||
else padSel = 0;
|
||||
REG_LGY_PAD_SEL = padSel;
|
||||
|
||||
#ifndef NDEBUG
|
||||
debugTests();
|
||||
#endif
|
||||
|
||||
LGYFB_processFrame();
|
||||
|
||||
// Bit 0 triggers wakeup. Bit 1 sleep state/ack sleep end. Bit 2 unk. Bit 15 IRQ enable (triggers IRQ 89).
|
||||
//if(REG_LGY_SLEEP & 2u) REG_HID_PADCNT = REG_LGY_PADCNT;
|
||||
}
|
||||
|
||||
Result LGY_backupGbaSave(void)
|
||||
|
@ -358,11 +123,7 @@ Result LGY_backupGbaSave(void)
|
|||
|
||||
void LGY_deinit(void)
|
||||
{
|
||||
//REG_LGY_PAD_VAL = 0x1FFF; // Force all buttons not pressed.
|
||||
//REG_LGY_PAD_SEL = 0x1FFF;
|
||||
|
||||
LGY_backupGbaSave();
|
||||
LGYFB_deinit();
|
||||
|
||||
IRQ_unregisterIsr(IRQ_LGY_SLEEP);
|
||||
IRQ_unregisterIsr(IRQ_HID_PADCNT);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#include "arm11/hardware/interrupt.h"
|
||||
#include "hardware/corelink_dma-330.h"
|
||||
#include "arm11/hardware/lcd.h"
|
||||
#include "hardware/gfx.h"
|
||||
#include "lgyfb_dma330.h"
|
||||
#include "kevent.h"
|
||||
|
||||
|
||||
#define LGYFB_TOP_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x11000)
|
||||
|
@ -75,7 +75,7 @@
|
|||
#define LGYFB_BOT_FIFO *((const vu32*)(0x10310000))
|
||||
|
||||
|
||||
static bool g_frameReady = false;
|
||||
static KEvent *g_frameReadyEvent = NULL;
|
||||
|
||||
|
||||
|
||||
|
@ -92,7 +92,7 @@ static void lgyFbDmaIrqHandler(UNUSED u32 intSource)
|
|||
else vtotal = 414; // Faster than GBA.
|
||||
REG_LCD_PDC0_VTOTAL = vtotal;
|
||||
|
||||
atomic_store_explicit(&g_frameReady, true, memory_order_relaxed);
|
||||
signalEvent(g_frameReadyEvent, false);
|
||||
}
|
||||
|
||||
static void setScaleMatrixTop(u32 len, u32 patt, const s16 *const matrix)
|
||||
|
@ -118,10 +118,12 @@ static void setScaleMatrixTop(u32 len, u32 patt, const s16 *const matrix)
|
|||
}
|
||||
}
|
||||
|
||||
void LGYFB_init(void)
|
||||
void LGYFB_init(KEvent *frameReadyEvent)
|
||||
{
|
||||
if(DMA330_run(0, program)) return;
|
||||
|
||||
g_frameReadyEvent = frameReadyEvent;
|
||||
|
||||
//REG_LGYFB_TOP_SIZE = LGYFB_SIZE(240u, 160u);
|
||||
REG_LGYFB_TOP_SIZE = LGYFB_SIZE(360u, 240u);
|
||||
REG_LGYFB_TOP_STAT = LGYFB_IRQ_MASK;
|
||||
|
@ -182,349 +184,6 @@ void LGYFB_init(void)
|
|||
LGYFB_HSCALE_E | LGYFB_VSCALE_E | LGYFB_ENABLE;
|
||||
|
||||
IRQ_registerIsr(IRQ_CDMA_EVENT0, 13, 0, lgyFbDmaIrqHandler);
|
||||
|
||||
|
||||
const double inGamma = 4.0;
|
||||
const double outGamma = 2.2;
|
||||
//const double contrast = .74851331406341291833644689906823; // GBA
|
||||
//const double brightness = .25148668593658708166355310093177; // GBA
|
||||
const double contrast = 1.0; // No-op
|
||||
const double brightness = 0.0; // No-op
|
||||
//REG_LCD_PDC0_GTBL_IDX = 0;
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
{
|
||||
// Credits for this algo go to Extrems.
|
||||
// Originally from Game Boy Interface Standard Edition for the Game Cube.
|
||||
//const u32 x = (i & ~7u) | i>>5;
|
||||
u32 res = pow(pow(contrast, inGamma) * pow((double)i / 255.0f + brightness / contrast, inGamma),
|
||||
1.0 / outGamma) * 255.0f;
|
||||
if(res > 255) res = 255;
|
||||
|
||||
// Same adjustment for red/green/blue.
|
||||
REG_LCD_PDC0_GTBL_FIFO = res<<16 | res<<8 | res;
|
||||
}
|
||||
}
|
||||
|
||||
static void rotateFrame(void)
|
||||
{
|
||||
// 360x240, no filter.
|
||||
alignas(16) static const u8 firstList[1136] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x44, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xCB, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xCC, 0x02, 0x7F, 0x00,
|
||||
0x01, 0xF0, 0x07, 0x4E, 0x02, 0x08, 0x02, 0x08, 0x03, 0x18, 0x02, 0x08,
|
||||
0x04, 0x28, 0x02, 0x08, 0x05, 0x38, 0x02, 0x08, 0x06, 0x10, 0x20, 0x4C,
|
||||
0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0xBF, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x02, 0x0F, 0x00,
|
||||
0x6E, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x6F, 0x00, 0xA1, 0x0A, 0x00, 0x00,
|
||||
0x68, 0xC3, 0x06, 0x00, 0x64, 0xC3, 0x06, 0x00, 0x62, 0xC3, 0x06, 0x00,
|
||||
0x61, 0xC3, 0x06, 0x00, 0x6F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F,
|
||||
0xBA, 0x02, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, 0xBD, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x4A, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x51, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x01, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x7F, 0x80, 0x00, 0x01, 0x02, 0x03,
|
||||
0x0C, 0x0D, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x0F, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x54, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0,
|
||||
0x89, 0x02, 0x0F, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x01, 0x02, 0x1F, 0x80,
|
||||
0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xA0,
|
||||
0xB9, 0x02, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x02, 0x0F, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x0F, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xBF, 0x00,
|
||||
0x4D, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x04, 0x01, 0x3F, 0x80, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x76, 0x76, 0x01, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0F, 0x00, 0x00, 0x01, 0xE4, 0x00,
|
||||
0x00, 0x01, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x80, 0x30, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x81, 0x00, 0x4F, 0x80, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, // Last 4 bytes: Texture format.
|
||||
0x8E, 0x00, 0x0F, 0x00, 0x01, 0x10, 0x01, 0x00, 0x80, 0x00, 0x0B, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE1, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xC8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x3F, 0x80,
|
||||
0xBF, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xD7, 0xA3, 0xBB,
|
||||
0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0x7F, 0xB0, 0x02, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x5E, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x00, 0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00,
|
||||
0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x68, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x31, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x63, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00
|
||||
};
|
||||
// 240x160 with bilinear scaling, no filter.
|
||||
/*alignas(16) static const u8 firstList[1136] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x44, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xCB, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xCC, 0x02, 0x7F, 0x00,
|
||||
0x01, 0xF0, 0x07, 0x4E, 0x02, 0x08, 0x02, 0x08, 0x03, 0x18, 0x02, 0x08,
|
||||
0x04, 0x28, 0x02, 0x08, 0x05, 0x38, 0x02, 0x08, 0x06, 0x10, 0x20, 0x4C,
|
||||
0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0xBF, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x02, 0x0F, 0x00,
|
||||
0x6E, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x6F, 0x00, 0xA1, 0x0A, 0x00, 0x00,
|
||||
0x68, 0xC3, 0x06, 0x00, 0x64, 0xC3, 0x06, 0x00, 0x62, 0xC3, 0x06, 0x00,
|
||||
0x61, 0xC3, 0x06, 0x00, 0x6F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x7F,
|
||||
0xBA, 0x02, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, 0xBD, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x4A, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x51, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x01, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x7F, 0x80, 0x00, 0x01, 0x02, 0x03,
|
||||
0x0C, 0x0D, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x0F, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x29, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x54, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0,
|
||||
0x89, 0x02, 0x0F, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x01, 0x02, 0x1F, 0x80,
|
||||
0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xA0,
|
||||
0xB9, 0x02, 0x0B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x02, 0x0F, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x0F, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xBF, 0x00,
|
||||
0x4D, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x04, 0x01, 0x3F, 0x80, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x76, 0x76, 0x01, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0F, 0x00, 0x00, 0x01, 0xE4, 0x00,
|
||||
0x00, 0x01, 0x07, 0x00, 0x00, 0x3C, 0x00, 0x80, 0x30, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x81, 0x00, 0x4F, 0x80, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8E, 0x00, 0x0F, 0x00, 0x01, 0x10, 0x01, 0x00, 0x80, 0x00, 0x0B, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x8B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE1, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xC8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xD8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF0, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xF8, 0x00, 0x4F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x3F, 0x80,
|
||||
0xBF, 0x00, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xD7, 0xA3, 0xBB,
|
||||
0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0x7F, 0xB0, 0x02, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x5E, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x0F, 0x00, 0x00, 0x00, 0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00,
|
||||
0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0xE0, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x31, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x63, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00
|
||||
};*/
|
||||
|
||||
// 360x240, no filter.
|
||||
alignas(16) static const u8 secondList[448] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xC0, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0xD7, 0xA3, 0xBB, 0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5E, 0x02, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x10, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x10, 0x3E, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x68, 0x3E, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12,
|
||||
0x10, 0x00, 0x0F, 0x00
|
||||
};
|
||||
// 240x160 with bilinear scaling, no filter.
|
||||
/*alignas(16) static const u8 secondList[448] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x03,
|
||||
0x1C, 0x01, 0x2F, 0x80, 0x00, 0x00, 0x03, 0x03, 0xF0, 0xF0, 0x18, 0x01,
|
||||
0xF0, 0xF0, 0x18, 0x01, 0x6E, 0x00, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x16, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x17, 0x01, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x12, 0x01, 0x3F, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x45, 0x00,
|
||||
0x41, 0x00, 0x3F, 0x80, 0x10, 0x11, 0x11, 0x38, 0x00, 0x90, 0x46, 0x00,
|
||||
0x14, 0xAE, 0x47, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xC0, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x80, 0xBF, 0xC1, 0x02, 0xFF, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x08, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0A, 0xD7, 0xA3, 0xBB, 0x00, 0x00, 0x80, 0xBF, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x5E, 0x02, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x5F, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x27, 0x02, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x53, 0x02, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x32, 0x02, 0x0F, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x80, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80,
|
||||
0x80, 0x3D, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x3F,
|
||||
0x33, 0x02, 0x2F, 0x80, 0xE0, 0x46, 0x00, 0x00, 0x00, 0x40, 0x43, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x33, 0x02, 0x2F, 0x80,
|
||||
0xE0, 0x46, 0x00, 0x00, 0x00, 0x7C, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x33, 0x02, 0x2F, 0x80, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xE0, 0x3E, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x45, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x53, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x02, 0x0F, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x10, 0x01, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x0F, 0x00,
|
||||
0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x0F, 0x00, 0x78, 0x56, 0x34, 0x12,
|
||||
0x10, 0x00, 0x0F, 0x00
|
||||
};*/
|
||||
|
||||
static bool normalRender = false;
|
||||
u32 listSize;
|
||||
const u32 *list;
|
||||
if(normalRender == false)
|
||||
{
|
||||
normalRender = true;
|
||||
|
||||
listSize = 1136;
|
||||
list = (u32*)firstList;
|
||||
}
|
||||
else
|
||||
{
|
||||
listSize = 448;
|
||||
list = (u32*)secondList;
|
||||
}
|
||||
GX_processCommandList(listSize, list);
|
||||
GFX_waitForP3D();
|
||||
GX_displayTransfer((u32*)(0x18180000 + (16 * 240 * 3)), 368u<<16 | 240u,
|
||||
GFX_getFramebuffer(SCREEN_TOP) + (16 * 240 * 3), 368u<<16 | 240u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForPPF();
|
||||
}
|
||||
|
||||
void LGYFB_processFrame(void)
|
||||
{
|
||||
if(atomic_load_explicit(&g_frameReady, memory_order_relaxed))
|
||||
{
|
||||
atomic_store_explicit(&g_frameReady, false, memory_order_relaxed);
|
||||
|
||||
// Rotate the frame using the GPU.
|
||||
// 240x160: TODO.
|
||||
// 360x240: about 0.623620315 ms.
|
||||
rotateFrame();
|
||||
GFX_swapFramebufs();
|
||||
}
|
||||
}
|
||||
|
||||
void LGYFB_deinit(void)
|
||||
|
@ -534,17 +193,18 @@ void LGYFB_deinit(void)
|
|||
DMA330_kill(0);
|
||||
|
||||
IRQ_unregisterIsr(IRQ_CDMA_EVENT0);
|
||||
g_frameReadyEvent = NULL;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "fsutil.h"
|
||||
void LGYFB_dbgDumpFrame(void)
|
||||
/*void LGYFB_dbgDumpFrame(void)
|
||||
{
|
||||
/*GX_displayTransfer((u32*)0x18200000, 160u<<16 | 256u, (u32*)0x18400000, 160u<<16 | 256u, 1u<<12 | 1u<<8);
|
||||
GX_displayTransfer((u32*)0x18200000, 160u<<16 | 256u, (u32*)0x18400000, 160u<<16 | 256u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForEvent(GFX_EVENT_PPF, false);
|
||||
fsQuickWrite((void*)0x18400000, "sdmc:/lgyfb_dbg_frame.bgr", 256 * 160 * 3);*/
|
||||
GX_displayTransfer((u32*)0x18200000, 240u<<16 | 512u, (u32*)0x18400000, 240u<<16 | 512u, 1u<<12 | 1u<<8);
|
||||
fsQuickWrite("sdmc:/lgyfb_dbg_frame.bgr", (void*)0x18400000, 256 * 160 * 3);*/
|
||||
/*GX_displayTransfer((u32*)0x18200000, 240u<<16 | 512u, (u32*)0x18400000, 240u<<16 | 512u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForEvent(GFX_EVENT_PPF, false);
|
||||
fsQuickWrite((void*)0x18400000, "sdmc:/lgyfb_dbg_frame.bgr", 512 * 240 * 3);
|
||||
}
|
||||
fsQuickWrite("sdmc:/lgyfb_dbg_frame.bgr", (void*)0x18400000, 512 * 240 * 3);
|
||||
}*/
|
||||
#endif
|
||||
|
|
|
@ -16,66 +16,41 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "fs.h"
|
||||
#include "hardware/gfx.h"
|
||||
#include "arm11/console.h"
|
||||
#include "arm11/open_agb_firm.h"
|
||||
#include "arm11/hardware/hid.h"
|
||||
#include "arm11/hardware/codec.h"
|
||||
#include "hardware/lgy.h"
|
||||
#include "arm11/console.h"
|
||||
#include "arm11/fmt.h"
|
||||
#include "arm11/hardware/mcu.h"
|
||||
#include "arm11/power.h"
|
||||
#include "hardware/gfx.h"
|
||||
#include "fs.h"
|
||||
#include "arm11/filebrowser.h"
|
||||
#include "arm.h"
|
||||
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Result res = fMount(FS_DRIVE_SDMC);
|
||||
GFX_init(GFX_BGR8, GFX_RGB565);
|
||||
GFX_setBrightness(DEFAULT_BRIGHTNESS, DEFAULT_BRIGHTNESS);
|
||||
consoleInit(SCREEN_BOT, NULL);
|
||||
//CODEC_init();
|
||||
|
||||
Result res;
|
||||
char *romPath = (char*)malloc(512);
|
||||
*romPath = '\0';
|
||||
if((res = fMount(FS_DRIVE_SDMC)) != RES_OK || (res = browseFiles("sdmc:/", romPath)) != RES_OK || *romPath == '\0')
|
||||
goto end;
|
||||
|
||||
ee_puts("Reading ROM and save...");
|
||||
if((res = LGY_prepareGbaMode(false, romPath)) == RES_OK)
|
||||
if(res == RES_OK && (res = oafInitAndRun()) == RES_OK)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
GFX_setForceBlack(false, true);
|
||||
// Don't turn the backlight off on 2DS.
|
||||
if(MCU_getSystemModel() != 3) GFX_powerOffBacklights(GFX_BLIGHT_BOT);
|
||||
#endif
|
||||
// Sync LgyFb start with LCD VBlank.
|
||||
GFX_waitForVBlank0();
|
||||
LGY_switchMode();
|
||||
|
||||
do
|
||||
{
|
||||
hidScanInput();
|
||||
if(hidGetExtraKeys(0) & (KEY_POWER_HELD | KEY_POWER)) break;
|
||||
|
||||
LGY_handleEvents();
|
||||
|
||||
__wfi();
|
||||
oafUpdate();
|
||||
} while(1);
|
||||
|
||||
oafFinish();
|
||||
}
|
||||
else printErrorWaitInput(res, 0);
|
||||
|
||||
end:
|
||||
free(romPath);
|
||||
if(res != RES_OK) printErrorWaitInput(res, 0);
|
||||
|
||||
LGY_deinit();
|
||||
fUnmount(FS_DRIVE_SDMC);
|
||||
CODEC_deinit();
|
||||
GFX_deinit();
|
||||
fUnmount(FS_DRIVE_SDMC);
|
||||
|
||||
power_off();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,709 @@
|
|||
/*
|
||||
* This file is part of fastboot 3DS
|
||||
* Copyright (C) 2017 derrek, profi200
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "arm_intrinsic.h"
|
||||
#include "util.h"
|
||||
#include "arm11/hardware/hash.h"
|
||||
#include "arm11/hardware/hid.h"
|
||||
#include "hardware/lgy.h"
|
||||
#include "arm11/hardware/lgyfb.h"
|
||||
#include "arm11/console.h"
|
||||
#include "arm11/fmt.h"
|
||||
#include "hardware/gfx.h"
|
||||
#include "fs.h"
|
||||
#include "fsutil.h"
|
||||
#include "inih/ini.h"
|
||||
#include "arm11/filebrowser.h"
|
||||
#include "arm11/hardware/lcd.h"
|
||||
#include "arm11/gpu_cmd_lists.h"
|
||||
#include "arm11/hardware/mcu.h"
|
||||
#include "kernel.h"
|
||||
#include "kevent.h"
|
||||
|
||||
|
||||
//#define OAF_SAVE_DB_DEBUG (1)
|
||||
#define OAF_WORK_DIR "sdmc:/3ds/open_agb_firm"
|
||||
#define INI_BUF_SIZE (1024u)
|
||||
#define DEFAULT_CONFIG "[general]\n" \
|
||||
"backlight=40\n" \
|
||||
"biosIntro=true\n\n" \
|
||||
"[video]\n" \
|
||||
"inGamma=2.2\n" \
|
||||
"outGamma=1.54\n" \
|
||||
"contrast=1.0\n" \
|
||||
"brightness=0.0\n"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// [general]
|
||||
u8 backlight; // Both LCDs.
|
||||
bool biosIntro;
|
||||
|
||||
// [video]
|
||||
float inGamma;
|
||||
float outGamma;
|
||||
float contrast;
|
||||
float brightness;
|
||||
} OafConfig;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// [game]
|
||||
u16 saveType;
|
||||
u8 saveSlot;
|
||||
} OafGameConfig;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[200];
|
||||
char gameCode[4];
|
||||
u8 sha1[20];
|
||||
u32 attr;
|
||||
} GameDbEntry;
|
||||
|
||||
|
||||
// Default config.
|
||||
static OafConfig g_oafConfig =
|
||||
{
|
||||
40,
|
||||
true,
|
||||
2.2f,
|
||||
1.54f,
|
||||
1.f,
|
||||
0.f
|
||||
};
|
||||
static KEvent *g_frameReadyEvent = NULL;
|
||||
|
||||
|
||||
|
||||
static u32 fixRomPadding(u32 romFileSize)
|
||||
{
|
||||
// Pad unused ROM area with 0xFFs (trimmed ROMs).
|
||||
// Smallest retail ROM chip is 8 Mbit (1 MiB).
|
||||
u32 romSize = nextPow2(romFileSize);
|
||||
if(romSize < 0x100000u) romSize = 0x100000u;
|
||||
memset((void*)(ROM_LOC + romFileSize), 0xFFFFFFFFu, romSize - romFileSize);
|
||||
|
||||
if(romSize > 0x100000u)
|
||||
{
|
||||
// Fake "open bus" padding.
|
||||
u32 padding = (ROM_LOC + romSize) / 2;
|
||||
padding = __pkhbt(padding, padding + 1, 16); // Copy lower half + 1 to upper half.
|
||||
for(uintptr_t i = ROM_LOC + romSize; i < ROM_LOC + MAX_ROM_SIZE; i += 4)
|
||||
{
|
||||
*(u32*)i = padding;
|
||||
padding = __uadd16(padding, 0x00020002u); // Unsigned parallel halfword-wise addition.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ROM mirroring (Classic NES Series/possibly others with 8 Mbit ROM).
|
||||
// Mirror ROM across the entire 32 MiB area.
|
||||
for(uintptr_t i = ROM_LOC + romSize; i < ROM_LOC + MAX_ROM_SIZE; i += romSize)
|
||||
{
|
||||
//memcpy((void*)i, (void*)(i - romSize), romSize); // 0x23A15DD
|
||||
memcpy((void*)i, (void*)ROM_LOC, romSize); // 0x237109B
|
||||
}
|
||||
}
|
||||
|
||||
return romSize;
|
||||
}
|
||||
|
||||
static Result loadGbaRom(const char *const path, u32 *const romSizeOut)
|
||||
{
|
||||
Result res;
|
||||
FHandle f;
|
||||
if((res = fOpen(&f, path, FA_OPEN_EXISTING | FA_READ)) == RES_OK)
|
||||
{
|
||||
u32 fileSize;
|
||||
if((fileSize = fSize(f)) <= MAX_ROM_SIZE)
|
||||
{
|
||||
u8 *ptr = (u8*)ROM_LOC;
|
||||
u32 read;
|
||||
while((res = fRead(f, ptr, 0x100000u, &read)) == RES_OK && read == 0x100000u)
|
||||
ptr += 0x100000u;
|
||||
|
||||
*romSizeOut = fixRomPadding(fileSize);
|
||||
}
|
||||
else res = RES_ROM_TOO_BIG;
|
||||
|
||||
fClose(f);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Search for entry with first u64 of the SHA1 = x using binary search.
|
||||
static Result searchGameDb(u64 x, GameDbEntry *const db, s32 *const entryPos)
|
||||
{
|
||||
debug_printf("Database search: '%016" PRIX64 "'\n", __builtin_bswap64(x));
|
||||
|
||||
Result res;
|
||||
FHandle f;
|
||||
if((res = fOpen(&f, "gba_db.bin", FA_OPEN_EXISTING | FA_READ)) == RES_OK)
|
||||
{
|
||||
s32 l = 0;
|
||||
s32 r = fSize(f) / sizeof(GameDbEntry) - 1; // TODO: Check for 0!
|
||||
while(1)
|
||||
{
|
||||
const s32 mid = l + (r - l) / 2;
|
||||
debug_printf("l: %ld r: %ld mid: %ld\n", l, r, mid);
|
||||
|
||||
if((res = fLseek(f, sizeof(GameDbEntry) * mid)) != RES_OK) break;
|
||||
if((res = fRead(f, db, sizeof(GameDbEntry), NULL)) != RES_OK) break;
|
||||
const u64 tmp = *(u64*)db->sha1; // Unaligned access.
|
||||
if(tmp == x)
|
||||
{
|
||||
*entryPos = mid; // TODO: Remove.
|
||||
break;
|
||||
}
|
||||
|
||||
if(r <= l || r < 0)
|
||||
{
|
||||
res = RES_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmp > x) r = mid - 1;
|
||||
else l = mid + 1;
|
||||
}
|
||||
|
||||
fClose(f);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static u16 checkSaveOverride(u32 gameCode)
|
||||
{
|
||||
if((gameCode & 0xFFu) == 'F') // Classic NES Series.
|
||||
{
|
||||
return SAVE_TYPE_EEPROM_8k;
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
alignas(4) char gameCode[4];
|
||||
u16 saveType;
|
||||
} overrideLut[] =
|
||||
{
|
||||
{"\0\0\0\0", SAVE_TYPE_SRAM_256k}, // Homebrew. TODO: Set WAITCNT to 0x4014?
|
||||
{"GMB\0", SAVE_TYPE_SRAM_256k}, // Goomba Color (Homebrew).
|
||||
{"AA2\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 2.
|
||||
{"A3A\0", SAVE_TYPE_EEPROM_64k}, // Super Mario Advance 3.
|
||||
{"AZL\0", SAVE_TYPE_EEPROM_64k}, // Legend of Zelda, The - A Link to the Past & Four Swords.
|
||||
};
|
||||
|
||||
for(u32 i = 0; i < sizeof(overrideLut) / sizeof(*overrideLut); i++)
|
||||
{
|
||||
// Compare Game Code without region.
|
||||
if((gameCode & 0xFFFFFFu) == *((u32*)overrideLut[i].gameCode))
|
||||
{
|
||||
return overrideLut[i].saveType;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static u16 tryDetectSaveType(u32 romSize)
|
||||
{
|
||||
const u32 *romPtr = (u32*)ROM_LOC;
|
||||
u16 saveType;
|
||||
if((saveType = checkSaveOverride(romPtr[0xAC / 4])) != 0xFF)
|
||||
{
|
||||
debug_printf("Game Code in override list. Using save type %" PRIu16 ".\n", saveType);
|
||||
return saveType;
|
||||
}
|
||||
|
||||
// Code based on: https://github.com/Gericom/GBARunner2/blob/master/arm9/source/save/Save.vram.cpp
|
||||
romPtr += 0xE4 / 4; // Skip headers.
|
||||
saveType = SAVE_TYPE_NONE;
|
||||
for(; romPtr < (u32*)(ROM_LOC + romSize); romPtr++)
|
||||
{
|
||||
u32 tmp = *romPtr;
|
||||
|
||||
// "EEPR" "FLAS" "SRAM"
|
||||
if(tmp == 0x52504545u || tmp == 0x53414C46u || tmp == 0x4D415253u)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const char *str;
|
||||
u16 saveType;
|
||||
} saveTypeLut[25] =
|
||||
{
|
||||
// EEPROM
|
||||
{"EEPROM_V111", SAVE_TYPE_EEPROM_8k}, // Actually EEPROM 4k.
|
||||
{"EEPROM_V120", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
{"EEPROM_V121", SAVE_TYPE_EEPROM_64k}, // Confirmed.
|
||||
{"EEPROM_V122", SAVE_TYPE_EEPROM_8k}, // Confirmed. Except Super Mario Advance 2/3.
|
||||
{"EEPROM_V124", SAVE_TYPE_EEPROM_64k}, // Confirmed.
|
||||
{"EEPROM_V125", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
{"EEPROM_V126", SAVE_TYPE_EEPROM_8k}, // Confirmed.
|
||||
|
||||
// FLASH
|
||||
// Assume they all have RTC.
|
||||
{"FLASH_V120", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V121", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V123", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V124", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V125", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH_V126", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V130", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V131", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH512_V133", SAVE_TYPE_FLASH_512k_PSC_RTC},
|
||||
{"FLASH1M_V102", SAVE_TYPE_FLASH_1m_MRX_RTC},
|
||||
{"FLASH1M_V103", SAVE_TYPE_FLASH_1m_MRX_RTC},
|
||||
|
||||
// FRAM & SRAM
|
||||
{"SRAM_F_V100", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_F_V102", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_F_V103", SAVE_TYPE_SRAM_256k},
|
||||
|
||||
{"SRAM_V110", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V111", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V112", SAVE_TYPE_SRAM_256k},
|
||||
{"SRAM_V113", SAVE_TYPE_SRAM_256k}
|
||||
};
|
||||
|
||||
for(u32 i = 0; i < 25; i++)
|
||||
{
|
||||
const char *const str = saveTypeLut[i].str;
|
||||
u16 tmpSaveType = saveTypeLut[i].saveType;
|
||||
|
||||
if(memcmp(romPtr, str, strlen(str)) == 0)
|
||||
{
|
||||
if(tmpSaveType == SAVE_TYPE_EEPROM_8k || tmpSaveType == SAVE_TYPE_EEPROM_64k)
|
||||
{
|
||||
// If ROM bigger than 16 MiB --> SAVE_TYPE_EEPROM_8k_2 or SAVE_TYPE_EEPROM_64k_2.
|
||||
if(romSize > 0x1000000) tmpSaveType++;
|
||||
}
|
||||
saveType = tmpSaveType;
|
||||
debug_printf("Detected SDK save type '%s'.\n", str);
|
||||
goto saveTypeFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveTypeFound:
|
||||
|
||||
return saveType;
|
||||
}
|
||||
|
||||
static u16 saveDbDebug(const char *const savePath, u32 romSize)
|
||||
{
|
||||
FILINFO fi;
|
||||
const bool saveExists = fStat(savePath, &fi) == RES_OK;
|
||||
const u16 autoSaveType = tryDetectSaveType(romSize);
|
||||
|
||||
// TODO: Check for homebrew before searching the db.
|
||||
u64 sha1[3];
|
||||
hash((u32*)ROM_LOC, romSize, (u32*)sha1, HASH_INPUT_BIG | HASH_MODE_1, HASH_OUTPUT_BIG);
|
||||
|
||||
Result res;
|
||||
GameDbEntry dbEntry;
|
||||
s32 dbPos = -1;
|
||||
u16 saveType = SAVE_TYPE_NONE;
|
||||
if((res = searchGameDb(*sha1, &dbEntry, &dbPos)) == RES_OK) saveType = dbEntry.attr & 0xFu;
|
||||
else
|
||||
{
|
||||
ee_puts("Could not access the game db! Press the power button twice.");
|
||||
printErrorWaitInput(res, 0);
|
||||
return SAVE_TYPE_NONE;
|
||||
}
|
||||
|
||||
consoleClear();
|
||||
ee_printf("Save file (Press (X) to delete): %s\n"
|
||||
"Save type (from db): %u\n"
|
||||
"Save type (auto detect): %u\n\n"
|
||||
" EEPROM 4k/8k (0, 1)\n"
|
||||
" EEPROM 64k (2, 3)\n"
|
||||
" Flash 512k RTC (4, 6, 8)\n"
|
||||
" Flash 512k (5, 7, 9)\n"
|
||||
" Flash 1m RTC (10, 12)\n"
|
||||
" Flash 1m (11, 13)\n"
|
||||
" SRAM 256k (14)\n"
|
||||
" None (15)\n\n\n", (saveExists ? "found" : "not found"), saveType, autoSaveType);
|
||||
ee_puts("Please note:\n"
|
||||
"- Auto detection is broken for EEPROM save types.\n"
|
||||
"- Choose the lowest size save type first and work your way up until the game fully works.\n"
|
||||
"- If the game works with a Flash save type try without RTC first.\n"
|
||||
"- Delete the save before you try a new save type.\n"
|
||||
"- Make sure all your dumps are verified good dumps (no-intro.org)!");
|
||||
|
||||
static const u8 saveTypeCursorLut[16] = {0, 0, 1, 1, 2, 3, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7};
|
||||
u8 oldCursor = 0;
|
||||
u8 cursor = saveTypeCursorLut[saveType];
|
||||
while(1)
|
||||
{
|
||||
ee_printf("\x1b[%u;H ", oldCursor + 4);
|
||||
ee_printf("\x1b[%u;H>", cursor + 4);
|
||||
oldCursor = cursor;
|
||||
|
||||
u32 kDown;
|
||||
do
|
||||
{
|
||||
GFX_waitForVBlank0();
|
||||
|
||||
hidScanInput();
|
||||
if(hidGetExtraKeys(0) & (KEY_POWER_HELD | KEY_POWER)) goto end;
|
||||
kDown = hidKeysDown();
|
||||
} while(kDown == 0);
|
||||
|
||||
if((kDown & KEY_DUP) && cursor > 0) cursor--;
|
||||
else if((kDown & KEY_DDOWN) && cursor < 7) cursor++;
|
||||
else if(kDown & KEY_X)
|
||||
{
|
||||
fUnlink(savePath);
|
||||
ee_printf("\x1b[0;33Hdeleted ");
|
||||
}
|
||||
else if(kDown & KEY_A) break;
|
||||
}
|
||||
|
||||
static const u8 cursorSaveTypeLut[8] = {0, 2, 8, 9, 10, 11, 14, 15};
|
||||
saveType = cursorSaveTypeLut[cursor];
|
||||
if(saveType == SAVE_TYPE_EEPROM_8k || saveType == SAVE_TYPE_EEPROM_64k)
|
||||
{
|
||||
// If ROM bigger than 16 MiB --> SAVE_TYPE_EEPROM_8k_2 or SAVE_TYPE_EEPROM_64k_2.
|
||||
if(romSize > 0x1000000) saveType++;
|
||||
}
|
||||
if(dbEntry.attr != saveType)
|
||||
{
|
||||
if(dbPos > -1 || dbPos < 3253)
|
||||
{
|
||||
dbEntry.attr = (intLog2(romSize)<<27) | saveType;
|
||||
FHandle f;
|
||||
if(fOpen(&f, "gba_db.bin", FA_OPEN_EXISTING | FA_WRITE) == RES_OK)
|
||||
{
|
||||
fLseek(f, (sizeof(GameDbEntry) * dbPos) + offsetof(GameDbEntry, attr));
|
||||
fWrite(f, &dbEntry.attr, sizeof(dbEntry.attr), NULL);
|
||||
fClose(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
ee_puts("Could not open db for write!");
|
||||
saveType = SAVE_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ee_puts("Db position out of range!");
|
||||
saveType = SAVE_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return saveType;
|
||||
}
|
||||
|
||||
static void adjustGammaTableForGba(void)
|
||||
{
|
||||
const float inGamma = g_oafConfig.inGamma;
|
||||
const float outGamma = g_oafConfig.outGamma;
|
||||
const float contrast = g_oafConfig.contrast;
|
||||
const float brightness = g_oafConfig.brightness;
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
{
|
||||
// Credits for this algo go to Extrems.
|
||||
// Originally from Game Boy Interface Standard Edition for the GameCube.
|
||||
u32 res = powf(powf(contrast, inGamma) * powf((float)i / 255.0f + brightness / contrast, inGamma),
|
||||
1.0f / outGamma) * 255.0f;
|
||||
if(res > 255) res = 255;
|
||||
|
||||
// Same adjustment for red/green/blue.
|
||||
REG_LCD_PDC0_GTBL_FIFO = res<<16 | res<<8 | res;
|
||||
}
|
||||
}
|
||||
|
||||
static Result dumpFrameTex(void)
|
||||
{
|
||||
// 512x-512 (hight negative to flip vertically).
|
||||
// Pixels at offset 0x40.
|
||||
alignas(4) static const u8 bmpHeader[54] =
|
||||
{
|
||||
0x42, 0x4D, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x28, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFE,
|
||||
0xFF, 0xFF, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x13, 0x0B,
|
||||
0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/*GX_displayTransfer((u32*)0x18200000, 160u<<16 | 256u, (u32*)0x18400000, 160u<<16 | 256u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForPPF();
|
||||
//fsQuickWrite("sdmc:/lgyfb_dbg_frame.bgr", (void*)0x18400000, 256 * 160 * 3);*/
|
||||
GX_displayTransfer((u32*)0x18200000, 240u<<16 | 512u, (u32*)0x18400040, 240u<<16 | 512u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForPPF();
|
||||
|
||||
memcpy((void*)0x18400000, bmpHeader, sizeof(bmpHeader));
|
||||
|
||||
return fsQuickWrite("texture_dump.bmp", (void*)0x18400000, 0x40 + 512 * 512 * 3);
|
||||
}
|
||||
|
||||
static void gbaGfxHandler(void *args)
|
||||
{
|
||||
KEvent *const event = (KEvent*)args;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if(waitForEvent(event) != KRES_OK) break;
|
||||
clearEvent(event);
|
||||
|
||||
// Rotate the frame using the GPU.
|
||||
// 240x160: TODO.
|
||||
// 360x240: about 0.623620315 ms.
|
||||
static bool inited = false;
|
||||
u32 listSize;
|
||||
const u32 *list;
|
||||
if(inited == false)
|
||||
{
|
||||
inited = true;
|
||||
|
||||
listSize = sizeof(gbaGpuInitList);
|
||||
list = (u32*)gbaGpuInitList;
|
||||
}
|
||||
else
|
||||
{
|
||||
listSize = sizeof(gbaGpuList2);
|
||||
list = (u32*)gbaGpuList2;
|
||||
}
|
||||
GX_processCommandList(listSize, list);
|
||||
GFX_waitForP3D();
|
||||
GX_displayTransfer((u32*)(0x18180000 + (16 * 240 * 3)), 368u<<16 | 240u,
|
||||
GFX_getFramebuffer(SCREEN_TOP) + (16 * 240 * 3), 368u<<16 | 240u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForPPF();
|
||||
GFX_swapFramebufs();
|
||||
|
||||
if(hidKeysDown() & KEY_Y) dumpFrameTex();
|
||||
}
|
||||
|
||||
taskExit();
|
||||
}
|
||||
|
||||
static int confIniHandler(void* user, const char* section, const char* name, const char* value)
|
||||
{
|
||||
OafConfig *const config = (OafConfig*)user;
|
||||
|
||||
if(strcmp(section, "general") == 0)
|
||||
{
|
||||
if(strcmp(name, "backlight") == 0)
|
||||
config->backlight = (u8)strtoul(value, NULL, 10);
|
||||
else if(strcmp(name, "biosIntro") == 0)
|
||||
config->biosIntro = (strcmp(value, "true") == 0 ? true : false);
|
||||
}
|
||||
else if(strcmp(section, "video") == 0)
|
||||
{
|
||||
if(strcmp(name, "inGamma") == 0)
|
||||
config->inGamma = str2float(value);
|
||||
else if(strcmp(name, "outGamma") == 0)
|
||||
config->outGamma = str2float(value);
|
||||
else if(strcmp(name, "contrast") == 0)
|
||||
config->contrast = str2float(value);
|
||||
else if(strcmp(name, "brightness") == 0)
|
||||
config->brightness = str2float(value);
|
||||
}
|
||||
/*else if(strcmp(section, "audio") == 0)
|
||||
{
|
||||
}
|
||||
else if(strcmp(section, "input") == 0)
|
||||
{
|
||||
}*/
|
||||
else return 0; // Error.
|
||||
|
||||
return 1; // 1 is no error? Really?
|
||||
}
|
||||
|
||||
/*static int gameConfIniHandler(void* user, const char* section, const char* name, const char* value)
|
||||
{
|
||||
OafGameConfig *const config = (OafGameConfig*)user;
|
||||
|
||||
if(strcmp(section, "game") == 0)
|
||||
{
|
||||
// Save type.
|
||||
// Save slot.
|
||||
}
|
||||
else if(strcmp(section, "video") == 0)
|
||||
{
|
||||
if(strcmp(name, "inGamma") == 0)
|
||||
config->inGamma = str2float(value);
|
||||
else if(strcmp(name, "outGamma") == 0)
|
||||
config->outGamma = str2float(value);
|
||||
else if(strcmp(name, "contrast") == 0)
|
||||
config->contrast = str2float(value);
|
||||
else if(strcmp(name, "brightness") == 0)
|
||||
config->brightness = str2float(value);
|
||||
}
|
||||
else if(strcmp(section, "audio") == 0)
|
||||
{
|
||||
}
|
||||
else if(strcmp(section, "input") == 0)
|
||||
{
|
||||
}
|
||||
else return 0; // Error.
|
||||
|
||||
return 1; // 1 is no error? Really?
|
||||
}*/
|
||||
|
||||
static Result parseConfig(const char *const path, /* u8 confType, */ void *config)
|
||||
{
|
||||
char *iniBuf = (char*)calloc(INI_BUF_SIZE, 1);
|
||||
if(iniBuf == NULL) return RES_OUT_OF_MEM;
|
||||
|
||||
Result res = fsQuickRead(path, iniBuf, INI_BUF_SIZE - 1);
|
||||
if(res == RES_OK) ini_parse_string(iniBuf, /* (confType == 0 ? */ confIniHandler /* : gameConfIniHandler) */, config);
|
||||
else
|
||||
{
|
||||
const char *const defaultConfig = DEFAULT_CONFIG;
|
||||
res = fsQuickWrite(path, defaultConfig, strlen(defaultConfig));
|
||||
}
|
||||
|
||||
free(iniBuf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result handleFsStuff(char romAndSavePath[512])
|
||||
{
|
||||
Result res;
|
||||
char *lastDir = (char*)calloc(512, 1);
|
||||
if(lastDir != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
// Create the work dir and switch to it.
|
||||
if((res = fsMakePath(OAF_WORK_DIR)) != RES_OK && res != RES_FR_EXIST) break;
|
||||
if((res = fChdir(OAF_WORK_DIR)) != RES_OK) break;
|
||||
|
||||
// Parse config.
|
||||
parseConfig("config.ini", /* 0, */ &g_oafConfig);
|
||||
{ // TODO: Move this elsewhere?
|
||||
const u8 backlight = g_oafConfig.backlight;
|
||||
GFX_setBrightness(backlight, backlight);
|
||||
}
|
||||
|
||||
// Get last ROM launch path.
|
||||
if((res = fsQuickRead("lastdir.bin", lastDir, 511)) != RES_OK)
|
||||
{
|
||||
if(res == RES_FR_NO_FILE) strcpy(lastDir, "sdmc:/");
|
||||
else break;
|
||||
}
|
||||
|
||||
// Show file browser.
|
||||
*romAndSavePath = '\0';
|
||||
if((res = browseFiles(lastDir, romAndSavePath)) == RES_FR_NO_PATH)
|
||||
{
|
||||
// Second chance in case the last dir has been deleted.
|
||||
strcpy(lastDir, "sdmc:/");
|
||||
if((res = browseFiles(lastDir, romAndSavePath)) != RES_OK) break;
|
||||
}
|
||||
else if(res != RES_OK) break;
|
||||
|
||||
size_t cmpLen = strrchr(romAndSavePath, '/') - romAndSavePath;
|
||||
if((size_t)(strchr(romAndSavePath, '/') - romAndSavePath) == cmpLen) cmpLen++; // Keep the first '/'.
|
||||
if(cmpLen < 512)
|
||||
{
|
||||
if(cmpLen < strlen(lastDir) || strncmp(lastDir, romAndSavePath, cmpLen) != 0)
|
||||
{
|
||||
strncpy(lastDir, romAndSavePath, cmpLen);
|
||||
lastDir[cmpLen] = '\0';
|
||||
res = fsQuickWrite("lastdir.bin", lastDir, cmpLen + 1);
|
||||
}
|
||||
}
|
||||
} while(0);
|
||||
|
||||
free(lastDir);
|
||||
}
|
||||
else res = RES_OUT_OF_MEM;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result oafInitAndRun(void)
|
||||
{
|
||||
Result res;
|
||||
char *const romAndSavePath = (char*)malloc(512);
|
||||
if(romAndSavePath != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
if((res = handleFsStuff(romAndSavePath)) != RES_OK || *romAndSavePath == '\0') break;
|
||||
|
||||
ee_puts("Loading...");
|
||||
u32 romSize;
|
||||
if((res = loadGbaRom(romAndSavePath, &romSize)) != RES_OK) break;
|
||||
|
||||
#ifndef OAF_SAVE_DB_DEBUG
|
||||
// Detect save type and adjust path for the save file.
|
||||
const u16 saveType = tryDetectSaveType(romSize);
|
||||
strcpy(romAndSavePath + strlen(romAndSavePath) - 4, ".sav");
|
||||
#else
|
||||
strcpy(romAndSavePath + strlen(romAndSavePath) - 4, ".sav");
|
||||
const u16 saveType = saveDbDebug(romAndSavePath, romSize);
|
||||
#endif
|
||||
|
||||
// Prepare ARM9 for GBA mode + settings and save loading.
|
||||
if((res = LGY_prepareGbaMode(g_oafConfig.biosIntro, saveType, romAndSavePath)) == RES_OK)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
GFX_setForceBlack(false, true);
|
||||
// Don't turn the backlight off on 2DS.
|
||||
if(MCU_getSystemModel() != 3) GFX_powerOffBacklights(GFX_BLIGHT_BOT);
|
||||
#endif
|
||||
|
||||
KEvent *const frameReadyEvent = createEvent(false);
|
||||
LGYFB_init(frameReadyEvent); // Setup Legacy Framebuffer.
|
||||
createTask(0x800, 3, gbaGfxHandler, frameReadyEvent);
|
||||
g_frameReadyEvent = frameReadyEvent;
|
||||
|
||||
// Adjust gamma table and sync LgyFb start with LCD VBlank.
|
||||
adjustGammaTableForGba();
|
||||
GFX_waitForVBlank0();
|
||||
LGY_switchMode();
|
||||
}
|
||||
} while(0);
|
||||
}
|
||||
else res = RES_OUT_OF_MEM;
|
||||
|
||||
free(romAndSavePath);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void oafUpdate(void)
|
||||
{
|
||||
LGY_handleOverrides();
|
||||
waitForEvent(g_frameReadyEvent);
|
||||
}
|
||||
|
||||
void oafFinish(void)
|
||||
{
|
||||
LGYFB_deinit();
|
||||
if(g_frameReadyEvent != NULL)
|
||||
{
|
||||
deleteEvent(g_frameReadyEvent); // gbaGfxHandler() will automatically terminate.
|
||||
g_frameReadyEvent = NULL;
|
||||
}
|
||||
LGY_deinit();
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include "arm11/start.h"
|
||||
#include "arm11/hardware/interrupt.h"
|
||||
#include "arm11/hardware/timer.h"
|
||||
#include "kernel.h"
|
||||
#include "hardware/corelink_dma-330.h"
|
||||
#include "arm11/hardware/i2c.h"
|
||||
#include "arm11/hardware/mcu.h"
|
||||
|
@ -37,6 +38,7 @@ void WEAK __systemInit(void)
|
|||
|
||||
if(!__getCpuId()) // Core 0
|
||||
{
|
||||
kernelInit(2);
|
||||
DMA330_init();
|
||||
I2C_init();
|
||||
hidInit();
|
||||
|
|
|
@ -6,48 +6,55 @@
|
|||
|
||||
|
||||
|
||||
BEGIN_ASM_FUNC _a7_overlay_stub
|
||||
mov r0, #1
|
||||
mov r1, #0x4000000
|
||||
strb r0, [r1, #0x300] @ "POSTFLG"
|
||||
ldr pc, =0x3007E00
|
||||
|
||||
.pool
|
||||
.global _a7_overlay_stub_size
|
||||
_a7_overlay_stub_size = . - _a7_overlay_stub
|
||||
END_ASM_FUNC
|
||||
|
||||
@ Must be located at 0x3007E00.
|
||||
BEGIN_ASM_FUNC _arm7_stub_start
|
||||
mov r0, #PSR_INT_OFF | PSR_SVC_MODE
|
||||
adr r1, _arm7_stub_start + 0x200 @ 0x3008000
|
||||
msr CPSR_cxsf, r0
|
||||
@mov r0, #PSR_INT_OFF | PSR_IRQ_MODE
|
||||
mov sp, r1
|
||||
@msr CPSR_cxsf, r0
|
||||
mov r0, #PSR_INT_OFF | PSR_SYS_MODE
|
||||
@sub sp, r1, #0x60 @ 0x3007FA0
|
||||
msr CPSR_cxsf, r0
|
||||
BEGIN_ASM_FUNC _a7_stub_start
|
||||
adr r1, _a7_stub_thumb + 1 @ 0x3007E1D
|
||||
msr CPSR_fsxc, #PSR_INT_OFF | PSR_SVC_MODE @ Already set on reset.
|
||||
add sp, r1, #0x6B @ 0x3007E88
|
||||
msr CPSR_fsxc, #PSR_INT_OFF | PSR_SYS_MODE
|
||||
add sp, r1, #0x5B @ 0x3007E78
|
||||
mov r3, #0x4700000
|
||||
adr r2, _arm7_stub_16 + 1
|
||||
sub sp, r1, #0x80 @ 0x3007F80
|
||||
bx r2
|
||||
bx r1
|
||||
|
||||
.thumb
|
||||
_arm7_stub_16:
|
||||
_a7_stub_thumb:
|
||||
mov r0, #1
|
||||
str r0, [r3] @ Disable BIOS overlay.
|
||||
@ The original ARM7 stub waits 256 cycles here (for the BIOS overlay disable?).
|
||||
@ The original ARM7 stub waits 1677800 cycles (100 ms) here for LCD/LgyFb sync.
|
||||
@ The original ARM7 stub waits for REG_VCOUNT = 160 here.
|
||||
|
||||
lsl r3, r0, #26 @ 0x4000000
|
||||
wait_vcount_160_lp:
|
||||
ldrb r0, [r3, #6] @ REG_VCOUNT
|
||||
cmp r0, #160 @ Wait for REG_VCOUNT == 160.
|
||||
bne wait_vcount_160_lp
|
||||
lsl r4, r0, #26 @ 0x4000000 Needed for "function" call 0xBC below.
|
||||
mov r0, #0xFF @ Clear WRAM, iWRAM, palette RAM, VRAM, OAM
|
||||
@ + reset SIO, sound and all other registers.
|
||||
|
||||
mov r4, r3 @ Needed for "function" call 0xBC below.
|
||||
mov r0, #0xFF @ Clear WRAM, iWRAM, palette RAM, VRAM, OAM
|
||||
@ + reset SIO, sound and all other registers.
|
||||
|
||||
.global _arm7_stub_swi
|
||||
_arm7_stub_swi = . - _arm7_stub_start + 0x80BFE00 @ Final ARM9 mem location.
|
||||
.global _a7_stub9_swi
|
||||
_a7_stub9_swi = . - _a7_stub_start + 0x80BFE00 @ Final ARM9 mem location.
|
||||
swi 0x01 @ RegisterRamReset
|
||||
|
||||
mov r0, #0xBC @ SoftReset (0xB4) but skipping r2 & r4 loading.
|
||||
mov r2, #0
|
||||
|
||||
@ REG_VCOUNT should be 126 at ROM entry like after BIOS intro.
|
||||
_a7_stub_vcount_lp:
|
||||
ldrb r1, [r4, #6] @ REG_VCOUNT
|
||||
cmp r1, #126 @ Loop until REG_VCOUNT == 126.
|
||||
bne _a7_stub_vcount_lp
|
||||
|
||||
bx r0
|
||||
|
||||
.pool
|
||||
.align 2
|
||||
.global _arm7_stub_size
|
||||
_arm7_stub_size = . - _arm7_stub_start
|
||||
.global _a7_stub_size
|
||||
_a7_stub_size = . - _a7_stub_start
|
||||
END_ASM_FUNC
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
|
||||
|
||||
noreturn void panic()
|
||||
NOINLINE NOINLINE noreturn void panic(void)
|
||||
{
|
||||
enterCriticalSection();
|
||||
//fsDeinit();
|
||||
|
@ -44,7 +44,7 @@ noreturn void panic()
|
|||
}
|
||||
}
|
||||
|
||||
noreturn void panicMsg(UNUSED const char *msg)
|
||||
NOINLINE noreturn void panicMsg(UNUSED const char *msg)
|
||||
{
|
||||
enterCriticalSection();
|
||||
//fsDeinit();
|
||||
|
@ -60,7 +60,7 @@ noreturn void panicMsg(UNUSED const char *msg)
|
|||
|
||||
// Expects the registers in the exception stack to be in the following order:
|
||||
// r0-r14, pc (unmodified), cpsr
|
||||
noreturn void guruMeditation(UNUSED u8 type, UNUSED const u32 *excStack)
|
||||
NOINLINE noreturn void guruMeditation(UNUSED u8 type, UNUSED const u32 *excStack)
|
||||
{
|
||||
// avoid fs corruptions
|
||||
//fsDeinit();
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include "types.h"
|
||||
#include "error_codes.h"
|
||||
#include "util.h"
|
||||
#include "fs.h"
|
||||
#include "arm9/debug.h"
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
|
||||
|
@ -44,7 +40,7 @@ static struct
|
|||
|
||||
static Result fres2Res(FRESULT fr)
|
||||
{
|
||||
if(fr != FR_OK) return fr + 3;
|
||||
if(fr != FR_OK) return fr + RES_FR_DISK_ERR - 1;
|
||||
else return RES_OK;
|
||||
}
|
||||
|
||||
|
@ -201,6 +197,11 @@ Result fStat(const char *const path, FILINFO *const fi)
|
|||
return fres2Res(f_stat(path, fi));
|
||||
}
|
||||
|
||||
Result fChdir(const char *const path)
|
||||
{
|
||||
return fres2Res(f_chdir(path));
|
||||
}
|
||||
|
||||
Result fOpenDir(DHandle *const hOut, const char *const path)
|
||||
{
|
||||
if(hOut == NULL) return RES_INVALID_ARG;
|
||||
|
|
|
@ -31,29 +31,15 @@ static char g_savePath[512] = {0};
|
|||
|
||||
|
||||
|
||||
#define STRINGIFY(s) #s
|
||||
#define STR(s) STRINGIFY(s)
|
||||
NAKED static void _overlay_stub(void)
|
||||
{
|
||||
__asm__("mov r0, #0x4000000\n\t"
|
||||
"mov r1, #1\n\t"
|
||||
"strb r1, [r0, #0x300]\n\t" // "POSTFLG"
|
||||
"ldr pc, _overlay_stub_jmp\n\t"
|
||||
"_overlay_stub_jmp: .4byte " STR(ARM7_STUB_LOC) "\n\t"
|
||||
"_overlay_stub_size = . - _overlay_stub\n\t" : : : );
|
||||
}
|
||||
extern const u32 _overlay_stub_size[];
|
||||
|
||||
static void setupBiosOverlay(bool biosIntro)
|
||||
{
|
||||
iomemcpy(REGs_LGY_A7_VECTOR, (u32*)_overlay_stub, (u32)_overlay_stub_size);
|
||||
iomemcpy(REGs_LGY_A7_VECTOR, (u32*)_a7_overlay_stub, (u32)_a7_overlay_stub_size);
|
||||
//static const u32 biosVectors[8] = {0xEA000018, 0xEA000004, 0xEA00004C, 0xEA000002,
|
||||
// 0xEA000001, 0xEA000000, 0xEA000042, 0xE59FD1A0};
|
||||
//iomemcpy(REGs_LGY_A7_VECTOR, biosVectors, 32);
|
||||
|
||||
NDMA_copy((u32*)ARM7_STUB_LOC9, _arm7_stub_start, (u32)_arm7_stub_size);
|
||||
// Patch swi 0x01 (RegisterRamReset) to swi 0x26 (HardReset).
|
||||
if(biosIntro) *((u8*)_arm7_stub_swi) = 0x26;
|
||||
NDMA_copy((u32*)ARM7_STUB_LOC9, (u32*)_a7_stub_start, (u32)_a7_stub_size);
|
||||
if(biosIntro) *((vu8*)_a7_stub9_swi) = 0x26; // Patch swi 0x01 (RegisterRamReset) to swi 0x26 (HardReset).
|
||||
}
|
||||
|
||||
static u32 setupSaveType(u16 saveType)
|
||||
|
@ -93,7 +79,7 @@ Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePa
|
|||
Result res = RES_OK;
|
||||
if(saveSize != 0)
|
||||
{
|
||||
res = fsQuickRead((void*)SAVE_LOC, savePath, MAX_SAVE_SIZE);
|
||||
res = fsQuickRead(savePath, (void*)SAVE_LOC, MAX_SAVE_SIZE);
|
||||
if(res == RES_FR_NO_FILE)
|
||||
{
|
||||
res = RES_OK; // Ignore a missing save file.
|
||||
|
@ -169,7 +155,7 @@ Result LGY_backupGbaSave(void)
|
|||
// Update hash.
|
||||
memcpy(g_saveHash, newHash, 32);
|
||||
|
||||
res = fsQuickWrite((void*)SAVE_LOC, g_savePath, saveSize);
|
||||
res = fsQuickWrite(g_savePath, (void*)SAVE_LOC, saveSize);
|
||||
}
|
||||
|
||||
// Disable savegame mem region.
|
||||
|
|
|
@ -470,7 +470,7 @@ int SD_Init()
|
|||
set_target(&handleSD);
|
||||
// TMIO base clock is half of the CPU clock so 2 CPU cycles = 1 base clock pulse.
|
||||
// cycles = 2 * [TMIO clock divider] * 74
|
||||
wait(2 * 128 * 74);
|
||||
wait_cycles(2 * 128 * 74);
|
||||
|
||||
sdmmc_send_command(&handleSD,0,0);
|
||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
||||
|
|
|
@ -73,6 +73,9 @@ u32 IPC_handleCmd(u8 cmdId, u32 inBufs, u32 outBufs, const u32 *const buf)
|
|||
case IPC_CMD_ID_MASK(IPC_CMD9_FSTAT):
|
||||
result = fStat((const char *const)buf[0], (FILINFO *const)buf[2]);
|
||||
break;
|
||||
case IPC_CMD_ID_MASK(IPC_CMD9_FCHDIR):
|
||||
result = fChdir((const char *const)buf[0]);
|
||||
break;
|
||||
case IPC_CMD_ID_MASK(IPC_CMD9_FOPEN_DIR):
|
||||
result = fOpenDir((DHandle *const)buf[2], (const char *const)buf[0]);
|
||||
break;
|
||||
|
@ -108,6 +111,7 @@ u32 IPC_handleCmd(u8 cmdId, u32 inBufs, u32 outBufs, const u32 *const buf)
|
|||
|
||||
// Miscellaneous API.
|
||||
case IPC_CMD_ID_MASK(IPC_CMD9_PREPARE_POWER):
|
||||
fsDeinit();
|
||||
break;
|
||||
default:
|
||||
panic();
|
||||
|
|
|
@ -15,8 +15,11 @@ void printError(Result res)
|
|||
{
|
||||
"OK",
|
||||
"SD card removed",
|
||||
"Disk full",
|
||||
"Invalid argument",
|
||||
"Out of memory",
|
||||
"Out of range",
|
||||
"Not found",
|
||||
|
||||
// fatfs errors.
|
||||
"fatfs disk error",
|
||||
|
|
|
@ -16,13 +16,15 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "fsutil.h"
|
||||
#include "fs.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
|
||||
Result fsQuickRead(void *const buf, const char *const path, u32 size)
|
||||
Result fsQuickRead(const char *const path, void *const buf, u32 size)
|
||||
{
|
||||
Result res;
|
||||
FHandle f;
|
||||
|
@ -36,7 +38,7 @@ Result fsQuickRead(void *const buf, const char *const path, u32 size)
|
|||
return res;
|
||||
}
|
||||
|
||||
Result fsQuickWrite(void *const buf, const char *const path, u32 size)
|
||||
Result fsQuickWrite(const char *const path, const void *const buf, u32 size)
|
||||
{
|
||||
Result res;
|
||||
FHandle f;
|
||||
|
@ -49,3 +51,39 @@ Result fsQuickWrite(void *const buf, const char *const path, u32 size)
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result fsMakePath(const char *const path)
|
||||
{
|
||||
Result res = fMkdir(path);
|
||||
if(res != RES_FR_NO_PATH) return res;
|
||||
|
||||
char *tmpPath = (char*)malloc(512);
|
||||
if(tmpPath == NULL) return RES_OUT_OF_MEM;
|
||||
safeStrcpy(tmpPath, path, 512);
|
||||
|
||||
char *str;
|
||||
if((str = strchr(tmpPath, ':')) == NULL) str = tmpPath;
|
||||
else str++;
|
||||
|
||||
// Empty path.
|
||||
if(*str == '\0')
|
||||
{
|
||||
free(tmpPath);
|
||||
return RES_INVALID_ARG;
|
||||
}
|
||||
|
||||
while((str = strchr(str + 1, '/')) != NULL)
|
||||
{
|
||||
*str = '\0';
|
||||
if((res = fMkdir(tmpPath)) != RES_OK && res != RES_FR_EXIST) break;
|
||||
*str = '/';
|
||||
}
|
||||
|
||||
// Only create the last dir in the path if the
|
||||
// previous error code is not an unexpected one.
|
||||
if(res == RES_OK || res == RES_FR_EXIST) res = fMkdir(tmpPath);
|
||||
|
||||
free(tmpPath);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ u32 PXI_sendCmd(u32 cmd, const u32 *buf, u32 words)
|
|||
for(u32 i = inBufs; i < inBufs + outBufs; i++)
|
||||
{
|
||||
const IpcBuffer *const outBuf = (IpcBuffer*)&buf[i * sizeof(IpcBuffer) / 4];
|
||||
if(outBuf->ptr && outBuf->size) invalidateDCacheRange(outBuf->ptr, outBuf->size);
|
||||
if(outBuf->ptr && outBuf->size) flushDCacheRange(outBuf->ptr, outBuf->size);
|
||||
}
|
||||
|
||||
pxiSendWord(cmd);
|
||||
|
|
|
@ -23,18 +23,63 @@
|
|||
|
||||
|
||||
|
||||
NAKED void wait(u32 cycles)
|
||||
NAKED void wait_cycles(u32 cycles)
|
||||
{
|
||||
#ifdef ARM9
|
||||
__asm__("1: subs %0, %0, #4\n\t"
|
||||
#elif ARM11
|
||||
__asm__("1: subs %0, %0, #2\n\t"
|
||||
"nop\n\t"
|
||||
"yield\n\t"
|
||||
#endif
|
||||
"bhi 1b\n\t"
|
||||
"bx lr\n\t" : : "r" (cycles) : "cc");
|
||||
}
|
||||
|
||||
size_t safeStrcpy(char *const dst, const char *const src, size_t num)
|
||||
{
|
||||
if(num == 0) return 0;
|
||||
|
||||
const size_t len = strlen(src) + 1;
|
||||
if(len > num)
|
||||
{
|
||||
*dst = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(dst, src, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Limited to 6 decimal places. Doesn't support exponents.
|
||||
// Based on: https://codereview.stackexchange.com/a/158724
|
||||
float str2float(const char *str)
|
||||
{
|
||||
for(; isspace((unsigned char)*str) != 0; str++); // Skip whitespaces.
|
||||
|
||||
const float sign = (*str == '-' ? -1.f : 1.f);
|
||||
if(*str == '-' || *str == '+') str++;
|
||||
|
||||
float val = 0.f;
|
||||
while(isdigit((unsigned char)*str) != 0)
|
||||
{
|
||||
val = val * 10.f + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
|
||||
if(*str == '.') str++;
|
||||
|
||||
u32 place = 1;
|
||||
while(isdigit((unsigned char)*str) != 0)
|
||||
{
|
||||
val = val * 10.f + (*str - '0');
|
||||
place *= 10;
|
||||
str++;
|
||||
}
|
||||
|
||||
return val * sign / place;
|
||||
}
|
||||
|
||||
// case insensitive string compare function
|
||||
int strnicmp(const char *str1, const char *str2, u32 len)
|
||||
{
|
||||
|
|
|
@ -328,3 +328,13 @@ R0.13c (October 14, 2018)
|
|||
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)
|
||||
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
|
||||
|
||||
|
||||
|
||||
R0.14 (October 14, 2019)
|
||||
Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1)
|
||||
Changed some API functions, f_mkfs() and f_fdisk().
|
||||
Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters.
|
||||
Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters.
|
||||
Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12)
|
||||
Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FatFs Module Source Files R0.13c
|
||||
FatFs Module Source Files R0.14
|
||||
|
||||
|
||||
FILES
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
FatFs License
|
||||
|
||||
FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.
|
||||
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module Rx.xx /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 20xx, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
Therefore FatFs license is one of the BSD-style licenses but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, does not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses including GNU GPL. When you redistribute the FatFs source code with any changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.
|
|
@ -1,5 +1,5 @@
|
|||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
|
@ -60,7 +60,7 @@ DSTATUS disk_initialize (
|
|||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ DRESULT disk_read (
|
|||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
|
@ -9,7 +9,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
|
@ -29,8 +28,8 @@ typedef enum {
|
|||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,8 @@
|
|||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.13c /
|
||||
/ FatFs - Generic FAT Filesystem module R0.14 /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2018, ChaN, all right reserved.
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
|
@ -20,7 +20,7 @@
|
|||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86604 /* Revision ID */
|
||||
#define FF_DEFINED 86606 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -45,16 +45,16 @@ typedef unsigned __int64 QWORD;
|
|||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint16_t WCHAR; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned short WCHAR; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@ typedef struct {
|
|||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
|
@ -105,15 +105,24 @@ typedef char TCHAR;
|
|||
|
||||
|
||||
|
||||
/* Type of file size variables */
|
||||
/* Type of file size and LBA variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#if FF_LBA64
|
||||
typedef QWORD LBA_t;
|
||||
#else
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
#else
|
||||
#if FF_LBA64
|
||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||
#endif
|
||||
typedef DWORD FSIZE_t;
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -155,14 +164,14 @@ typedef struct {
|
|||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
DWORD volbase; /* Volume base sector */
|
||||
DWORD fatbase; /* FAT base sector */
|
||||
DWORD dirbase; /* Root directory base sector/cluster */
|
||||
DWORD database; /* Data base sector */
|
||||
LBA_t volbase; /* Volume base sector */
|
||||
LBA_t fatbase; /* FAT base sector */
|
||||
LBA_t dirbase; /* Root directory base sector/cluster */
|
||||
LBA_t database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD bitbase; /* Allocation bitmap base sector */
|
||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
@ -199,9 +208,9 @@ typedef struct {
|
|||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
|
@ -220,7 +229,7 @@ typedef struct {
|
|||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
|
@ -250,6 +259,18 @@ typedef struct {
|
|||
|
||||
|
||||
|
||||
/* Format parameter structure (MKFS_PARM) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||
BYTE n_fat; /* Number of FATs */
|
||||
UINT align; /* Data area alignment (sector) */
|
||||
UINT n_root; /* Number of root directory entries */
|
||||
DWORD au_size; /* Cluster size (byte) */
|
||||
} MKFS_PARM;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
|
@ -305,10 +326,10 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get numbe
|
|||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86604 /* Revision ID */
|
||||
#define FFCONF_DEF 86606 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
|
@ -102,7 +102,7 @@
|
|||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
|
@ -110,11 +110,11 @@
|
|||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
|
||||
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() in ffsystem.c, need to be added to the project. */
|
||||
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#define FF_LFN_UNICODE 2
|
||||
|
@ -150,7 +150,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
#define FF_FS_RPATH 1
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
|
@ -200,30 +200,28 @@
|
|||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_LBA64 0
|
||||
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
|
||||
#define FF_MIN_GPT 0x100000000
|
||||
/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY 1
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
|
@ -237,8 +235,8 @@
|
|||
|
||||
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_MON 11
|
||||
#define FF_NORTC_MDAY 23
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
|
@ -247,7 +245,19 @@
|
|||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_LOCK 0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*------------------------------------------------------------------------*/
|
||||
/* Unicode handling functions for FatFs R0.13c */
|
||||
/* Unicode handling functions for FatFs R0.13+ */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This module will occupy a huge memory in the .const section when the /
|
||||
/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
|
||||
|
@ -7,7 +7,7 @@
|
|||
/ that function to avoid silly memory consumption. /
|
||||
/-------------------------------------------------------------------------*/
|
||||
/*
|
||||
/ Copyright (C) 2018, ChaN, all right reserved.
|
||||
/ Copyright (C) 2014, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
|
@ -25,11 +25,7 @@
|
|||
|
||||
#include "ff.h"
|
||||
|
||||
#if FF_USE_LFN /* This module will be blanked at non-LFN configuration */
|
||||
|
||||
#if FF_DEFINED != 86604 /* Revision ID */
|
||||
#error Wrong include file (ff.h).
|
||||
#endif
|
||||
#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */
|
||||
|
||||
#define MERGE2(a, b) a ## b
|
||||
#define CVTBL(tbl, cp) MERGE2(tbl, cp)
|
||||
|
@ -15245,7 +15241,7 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
|
|||
return c;
|
||||
}
|
||||
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
|
||||
WCHAR oem, /* OEM code to be converted */
|
||||
WORD cp /* Code page for the conversion */
|
||||
)
|
||||
|
@ -15312,7 +15308,7 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
|
|||
}
|
||||
|
||||
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
|
||||
WCHAR oem, /* OEM code to be converted */
|
||||
WORD cp /* Code page for the conversion */
|
||||
)
|
||||
|
@ -15411,7 +15407,7 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
|
|||
}
|
||||
|
||||
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
|
||||
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
|
||||
WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */
|
||||
WORD cp /* Code page for the conversion */
|
||||
)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
The "inih" library is distributed under the New BSD license:
|
||||
|
||||
Copyright (c) 2009, Ben Hoyt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Ben Hoyt nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,158 @@
|
|||
# inih (INI Not Invented Here)
|
||||
|
||||
[](https://travis-ci.org/benhoyt/inih)
|
||||
|
||||
**inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries.
|
||||
|
||||
To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation.
|
||||
|
||||
You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O.
|
||||
|
||||
Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros.
|
||||
|
||||
|
||||
## Compile-time options ##
|
||||
|
||||
You can control various aspects of inih using preprocessor defines:
|
||||
|
||||
### Syntax options ###
|
||||
|
||||
* **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`.
|
||||
* **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`.
|
||||
* **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`.
|
||||
* **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`.
|
||||
* **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL.
|
||||
|
||||
### Parsing options ###
|
||||
|
||||
* **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`.
|
||||
* **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`.
|
||||
* **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL.
|
||||
|
||||
### Memory options ###
|
||||
|
||||
* **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`.
|
||||
* **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL).
|
||||
* **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes.
|
||||
* **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`.
|
||||
* **Custom allocator:** By default when using the heap, the standard library's `malloc`, `free`, and `realloc` functions are used; to use a custom allocator, specify `-DINI_CUSTOM_ALLOCATOR=1` (and `-DINI_USE_STACK=0`). You must define and link functions named `ini_malloc`, `ini_free`, and (if `INI_ALLOW_REALLOC` is set) `ini_realloc`, which must have the same signatures as the `stdlib.h` memory allocation functions.
|
||||
|
||||
## Simple example in C ##
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../ini.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
const char* name;
|
||||
const char* email;
|
||||
} configuration;
|
||||
|
||||
static int handler(void* user, const char* section, const char* name,
|
||||
const char* value)
|
||||
{
|
||||
configuration* pconfig = (configuration*)user;
|
||||
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
if (MATCH("protocol", "version")) {
|
||||
pconfig->version = atoi(value);
|
||||
} else if (MATCH("user", "name")) {
|
||||
pconfig->name = strdup(value);
|
||||
} else if (MATCH("user", "email")) {
|
||||
pconfig->email = strdup(value);
|
||||
} else {
|
||||
return 0; /* unknown section/name, error */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
configuration config;
|
||||
|
||||
if (ini_parse("test.ini", handler, &config) < 0) {
|
||||
printf("Can't load 'test.ini'\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n",
|
||||
config.version, config.name, config.email);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## C++ example ##
|
||||
|
||||
If you're into C++ and the STL, there is also an easy-to-use [INIReader class](https://github.com/benhoyt/inih/blob/master/cpp/INIReader.h) that stores values in a `map` and lets you `Get()` them:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include "INIReader.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
INIReader reader("../examples/test.ini");
|
||||
|
||||
if (reader.ParseError() < 0) {
|
||||
std::cout << "Can't load 'test.ini'\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "Config loaded from 'test.ini': version="
|
||||
<< reader.GetInteger("protocol", "version", -1) << ", name="
|
||||
<< reader.Get("user", "name", "UNKNOWN") << ", email="
|
||||
<< reader.Get("user", "email", "UNKNOWN") << ", pi="
|
||||
<< reader.GetReal("user", "pi", -1) << ", active="
|
||||
<< reader.GetBoolean("user", "active", true) << "\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
This simple C++ API works fine, but it's not very fully-fledged. I'm not planning to work more on the C++ API at the moment, so if you want a bit more power (for example `GetSections()` and `GetFields()` functions), see these forks:
|
||||
|
||||
* https://github.com/Blandinium/inih
|
||||
* https://github.com/OSSystems/inih
|
||||
|
||||
|
||||
## Differences from ConfigParser ##
|
||||
|
||||
Some differences between inih and Python's [ConfigParser](http://docs.python.org/library/configparser.html) standard library module:
|
||||
|
||||
* INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error.
|
||||
* Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser).
|
||||
|
||||
|
||||
## Platform-specific notes ##
|
||||
|
||||
* Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling.
|
||||
|
||||
## Meson notes ##
|
||||
|
||||
* The `meson.build` file is not required to use or compile inih, its main purpose is for distributions.
|
||||
* By default Meson only creates a static library for inih, but Meson can be used to configure this behavior:
|
||||
* with `-Ddefault_library=shared` a shared library is build.
|
||||
* with `-Ddistro_install=true` the library will be installed with the header and a pkg-config entry, you may want to set `-Ddefault_library=shared` when using this.
|
||||
* with `-Dwith_INIReader` you can build (and install if selected) the C++ library.
|
||||
* all compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`.
|
||||
* If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`.
|
||||
* In case you use inih as a subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables.
|
||||
|
||||
## Building from vcpkg ##
|
||||
|
||||
You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install inih
|
||||
|
||||
The inih port in vcpkg is kept up to date by microsoft team members and community contributors.
|
||||
If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Related links ##
|
||||
|
||||
* [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager)
|
|
@ -0,0 +1,298 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ini.h"
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#if INI_CUSTOM_ALLOCATOR
|
||||
#include <stddef.h>
|
||||
void* ini_malloc(size_t size);
|
||||
void ini_free(void* ptr);
|
||||
void* ini_realloc(void* ptr, size_t size);
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#define ini_malloc malloc
|
||||
#define ini_free free
|
||||
#define ini_realloc realloc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||
typedef struct {
|
||||
const char* ptr;
|
||||
size_t num_left;
|
||||
} ini_parse_string_ctx;
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
static char* rstrip(char* s)
|
||||
{
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p)))
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
static char* lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to NUL at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Similar to strncpy, but ensures dest (size bytes) is
|
||||
NUL-terminated, and doesn't pad with NULs. */
|
||||
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
|
||||
size_t i;
|
||||
for (i = 0; i < size - 1 && src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
int max_line = INI_MAX_LINE;
|
||||
#else
|
||||
char* line;
|
||||
size_t max_line = INI_INITIAL_ALLOC;
|
||||
#endif
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
char* new_line;
|
||||
size_t offset;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, (int)max_line, stream) != NULL) {
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
offset = strlen(line);
|
||||
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE)
|
||||
max_line = INI_MAX_LINE;
|
||||
new_line = ini_realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
ini_free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||
break;
|
||||
if (max_line >= INI_MAX_LINE)
|
||||
break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
#if INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
if (!HANDLER(user, section, NULL, NULL) && !error)
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = end + 1;
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
value = lskip(value);
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
#if INI_ALLOW_NO_VALUE
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
if (!HANDLER(user, section, name, NULL) && !error)
|
||||
error = lineno;
|
||||
#else
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
ini_free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* An ini_reader function to read the next line from a string buffer. This
|
||||
is the fgets() equivalent used by ini_parse_string(). */
|
||||
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||
const char* ctx_ptr = ctx->ptr;
|
||||
size_t ctx_num_left = ctx->num_left;
|
||||
char* strp = str;
|
||||
char c;
|
||||
|
||||
if (ctx_num_left == 0 || num < 2)
|
||||
return NULL;
|
||||
|
||||
while (num > 1 && ctx_num_left != 0) {
|
||||
c = *ctx_ptr++;
|
||||
ctx_num_left--;
|
||||
*strp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
num--;
|
||||
}
|
||||
|
||||
*strp = '\0';
|
||||
ctx->ptr = ctx_ptr;
|
||||
ctx->num_left = ctx_num_left;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||
ini_parse_string_ctx ctx;
|
||||
|
||||
ctx.ptr = string;
|
||||
ctx.num_left = strlen(string);
|
||||
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||
user);
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef INI_H
|
||||
#define INI_H
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||
#ifndef INI_HANDLER_LINENO
|
||||
#define INI_HANDLER_LINENO 0
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
#if INI_HANDLER_LINENO
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value,
|
||||
int lineno);
|
||||
#else
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O (see also
|
||||
ini_parse_string). */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||
instead of a file. Useful for parsing INI data from a network socket or
|
||||
already in memory. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||
both ; and # comments at the start of a line by default. */
|
||||
#ifndef INI_START_COMMENT_PREFIXES
|
||||
#define INI_START_COMMENT_PREFIXES ";#"
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||
zero. */
|
||||
#ifndef INI_ALLOW_REALLOC
|
||||
#define INI_ALLOW_REALLOC 0
|
||||
#endif
|
||||
|
||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||
is zero. */
|
||||
#ifndef INI_INITIAL_ALLOC
|
||||
#define INI_INITIAL_ALLOC 200
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to call the handler at the start of each new section (with
|
||||
name and value NULL). Default is to only call the handler on
|
||||
each name=value pair. */
|
||||
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||
call the handler with value NULL in this case. Default is to treat
|
||||
no-value lines as an error. */
|
||||
#ifndef INI_ALLOW_NO_VALUE
|
||||
#define INI_ALLOW_NO_VALUE 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
|
||||
allocation functions (INI_USE_STACK must also be 0). These functions must
|
||||
have the same signatures as malloc/free/realloc and behave in a similar
|
||||
way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
|
||||
#ifndef INI_CUSTOM_ALLOCATOR
|
||||
#define INI_CUSTOM_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INI_H */
|
Loading…
Reference in New Issue