From 7925fe4eeaab81041f93ecc36420cb35e898ca14 Mon Sep 17 00:00:00 2001 From: chaoskagami Date: Tue, 26 Apr 2016 06:22:33 -0400 Subject: [PATCH] More work on menus --- Makefile | 8 +++ README.md | 47 +++++++++++++++ source/common.h | 3 + source/i2c.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++ source/i2c.h | 35 +++++++++++ source/input.h | 19 ++++++ source/main.c | 121 ++++++++++++++++++++++++++++++++++--- source/std/draw.c | 5 ++ 8 files changed, 379 insertions(+), 8 deletions(-) create mode 100644 README.md create mode 100644 source/i2c.c create mode 100644 source/i2c.h create mode 100644 source/input.h diff --git a/Makefile b/Makefile index a20cf49..eda87a6 100644 --- a/Makefile +++ b/Makefile @@ -68,4 +68,12 @@ $(dir_build)/std/%.o: $(dir_source)/std/%.s @mkdir -p "$(@D)" $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $< +$(dir_build)/firm/%.o: $(dir_source)/firm/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $< + +$(dir_build)/firm/%.o: $(dir_source)/firm/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $< + include $(call rwildcard, $(dir_build), *.d) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c1bbea --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +Corbenik +============================== + +This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch for fun, and because I'm a control freak. + +Conceptually, and in operation, it is most similar to mid-kid/CakesForeveryWan out of the bunch. That is, it uses external patches from the filesystem. Unlike cakes, patches are dynamically offset - the same way Luma3DS and ReiNand do things. But like cakes, they're software-defined. + +More importantly - for arm9 programmers who don't like headaches - I have an implementation of console printf you may be interested in, and a very close imitation of stdio around fatfs in the `std` folder. + +The version of loader in this repo is very tightly tied to the operation of Corbenik. It takes care of all binary changes on initialization. It should not be used with other versions of loader. + +If you want to know how it sizes up to other CFWs - here's a quicklist of things it does: + +Signature patches +-------------------------- + +Done via standard search and replace. + +FIRM Protection +-------------------------- + +Done similarly to Reinand - replacing the 'exe' string with 'prt' + +System module replacement +-------------------------- + +Overrides the complete module - can't change size. + +ExeFs replacement +-------------------------- + +Overrides the whole exefs. + +Locale Emulation +-------------------------- + +Different from existing solutions - a single text file of the format: +``` + +``` +This is technically superior because it doesn't involve large and unloadable directory trees. Corbenik comes with an example locale configuration built automatically from single-region single-language games on 3DSDB. + +ARM9 control thread +-------------------------- + +Because...well, I hate NTR with a passion. This is one of the primary features differentiating Corbenik from other CFWs. Hitting X+Y pops open a menu and allows configuring any loader-based patches, as well as taking screenshots and performing memory dumps. I will not add plugins created in ARM9 code, but I may embed an interpreter someday. + diff --git a/source/common.h b/source/common.h index f603257..7f41f50 100644 --- a/source/common.h +++ b/source/common.h @@ -13,5 +13,8 @@ #include "patch_format.h" #include "config.h" +#include "input.h" + +#include "i2c.h" #endif diff --git a/source/i2c.c b/source/i2c.c new file mode 100644 index 0000000..fcaab9c --- /dev/null +++ b/source/i2c.c @@ -0,0 +1,149 @@ +#include "i2c.h" + +//----------------------------------------------------------------------------- + +static const struct { uint8_t bus_id, reg_addr } dev_data[] = { + {0, 0x4A}, {0, 0x7A}, {0, 0x78}, + {1, 0x4A}, {1, 0x78}, {1, 0x2C}, + {1, 0x2E}, {1, 0x40}, {1, 0x44}, + {2, 0xD6}, {2, 0xD0}, {2, 0xD2}, + {2, 0xA4}, {2, 0x9A}, {2, 0xA0}, +}; + +const uint8_t i2cGetDeviceBusId(uint8_t device_id) { + return dev_data[device_id].bus_id; +} + +const uint8_t i2cGetDeviceRegAddr(uint8_t device_id) { + return dev_data[device_id].reg_addr; +} + +//----------------------------------------------------------------------------- + +static volatile uint8_t* const reg_data_addrs[] = { + (volatile uint8_t*)(I2C1_REG_OFF + I2C_REG_DATA), + (volatile uint8_t*)(I2C2_REG_OFF + I2C_REG_DATA), + (volatile uint8_t*)(I2C3_REG_OFF + I2C_REG_DATA), +}; + +volatile uint8_t* const i2cGetDataReg(uint8_t bus_id) { + return reg_data_addrs[bus_id]; +} + +//----------------------------------------------------------------------------- + +static volatile uint8_t* const reg_cnt_addrs[] = { + (volatile uint8_t*)(I2C1_REG_OFF + I2C_REG_CNT), + (volatile uint8_t*)(I2C2_REG_OFF + I2C_REG_CNT), + (volatile uint8_t*)(I2C3_REG_OFF + I2C_REG_CNT), +}; + +volatile uint8_t* const i2cGetCntReg(uint8_t bus_id) { + return reg_cnt_addrs[bus_id]; +} + +//----------------------------------------------------------------------------- + +int i2cWaitBusy(uint8_t bus_id) { + while (*i2cGetCntReg(bus_id) & 0x80); +} + +int i2cGetResult(uint8_t bus_id) { + i2cWaitBusy(bus_id); + return (*i2cGetCntReg(bus_id) >> 4) & 1; +} + +void i2cStop(uint8_t bus_id, uint8_t arg0) { + *i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0; + i2cWaitBusy(bus_id); + *i2cGetCntReg(bus_id) = 0xC5; +} + +//----------------------------------------------------------------------------- + +int i2cSelectDevice(uint8_t bus_id, uint8_t dev_reg) { + i2cWaitBusy(bus_id); + *i2cGetDataReg(bus_id) = dev_reg; + *i2cGetCntReg(bus_id) = 0xC2; + return i2cGetResult(bus_id); +} + +int i2cSelectRegister(uint8_t bus_id, uint8_t reg) { + i2cWaitBusy(bus_id); + *i2cGetDataReg(bus_id) = reg; + *i2cGetCntReg(bus_id) = 0xC0; + return i2cGetResult(bus_id); +} + +//----------------------------------------------------------------------------- + +uint8_t i2cReadRegister(uint8_t dev_id, uint8_t reg) { + uint8_t bus_id = i2cGetDeviceBusId(dev_id); + uint8_t dev_addr = i2cGetDeviceRegAddr(dev_id); + + for (size_t i = 0; i < 8; i++) { + if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) { + if (i2cSelectDevice(bus_id, dev_addr | 1)) { + i2cWaitBusy(bus_id); + i2cStop(bus_id, 1); + i2cWaitBusy(bus_id); + return *i2cGetDataReg(bus_id); + } + } + *i2cGetCntReg(bus_id) = 0xC5; + i2cWaitBusy(bus_id); + } + return 0xff; +} + +int i2cReadRegisterBuffer(unsigned int dev_id, int reg, uint8_t* buffer, size_t buf_size) { + uint8_t bus_id = i2cGetDeviceBusId(dev_id); + uint8_t dev_addr = i2cGetDeviceRegAddr(dev_id); + + size_t j = 0; + while (!i2cSelectDevice(bus_id, dev_addr) + || !i2cSelectRegister(bus_id, reg) + || !i2cSelectDevice(bus_id, dev_addr | 1)) + { + i2cWaitBusy(bus_id); + *i2cGetCntReg(bus_id) = 0xC5; + i2cWaitBusy(bus_id); + if (++j >= 8) + return 0; + } + + if (buf_size != 1) { + for (int i = 0; i < buf_size - 1; i++) { + i2cWaitBusy(bus_id); + *i2cGetCntReg(bus_id) = 0xF0; + i2cWaitBusy(bus_id); + buffer[i] = *i2cGetDataReg(bus_id); + } + } + + i2cWaitBusy(bus_id); + *i2cGetCntReg(bus_id) = 0xE1; + i2cWaitBusy(bus_id); + *buffer = *i2cGetDataReg(bus_id); + return 1; +} + +int i2cWriteRegister(uint8_t dev_id, uint8_t reg, uint8_t data) { + uint8_t bus_id = i2cGetDeviceBusId(dev_id); + uint8_t dev_addr = i2cGetDeviceRegAddr(dev_id); + + for (int i = 0; i < 8; i++) { + if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) { + i2cWaitBusy(bus_id); + *i2cGetDataReg(bus_id) = data; + *i2cGetCntReg(bus_id) = 0xC1; + i2cStop(bus_id, 0); + if (i2cGetResult(bus_id)) + return 1; + } + *i2cGetCntReg(bus_id) = 0xC5; + i2cWaitBusy(bus_id); + } + + return 0; +} diff --git a/source/i2c.h b/source/i2c.h new file mode 100644 index 0000000..8620e4c --- /dev/null +++ b/source/i2c.h @@ -0,0 +1,35 @@ +#pragma once + +#include "common.h" + +#define I2C1_REG_OFF 0x10161000 +#define I2C2_REG_OFF 0x10144000 +#define I2C3_REG_OFF 0x10148000 + +#define I2C_REG_DATA 0 +#define I2C_REG_CNT 1 +#define I2C_REG_CNTEX 2 +#define I2C_REG_SCL 4 + +#define I2C_DEV_MCU 3 +#define I2C_DEV_GYRO 10 +#define I2C_DEV_IR 13 + +const uint8_t i2cGetDeviceBusId(uint8_t device_id); +const uint8_t i2cGetDeviceRegAddr(uint8_t device_id); + +volatile uint8_t* const i2cGetDataReg(uint8_t bus_id); +volatile uint8_t* const i2cGetCntReg(uint8_t bus_id); + +int i2cWaitBusy(uint8_t bus_id); +int i2cGetResult(uint8_t bus_id); +uint8_t i2cGetData(uint8_t bus_id); +void i2cStop(uint8_t bus_id, uint8_t arg0); + +int i2cSelectDevice(uint8_t bus_id, uint8_t dev_reg); +int i2cSelectRegister(uint8_t bus_id, uint8_t reg); + +uint8_t i2cReadRegister(uint8_t dev_id, uint8_t reg); +int i2cWriteRegister(uint8_t dev_id, uint8_t reg, uint8_t data); + +int i2cReadRegisterBuffer(unsigned int dev_id, int reg, uint8_t* buffer, size_t buf_size); diff --git a/source/input.h b/source/input.h new file mode 100644 index 0000000..c08a0ef --- /dev/null +++ b/source/input.h @@ -0,0 +1,19 @@ +#define BUTTON_A (1 << 0) +#define BUTTON_B (1 << 1) +#define BUTTON_SEL (1 << 2) +#define BUTTON_STA (1 << 3) +#define BUTTON_RIGHT (1 << 4) +#define BUTTON_LEFT (1 << 5) +#define BUTTON_UP (1 << 6) +#define BUTTON_DOWN (1 << 7) +#define BUTTON_R (1 << 8) +#define BUTTON_L (1 << 9) +#define BUTTON_X (1 << 10) +#define BUTTON_Y (1 << 11) +/* FIXME - I had ZR and ZL in, but they don't appear to + behave rationally without some initialization. */ + +#define BUTTON_ANY 0xFFF + +#define HID_PAD ((*(volatile uint32_t *)0x10146000) ^ BUTTON_ANY) + diff --git a/source/main.c b/source/main.c index 099f7bd..8247a1b 100644 --- a/source/main.c +++ b/source/main.c @@ -4,18 +4,92 @@ void init_system() {} #define MENU_BOOTME -1 #define MENU_MAIN 1 + #define MENU_OPTIONS 2 #define MENU_PATCHES 3 #define MENU_INFO 4 -#define MENU_RESET 5 -#define MENU_POWER 6 +#define MENU_HELP 5 +#define MENU_RESET 6 +#define MENU_POWER 7 static int cursor_y = 0; static int which_menu = 1; +uint32_t wait_key() { + uint32_t get = 0; + while(get == 0) { + if(HID_PAD & BUTTON_UP) + get = BUTTON_UP; + else if (HID_PAD & BUTTON_DOWN) + get = BUTTON_DOWN; + else if (HID_PAD & BUTTON_A) + get = BUTTON_A; + else if (HID_PAD & BUTTON_B) + get = BUTTON_B; + } + while(HID_PAD&get); + return get; +} + +void header() { + cprintf(TOP_SCREEN, "%p[Corbenik - The Rebirth]\n", COLOR(CYAN, BLACK)); +} + int menu_options() { return MENU_MAIN; } + int menu_patches() { return MENU_MAIN; } -int menu_info() { return MENU_MAIN; } + +int menu_info() { return MENU_MAIN; } + +int menu_help() { + set_cursor(TOP_SCREEN, 0, 0); + + header(); + + cprintf(TOP_SCREEN, "Corbenik is a 3DS firmware patcher\n" + " commonly known as a CFW. It seeks to address\n" + " some faults in other CFWs and is generally\n" + " just another choice for users - but primarily\n" + " the kind of person who runs Gentoo or LFS. ;P\n" + "\n" + "Credits to people who've helped me put this\n" + " together either by having written GPL code,\n" + " or being just generally helpful/cool people:\n" + " @mid-kid, @Wolfvak, @Reisyukaku, @AuroraWright\n" + " @d0k3, and others\n" + "\n" + "The name of this comes from the .hack//series.\n" + " Look it up, if you don't already know it.\n" + "\n" + "Any bugs filed including the letters S, A\n" + " and O will be closed with no discussion.\n" + "\n" + " \n" + "\n" + "Press B to return.\n"); + while (1) { + if (wait_key() & BUTTON_B) + break; + } + + clear_screen(TOP_SCREEN); + + return MENU_MAIN; +} + +int menu_reset() { + // Reboot. + cprintf(BOTTOM_SCREEN, "Resetting system.\n"); + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); + while(1); +} + +int menu_poweroff() { + // Reboot. + cprintf(BOTTOM_SCREEN, "Powering off system.\n"); + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); + while(1); +} int menu_main() { set_cursor(TOP_SCREEN, 0, 0); @@ -24,14 +98,17 @@ int menu_main() { "Options", "Patches", "Info", - "Boot firmware", + "Help/Readme", "Reset", - "Power off" + "Power off", + "Boot firmware" }; - cprintf(TOP_SCREEN, "%p[Corbenik - The Rebirth]\n", COLOR(CYAN, BLACK)); + header(); - for(int i=0; i < 6; i++) { + int menu_max = 6; + + for(int i=0; i < menu_max; i++) { if (cursor_y == i) cprintf(TOP_SCREEN, "%p-> ", COLOR(GREEN, BLACK)); else @@ -39,6 +116,28 @@ int menu_main() { cprintf(TOP_SCREEN, "%s\n", list[i]); } + uint32_t key = wait_key(); + int entry = cursor_y + 2; + if (cursor_y > MENU_POWER) + entry = MENU_BOOTME; + + switch(key) { + case BUTTON_UP: + cursor_y -= 1; + break; + case BUTTON_DOWN: + cursor_y += 1; + break; + case BUTTON_A: + return entry; + break; + } + + if (cursor_y < 0) + cursor_y = 0; + if (cursor_y > menu_max - 1) + cursor_y = menu_max - 1; + return 0; } @@ -57,9 +156,15 @@ int menu_handler() { case MENU_INFO: to_menu = menu_info(); break; + case MENU_HELP: + to_menu = menu_help(); + break; case MENU_BOOTME: - default: return 0; + case MENU_RESET: + menu_reset(); + case MENU_POWER: + menu_poweroff(); } if (to_menu != 0) diff --git a/source/std/draw.c b/source/std/draw.c index e722559..2883962 100644 --- a/source/std/draw.c +++ b/source/std/draw.c @@ -38,6 +38,11 @@ void clear_screen(uint8_t* screen) { uint32_t size = 0; char* buffer = 0; uint32_t buffer_size = 0; + if (screen == TOP_SCREEN) + screen = framebuffers->top_left; + else if (screen == BOTTOM_SCREEN) + screen = framebuffers->bottom; + if(screen == framebuffers->top_left || screen == framebuffers->top_right) { size = SCREEN_TOP_SIZE; -- 2.39.5