Skip to content

Instantly share code, notes, and snippets.

@ITotalJustice
Last active July 25, 2025 00:01
Show Gist options
  • Save ITotalJustice/b6c2f630c6ac5fff1e8b117681e27abd to your computer and use it in GitHub Desktop.
Save ITotalJustice/b6c2f630c6ac5fff1e8b117681e27abd to your computer and use it in GitHub Desktop.
ezflash omega+de sd card routines, can be used with fatfs directly or libfat.
/*-----------------------------------------------------------------------*/
/* 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. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "../io_ezfo.h"
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
if (!_EZFO_readSectors(sector, count, buff)) {
return RES_ERROR;
}
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
if (!_EZFO_writeSectors(sector, count, buff)) {
return RES_ERROR;
}
return RES_OK;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
return RES_OK;
}
/*
io_ezfo.c
Hardware Routines for reading the EZ Flash Omega filesystem
*/
#include "io_ezfo.h"
#include <gba_dma.h>
#include <gba_interrupt.h>
#include <string.h>
static u8 ALIGN(4) EWRAM_BSS g_buf[512 * 4];
// SOURCE: https://github.com/ez-flash/omega-de-kernel/blob/main/source/Ezcard_OP.c
static void EWRAM_CODE delay(u32 R0)
{
int volatile i;
for ( i = R0; i; --i );
return;
}
// --------------------------------------------------------------------
static void EWRAM_CODE SetSDControl(u16 control)
{
*(vu16 *)0x9fe0000 = 0xd200;
*(vu16 *)0x8000000 = 0x1500;
*(vu16 *)0x8020000 = 0xd200;
*(vu16 *)0x8040000 = 0x1500;
*(vu16 *)0x9400000 = control;
*(vu16 *)0x9fc0000 = 0x1500;
}
// --------------------------------------------------------------------
static void EWRAM_CODE SD_Enable(void)
{
SetSDControl(1);
}
// --------------------------------------------------------------------
static void EWRAM_CODE SD_Read_state(void)
{
SetSDControl(3);
}
// --------------------------------------------------------------------
static void EWRAM_CODE SD_Disable(void)
{
SetSDControl(0);
}
// --------------------------------------------------------------------
static u16 EWRAM_CODE SD_Response(void)
{
return *(vu16 *)0x9E00000;
}
// --------------------------------------------------------------------
static u32 EWRAM_CODE Wait_SD_Response()
{
vu16 res;
u32 count=0;
while(1)
{
res = SD_Response();
if(res != 0xEEE1)
{
return 0;
}
count++;
if(count>0x100000)
{
//DEBUG_printf("time out %x",res);
//wait_btn();
return 1;
}
}
}
// --------------------------------------------------------------------
static u32 EWRAM_CODE Read_SD_sectors(u32 address,u16 count,u8* SDbuffer)
{
SD_Enable();
u8* wbuf = SDbuffer;
if ((u32)SDbuffer & 0x1) {
wbuf = g_buf;
}
u16 i;
u16 blocks;
u32 res;
u32 times=2;
for(i=0;i<count;i+=4)
{
blocks = (count-i>4)?4:(count-i);
const u32 size = blocks * 512;
read_again:
*(vu16 *)0x9fe0000 = 0xd200;
*(vu16 *)0x8000000 = 0x1500;
*(vu16 *)0x8020000 = 0xd200;
*(vu16 *)0x8040000 = 0x1500;
*(vu16 *)0x9600000 = ((address+i)&0x0000FFFF) ;
*(vu16 *)0x9620000 = ((address+i)&0xFFFF0000) >>16;
*(vu16 *)0x9640000 = blocks;
*(vu16 *)0x9fc0000 = 0x1500;
SD_Read_state();
res = Wait_SD_Response();
SD_Enable();
if(res==1)
{
times--;
if(times)
{
delay(5000);
goto read_again;
}
}
dmaCopy((void*)0x9E00000, wbuf, size);
if ((u32)SDbuffer & 0x1) {
// don't call memcpy because we have no rom to jump to!
for (u32 j = 0; j < size; j++) {
SDbuffer[j+i*512] = g_buf[j];
}
} else {
wbuf += size;
}
}
SD_Disable();
return 0;
}
// --------------------------------------------------------------------
static u32 EWRAM_CODE Write_SD_sectors(u32 address,u16 count, const u8* SDbuffer)
{
SD_Enable();
SD_Read_state();
u16 i;
u16 blocks;
u32 res;
for(i=0;i<count;i+=4)
{
blocks = (count-i>4)?4:(count-i);
const u32 size = blocks * 512;
const u8* rbuf = SDbuffer + i*512;
if ((u32)SDbuffer & 0x1) {
rbuf = g_buf;
// don't call memcpy because we have no rom to jump to!
for (u32 j = 0; j < size; j++) {
g_buf[j] = SDbuffer[j+i*512];
}
}
dmaCopy(rbuf,(void*)0x9E00000, size);
*(vu16 *)0x9fe0000 = 0xd200;
*(vu16 *)0x8000000 = 0x1500;
*(vu16 *)0x8020000 = 0xd200;
*(vu16 *)0x8040000 = 0x1500;
*(vu16 *)0x9600000 = ((address+i)&0x0000FFFF);
*(vu16 *)0x9620000 = ((address+i)&0xFFFF0000) >>16;
*(vu16 *)0x9640000 = 0x8000+blocks;
*(vu16 *)0x9fc0000 = 0x1500;
res = Wait_SD_Response();
if(res==1)
return 1;
}
delay(3000);
SD_Disable();
return 0;
}
// --------------------------------------------------------------------
static void EWRAM_CODE SetRompage(u16 page)
{
*(vu16 *)0x9fe0000 = 0xd200;
*(vu16 *)0x8000000 = 0x1500;
*(vu16 *)0x8020000 = 0xd200;
*(vu16 *)0x8040000 = 0x1500;
*(vu16 *)0x9880000 = page;//C4
*(vu16 *)0x9fc0000 = 0x1500;
}
// --------------------------------------------------------------------
#define ROMPAGE_BOOTLOADER 0x8000
#define ROMPAGE_PSRAM 0x200
#define S98WS512PE0_FLASH_PAGE_MAX 0x200
#define ROM_HEADER_CHECKSUM *(vu16*)(0x8000000 + 188)
static u16 EWRAM_BSS ROMPAGE_ROM;
// returns true if the data is a match
static bool EWRAM_CODE _EZFO_TestRompage(u16 wanted, u16 page)
{
SetRompage(page);
if (wanted == ROM_HEADER_CHECKSUM)
{
ROMPAGE_ROM = page;
return true;
}
return false;
}
static bool EWRAM_CODE _EZFO_startUp(void)
{
const u16 complement = ROM_HEADER_CHECKSUM;
// unmap rom, if the data matches, then this is not an ezflash
if (_EZFO_TestRompage(complement, ROMPAGE_BOOTLOADER))
{
return false;
}
// find where the rom is mapped, try psram first
if (_EZFO_TestRompage(complement, ROMPAGE_PSRAM))
{
return true;
}
// try and find it within norflash, test each 1MiB page (512 pages)
for (int i = 0; i < S98WS512PE0_FLASH_PAGE_MAX; i++)
{
if (_EZFO_TestRompage(complement, i))
{
return true;
}
}
// this literally shouldn't happen, contact me if you hit this!
return false;
}
bool EWRAM_CODE _EZFO_readSectors(u32 address, u32 count, void * buffer)
{
const u16 ime = REG_IME;
REG_IME = 0;
SetRompage(ROMPAGE_BOOTLOADER);
const u32 result = Read_SD_sectors(address, count, buffer);
SetRompage(ROMPAGE_ROM);
REG_IME = ime;
return result == 0;
}
bool EWRAM_CODE _EZFO_writeSectors(u32 address, u32 count, const void * buffer)
{
const u16 ime = REG_IME;
REG_IME = 0;
SetRompage(ROMPAGE_BOOTLOADER);
const u32 result = Write_SD_sectors(address, count, buffer);
SetRompage(ROMPAGE_ROM);
REG_IME = ime;
return result == 0;
}
static bool _EZFO_nop(void)
{
return true;
}
const DISC_INTERFACE _io_ezfo = {
DEVICE_TYPE_EZFO,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
( FN_MEDIUM_STARTUP )&_EZFO_startUp,
( FN_MEDIUM_ISINSERTED )&_EZFO_nop,
( FN_MEDIUM_READSECTORS )&_EZFO_readSectors,
( FN_MEDIUM_WRITESECTORS )&_EZFO_writeSectors,
( FN_MEDIUM_CLEARSTATUS )&_EZFO_nop,
( FN_MEDIUM_SHUTDOWN )&_EZFO_nop
};
/*
io_ezfo.h
Hardware Routines for reading the EZ Flash Omega filesystem
*/
#ifndef __IO_EZFO_H__
#define __IO_EZFO_H__
// 'EZFO'
#define DEVICE_TYPE_EZFO 0x4F465A45
#include <disc_io.h>
#include <gba_base.h>
// direct access to r/w sector functions to be used with fatfs.
bool EWRAM_CODE _EZFO_readSectors(u32 address, u32 count, void * buffer);
bool EWRAM_CODE _EZFO_writeSectors(u32 address, u32 count, const void * buffer);
// Export interface
extern const DISC_INTERFACE _io_ezfo;
#endif // define __IO_EZFO_H__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment