diff --git a/Makefile b/Makefile index 3369996..e77cdbc 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ endif BUILD := build SOURCES := source source/common source/filesys source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/quicklz DATA := data -INCLUDES := source source/common source/font source/filesys source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/quicklz +INCLUDES := common source source/common source/font source/filesys source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/quicklz #--------------------------------------------------------------------------------- # options for code generation diff --git a/common/cpu.h b/common/cpu.h new file mode 100644 index 0000000..4661c1e --- /dev/null +++ b/common/cpu.h @@ -0,0 +1,68 @@ +/* + Written by Wolfvak, specially sublicensed under the GPLv2 + Read LICENSE for more details +*/ + +#pragma once +#include + +#define asm __asm volatile + +static inline u32 CPU_ReadCPSR(void) +{ + u32 cpsr; + asm("mrs %0, cpsr\n\t":"=r"(cpsr)); + return cpsr; +} + +static inline void CPU_WriteCPSR_c(u32 cpsr) +{ + asm("msr cpsr_c, %0\n\t"::"r"(cpsr)); + return; +} + +static inline u32 CPU_ReadCR(void) +{ + u32 cr; + asm("mrc p15, 0, %0, c1, c0, 0\n\t":"=r"(cr)); + return cr; +} + +static inline void CPU_WriteCR(u32 cr) +{ + asm("mcr p15, 0, %0, c1, c0, 0\n\t"::"r"(cr)); + return; +} + +static inline void CPU_DisableIRQ(void) +{ + #ifdef ARM9 + CPU_WriteCPSR_c(CPU_ReadCPSR() | (0xC0)); + #else + asm("cpsid if\n\t"); + #endif + return; +} + +static inline void CPU_EnableIRQ(void) +{ + #ifdef ARM9 + CPU_WriteCPSR_c(CPU_ReadCPSR() & ~(0xC0)); + #else + asm("cpsie if\n\t"); + #endif + return; +} + +static inline void CPU_EnterCritical(u32 *ss) +{ + *ss = CPU_ReadCPSR(); + CPU_DisableIRQ(); + return; +} + +static inline void CPU_LeaveCritical(u32 *ss) +{ + CPU_WriteCPSR_c(*ss); + return; +} diff --git a/common/pxi.h b/common/pxi.h new file mode 100644 index 0000000..63b4fa3 --- /dev/null +++ b/common/pxi.h @@ -0,0 +1,101 @@ +/* + Written by Wolfvak, specially sublicensed under the GPLv2 + Read LICENSE for more details +*/ + +#pragma once +#include + +#ifdef ARM9 +#define PXI_BASE (0x10008000) +#define IRQ_PXI_SYNC (12) +#else +#define PXI_BASE (0x10163000) +#define IRQ_PXI_SYNC (80) +#endif + +enum { + PXI_NOCMD = 0, + PXI_SETBRIGHTNESS = 1 +}; + +#define PXI_SYNC_RECV ((volatile uint8_t*)(PXI_BASE + 0x00)) +#define PXI_SYNC_SEND ((volatile uint8_t*)(PXI_BASE + 0x01)) +#define PXI_SYNC_IRQ ((volatile uint8_t*)(PXI_BASE + 0x03)) +#define PXI_CNT ((volatile uint16_t*)(PXI_BASE + 0x04)) +#define PXI_SEND ((volatile uint32_t*)(PXI_BASE + 0x08)) +#define PXI_RECV ((volatile uint32_t*)(PXI_BASE + 0x0C)) + +#define PXI_CNT_SEND_FIFO_EMPTY (1<<0) +#define PXI_CNT_SEND_FIFO_FULL (1<<1) +#define PXI_CNT_SEND_FIFO_EMPTY_IRQ (1<<2) +#define PXI_CNT_SEND_FIFO_FLUSH (1<<3) +#define PXI_CNT_RECV_FIFO_EMPTY (1<<8) +#define PXI_CNT_RECV_FIFO_FULL (1<<9) +#define PXI_CNT_RECV_FIFO_NEMPTY_IRQ (1<<10) +#define PXI_CNT_ERROR_ACK (1<<14) +#define PXI_CNT_ENABLE_FIFO (1<<15) + +#define PXI_SYNC_TRIGGER_MPCORE (1<<5) +#define PXI_SYNC_TRIGGER_OLDARM (1<<6) +#define PXI_SYNC_ENABLE_IRQ (1<<7) + +static inline void PXI_SetRemote(u8 msg) +{ + *PXI_SYNC_SEND = msg; +} + +static inline u8 PXI_GetRemote(void) +{ + return *PXI_SYNC_RECV; +} + +static inline void PXI_EnableIRQ(void) +{ + *PXI_SYNC_IRQ = PXI_SYNC_ENABLE_IRQ; +} + +static inline void PXI_DisableIRQ(void) +{ + *PXI_SYNC_IRQ = 0; +} + +static inline void PXI_Wait(void) +{ + while(PXI_GetRemote() != PXI_NOCMD); +} + +static inline void PXI_Sync(void) +{ + #ifdef ARM9 + *PXI_SYNC_IRQ |= PXI_SYNC_TRIGGER_MPCORE; + #else + *PXI_SYNC_IRQ |= PXI_SYNC_TRIGGER_OLDARM; + #endif +} + +static inline void PXI_Reset(void) +{ + *PXI_SYNC_SEND = 0; + *PXI_SYNC_IRQ = 0; + *PXI_CNT = PXI_CNT_SEND_FIFO_FLUSH | PXI_CNT_ENABLE_FIFO; +} + +static inline void PXI_Send(u32 w) +{ + while(*PXI_CNT & PXI_CNT_SEND_FIFO_FULL); + do { + *PXI_SEND = w; + } while(*PXI_CNT & PXI_CNT_ERROR_ACK); + return; +} + +static inline u32 PXI_Recv(void) +{ + u32 ret; + while(*PXI_CNT & PXI_CNT_RECV_FIFO_EMPTY); + do { + ret = *PXI_RECV; + } while(*PXI_CNT & PXI_CNT_ERROR_ACK); + return ret; +} diff --git a/common/types.h b/common/types.h new file mode 100644 index 0000000..2d52816 --- /dev/null +++ b/common/types.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef ARM9 +#ifndef ARM11 +#error "Unknown processor" +#endif +#endif + +#include + +#define BIT(x) (1<<(x)) + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int64_t s8; +typedef int64_t s16; +typedef int64_t s32; +typedef int64_t s64; + +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; + +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; diff --git a/screeninit/Makefile b/screeninit/Makefile index 8f563e6..ca57208 100644 --- a/screeninit/Makefile +++ b/screeninit/Makefile @@ -12,9 +12,11 @@ dir_source := source dir_build := build dir_out := ../$(dir_build) -ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math -LDFLAGS := -nostdlib +ASFLAGS := -mcpu=mpcore -mfloat-abi=soft +CFLAGS := -DARM11 -Wall -Wextra -MMD -MP -marm -mno-thumb-interwork \ + $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math \ + -I$(dir_source) -I../common +LDFLAGS := -nostdlib -nostartfiles objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ diff --git a/screeninit/linker.ld b/screeninit/linker.ld index fc311e9..c2855bb 100644 --- a/screeninit/linker.ld +++ b/screeninit/linker.ld @@ -9,8 +9,10 @@ SECTIONS .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } .data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); } + .bss : ALIGN(4) { __bss_start = .; *(.bss*); __bss_end = .; } . = ALIGN(4); - - __stack_top = 0x1FFFF800; + + __irq_stack = 0x1FFFF800; + __prg_stack = 0x1FFFF400; } diff --git a/screeninit/source/boot.s b/screeninit/source/boot.s index 711c766..da173fd 100644 --- a/screeninit/source/boot.s +++ b/screeninit/source/boot.s @@ -5,8 +5,8 @@ .global __boot __boot: - @ Disable interrupts and switch to Supervisor - cpsid aif, #0x13 + @ Disable interrupts and switch to IRQ + cpsid aif, #0x12 @ Writeback and invalidate caches mov r0, #0 @@ -14,7 +14,11 @@ __boot: mcr p15, 0, r0, c7, c14, 0 mcr p15, 0, r0, c7, c10, 4 - ldr sp, =__stack_top + ldr sp, =__irq_stack + + @ Switch to SVC + cpsid aif, #0x13 + ldr sp, =__prg_stack @ Reset values ldr r0, =0x00054078 @@ -26,6 +30,14 @@ __boot: mcr p15, 0, r1, c1, c0, 1 mcr p15, 0, r2, c1, c0, 2 + ldr r0, =__bss_start + ldr r1, =__bss_end + mov r2, #0 + .Lclearbss: + str r2, [r0], #4 + cmp r0, r1 + blt .Lclearbss + bl main b __boot diff --git a/screeninit/source/gic.c b/screeninit/source/gic.c new file mode 100644 index 0000000..2236cd1 --- /dev/null +++ b/screeninit/source/gic.c @@ -0,0 +1,57 @@ +/* + Written by Wolfvak, specially sublicensed under the GPLv2 + Read LICENSE for more details +*/ + +#include +#include +#include + +#define IRQ_BASE ((vu32*)0x1FFFFFA0) + +irq_handler handler_table[MAX_IRQ]; + +void __attribute__((interrupt("IRQ"))) gic_irq_handler(void) +{ + u32 xrq, ss; + CPU_EnterCritical(&ss); + xrq = *GIC_IRQACK; + if (xrq < MAX_IRQ && handler_table[xrq]) { + (handler_table[xrq])(xrq); + } + *GIC_IRQEND = xrq; + CPU_LeaveCritical(&ss); + return; +} + +void GIC_Configure(u32 irq_id, irq_handler hndl) +{ + handler_table[irq_id] = hndl; + DIC_CLRENABLE[irq_id/32] |= BIT(irq_id & 0x1F); + DIC_SETENABLE[irq_id/32] |= BIT(irq_id & 0x1F); + DIC_PROCTGT[irq_id] = 1; + return; +} + +void GIC_Reset(void) +{ + *GIC_CONTROL = 0; + *GIC_PRIOMASK = ~0; + + for (int i = 0; i < (BIT(9)-1); i++) { + *GIC_IRQEND |= i; + } + + *DIC_CONTROL = 0; + for (int i = 0; i < (0x20/4); i++) { + DIC_CLRENABLE[i] = ~0; + DIC_PRIORITY[i] = 0; + } + + *DIC_CONTROL = 1; + *GIC_CONTROL = 1; + + IRQ_BASE[1] = (u32)gic_irq_handler; + IRQ_BASE[0] = 0xE51FF004; + return; +} diff --git a/screeninit/source/gic.h b/screeninit/source/gic.h new file mode 100644 index 0000000..82ae829 --- /dev/null +++ b/screeninit/source/gic.h @@ -0,0 +1,56 @@ +/* + Written by Wolfvak, specially sublicensed under the GPLv2 + Read LICENSE for more details +*/ + +#pragma once +#include + +typedef void (*irq_handler)(u32); + +#define MAX_IRQ (0x80) + +#define GIC_BASE (0x17E00100) +#define DIC_BASE (0x17E01000) + +/* Setting bit 0 enables the GIC */ +#define GIC_CONTROL ((vu32*)(GIC_BASE + 0x00)) +/* Bits [7:0] control the min priority accepted */ +#define GIC_PRIOMASK ((vu32*)(GIC_BASE + 0x04)) +/* When an IRQ occurrs, this register holds the IRQ ID */ +#define GIC_IRQACK ((vu32*)(GIC_BASE + 0x0C)) +/* Write the IRQ ID here to acknowledge it */ +#define GIC_IRQEND ((vu32*)(GIC_BASE + 0x10)) + + +/* Setting bit 0 enables the DIC */ +#define DIC_CONTROL ((vu32*)(DIC_BASE + 0x000)) +/* + Write here to enable an IRQ ID + The register address is DIC_SETENABLE + (N/32)*4 and its + corresponding bit index is (N%32) +*/ +#define DIC_SETENABLE ((vu32*)(DIC_BASE + 0x100)) + +/* same as above but disables the IRQ */ +#define DIC_CLRENABLE ((vu32*)(DIC_BASE + 0x180)) + +/* sets the IRQ priority */ +#define DIC_PRIORITY ((vu32*)(DIC_BASE + 0x400)) + +/* specifies which CPUs are allowed to be forwarded the IRQ */ +#define DIC_PROCTGT ((vu8*)(DIC_BASE + 0x800)) + +/* + each irq has 2 bits assigned + bit 0 = 0: uses 1-N model + 1: uses N-N model + + bit 1 = 0: level high active + 1: rising edge sensitive + */ +#define DIC_CFGREG ((vu32*)(DIC_BASE + 0xC00)) + +void gic_irq_handler(void); +void GIC_Configure(u32 irq_id, irq_handler hndl); +void GIC_Reset(void); diff --git a/screeninit/source/main.c b/screeninit/source/main.c index 9c11c14..a75e98b 100644 --- a/screeninit/source/main.c +++ b/screeninit/source/main.c @@ -1,26 +1,33 @@ // screeninit source taken over from https://github.com/AuroraWright/arm9loaderhax/tree/master/payload_stage2/arm11 // check there for license info // thanks go to AuroraWright -#include "types.h" +#include +#include +#include +#include // see: https://github.com/AuroraWright/Luma3DS/blob/53209b9be0c264af00fb81b32146d27f0d9498ac/source/screen.h#L32-L34 #define PDN_GPU_CNT (*(vu8 *)0x10141200) #define ARESCREENSINITIALIZED (PDN_GPU_CNT != 1) -#define BRIGHTNESS (0xBF) +#define BASE_BRIGHTNESS (0x1F) -void main(void) +static volatile struct fb { + u8 *top_left; + u8 *top_right; + u8 *bottom; +} *const fb = (volatile struct fb *)0x23FFFE00; + +void screen_init(void) { char do_disco = !ARESCREENSINITIALIZED; - vu32 *arm11Entry = (vu32 *)0x1FFFFFFC; - u32 entry; *(vu32 *)0x10141200 = 0x1007F; *(vu32 *)0x10202014 = 0x00000001; *(vu32 *)0x1020200C &= 0xFFFEFFFE; - *(vu32 *)0x10202240 = BRIGHTNESS; - *(vu32 *)0x10202A40 = BRIGHTNESS; + *(vu32 *)0x10202240 = BASE_BRIGHTNESS; + *(vu32 *)0x10202A40 = BASE_BRIGHTNESS; *(vu32 *)0x10202244 = 0x1023E; *(vu32 *)0x10202A44 = 0x1023E; @@ -107,21 +114,59 @@ void main(void) *REGs_PSC1 = (vu32 *)0x10400020; REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address - REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address + REGs_PSC0[1] = (u32)(fb->top_left + 0x46500) >> 3; //End address REGs_PSC0[2] = 0; //Fill value REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address - REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address + REGs_PSC1[1] = (u32)(fb->bottom + 0x38400) >> 3; //End address REGs_PSC1[2] = 0; //Fill value REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2))); + return; +} + +void set_brightness(u8 brightness) +{ + *(vu32 *)0x10202240 = brightness; + *(vu32 *)0x10202A40 = brightness; +} + +void pxi_interrupt_handler(__attribute__((unused)) u32 xrq_n) +{ + u8 msg = PXI_GetRemote(); + switch(msg) { + case PXI_NOCMD: + break; + case PXI_SETBRIGHTNESS: + set_brightness(PXI_Recv()); + break; + } + PXI_SetRemote(PXI_NOCMD); + return; +} + +void main(void) +{ + vu32 *arm11Entry = (vu32 *)0x1FFFFFFC; + u32 entry; + + PXI_Reset(); + GIC_Reset(); + screen_init(); // Clear ARM11 entrypoint *arm11Entry = 0; + GIC_Configure(IRQ_PXI_SYNC, pxi_interrupt_handler); + PXI_EnableIRQ(); + CPU_EnableIRQ(); + //Wait for the entrypoint to be set, then branch to it while((entry=*arm11Entry) == 0); + + CPU_DisableIRQ(); + PXI_DisableIRQ(); ((void (*)())(entry))(); } diff --git a/screeninit/source/types.h b/screeninit/source/types.h deleted file mode 100644 index 3f04590..0000000 --- a/screeninit/source/types.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -//Common data types -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; -typedef volatile u8 vu8; -typedef volatile u16 vu16; -typedef volatile u32 vu32; -typedef volatile u64 vu64; - -#define SCREEN_TOP_WIDTH 400 -#define SCREEN_BOTTOM_WIDTH 320 -#define SCREEN_HEIGHT 240 -#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT) -#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT) - -static volatile struct fb { - u8 *top_left; - u8 *top_right; - u8 *bottom; -} *const fb = (volatile struct fb *)0x23FFFE00; diff --git a/source/common/common.h b/source/common/common.h index 813f737..6ae2bce 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -7,16 +7,7 @@ #include #include #include - -#define u8 uint8_t -#define u16 uint16_t -#define u32 uint32_t -#define u64 uint64_t - -#define vu8 volatile u8 -#define vu16 volatile u16 -#define vu32 volatile u32 -#define vu64 volatile u64 +#include #define max(a,b) \ (((a) > (b)) ? (a) : (b)) diff --git a/source/common/hid.c b/source/common/hid.c index f74931e..af67123 100644 --- a/source/common/hid.c +++ b/source/common/hid.c @@ -1,6 +1,7 @@ #include "hid.h" #include "i2c.h" #include "timer.h" +#include "power.h" u32 InputWait(u32 timeout_sec) { static u64 delay = 0; @@ -23,6 +24,7 @@ u32 InputWait(u32 timeout_sec) { return sd_state ? SD_INSERT : SD_EJECT; u8 special_key; if ((timer_msec(timer_mcu) >= 64) && (I2C_readRegBuf(I2C_DEV_MCU, 0x10, &special_key, 1))) { + CheckBrightness(); if (special_key == 0x01) return pad_state | BUTTON_POWER; else if (special_key == 0x04) diff --git a/source/common/power.c b/source/common/power.c index f8064c6..74b0030 100644 --- a/source/common/power.c +++ b/source/common/power.c @@ -2,6 +2,23 @@ #include "i2c.h" #include "cache.h" #include "timer.h" +#include "pxi.h" + +static const u8 br_settings[] = {0x1F, 0x3F, 0x7F, 0xBF}; +static int prev_brightness = -1; +void CheckBrightness() { + u8 curSlider; + I2C_readRegBuf(I2C_DEV_MCU, 0x09, &curSlider, 1); + curSlider >>= 4; + if (curSlider != prev_brightness) { + PXI_Wait(); + PXI_Send(br_settings[curSlider]); + PXI_SetRemote(PXI_SETBRIGHTNESS); + PXI_Sync(); + prev_brightness = curSlider; + } + return; +} void ScreenOn() { wait_msec(3); // wait 3ms (cause profi200 said so) diff --git a/source/common/power.h b/source/common/power.h index 6b751b8..42abc1e 100644 --- a/source/common/power.h +++ b/source/common/power.h @@ -2,6 +2,7 @@ #include "common.h" +void CheckBrightness(); void ScreenOn(); void Reboot(); void PowerOff(); diff --git a/source/main.c b/source/main.c index cffe3f0..00335bf 100644 --- a/source/main.c +++ b/source/main.c @@ -1,14 +1,15 @@ #include "godmode.h" #include "power.h" +#include "pxi.h" void main(int argc, char** argv) { (void) argc; // unused for now (void) argv; // unused for now - + // Screen on ScreenOn(); - + // Run the main program if (GodMode() == GODMODE_EXIT_REBOOT) Reboot(); else PowerOff();