Skip to content

Instantly share code, notes, and snippets.

@ITotalJustice
Created October 29, 2022 08:19
Show Gist options
  • Select an option

  • Save ITotalJustice/43ac82e77c3b6763519fe7f9d986abce to your computer and use it in GitHub Desktop.

Select an option

Save ITotalJustice/43ac82e77c3b6763519fe7f9d986abce to your computer and use it in GitHub Desktop.
this is what killed my ezflash. !!DO NOT RUN THIS CODE ON YOUR EZFLASH!!!
#include <gba_console.h>
#include <gba_video.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba.h>
#include <stdio.h>
enum NorS71InfoOffset
{
NOR_S71_INFO_OFFSET_SAVE = 0x790000,
NOR_S71_INFO_OFFSET_NOR = 0x7A0000,
NOR_S71_INFO_OFFSET_SET = 0x7B0000,
};
enum NorS71SetInfo
{
NOR_S71_SET_INFO_LANG = NOR_S71_INFO_OFFSET_SET + (0 * 2),
NOR_S71_SET_INFO_RESET = NOR_S71_INFO_OFFSET_SET + (1 * 2),
NOR_S71_SET_INFO_RTS = NOR_S71_INFO_OFFSET_SET + (2 * 2),
NOR_S71_SET_INFO_SLEEP = NOR_S71_INFO_OFFSET_SET + (3 * 2),
NOR_S71_SET_INFO_CHEAT = NOR_S71_INFO_OFFSET_SET + (4 * 2),
NOR_S71_SET_INFO_SLEEP_KEY0 = NOR_S71_INFO_OFFSET_SET + (5 * 2),
NOR_S71_SET_INFO_SLEEP_KEY1 = NOR_S71_INFO_OFFSET_SET + (6 * 2),
NOR_S71_SET_INFO_SLEEP_KEY2 = NOR_S71_INFO_OFFSET_SET + (7 * 2),
NOR_S71_SET_INFO_MENU_KEY0 = NOR_S71_INFO_OFFSET_SET + (8 * 2),
NOR_S71_SET_INFO_MENU_KEY1 = NOR_S71_INFO_OFFSET_SET + (9 * 2),
NOR_S71_SET_INFO_MENU_KEY2 = NOR_S71_INFO_OFFSET_SET + (10 * 2),
NOR_S71_SET_INFO_ENGINE = NOR_S71_INFO_OFFSET_SET + (11 * 2),
NOR_S71_SET_INFO_SHOW_THUMBNAIL = NOR_S71_INFO_OFFSET_SET + (12 * 2),
NOR_S71_SET_INFO_RTC_OPEN_STATUS = NOR_S71_INFO_OFFSET_SET + (13 * 2),
// DE setting
NOR_S71_SET_INFO_AUTO_SAVE_SEL = NOR_S71_INFO_OFFSET_SET + (14 * 2),
NOR_S71_SET_INFO_MODE_B_INIT = NOR_S71_INFO_OFFSET_SET + (15 * 2),
NOR_S71_SET_INFO_LED_OPEN_SEL = NOR_S71_INFO_OFFSET_SET + (16 * 2),
NOR_S71_SET_INFO_BREATHING_R = NOR_S71_INFO_OFFSET_SET + (17 * 2),
NOR_S71_SET_INFO_BREATHING_G = NOR_S71_INFO_OFFSET_SET + (18 * 2),
NOR_S71_SET_INFO_BREATHING_B = NOR_S71_INFO_OFFSET_SET + (19 * 2),
NOR_S71_SET_INFO_SD_R = NOR_S71_INFO_OFFSET_SET + (20 * 2),
NOR_S71_SET_INFO_SD_G = NOR_S71_INFO_OFFSET_SET + (21 * 2),
NOR_S71_SET_INFO_SD_B = NOR_S71_INFO_OFFSET_SET + (22 * 2),
};
// some of these addr are labled as unknown but that's not entierly true
// i just don't know of an appropriate name to give them.
// most of the unk are part of a sequence to r/w to a device
// such as the sd card, fpga or spi.
enum RegAddr
{
UNK0 = 0x9FE0000, // writes always 0xD200
UNK1 = 0x8000000, // writes always 0x1500
UNK2 = 0x8020000, // writes always 0xD200
UNK3 = 0x8040000, // writes always 0x1500
// i think this is used to flush pending data to sd card
UNK5 = 0x9FC0000, // writes always 0x1500
SD_RESPONSE = 0x9E00000,
SD_CONTROL = 0x9400000,
BUFFER_CONTROL = 0x9420000, // (A1)
ROMPAGE = 0x9880000, // (C4)
PS_RAMPAGE = 0x9860000, // (C3)
RAMPAGE = 0x9C00000, // (E0)
SPI_CONTROL = 0x9660000,
SPI_WRITE = 0x9680000,
RTC_STATUS = 0x96A0000,
AUTO_SAVE_MODE = 0x96C0000,
// DE stuff
LED_CONTROL = 0x96E0000,
RUMBLE_CONTROL = 0x9E20000,
ROM64_FLAG = 0x9700000, // ???
SD_ADDR_LOW = 0x9600000, // low 16 bits
SD_ADDR_HIGH = 0x9620000, // upper 16 bits
SD_BLOCKS = 0x9640000, // number of blocks to transfer
// this is used for many things depending on spi control
// is spi control is enabled then this is used for fpga version
// otherwise, it is used for ???
FPGA_VER = 0x9E00000,
FLASH_BASE_S71 = 0x08000000, // writes always 0xF0
FLASH_BASE_S71_END = 0x087FFFFF,
FLASH_BASE_S98 = 0x09000000, // writes always 0xF0
FLASH_BASE_S98_END = 0x097FFFFF,
SET_INFO_LANG = 0x08000000 + NOR_S71_SET_INFO_LANG,
SET_INFO_RESET = 0x08000000 + NOR_S71_SET_INFO_RESET,
SET_INFO_RTS = 0x08000000 + NOR_S71_SET_INFO_RTS,
SET_INFO_SLEEP = 0x08000000 + NOR_S71_SET_INFO_SLEEP,
SET_INFO_CHEAT = 0x08000000 + NOR_S71_SET_INFO_CHEAT,
SET_INFO_SLEEP_KEY0 = 0x08000000 + NOR_S71_SET_INFO_SLEEP_KEY0,
SET_INFO_SLEEP_KEY1 = 0x08000000 + NOR_S71_SET_INFO_SLEEP_KEY1,
SET_INFO_SLEEP_KEY2 = 0x08000000 + NOR_S71_SET_INFO_SLEEP_KEY2,
SET_INFO_MENU_KEY0 = 0x08000000 + NOR_S71_SET_INFO_MENU_KEY0,
SET_INFO_MENU_KEY1 = 0x08000000 + NOR_S71_SET_INFO_MENU_KEY1,
SET_INFO_MENU_KEY2 = 0x08000000 + NOR_S71_SET_INFO_MENU_KEY2,
SET_INFO_ENGINE = 0x08000000 + NOR_S71_SET_INFO_ENGINE,
SET_INFO_SHOW_THUMBNAIL = 0x08000000 + NOR_S71_SET_INFO_SHOW_THUMBNAIL,
SET_INFO_RTC_OPEN_STATUS = 0x08000000 + NOR_S71_SET_INFO_RTC_OPEN_STATUS,
// DE
SET_INFO_AUTO_SAVE_SEL = 0x08000000 + NOR_S71_SET_INFO_AUTO_SAVE_SEL,
SET_INFO_MODE_B_INIT = 0x08000000 + NOR_S71_SET_INFO_MODE_B_INIT,
SET_INFO_LED_OPEN_SEL = 0x08000000 + NOR_S71_SET_INFO_LED_OPEN_SEL,
SET_INFO_BREATHING_R = 0x08000000 + NOR_S71_SET_INFO_BREATHING_R,
SET_INFO_BREATHING_G = 0x08000000 + NOR_S71_SET_INFO_BREATHING_G,
SET_INFO_BREATHING_B = 0x08000000 + NOR_S71_SET_INFO_BREATHING_B,
SET_INFO_SD_R = 0x08000000 + NOR_S71_SET_INFO_SD_R,
SET_INFO_SD_G = 0x08000000 + NOR_S71_SET_INFO_SD_G,
SET_INFO_SD_B = 0x08000000 + NOR_S71_SET_INFO_SD_B,
RTC_DATA = 0x080000C4,
RTC_RW = 0x080000C6,
RTC_ENABLE = 0x080000C8,
// this is unused in ezflash kernel
// todo: debug on hw what the output of
// polling this register is!
RTC_CART_NAME = 0x080000A0,
};
void IWRAM_CODE SetRompage(u16 page)
{
*(vu16 *)0x9fe0000 = 0xd200;
*(vu16 *)0x8000000 = 0x1500;
*(vu16 *)0x8020000 = 0xd200;
*(vu16 *)0x8040000 = 0x1500;
*(vu16 *)0x9880000 = page;//C4
*(vu16 *)0x9fc0000 = 0x1500;
}
void IWRAM_CODE SetSPIControl(u16 control)
{
*(u16 *)0x9fe0000 = 0xd200;
*(u16 *)0x8000000 = 0x1500;
*(u16 *)0x8020000 = 0xd200;
*(u16 *)0x8040000 = 0x1500;
*(u16 *)SPI_WRITE = control;
*(u16 *)0x9fc0000 = 0x1500;
}
// --------------------------------------------------------------------
void IWRAM_CODE SPI_Enable(void)
{
SetSPIControl(1);
}
// --------------------------------------------------------------------
void IWRAM_CODE SPI_Disable(void)
{
SetSPIControl(0);
}
void IWRAM_CODE clrs()
{
printf("\x1b[2J\n");
}
char game_name[17] = {0};
vu16 v0x9fe0000 = 0;
vu16 vSD_CONTROL = 0;
vu16 vBUFFER_CONTROL = 0;
vu16 vROMPAGE = 0;
vu16 vPS_RAMPAGE = 0;
vu16 vRAMPAGE = 0;
vu16 vSPI_CONTROL = 0;
vu16 vSPI_WRITE = 0;
vu16 vRTC_STATUS = 0;
vu16 vAUTO_SAVE_MODE = 0;
vu16 vSD_ADDR_LOW = 0;
vu16 vSD_ADDR_HIGH = 0;
vu16 vSD_BLOCKS = 0;
vu16 vFPGA_VER = 0;
vu16 vSET_INFO_LANG = 0;
vu16 vSET_INFO_RESET = 0;
vu16 vSET_INFO_RTS = 0;
vu16 vSET_INFO_SLEEP = 0;
vu16 vSET_INFO_CHEAT = 0;
vu16 vSET_INFO_SLEEP_KEY0 = 0;
vu16 vSET_INFO_SLEEP_KEY1 = 0;
vu16 vSET_INFO_SLEEP_KEY2 = 0;
vu16 vSET_INFO_MENU_KEY0 = 0;
vu16 vSET_INFO_MENU_KEY1 = 0;
vu16 vSET_INFO_MENU_KEY2 = 0;
vu16 vSET_INFO_ENGINE = 0;
vu16 vSET_INFO_SHOW_THUMBNAIL = 0;
vu16 vSET_INFO_RTC_OPEN_STATUS = 0;
void IWRAM_CODE update_regs()
{
for (int i = 0; i < 16; i++)
{
game_name[i] = *(vu8*)(0x8000000+160+i);
}
game_name[16] = '\0';
SPI_Enable();
vFPGA_VER = *(vu16*)FPGA_VER;
SPI_Disable();
vSET_INFO_LANG = *(vu16*)SET_INFO_LANG;
vSET_INFO_RESET = *(vu16*)SET_INFO_RESET;
vSET_INFO_RTS = *(vu16*)SET_INFO_RTS;
vSET_INFO_SLEEP = *(vu16*)SET_INFO_SLEEP;
vSET_INFO_CHEAT = *(vu16*)SET_INFO_CHEAT;
vSET_INFO_SLEEP_KEY0 = *(vu16*)SET_INFO_SLEEP_KEY0;
vSET_INFO_SLEEP_KEY1 = *(vu16*)SET_INFO_SLEEP_KEY1;
vSET_INFO_SLEEP_KEY2 = *(vu16*)SET_INFO_SLEEP_KEY2;
vSET_INFO_MENU_KEY0 = *(vu16*)SET_INFO_MENU_KEY0;
vSET_INFO_MENU_KEY1 = *(vu16*)SET_INFO_MENU_KEY1;
vSET_INFO_MENU_KEY2 = *(vu16*)SET_INFO_MENU_KEY2;
vSET_INFO_ENGINE = *(vu16*)SET_INFO_ENGINE;
vSET_INFO_SHOW_THUMBNAIL = *(vu16*)SET_INFO_SHOW_THUMBNAIL;
vSET_INFO_RTC_OPEN_STATUS = *(vu16*)SET_INFO_RTC_OPEN_STATUS;
v0x9fe0000 = *(vu16*)0x9fe0000;
vSD_CONTROL = *(vu16*)SD_CONTROL;
vBUFFER_CONTROL = *(vu16*)BUFFER_CONTROL;
vROMPAGE = *(vu16*)ROMPAGE;
vPS_RAMPAGE = *(vu16*)PS_RAMPAGE;
vRAMPAGE = *(vu16*)RAMPAGE;
vSPI_CONTROL = *(vu16*)SPI_CONTROL;
vSPI_WRITE = *(vu16*)SPI_WRITE;
vRTC_STATUS = *(vu16*)RTC_STATUS;
vAUTO_SAVE_MODE = *(vu16*)AUTO_SAVE_MODE;
vSD_ADDR_LOW = *(vu16*)SD_ADDR_LOW;
vSD_ADDR_HIGH = *(vu16*)SD_ADDR_HIGH;
vSD_BLOCKS = *(vu16*)SD_BLOCKS;
}
void IWRAM_CODE log_reg()
{
printf("idk: 0x%04X\n", v0x9fe0000);
printf("SD_CONTROL: 0x%04X\n", vSD_CONTROL);
printf("BUFFER_CONTROL: 0x%04X\n", vBUFFER_CONTROL);
printf("ROMPAGE: 0x%04X\n", vROMPAGE);
printf("PS_RAMPAGE: 0x%04X\n", vPS_RAMPAGE);
printf("RAMPAGE: 0x%04X\n", vRAMPAGE);
printf("SPI_CONTROL: 0x%04X\n", vSPI_CONTROL);
printf("SPI_WRITE: 0x%04X\n", vSPI_WRITE);
printf("RTC_STATUS: 0x%04X\n", vRTC_STATUS);
printf("AUTO_SAVE_MODE: 0x%04X\n", vAUTO_SAVE_MODE);
printf("SD_ADDR_LOW: 0x%04X\n", vSD_ADDR_LOW);
printf("SD_ADDR_HIGH: 0x%04X\n", vSD_ADDR_HIGH);
printf("SD_BLOCKS: 0x%04X\n", vSD_BLOCKS);
printf("game_name: %s\n", game_name);
}
void IWRAM_CODE log_set()
{
printf("FPGA_VER: 0x%04X\n", vFPGA_VER);
printf("SET_INFO_LANG: 0x%04X\n", vSET_INFO_LANG);
printf("SET_INFO_RESET: 0x%04X\n", vSET_INFO_RESET);
printf("SET_INFO_RTS: 0x%04X\n", vSET_INFO_RTS);
printf("SET_INFO_SLEEP: 0x%04X\n", vSET_INFO_SLEEP);
printf("SET_INFO_CHEAT: 0x%04X\n", vSET_INFO_CHEAT);
printf("SET_INFO_SLEEP_KEY0: 0x%04X\n", vSET_INFO_SLEEP_KEY0);
printf("SET_INFO_SLEEP_KEY1: 0x%04X\n", vSET_INFO_SLEEP_KEY1);
printf("SET_INFO_SLEEP_KEY2: 0x%04X\n", vSET_INFO_SLEEP_KEY2);
printf("SET_INFO_MENU_KEY0: 0x%04X\n", vSET_INFO_MENU_KEY0);
printf("SET_INFO_MENU_KEY1: 0x%04X\n", vSET_INFO_MENU_KEY1);
printf("SET_INFO_MENU_KEY2: 0x%04X\n", vSET_INFO_MENU_KEY2);
printf("SET_INFO_ENGINE: 0x%04X\n", vSET_INFO_ENGINE);
printf("SET_INFO_SHOW_THUMBNAIL: 0x%04X\n", vSET_INFO_SHOW_THUMBNAIL);
printf("SET_INFO_RTC_OPEN_STATUS: 0x%04X\n", vSET_INFO_RTC_OPEN_STATUS);
}
void IWRAM_CODE wait_vblank()
{
while (*(vu16*)0x04000006 != 160);
}
void IWRAM_CODE loop()
{
#if 0
RESET_EWRAM = (1<<0), /*!< Clear 256K on-board WRAM */
RESET_IWRAM = (1<<1), /*!< Clear 32K in-chip WRAM */
RESET_PALETTE = (1<<2), /*!< Clear Palette */
RESET_VRAM = (1<<3), /*!< Clear VRAM */
RESET_OAM = (1<<4), /*!< Clear OAM */
RESET_SIO = (1<<5), /*!< Switches to general purpose mode */
RESET_SOUND = (1<<6), /*!< Reset Sound registers */
RESET_OTHER = (1<<7) /*!< all other registers */
#endif
while (1) {
scanKeys();
u16 keys = keysDown();
if (keys & KEY_SELECT)
{
clrs();
printf("BOOTLOADER\n");
REG_IME = 0;
SetRompage(0x8000);
wait_vblank();
update_regs();
SetRompage(0x200);
REG_IME = 1;
printf("DID BOOTLOADER\n");
}
if (keys & KEY_L)
{
clrs();
REG_IME = 0;
printf("KERNEL\n");
SetRompage(0x8002);
wait_vblank();
update_regs();
SetRompage(0x200);
REG_IME = 1;
printf("DID KERNEL\n");
}
if (keys & KEY_A)
{
clrs();
printf("RELOGGING REG\n\n");
log_reg();
}
if (keys & KEY_B)
{
clrs();
printf("RELOGGING SET\n\n");
log_set();
}
if (keys & KEY_R)
{
clrs();
printf("OS MODE AND THEN GAMEMODE\n");
REG_IME = 0;
SetRompage(0x8000);
wait_vblank();
update_regs();
SetRompage(0x200);
REG_IME = 1;
printf("GAMEMODE\n");
}
if (keys & KEY_UP)
{
clrs();
printf("changing to nor\n");
REG_IME = 0;
SetRompage(0x0);
wait_vblank();
update_regs();
SetRompage(0x200);
REG_IME = 1;
printf("GAMEMODE\n");
}
if (keys & KEY_DOWN)
{
update_regs();
}
VBlankIntrWait();
}
}
//---------------------------------------------------------------------------------
// Program entry point
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
// the vblank interrupt must be enabled for VBlankIntrWait() to work
// since the default dispatcher handles the bios flags no vblank handler
// is required
irqInit();
irqEnable(IRQ_VBLANK);
consoleDemoInit();
/*
reading via ezflash kernel:
idk = 0xE832
SD_CONTROL = 0x682A
BUFFER_CONTROL = 0x6A09
ROMPAGE = 0x0095
PS_RAMPAGE = 0x4005
RAMPAGE = 0x5209
SPI_CONTROL = 0x9C04
SPI_WRITE = 0x4ED4
RTC_STATUS = 0x42D5
AUTO_SAVE_MODE = 0x8245
*/
#if 0
// ansi escape sequence to clear screen and home cursor
// /x1b[line;columnH
printf("\x1b[2J\n");
printf("\n\n");
// ansi escape sequence to set print co-ordinates
// /x1b[line;columnH
printf("\x1b[10;10HHello World!\n");
// ansi escape sequence to move cursor up
// /x1b[linesA
printf("\x1b[10ALine 0\n");
// ansi escape sequence to move cursor left
// /x1b[columnsD
printf("\x1b[28DColumn 0\n");
// ansi escape sequence to move cursor down
// /x1b[linesB
printf("\x1b[19BLine 19\n");
// ansi escape sequence to move cursor right
// /x1b[columnsC
printf("\x1b[5CColumn 20\n");
#endif
printf("Hello world :)\n\n");
loop();
return 0;
}

it seems that if you do:

// enable
*(u16 *)0x9fe0000 = 0xd200;
*(u16 *)0x8000000 = 0x1500;
*(u16 *)0x8020000 = 0xd200;
*(u16 *)0x8040000 = 0x1500;
*(u16 *)SPI_WRITE = 1;
*(u16 *)0x9fc0000 = 0x1500;

// disable
*(u16 *)0x9fe0000 = 0xd200;
*(u16 *)0x8000000 = 0x1500;
*(u16 *)0x8020000 = 0xd200;
*(u16 *)0x8040000 = 0x1500;
*(u16 *)SPI_WRITE = 0;
*(u16 *)0x9fc0000 = 0x1500;

it nukes the entire norS71 flash. i think i did this in os mode, though it may be possible in game mode as well.

this was an accident on my part, i used the wrong addr, SPI_WRITE instead of SPI_CONTROL. please be careful if you mess around with reading / writing to registers on ezflash. be especially careful of any custom kernels.


console.c was a quickly modified version of the ansi demo from dkp because i wanted to see if the registers were write only. main reason because i wanted to see if you could read the rompage, if so, it would be useful for https://github.com/felixjones/ezfo-disc_io which didnt work on my ezflash. i noticed it reads from 0x9fc0000 in game mode expecting a certain value but my ezflash never returned that value in both game and os mode. even if i ignored that value, it failed to switch into os mode.

after some trial and error, i noticed that if i set IME=0 before the switch, it worked! i then re did all my tests, reading the registers in different modes. what puzzled me was that S71 was not readable, i couldnt read any of the settings.

however, once i added the fpga read version sequence, i could read the entire norflash and get the correct values from the settings. i then cleaned it up, started adding names to the addresses instead of magic numbers. made a quick change to see if i could read the title name of a game installed on the norS98 (it worked btw), i tried reading the regs again, thats when everything died.


end result is my ds does not detect the ezflash being inserted at all. i dont know what damage is done tbh.

the spi write stuff is used in the kernel when updating the kernel. it first does a fat transfer along with data for the new kernel via a dma, then finally enables spi write. it polls the sd card after for when it has finished the transfer. it then disables spi write. this loops for the size of the new kernel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment