aboutsummaryrefslogtreecommitdiff
path: root/backends/platform/ds/arm9/source/fat
diff options
context:
space:
mode:
authorMax Horn2006-07-09 11:47:17 +0000
committerMax Horn2006-07-09 11:47:17 +0000
commitbea72e9514a5b3ced091d952762a5fa633e27740 (patch)
treec697df47b449a0952c2bb18f10502cae8ae3d939 /backends/platform/ds/arm9/source/fat
parent51ad5aa7197b3ced348ae37e2bc1586cb25dff3e (diff)
downloadscummvm-rg350-bea72e9514a5b3ced091d952762a5fa633e27740.tar.gz
scummvm-rg350-bea72e9514a5b3ced091d952762a5fa633e27740.tar.bz2
scummvm-rg350-bea72e9514a5b3ced091d952762a5fa633e27740.zip
Patch #1519399: DS Backend
svn-id: r23452
Diffstat (limited to 'backends/platform/ds/arm9/source/fat')
-rw-r--r--backends/platform/ds/arm9/source/fat/disc_io.c844
-rw-r--r--backends/platform/ds/arm9/source/fat/disc_io.h364
-rw-r--r--backends/platform/ds/arm9/source/fat/gba_nds_fat.c6660
-rw-r--r--backends/platform/ds/arm9/source/fat/gba_nds_fat.h888
-rw-r--r--backends/platform/ds/arm9/source/fat/io_efa2.c642
-rw-r--r--backends/platform/ds/arm9/source/fat/io_efa2.h56
-rw-r--r--backends/platform/ds/arm9/source/fat/io_fcsr.c658
-rw-r--r--backends/platform/ds/arm9/source/fat/io_fcsr.h48
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3cf.c734
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3cf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd.c750
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_m3sd_asm.s392
-rw-r--r--backends/platform/ds/arm9/source/fat/io_mpcf.c714
-rw-r--r--backends/platform/ds/arm9/source/fat/io_mpcf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_nmmc.c716
-rw-r--r--backends/platform/ds/arm9/source/fat/io_nmmc.h54
-rw-r--r--backends/platform/ds/arm9/source/fat/io_sccf.c174
-rw-r--r--backends/platform/ds/arm9/source/fat/io_sccf.h50
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd.c186
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd.h58
-rw-r--r--backends/platform/ds/arm9/source/fat/io_scsd_asm.s1042
22 files changed, 15180 insertions, 0 deletions
diff --git a/backends/platform/ds/arm9/source/fat/disc_io.c b/backends/platform/ds/arm9/source/fat/disc_io.c
new file mode 100644
index 0000000000..f2fbee5d65
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/disc_io.c
@@ -0,0 +1,844 @@
+/*
+
+ disc_io.c
+
+ uniformed io-interface to work with Chishm's FAT library
+
+ Written by MightyMax
+
+ Modified by Chishm:
+ 2005-11-06
+ * Added WAIT_CR modifications for NDS
+
+ Modified by www.neoflash.com:
+ 2006-02-03
+ * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
+ for the given interface and stop code being linked to the binary
+
+ * Added support for MK2 MMC interface
+
+ * Added disc_Cache* functions
+
+ Modified by Chishm:
+ 2006-02-05
+ * Added Supercard SD support
+
+ Modified by CyteX:
+ 2006-02-26
+ * Added EFA2 support
+*/
+
+#include "disc_io.h"
+
+#ifdef NDS
+ #include <nds.h>
+#endif
+
+
+// Include known io-interfaces:
+#ifdef SUPPORT_MPCF
+ #include "io_mpcf.h"
+#endif
+
+#ifdef SUPPORT_M3CF
+ #include "io_m3cf.h"
+#endif
+
+#ifdef SUPPORT_M3SD
+ #include "io_m3sd.h"
+#endif
+
+#ifdef SUPPORT_SCCF
+ #include "io_sccf.h"
+#endif
+
+#ifdef SUPPORT_SCSD
+ #include "io_scsd.h"
+#endif
+
+#ifdef SUPPORT_FCSR
+ #include "io_fcsr.h"
+#endif
+
+#ifdef SUPPORT_NMMC
+ #include "io_nmmc.h"
+#endif
+
+#ifdef SUPPORT_EFA2
+ #include "io_efa2.h"
+#endif
+
+// Keep a pointer to the active interface
+LPIO_INTERFACE active_interface = 0;
+
+
+/*
+
+ Disc Cache functions
+ 2006-02-03:
+ Added by www.neoflash.com
+
+*/
+
+#ifdef DISC_CACHE
+
+#include <string.h>
+
+#define CACHE_FREE 0xFFFFFFFF
+
+static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ];
+
+static struct {
+ u32 sector;
+ u32 dirty;
+ u32 count;
+} cache[ DISC_CACHE_COUNT ];
+
+static u32 disc_CacheFind(u32 sector) {
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector == sector )
+ return i;
+ }
+
+ return CACHE_FREE;
+}
+
+static u32 disc_CacheFindFree(void) {
+
+ u32 i = 0, j;
+ u32 count = -1;
+
+ for( j = 0; j < DISC_CACHE_COUNT; j++ ) {
+
+ if( cache[ j ].sector == CACHE_FREE ) {
+ i = j;
+ break;
+ }
+
+ if( cache[ j ].count < count ) {
+ count = cache[ j ].count;
+ i = j;
+ }
+ }
+
+ if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) {
+
+ active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] );
+ /* todo: handle write error here */
+
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+ return i;
+}
+
+void disc_CacheInit(void) {
+
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+}
+
+bool disc_CacheFlush(void) {
+
+ u32 i;
+
+ if( !active_interface ) return false;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) {
+ if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+
+ cache[ i ].dirty = 0;
+ }
+ }
+ return true;
+}
+
+bool disc_CacheReadSector( void *buffer, u32 sector) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache[ i ].sector = sector;
+ if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)&cacheBuffer[ i * 512 ]
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( buffer, &cacheBuffer[ i * 512 ], 512 );
+#endif
+ cache[ i ].count++;
+ return true;
+}
+
+bool disc_CacheWriteSector( void *buffer, u32 sector ) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache [ i ].sector = sector;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)buffer;
+ DMA3_DEST = (u32)&cacheBuffer[ i * 512 ];
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( &cacheBuffer[ i * 512 ], buffer, 512 );
+#endif
+ cache[ i ].dirty=1;
+ cache[ i ].count++;
+ return true;
+}
+
+#endif
+
+/*
+
+ Hardware level disc funtions
+
+*/
+
+bool disc_setGbaSlotInterface (void)
+{
+ // If running on an NDS, make sure the correct CPU can access
+ // the GBA cart. First implemented by SaTa.
+#ifdef NDS
+ #ifdef ARM9
+// WAIT_CR &= ~(0x8080);
+ #endif
+ #ifdef ARM7
+// WAIT_CR |= (0x8080);
+ #endif
+#endif
+
+#ifdef SUPPORT_M3CF
+ // check if we have a M3 perfect CF plugged in
+ active_interface = M3CF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_M3SD
+ // check if we have a M3 perfect SD plugged in
+ active_interface = M3SD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 SD as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_MPCF
+ // check if we have a GBA Movie Player plugged in
+ active_interface = MPCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set GBAMP as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCCF
+ // check if we have a SuperCard CF plugged in
+ active_interface = SCCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCSD
+ // check if we have a SuperCard SD plugged in
+ active_interface = SCSD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC SD as default IO
+ return true ;
+ } ;
+#endif
+
+
+#ifdef SUPPORT_EFA2
+ // check if we have a EFA2 plugged in
+ active_interface = EFA2_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_FCSR
+ // check if we have a GBA Flash Cart plugged in
+ active_interface = FCSR_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set FC as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+
+#ifdef NDS
+// Check the DS card slot for a valid memory card interface
+// If an interface is found, it is set as the default interace
+// and it returns true. Otherwise the default interface is left
+// untouched and it returns false.
+bool disc_setDsSlotInterface (void)
+{
+#ifdef ARM9
+ WAIT_CR &= ~(1<<11);
+#endif
+#ifdef ARM7
+ WAIT_CR |= (1<<11);
+#endif
+
+#ifdef SUPPORT_NMMC
+ // check if we have a Neoflash MK2 / MK3 plugged in
+ active_interface = NMMC_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set Neoflash MK2 / MK3 as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+#endif
+
+
+bool disc_Init(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheInit();
+#endif
+
+
+ if (active_interface != 0) {
+ return true;
+ }
+
+#ifdef NDS
+ if (disc_setDsSlotInterface()) {
+ return true;
+ }
+#endif
+
+ if (disc_setGbaSlotInterface()) {
+ return true;
+ }
+
+ // could not find a working IO Interface
+ active_interface = 0 ;
+ return false ;
+}
+
+bool disc_IsInserted(void)
+{
+ if (active_interface) return active_interface->fn_IsInserted() ;
+ return false ;
+}
+
+bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheReadSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_ReadSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheWriteSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_WriteSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_ClearStatus(void)
+{
+ if (active_interface) return active_interface->fn_ClearStatus() ;
+ return false ;
+}
+
+bool disc_Shutdown(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheFlush();
+#endif
+ if (active_interface) active_interface->fn_Shutdown() ;
+ active_interface = 0 ;
+ return true ;
+}
+
+u32 disc_HostType (void)
+{
+ if (active_interface) {
+ return active_interface->ul_ioType;
+ } else {
+ return 0;
+ }
+}
+
+/*
+
+ disc_io.c
+
+ uniformed io-interface to work with Chishm's FAT library
+
+ Written by MightyMax
+
+ Modified by Chishm:
+ 2005-11-06
+ * Added WAIT_CR modifications for NDS
+
+ Modified by www.neoflash.com:
+ 2006-02-03
+ * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
+ for the given interface and stop code being linked to the binary
+
+ * Added support for MK2 MMC interface
+
+ * Added disc_Cache* functions
+
+ Modified by Chishm:
+ 2006-02-05
+ * Added Supercard SD support
+
+ Modified by CyteX:
+ 2006-02-26
+ * Added EFA2 support
+*/
+
+#include "disc_io.h"
+
+#ifdef NDS
+ #include <nds.h>
+#endif
+
+
+// Include known io-interfaces:
+#ifdef SUPPORT_MPCF
+ #include "io_mpcf.h"
+#endif
+
+#ifdef SUPPORT_M3CF
+ #include "io_m3cf.h"
+#endif
+
+#ifdef SUPPORT_M3SD
+ #include "io_m3sd.h"
+#endif
+
+#ifdef SUPPORT_SCCF
+ #include "io_sccf.h"
+#endif
+
+#ifdef SUPPORT_SCSD
+ #include "io_scsd.h"
+#endif
+
+#ifdef SUPPORT_FCSR
+ #include "io_fcsr.h"
+#endif
+
+#ifdef SUPPORT_NMMC
+ #include "io_nmmc.h"
+#endif
+
+#ifdef SUPPORT_EFA2
+ #include "io_efa2.h"
+#endif
+
+// Keep a pointer to the active interface
+LPIO_INTERFACE active_interface = 0;
+
+
+/*
+
+ Disc Cache functions
+ 2006-02-03:
+ Added by www.neoflash.com
+
+*/
+
+#ifdef DISC_CACHE
+
+#include <string.h>
+
+#define CACHE_FREE 0xFFFFFFFF
+
+static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ];
+
+static struct {
+ u32 sector;
+ u32 dirty;
+ u32 count;
+} cache[ DISC_CACHE_COUNT ];
+
+static u32 disc_CacheFind(u32 sector) {
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector == sector )
+ return i;
+ }
+
+ return CACHE_FREE;
+}
+
+static u32 disc_CacheFindFree(void) {
+
+ u32 i = 0, j;
+ u32 count = -1;
+
+ for( j = 0; j < DISC_CACHE_COUNT; j++ ) {
+
+ if( cache[ j ].sector == CACHE_FREE ) {
+ i = j;
+ break;
+ }
+
+ if( cache[ j ].count < count ) {
+ count = cache[ j ].count;
+ i = j;
+ }
+ }
+
+ if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) {
+
+ active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] );
+ /* todo: handle write error here */
+
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+ return i;
+}
+
+void disc_CacheInit(void) {
+
+ u32 i;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ cache[ i ].sector = CACHE_FREE;
+ cache[ i ].dirty = 0;
+ cache[ i ].count = 0;
+ }
+
+}
+
+bool disc_CacheFlush(void) {
+
+ u32 i;
+
+ if( !active_interface ) return false;
+
+ for( i = 0; i < DISC_CACHE_COUNT; i++ ) {
+ if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) {
+ if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+
+ cache[ i ].dirty = 0;
+ }
+ }
+ return true;
+}
+
+bool disc_CacheReadSector( void *buffer, u32 sector) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache[ i ].sector = sector;
+ if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false )
+ return false;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)&cacheBuffer[ i * 512 ]
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( buffer, &cacheBuffer[ i * 512 ], 512 );
+#endif
+ cache[ i ].count++;
+ return true;
+}
+
+bool disc_CacheWriteSector( void *buffer, u32 sector ) {
+ u32 i = disc_CacheFind( sector );
+ if( i == CACHE_FREE ) {
+ i = disc_CacheFindFree();
+ cache [ i ].sector = sector;
+ }
+#ifdef DISK_CACHE_DMA
+ DMA3_SRC = (u32)buffer;
+ DMA3_DEST = (u32)&cacheBuffer[ i * 512 ];
+ DMA3_CR = 128 | DMA_COPY_WORDS;
+#else
+ memcpy( &cacheBuffer[ i * 512 ], buffer, 512 );
+#endif
+ cache[ i ].dirty=1;
+ cache[ i ].count++;
+ return true;
+}
+
+#endif
+
+/*
+
+ Hardware level disc funtions
+
+*/
+
+bool disc_setGbaSlotInterface (void)
+{
+ // If running on an NDS, make sure the correct CPU can access
+ // the GBA cart. First implemented by SaTa.
+#ifdef NDS
+ #ifdef ARM9
+// WAIT_CR &= ~(0x8080);
+ #endif
+ #ifdef ARM7
+// WAIT_CR |= (0x8080);
+ #endif
+#endif
+
+#ifdef SUPPORT_M3CF
+ // check if we have a M3 perfect CF plugged in
+ active_interface = M3CF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_M3SD
+ // check if we have a M3 perfect SD plugged in
+ active_interface = M3SD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set M3 SD as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_MPCF
+ // check if we have a GBA Movie Player plugged in
+ active_interface = MPCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set GBAMP as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCCF
+ // check if we have a SuperCard CF plugged in
+ active_interface = SCCF_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC CF as default IO
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_SCSD
+ // check if we have a SuperCard SD plugged in
+ active_interface = SCSD_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set SC SD as default IO
+ return true ;
+ } ;
+#endif
+
+
+#ifdef SUPPORT_EFA2
+ // check if we have a EFA2 plugged in
+ active_interface = EFA2_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ return true ;
+ } ;
+#endif
+
+#ifdef SUPPORT_FCSR
+ // check if we have a GBA Flash Cart plugged in
+ active_interface = FCSR_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set FC as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+
+#ifdef NDS
+// Check the DS card slot for a valid memory card interface
+// If an interface is found, it is set as the default interace
+// and it returns true. Otherwise the default interface is left
+// untouched and it returns false.
+bool disc_setDsSlotInterface (void)
+{
+#ifdef ARM9
+ WAIT_CR &= ~(1<<11);
+#endif
+#ifdef ARM7
+ WAIT_CR |= (1<<11);
+#endif
+
+#ifdef SUPPORT_NMMC
+ // check if we have a Neoflash MK2 / MK3 plugged in
+ active_interface = NMMC_GetInterface() ;
+ if (active_interface->fn_StartUp())
+ {
+ // set Neoflash MK2 / MK3 as default IO
+ return true ;
+ } ;
+#endif
+
+ return false;
+}
+#endif
+
+
+bool disc_Init(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheInit();
+#endif
+
+
+ if (active_interface != 0) {
+ return true;
+ }
+
+#ifdef NDS
+ if (disc_setDsSlotInterface()) {
+ return true;
+ }
+#endif
+
+ if (disc_setGbaSlotInterface()) {
+ return true;
+ }
+
+ // could not find a working IO Interface
+ active_interface = 0 ;
+ return false ;
+}
+
+bool disc_IsInserted(void)
+{
+ if (active_interface) return active_interface->fn_IsInserted() ;
+ return false ;
+}
+
+bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheReadSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_ReadSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer)
+{
+#ifdef DISC_CACHE
+ u8 *p=(u8*)buffer;
+ u32 i;
+ u32 inumSecs=numSecs;
+ if(numSecs==0)
+ inumSecs=256;
+ for( i = 0; i<inumSecs; i++) {
+ if( disc_CacheWriteSector( &p[i*512], sector + i ) == false )
+ return false;
+ }
+ return true;
+#else
+ if (active_interface) return active_interface->fn_WriteSectors(sector,numSecs,buffer) ;
+ return false ;
+#endif
+}
+
+bool disc_ClearStatus(void)
+{
+ if (active_interface) return active_interface->fn_ClearStatus() ;
+ return false ;
+}
+
+bool disc_Shutdown(void)
+{
+#ifdef DISC_CACHE
+ disc_CacheFlush();
+#endif
+ if (active_interface) active_interface->fn_Shutdown() ;
+ active_interface = 0 ;
+ return true ;
+}
+
+u32 disc_HostType (void)
+{
+ if (active_interface) {
+ return active_interface->ul_ioType;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/backends/platform/ds/arm9/source/fat/disc_io.h b/backends/platform/ds/arm9/source/fat/disc_io.h
new file mode 100644
index 0000000000..f647f9ac02
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/disc_io.h
@@ -0,0 +1,364 @@
+#ifndef DISC_IO_H
+#define DISC_IO_H
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _CF_USE_DMA
+
+// Allow buffers not aligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+//#define _CF_ALLOW_UNALIGNED
+
+// Device support options, added by www.neoflash.com
+
+#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support
+#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support
+#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support
+#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support
+#define SUPPORT_SCCF // comment out this line to remove Supercard CF support
+#define SUPPORT_SCSD // comment out this line to remove Supercard SD support
+//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support
+#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support
+
+// Disk caching options, added by www.neoflash.com
+// Each additional sector cache uses 512 bytes of memory
+// Disk caching is disabled on GBA to conserve memory
+
+#define DISC_CACHE // uncomment this line to enable disc caching
+#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector)
+//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned
+
+
+//----------------------------------------------------------------------
+
+#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED
+ #error You can't use both DMA and unaligned memory
+#endif
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+// Disable NDS specific hardware and features if running on a GBA
+#ifndef NDS
+ #undef SUPPORT_NMMC
+ #undef DISC_CACHE
+#endif
+
+/*
+
+ Interface for host program
+
+*/
+
+#define BYTE_PER_READ 512
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------------------------
+disc_Init
+Detects the inserted hardware and initialises it if necessary
+bool return OUT: true if a suitable device was found
+-----------------------------------------------------------------*/
+extern bool disc_Init(void) ;
+
+/*-----------------------------------------------------------------
+disc_IsInserted
+Is a usable disc inserted?
+bool return OUT: true if a disc is inserted
+-----------------------------------------------------------------*/
+extern bool disc_IsInserted(void) ;
+
+/*-----------------------------------------------------------------
+disc_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on disc to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on disc to write
+u8 numSecs IN: number of 512 byte sectors to write ,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_ClearStatus
+Tries to make the disc go back to idle mode
+bool return OUT: true if the disc is idle
+-----------------------------------------------------------------*/
+extern bool disc_ClearStatus(void) ;
+
+/*-----------------------------------------------------------------
+disc_Shutdown
+unload the disc interface
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_Shutdown(void) ;
+
+/*-----------------------------------------------------------------
+disc_HostType
+Returns a unique u32 number identifying the host type
+u32 return OUT: 0 if no host initialised, else the identifier of
+ the host
+-----------------------------------------------------------------*/
+extern u32 disc_HostType(void);
+
+/*-----------------------------------------------------------------
+disc_CacheFlush
+Flushes any cache writes to disc
+bool return OUT: true if successful, false if an error occurs
+Added by www.neoflash.com
+-----------------------------------------------------------------*/
+#ifdef DISC_CACHE
+extern bool disc_CacheFlush(void);
+#else
+static inline bool disc_CacheFlush(void)
+{
+ return true;
+}
+#endif // DISC_CACHE
+
+
+/*
+
+ Interface for IO libs
+
+*/
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+
+typedef struct {
+ unsigned long ul_ioType ;
+ unsigned long ul_Features ;
+ FN_MEDIUM_STARTUP fn_StartUp ;
+ FN_MEDIUM_ISINSERTED fn_IsInserted ;
+ FN_MEDIUM_READSECTORS fn_ReadSectors ;
+ FN_MEDIUM_WRITESECTORS fn_WriteSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_ClearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_Shutdown ;
+} IO_INTERFACE, *LPIO_INTERFACE ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // define DISC_IO_H
+#ifndef DISC_IO_H
+#define DISC_IO_H
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _CF_USE_DMA
+
+// Allow buffers not aligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+//#define _CF_ALLOW_UNALIGNED
+
+// Device support options, added by www.neoflash.com
+
+#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support
+#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support
+#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support
+#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support
+#define SUPPORT_SCCF // comment out this line to remove Supercard CF support
+#define SUPPORT_SCSD // comment out this line to remove Supercard SD support
+//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support
+#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support
+
+// Disk caching options, added by www.neoflash.com
+// Each additional sector cache uses 512 bytes of memory
+// Disk caching is disabled on GBA to conserve memory
+
+#define DISC_CACHE // uncomment this line to enable disc caching
+#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector)
+//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned
+
+
+//----------------------------------------------------------------------
+
+#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED
+ #error You can't use both DMA and unaligned memory
+#endif
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+// Disable NDS specific hardware and features if running on a GBA
+#ifndef NDS
+ #undef SUPPORT_NMMC
+ #undef DISC_CACHE
+#endif
+
+/*
+
+ Interface for host program
+
+*/
+
+#define BYTE_PER_READ 512
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------------------------
+disc_Init
+Detects the inserted hardware and initialises it if necessary
+bool return OUT: true if a suitable device was found
+-----------------------------------------------------------------*/
+extern bool disc_Init(void) ;
+
+/*-----------------------------------------------------------------
+disc_IsInserted
+Is a usable disc inserted?
+bool return OUT: true if a disc is inserted
+-----------------------------------------------------------------*/
+extern bool disc_IsInserted(void) ;
+
+/*-----------------------------------------------------------------
+disc_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on disc to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on disc to write
+u8 numSecs IN: number of 512 byte sectors to write ,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ;
+#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer)
+
+/*-----------------------------------------------------------------
+disc_ClearStatus
+Tries to make the disc go back to idle mode
+bool return OUT: true if the disc is idle
+-----------------------------------------------------------------*/
+extern bool disc_ClearStatus(void) ;
+
+/*-----------------------------------------------------------------
+disc_Shutdown
+unload the disc interface
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+extern bool disc_Shutdown(void) ;
+
+/*-----------------------------------------------------------------
+disc_HostType
+Returns a unique u32 number identifying the host type
+u32 return OUT: 0 if no host initialised, else the identifier of
+ the host
+-----------------------------------------------------------------*/
+extern u32 disc_HostType(void);
+
+/*-----------------------------------------------------------------
+disc_CacheFlush
+Flushes any cache writes to disc
+bool return OUT: true if successful, false if an error occurs
+Added by www.neoflash.com
+-----------------------------------------------------------------*/
+#ifdef DISC_CACHE
+extern bool disc_CacheFlush(void);
+#else
+static inline bool disc_CacheFlush(void)
+{
+ return true;
+}
+#endif // DISC_CACHE
+
+
+/*
+
+ Interface for IO libs
+
+*/
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+
+typedef struct {
+ unsigned long ul_ioType ;
+ unsigned long ul_Features ;
+ FN_MEDIUM_STARTUP fn_StartUp ;
+ FN_MEDIUM_ISINSERTED fn_IsInserted ;
+ FN_MEDIUM_READSECTORS fn_ReadSectors ;
+ FN_MEDIUM_WRITESECTORS fn_WriteSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_ClearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_Shutdown ;
+} IO_INTERFACE, *LPIO_INTERFACE ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // define DISC_IO_H
diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.c b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c
new file mode 100644
index 0000000000..b3b2858e41
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c
@@ -0,0 +1,6660 @@
+/*
+ gba_nds_fat.c
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+// Includes
+
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include <string.h>
+#ifdef NDS
+ #include <nds/ipc.h> // Time on the NDS
+#endif
+//----------------------------------------------------------------
+// Data types
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//----------------------------------------------------------------
+// NDS memory access control register
+#ifdef NDS
+ #ifndef WAIT_CR
+ #define WAIT_CR (*(vu16*)0x04000204)
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// Appropriate placement of CF functions and data
+#ifdef NDS
+ #define _VARS_IN_RAM
+#else
+ #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
+#endif
+
+
+//-----------------------------------------------------------------
+// FAT constants
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x0000
+#define CLUSTER_FIRST 0x0002
+
+#define FILE_LAST 0x00
+#define FILE_FREE 0xE5
+
+#define FAT16_ROOT_DIR_CLUSTER 0x00
+
+
+//-----------------------------------------------------------------
+// long file name constants
+#define LFN_END 0x40
+#define LFN_DEL 0x80
+
+//-----------------------------------------------------------------
+// Data Structures
+
+// Take care of packing for GCC - it doesn't obey pragma pack()
+// properly for ARM targets.
+#ifdef __GNUC__
+ #define __PACKED __attribute__ ((__packed__))
+#else
+ #define __PACKED
+ #pragma pack(1)
+#endif
+
+// Boot Sector - must be packed
+typedef struct
+{
+ u8 jmpBoot[3];
+ u8 OEMName[8];
+ // BIOS Parameter Block
+ u16 bytesPerSector;
+ u8 sectorsPerCluster;
+ u16 reservedSectors;
+ u8 numFATs;
+ u16 rootEntries;
+ u16 numSectorsSmall;
+ u8 mediaDesc;
+ u16 sectorsPerFAT;
+ u16 sectorsPerTrk;
+ u16 numHeads;
+ u32 numHiddenSectors;
+ u32 numSectors;
+ union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
+ {
+ struct
+ {
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[448];
+ } __PACKED fat16;
+ struct
+ {
+ // FAT32 extended block
+ u32 sectorsPerFAT32;
+ u16 extFlags;
+ u16 fsVer;
+ u32 rootClus;
+ u16 fsInfo;
+ u16 bkBootSec;
+ u8 reserved[12];
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[420];
+ } __PACKED fat32;
+ } __PACKED extBlock;
+
+ u16 bootSig;
+
+} __PACKED BOOT_SEC;
+
+// Directory entry - must be packed
+typedef struct
+{
+ u8 name[8];
+ u8 ext[3];
+ u8 attrib;
+ u8 reserved;
+ u8 cTime_ms;
+ u16 cTime;
+ u16 cDate;
+ u16 aDate;
+ u16 startClusterHigh;
+ u16 mTime;
+ u16 mDate;
+ u16 startCluster;
+ u32 fileSize;
+} __PACKED DIR_ENT;
+
+// Long file name directory entry - must be packed
+typedef struct
+{
+ u8 ordinal; // Position within LFN
+ u16 char0;
+ u16 char1;
+ u16 char2;
+ u16 char3;
+ u16 char4;
+ u8 flag; // Should be equal to ATTRIB_LFN
+ u8 reserved1; // Always 0x00
+ u8 checkSum; // Checksum of short file name (alias)
+ u16 char5;
+ u16 char6;
+ u16 char7;
+ u16 char8;
+ u16 char9;
+ u16 char10;
+ u16 reserved2; // Always 0x0000
+ u16 char11;
+ u16 char12;
+} __PACKED DIR_ENT_LFN;
+
+const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+// End of packed structs
+#ifdef __PACKED
+ #undef __PACKED
+#endif
+#ifndef __GNUC__
+ #pragma pack()
+#endif
+
+//-----------------------------------------------------------------
+// Global Variables
+
+// _VARS_IN_RAM variables are stored in the largest section of WRAM
+// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
+
+// Files
+_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN];
+
+// Long File names
+_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
+bool lfnExists;
+
+// Locations on card
+int filesysRootDir;
+int filesysRootDirClus;
+int filesysFAT;
+int filesysSecPerFAT;
+int filesysNumSec;
+int filesysData;
+int filesysBytePerSec;
+int filesysSecPerClus;
+int filesysBytePerClus;
+
+FS_TYPE filesysType = FS_UNKNOWN;
+u32 filesysTotalSize;
+
+// Info about FAT
+u32 fatLastCluster;
+u32 fatFirstFree;
+
+// fatBuffer used to reduce wear on the CF card from multiple writes
+_VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
+u32 fatBufferCurSector;
+
+// Current working directory
+u32 curWorkDirCluster;
+
+// Position of the directory entry last retreived with FAT_GetDirEntry
+u32 wrkDirCluster;
+int wrkDirSector;
+int wrkDirOffset;
+
+// Global sector buffer to save on stack space
+_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];
+
+//-----------------------------------------------------------------
+// Functions contained in this file - predeclarations
+char ucase (char character);
+u16 getRTCtoFileTime (void);
+u16 getRTCtoFileDate (void);
+
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
+bool FAT_ClearLinks (u32 cluster);
+DIR_ENT FAT_DirEntFromPath (const char* path);
+u32 FAT_FirstFreeCluster(void);
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
+u32 FAT_LinkFreeCluster(u32 cluster);
+u32 FAT_NextCluster(u32 cluster);
+bool FAT_WriteFatEntry (u32 cluster, u32 value);
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);
+
+bool FAT_InitFiles (void);
+bool FAT_FreeFiles (void);
+int FAT_remove (const char* path);
+bool FAT_chdir (const char* path);
+FILE_TYPE FAT_FindFirstFile (char* filename);
+FILE_TYPE FAT_FindNextFile (char* filename);
+FILE_TYPE FAT_FileExists (const char* filename);
+bool FAT_GetAlias (char* alias);
+bool FAT_GetLongFilename (char* filename);
+u32 FAT_GetFileSize (void);
+u32 FAT_GetFileCluster (void);
+
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+bool FAT_fclose (FAT_FILE* file);
+bool FAT_feof(FAT_FILE* file);
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+u32 FAT_ftell (FAT_FILE* file);
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+char FAT_fgetc (FAT_FILE* file);
+char FAT_fputc (char c, FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+ucase
+Returns the uppercase version of the given char
+char IN: a character
+char return OUT: uppercase version of character
+-----------------------------------------------------------------*/
+char ucase (char character)
+{
+ if ((character > 0x60) && (character < 0x7B))
+ character = character - 0x20;
+ return (character);
+}
+
+
+/*-----------------------------------------------------------------
+getRTCtoFileTime and getRTCtoFileDate
+Returns the time / date in Dir Entry styled format
+u16 return OUT: time / date in Dir Entry styled format
+-----------------------------------------------------------------*/
+u16 getRTCtoFileTime (void)
+{
+#ifdef NDS
+ return (
+ ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
+ ( (IPC->rtc_minutes & 0x3F) << 5) |
+ ( (IPC->rtc_seconds >> 1) & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+u16 getRTCtoFileDate (void)
+{
+#ifdef NDS
+ return (
+ ( ((IPC->rtc_year + 20) & 0x7F) <<9) |
+ ( (IPC->rtc_month & 0xF) << 5) |
+ (IPC->rtc_day & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+
+/*-----------------------------------------------------------------
+Disc level FAT routines
+-----------------------------------------------------------------*/
+#define FAT_ClustToSect(m) \
+ (((m-2) * filesysSecPerClus) + filesysData)
+
+/*-----------------------------------------------------------------
+FAT_NextCluster
+Internal function - gets the cluster linked from input cluster
+-----------------------------------------------------------------*/
+u32 FAT_NextCluster(u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster = ((u8*)fatBuffer)[offset];
+ offset++;
+
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster |= (((u8*)fatBuffer)[offset]) << 8;
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = ((u16*)fatBuffer)[offset];
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntry
+Internal function - writes FAT information about a cluster
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntry (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ {
+ return false;
+ }
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ ((u16*)fatBuffer)[offset] = (value & 0xFFFF);
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ (((u32*)fatBuffer)[offset]) = value;
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+
+ return true;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ReadWriteFatEntryBuffered
+Internal function - writes FAT information about a cluster to a
+ buffer that should then be flushed to disc using
+ FAT_WriteFatEntryFlushBuffer()
+ Call FAT_WriteFatEntry first so as not to ruin the disc.
+ Also returns the entry being replaced
+-----------------------------------------------------------------*/
+u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+ u32 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return CLUSTER_FREE;
+
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ oldValue = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ if (oldValue >= 0x0FF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u16*)fatBuffer)[offset];
+ ((u16*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0xFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u32*)fatBuffer)[offset];
+ ((u32*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0x0FFFFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ default:
+ oldValue = CLUSTER_FREE;
+ break;
+ }
+
+ return oldValue;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntryFlushBuffer
+Flush the FAT buffer back to the disc
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntryFlushBuffer (void)
+{
+ // write the buffer disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ {
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FirstFreeCluster
+Internal function - gets the first available free cluster
+-----------------------------------------------------------------*/
+u32 FAT_FirstFreeCluster(void)
+{
+ // Start at first valid cluster
+ if (fatFirstFree < CLUSTER_FIRST)
+ fatFirstFree = CLUSTER_FIRST;
+
+ while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
+ {
+ fatFirstFree++;
+ }
+ if (fatFirstFree > fatLastCluster)
+ {
+ return CLUSTER_EOF;
+ }
+ return fatFirstFree;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_LinkFreeCluster
+Internal function - gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+-----------------------------------------------------------------*/
+u32 FAT_LinkFreeCluster(u32 cluster)
+{
+ u32 firstFree;
+ u32 curLink;
+
+ if (cluster > fatLastCluster)
+ {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = FAT_NextCluster (cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
+ {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = FAT_FirstFreeCluster();
+
+ // If couldn't get a free cluster then return
+ if (firstFree == CLUSTER_EOF)
+ {
+ return CLUSTER_FREE;
+ }
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
+ {
+ // Update the linked from FAT entry
+ FAT_WriteFatEntry (cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ FAT_WriteFatEntry (firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+#endif
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ClearLinks
+Internal function - frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool FAT_ClearLinks (u32 cluster)
+{
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return false;
+
+ // Store next cluster before erasing the link
+ nextCluster = FAT_NextCluster (cluster);
+
+ // Erase the link
+ FAT_WriteFatEntry (cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
+ {
+ cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
+ }
+
+ // Flush fat write buffer
+ FAT_WriteFatEntryFlushBuffer ();
+
+ return true;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void)
+{
+ int i;
+ int bootSector;
+ BOOT_SEC* bootSec;
+
+ if (!disc_Init())
+ {
+ return (false);
+ }
+
+ // Read first sector of CF card
+ if ( !disc_ReadSector(0, globalBuffer)) {
+ return false;
+ }
+
+ // Make sure it is a valid MBR or boot sector
+ if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
+ return false;
+ }
+
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
+ {
+ bootSector = 0;
+ }
+ // Check for FAT32
+ else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
+ {
+ bootSector = 0;
+ }
+ else // This is an MBR
+ {
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE)
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
+
+ // Go to first valid partition
+ if ( i != 0x1FE) // Make sure it found a partition
+ {
+ bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ bootSec = (BOOT_SEC*) globalBuffer;
+ if (!disc_ReadSector (bootSector, bootSec)) {
+ return false;
+ }
+
+ // Store required information about the file system
+ if (bootSec->sectorsPerFAT != 0)
+ {
+ filesysSecPerFAT = bootSec->sectorsPerFAT;
+ }
+ else
+ {
+ filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
+ }
+
+ if (bootSec->numSectorsSmall != 0)
+ {
+ filesysNumSec = bootSec->numSectorsSmall;
+ }
+ else
+ {
+ filesysNumSec = bootSec->numSectors;
+ }
+
+ filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes
+ filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
+ filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
+ filesysFAT = bootSector + bootSec->reservedSectors;
+
+ filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);
+ filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
+
+ filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;
+
+ // Store info about FAT
+ fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
+ fatFirstFree = CLUSTER_FIRST;
+ fatBufferCurSector = 0;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+
+ if (fatLastCluster < 4085)
+ {
+ filesysType = FS_FAT12; // FAT12 volume - unsupported
+ }
+ else if (fatLastCluster < 65525)
+ {
+ filesysType = FS_FAT16; // FAT16 volume
+ }
+ else
+ {
+ filesysType = FS_FAT32; // FAT32 volume
+ }
+
+ if (filesysType != FS_FAT32)
+ {
+ filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
+ }
+ else // Set up for the FAT32 way
+ {
+ filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
+ // Check if FAT mirroring is enabled
+ if (!(bootSec->extBlock.fat32.extFlags & 0x80))
+ {
+ // Use the active FAT
+ filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
+ }
+ }
+
+ // Set current directory to the root
+ curWorkDirCluster = filesysRootDirClus;
+ wrkDirCluster = filesysRootDirClus;
+ wrkDirSector = 0;
+ wrkDirOffset = 0;
+
+ // Set all files to free
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ openFiles[i].inUse = false;
+ }
+
+ // No long filenames so far
+ lfnExists = false;
+ for (i = 0; i < MAX_FILENAME_LENGTH; i++)
+ {
+ lfnName[i] = '\0';
+ }
+
+ return (true);
+}
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void)
+{
+ int i;
+
+ // Close all open files
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ if (openFiles[i].inUse == true)
+ {
+ FAT_fclose(&openFiles[i]);
+ }
+ }
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ // Clear card status
+ disc_ClearStatus();
+
+ // Return status of card
+ return disc_IsInserted();
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetDirEntry
+Return the file info structure of the next valid file entry
+u32 dirCluster: IN cluster of subdirectory table
+int entry: IN the desired file entry
+int origin IN: relative position of the entry
+DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
+ the entry does not exist.
+-----------------------------------------------------------------*/
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
+{
+ DIR_ENT dir;
+ DIR_ENT_LFN lfn;
+ int firstSector = 0;
+ bool notFound = false;
+ bool found = false;
+ int maxSectors;
+ int lfnPos, aliasPos;
+ u8 lfnChkSum, chkSum;
+
+ int i;
+
+ dir.name[0] = FILE_FREE; // default to no file found
+ dir.attrib = 0x00;
+
+ // Check if fat has been initialised
+ if (filesysBytePerSec == 0)
+ {
+ return (dir);
+ }
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ break;
+ case SEEK_CUR: // Don't change anything
+ break;
+ case SEEK_END: // Find entry signifying end of directory
+ // Subtraction will never reach 0, so it keeps going
+ // until reaches end of directory
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ entry = -1;
+ break;
+ default:
+ return dir;
+ }
+
+ lfnChkSum = 0;
+ maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+
+ // Scan Dir for correct entry
+ firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ found = false;
+ notFound = false;
+ do {
+ wrkDirOffset++;
+ if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ wrkDirOffset = 0;
+ wrkDirSector++;
+ if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ wrkDirSector = 0;
+ wrkDirCluster = FAT_NextCluster(wrkDirCluster);
+ if (wrkDirCluster == CLUSTER_EOF)
+ {
+ notFound = true;
+ }
+ firstSector = FAT_ClustToSect(wrkDirCluster);
+ }
+ else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
+ {
+ notFound = true; // Got to end of root dir
+ }
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ }
+ dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
+ if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
+ {
+ entry--;
+ if (lfnExists)
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 11; aliasPos++)
+ {
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
+ }
+ if (chkSum != lfnChkSum)
+ {
+ lfnExists = false;
+ lfnName[0] = '\0';
+ }
+ }
+ if (entry == 0)
+ {
+ if (!lfnExists)
+ {
+ FAT_GetFilename (dir, lfnName);
+ }
+ found = true;
+ }
+ }
+ else if (dir.name[0] == FILE_LAST)
+ {
+ if (origin == SEEK_END)
+ {
+ found = true;
+ }
+ else
+ {
+ notFound = true;
+ }
+ }
+ else if (dir.attrib == ATTRIB_LFN)
+ {
+ lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
+ if (lfn.ordinal & LFN_DEL)
+ {
+ lfnExists = false;
+ }
+ else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
+ {
+ lfnExists = true;
+ lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
+ lfnChkSum = lfn.checkSum;
+ }
+ if (lfnChkSum != lfn.checkSum)
+ {
+ lfnExists = false;
+ }
+ if (lfnExists)
+ {
+ lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
+ for (i = 0; i < 13; i++) {
+ lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/;
+ }
+ }
+ }
+ } while (!found && !notFound);
+
+ // If no file is found, return FILE_FREE
+ if (notFound)
+ {
+ dir.name[0] = FILE_FREE;
+ }
+
+ return (dir);
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile.
+ If a long name doesn't exist, it returns the short name
+ instead.
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename)
+{
+ if (filename == NULL)
+ return false;
+
+ strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
+ filename[MAX_FILENAME_LENGTH - 1] = '\0';
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetFilename
+Get the alias (short name) of the file or directory stored in
+ dirEntry
+DIR_ENT dirEntry: IN a valid directory table entry
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
+{
+ int i=0;
+ int j=0;
+
+ alias[0] = '\0';
+ if (dirEntry.name[0] != FILE_FREE)
+ {
+ if (dirEntry.name[0] == '.')
+ {
+ alias[0] = '.';
+ if (dirEntry.name[1] == '.')
+ {
+ alias[1] = '.';
+ alias[2] = '\0';
+ }
+ else
+ {
+ alias[1] = '\0';
+ }
+ }
+ else
+ {
+ // Copy the filename from the dirEntry to the string
+ for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++)
+ {
+ alias[i] = dirEntry.name[i];
+ }
+ // Copy the extension from the dirEntry to the string
+ if (dirEntry.ext[0] != ' ')
+ {
+ alias[i++] = '.';
+ for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
+ {
+ alias[i++] = dirEntry.ext[j];
+ }
+ }
+ alias[i] = '\0';
+ }
+ }
+
+ return (alias[0] != '\0');
+}
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias)
+{
+ if (alias == NULL)
+ {
+ return false;
+ }
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonLight
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
+{
+ // Get the file
+ if (!FAT_FileExists(filename)) {
+ return 0xff;
+ }
+
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes
+
+ disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
+{
+ struct tm timeInfo;
+
+ timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970
+ timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january
+ timeInfo.tm_mday = fileDate & 0x1f; // Day of the month
+
+ timeInfo.tm_hour = fileTime >> 11; // hours past midnight
+ timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour
+ timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute
+
+ return mktime(&timeInfo);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_DirEntFromPath
+Finds the directory entry for a file or directory from a path
+Path separator is a forward slash /
+const char* path: IN null terminated string of path.
+DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
+ if the file was not found
+-----------------------------------------------------------------*/
+DIR_ENT FAT_DirEntFromPath (const char* path)
+{
+ int pathPos;
+ char name[MAX_FILENAME_LENGTH];
+ char alias[13];
+ int namePos;
+ bool found, notFound;
+ DIR_ENT dirEntry;
+ u32 dirCluster;
+ bool flagLFN, dotSeen;
+
+ // Start at beginning of path
+ pathPos = 0;
+
+ if (path[pathPos] == '/')
+ {
+ dirCluster = filesysRootDirClus; // Start at root directory
+ }
+ else
+ {
+ dirCluster = curWorkDirCluster; // Start at current working dir
+ }
+
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search until can't continue
+ found = false;
+ notFound = false;
+ while (!notFound && !found)
+ {
+ flagLFN = false;
+ // Copy name from path
+ namePos = 0;
+ if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
+ // Dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
+ // Double dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ name[namePos++] = '.';
+ pathPos++;
+ } else {
+ // Copy name from path
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+ dotSeen = false;
+ while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
+ {
+ name[namePos] = ucase(path[pathPos]);
+ if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (name[namePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ namePos++;
+ pathPos++;
+ }
+ // Check if a long filename was specified
+ if (namePos > 12)
+ {
+ flagLFN = true;
+ }
+ }
+
+ // Add end of string char
+ name[namePos] = '\0';
+
+ // Move through path to correct place
+ while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
+ pathPos++;
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search current Dir for correct entry
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
+ while ( !found && !notFound)
+ {
+ // Match filename
+ found = true;
+ for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(lfnName[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
+ {
+ found = false;
+ }
+
+ // Check against alias as well.
+ if (!found)
+ {
+ FAT_GetFilename(dirEntry, alias);
+ found = true;
+ for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(alias[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
+ {
+ found = false;
+ }
+ }
+
+ if (dirEntry.name[0] == FILE_FREE)
+ // Couldn't find specified file
+ {
+ found = false;
+ notFound = true;
+ }
+ if (!found && !notFound)
+ {
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
+ }
+ }
+
+ if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
+ // It has found a directory from within the path that needs to be followed
+ {
+ found = false;
+ dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ }
+ }
+
+ if (notFound)
+ {
+ dirEntry.name[0] = FILE_FREE;
+ dirEntry.attrib = 0x00;
+ }
+
+ return (dirEntry);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_AddDirEntry
+Creates a new dir entry for a file
+Path separator is a forward slash /
+const char* path: IN null terminated string of path to file.
+DIR_ENT newDirEntry IN: The directory entry to use.
+int file IN: The file being added (optional, use -1 if not used)
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
+{
+ char filename[MAX_FILENAME_LENGTH];
+ int filePos, pathPos, aliasPos;
+ char tempChar;
+ bool flagLFN, dotSeen;
+ char fileAlias[13] = {0};
+ int tailNum;
+
+ unsigned char chkSum = 0;
+
+ u32 oldWorkDirCluster;
+
+ DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
+ u32 dirCluster;
+ int secOffset;
+ int entryOffset;
+ int maxSectors;
+ u32 firstSector;
+
+ DIR_ENT_LFN lfnEntry;
+ int lfnPos = 0;
+
+ int dirEntryLength = 0;
+ int dirEntryRemain = 0;
+ u32 tempDirCluster;
+ int tempSecOffset;
+ int tempEntryOffset;
+ bool dirEndFlag = false;
+
+ int i;
+
+ // Store current working directory
+ oldWorkDirCluster = curWorkDirCluster;
+
+ // Find filename within path and change to correct directory
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+ flagLFN = false;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ filename[filePos] = '\0';
+ if (FAT_chdir(filename) == false)
+ {
+ curWorkDirCluster = oldWorkDirCluster;
+ return false; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ filename[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Skip over last slashes
+ while (path[pathPos] == '/')
+ pathPos++;
+
+ // Check if the filename has a leading "."
+ // If so, it is an LFN
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+
+ // Copy name from path
+ filePos = 0;
+ dotSeen = false;
+
+ while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
+ {
+ filename[filePos] = path[pathPos];
+ if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (filename[filePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ filePos++;
+ pathPos++;
+ if ((filePos > 8) && !dotSeen) {
+ flagLFN = true;
+ }
+ }
+
+ if (filePos == 0) // No filename
+ {
+ return false;
+ }
+
+ // Check if a long filename was specified
+ if (filePos > 12)
+ {
+ flagLFN = true;
+ }
+
+ // Check if extension is > 3 characters long
+ if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
+ flagLFN = true;
+ }
+
+ lfnPos = (filePos - 1) / 13;
+
+ // Add end of string char
+ filename[filePos++] = '\0';
+ // Clear remaining chars
+ while (filePos < MAX_FILENAME_LENGTH)
+ filename[filePos++] = 0x01; // Set for LFN compatibility
+
+
+ if (flagLFN)
+ {
+ // Generate short filename - always a 2 digit number for tail
+ // Get first 5 chars of alias from LFN
+ aliasPos = 0;
+ filePos = 0;
+ if (filename[filePos] == '.') {
+ filePos++;
+ }
+ for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ // Pad Alias with underscores
+ while (aliasPos < 5)
+ fileAlias[aliasPos++] = '_';
+
+ fileAlias[5] = '~';
+ fileAlias[8] = '.';
+ fileAlias[9] = ' ';
+ fileAlias[10] = ' ';
+ fileAlias[11] = ' ';
+ if (strchr (filename, '.') != NULL) {
+ while(filename[filePos] != '\0')
+ {
+ filePos++;
+ if (filename[filePos] == '.')
+ {
+ pathPos = filePos;
+ }
+ }
+ filePos = pathPos + 1; //pathPos is used as a temporary variable
+ // Copy first 3 characters of extension
+ for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ } else {
+ aliasPos = 9;
+ }
+
+ // Pad Alias extension with spaces
+ while (aliasPos < 12)
+ fileAlias[aliasPos++] = ' ';
+
+ fileAlias[12] = '\0';
+
+
+ // Get a valid tail number
+ tailNum = 0;
+ do {
+ tailNum++;
+ fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit
+ fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit
+ } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));
+
+ if (tailNum < 100) // Found an alias not being used
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 12; aliasPos++)
+ {
+ // Skip '.'
+ if (fileAlias[aliasPos] == '.')
+ aliasPos++;
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
+ }
+ }
+ else // Couldn't find a valid alias
+ {
+ return false;
+ }
+
+ dirEntryLength = lfnPos + 2;
+ }
+ else // Its not a long file name
+ {
+ // Just copy alias straight from filename
+ for (aliasPos = 0; aliasPos < 13; aliasPos++)
+ {
+ tempChar = ucase(filename[aliasPos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos] = tempChar;
+ }
+ fileAlias[12] = '\0';
+
+ lfnPos = -1;
+
+ dirEntryLength = 1;
+ }
+
+ // Change dirEntry name to match alias
+ for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
+ {
+ newDirEntry.name[aliasPos] = fileAlias[aliasPos];
+ }
+ while (aliasPos < 8)
+ {
+ newDirEntry.name[aliasPos++] = ' ';
+ }
+ aliasPos = 0;
+ while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
+ aliasPos++;
+ filePos = 0;
+ while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
+ {
+ tempChar = fileAlias[aliasPos++];
+ if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
+ newDirEntry.ext[filePos++] = tempChar;
+ }
+ while (filePos < 3)
+ {
+ newDirEntry.ext[filePos++] = ' ';
+ }
+
+ // Scan Dir for free entry
+ dirCluster = curWorkDirCluster;
+ secOffset = 0;
+ entryOffset = 0;
+ maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+ firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+
+ // Search for a large enough space to fit in new directory entry
+ while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
+ {
+
+ entryOffset++;
+
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ entryOffset = 0;
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
+ {
+ dirEntryRemain--;
+ } else {
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+ }
+ }
+
+ // Modifying the last directory is a special case - have to erase following entries
+ if (dirEntries[entryOffset].name[0] == FILE_LAST)
+ {
+ dirEndFlag = true;
+ }
+
+ // Recall last used entry
+ dirCluster = tempDirCluster;
+ secOffset = tempSecOffset;
+ entryOffset = tempEntryOffset;
+ dirEntryRemain = dirEntryLength;
+
+ // Re-read in first sector that will be written to
+ if (dirEndFlag && (entryOffset == 0)) {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ // Add new directory entry
+ while (dirEntryRemain > 0)
+ {
+ // Move to next entry, first pass advances from last used entry
+ entryOffset++;
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ // Write out the current sector if we need to
+ entryOffset = 0;
+ if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
+ {
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+ }
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ if (dirEndFlag)
+ {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+ }
+
+ // Generate LFN entries
+ if (lfnPos >= 0)
+ {
+ lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
+ for (i = 0; i < 13; i++) {
+ if (filename [lfnPos * 13 + i] == 0x01) {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff;
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff;
+ } else {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i];
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00;
+ }
+ }
+
+ lfnEntry.checkSum = chkSum;
+ lfnEntry.flag = ATTRIB_LFN;
+ lfnEntry.reserved1 = 0;
+ lfnEntry.reserved2 = 0;
+
+ *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
+ lfnPos --;
+ lfnEntry.ordinal = 0;
+ } // end writing long filename entries
+ else
+ {
+ dirEntries[entryOffset] = newDirEntry;
+ if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
+ dirEntries[entryOffset+1].name[0] = FILE_LAST;
+ }
+
+ dirEntryRemain--;
+ }
+
+ // Write directory back to disk
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+
+ // Change back to Working DIR
+ curWorkDirCluster = oldWorkDirCluster;
+
+ return true;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile(char* filename)
+{
+ // Get the next directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile(char* filename)
+{
+ // Get the first directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindFirstFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindNextFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists(const char* filename)
+{
+ DIR_ENT dirEntry;
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (filename);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return FT_NONE;
+ }
+ else if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return FT_DIR;
+ }
+ else
+ {
+ return FT_FILE;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void)
+{
+ return filesysType;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void)
+{
+ return filesysTotalSize;
+}
+
+
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path)
+{
+ DIR_ENT dir;
+ if (path[0] == '/' && path[1] == '\0')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ return true;
+ }
+ if (path[0] == '\0') // Return true if changing relative to nothing
+ {
+ return true;
+ }
+
+ dir = FAT_DirEntFromPath (path);
+
+ if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE))
+ {
+ // Change directory
+ curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16);
+
+ // Move to correct cluster for root directory
+ if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER)
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ // Reset file position in directory
+ wrkDirCluster = curWorkDirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ return true;
+ }
+ else
+ {
+ // Couldn't change directory - wrong path specified
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns NULL if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode)
+{
+ int fileNum;
+ FAT_FILE* file;
+ DIR_ENT dirEntry;
+#ifdef CAN_WRITE_TO_DISC
+ u32 startCluster;
+ int clusCount;
+#endif
+
+ char* pchTemp;
+ // Check that a valid mode was specified
+ pchTemp = strpbrk ( mode, "rRwWaA" );
+ if (pchTemp == NULL)
+ {
+ return NULL;
+ }
+ if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL)
+ {
+ return NULL;
+ }
+
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Check that it is not a directory
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return NULL;
+ }
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check that it is not a read only file being openned in a writing mode
+ if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO))
+ {
+ return NULL;
+ }
+#else
+ if ( (strpbrk(mode, "wWaA+") != NULL))
+ {
+ return NULL;
+ }
+#endif
+
+ // Find a free file buffer
+ for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++);
+
+ if (fileNum == MAX_FILES_OPEN) // No free files
+ {
+ return NULL;
+ }
+
+ file = &openFiles[fileNum];
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+
+ if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // File must exist
+ {
+ return NULL;
+ }
+
+ file->read = true;
+#ifdef CAN_WRITE_TO_DISC
+ file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+#else
+ file->write = false;
+#endif
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check if file is openned for random. If it is, and currently has no cluster, one must be
+ // assigned to it.
+ if (file->write && file->firstCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+#endif
+
+ file->length = dirEntry.fileSize;
+ file->curPos = 0;
+ file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ } // mode "r"
+
+#ifdef CAN_WRITE_TO_DISC
+ if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ }
+ else // Already a file entry
+ {
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+ }
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (dirEntry.name[0] == FILE_FREE) // No file
+ {
+ // Have to create a new entry
+ if(!FAT_AddDirEntry (path, dirEntry))
+ {
+ return NULL;
+ }
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // Already a file
+ {
+ // Just modify the old entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+ file->write = true;
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = 0; // Should always have 0 bytes if openning in "w" mode
+ file->curPos = 0;
+ file->curClus = startCluster;
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ // Empty file, so empty read buffer
+ memset (file->readBuffer, 0, BYTE_PER_READ);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ }
+
+ if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+
+ // The file has no data in it
+ dirEntry.fileSize = 0;
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ if(!FAT_AddDirEntry (path, dirEntry))
+ return NULL;
+
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // File already exists - reuse the old directory entry
+ {
+ startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ // If it currently has no cluster, one must be assigned to it.
+ if (startCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ } else {
+
+ // Follow cluster list until last one is found
+ clusCount = dirEntry.fileSize / filesysBytePerClus;
+ file->appClus = startCluster;
+ while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF))
+ {
+ file->appClus = FAT_NextCluster (file->appClus);
+ }
+ if (clusCount >= 0) // Check if ran out of clusters
+ {
+ // Set flag to allocate new cluster when needed
+ file->appSect = filesysSecPerClus;
+ file->appByte = 0;
+ }
+ }
+ }
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL );
+ file->write = false;
+ file->append = true;
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ;
+ file->appByte = dirEntry.fileSize % BYTE_PER_READ;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = dirEntry.fileSize;
+ file->curPos = dirEntry.fileSize;
+ file->curClus = file->appClus;
+ file->curSect = file->appSect;
+ file->curByte = file->appByte;
+
+ // Read into buffer
+ disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer);
+ file->inUse = true; // We're using this file now
+ return file;
+ }
+#endif
+
+ // Can only reach here if a bad mode was specified
+ return NULL;
+}
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file)
+{
+ // Clear memory used by file information
+ if ((file != NULL) && (file->inUse == true))
+ {
+#ifdef CAN_WRITE_TO_DISC
+ if (file->write || file->append)
+ {
+ // Write new length, time and date back to directory entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length;
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate();
+
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ }
+#endif
+ file->inUse = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file)
+{
+ // Return the position as specified in the FAT_FILE structure
+ if ((file != NULL) && (file->inUse == true))
+ {
+ return file->curPos;
+ }
+ else
+ {
+ // Return -1 if no file was given
+ return -1;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+FAT_FILE* file: IN handle of an open file
+s32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin)
+{
+ u32 cluster, nextCluster;
+ int clusCount;
+ u32 position;
+ u32 curPos;
+
+ if ((file == NULL) || (file->inUse == false)) // invalid file
+ {
+ return -1;
+ }
+
+ // Can't seek in append only mode
+ if (!file->read && !file->write)
+ {
+ return -1;
+ }
+
+ curPos = file->curPos;
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ if (offset >= 0)
+ {
+ position = offset;
+ } else {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ break;
+ case SEEK_CUR:
+ if (offset >= 0)
+ {
+ position = curPos + offset;
+ }
+ else if ( (u32)(offset * -1) >= curPos )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = curPos - (u32)(offset * -1);
+ }
+ break;
+ case SEEK_END:
+ if (offset >= 0)
+ {
+ // Seeking to end of file
+ position = file->length; // Fixed thanks to MoonLight
+ }
+ else if ( (u32)(offset * -1) >= file->length )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = file->length - (u32)(offset * -1);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (position > file->length)
+ {
+ // Tried to go past end of file
+ position = file->length;
+ }
+
+ // Save position
+ file->curPos = position;
+
+
+ // Calculate where the correct cluster is
+ if (position > curPos)
+ {
+ clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ
+ cluster = file->curClus;
+ } else {
+ clusCount = position / filesysBytePerClus;
+ cluster = file->firstCluster;
+ }
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ;
+ file->curByte = position % BYTE_PER_READ;
+
+ // Follow cluster list until desired one is found
+ if (clusCount > 0) // Only look at next cluster if need to
+ {
+ nextCluster = FAT_NextCluster (cluster);
+ } else {
+ nextCluster = cluster;
+ }
+ while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
+ {
+ cluster = nextCluster;
+ nextCluster = FAT_NextCluster (cluster);
+ }
+ // Check if ran out of clusters, and the file is being written to
+ if ((clusCount >= 0) && (file->write || file->append))
+ {
+ // Set flag to allocate a new cluster
+ file->curSect = filesysSecPerClus;
+ file->curByte = 0;
+ }
+ file->curClus = cluster;
+
+ // Reload sector buffer for new position in file, if it is a different sector
+ if ((curPos ^ position) >= BYTE_PER_READ)
+ {
+ disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in size * count bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ char* data = (char*)buffer;
+
+ u32 length = size * count;
+ u32 remain;
+
+ bool flagNoError = true;
+
+ // Can't read non-existant files
+ if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL)
+ return 0;
+
+ // Can only read files openned for reading
+ if (!file->read)
+ return 0;
+
+ // Don't read past end of file
+ if (length + file->curPos > file->length)
+ length = file->length - file->curPos;
+
+ remain = length;
+
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(data, &(file->readBuffer[curByte]), tempVar);
+ remain -= tempVar;
+ data += tempVar;
+
+ curByte += tempVar;
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // align to cluster
+ // tempVar is number of sectors to read
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+
+ curSect += tempVar;
+ }
+
+ // Move onto next cluster
+ // It should get to here without reading anything if a cluster is due to be allocated
+ if (curSect >= filesysSecPerClus)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read in whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+
+ // Advance to next cluster
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer);
+ if (remain > 0)
+ {
+ memcpy(data, file->readBuffer, remain);
+ curByte += remain;
+ remain = 0;
+ }
+ }
+
+ // Length read is the wanted length minus the stuff not read
+ length = length - remain;
+
+ // Update file information
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ return length;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+
+ u32 tempNextCluster;
+ int tempVar;
+ u32 length = size * count;
+ u32 remain = length;
+ char* data = (char*)buffer;
+
+ char* writeBuffer;
+
+ bool flagNoError = true;
+ bool flagAppending = false;
+
+ if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL)
+ return 0;
+
+ if (file->write)
+ {
+ // Write at current read pointer
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Use read buffer as write buffer
+ writeBuffer = file->readBuffer;
+
+ // If it is writing past the current end of file, set appending flag
+ if (length + file->curPos > file->length)
+ {
+ flagAppending = true;
+ }
+ }
+ else if (file->append)
+ {
+ // Write at end of file
+ curByte = file->appByte;
+ curSect = file->appSect;
+ curClus = file->appClus;
+ flagAppending = true;
+
+ // Use global buffer as write buffer, don't touch read buffer
+ writeBuffer = (char*)globalBuffer;
+ disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer);
+ }
+ else
+ {
+ return 0;
+ }
+
+ // Move onto next cluster if needed
+ if (curSect >= filesysSecPerClus)
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ memset(writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ curClus = tempNextCluster;
+ disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(&(writeBuffer[curByte]), data, tempVar);
+ remain -= tempVar;
+ data += tempVar;
+ curByte += tempVar;
+
+ // Write buffer back to disk
+ disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer);
+
+ // Move onto next sector
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // Align to cluster
+ // tempVar is number of sectors to write
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0))
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+ if (remain > 0)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ break;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ } else {
+ // Allocate a new cluster when next writing the file
+ curSect = filesysSecPerClus;
+ }
+ }
+
+ // Write remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ if (flagAppending)
+ {
+ // Zero sector before using it
+ memset (writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ // Modify existing sector
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ if (remain > 0) {
+ memcpy(writeBuffer, data, remain);
+ curByte += remain;
+ remain = 0;
+ disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Amount read is the originally requested amount minus stuff remaining
+ length = length - remain;
+
+ // Update file information
+ if (file->write) // Writing also shifts the read pointer
+ {
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ if (file->length < file->curPos)
+ {
+ file->length = file->curPos;
+ }
+ }
+ else if (file->append) // Appending doesn't affect the read pointer
+ {
+ file->appByte = curByte;
+ file->appSect = curSect;
+ file->appClus = curClus;
+ file->length = file->length + length;
+ }
+
+ return length;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file)
+{
+ if ((file == NULL) || (file->inUse == false))
+ return true; // Return eof on invalid files
+
+ return (file->length == file->curPos);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path)
+{
+ DIR_ENT dirEntry;
+ u32 oldWorkDirCluster;
+ char checkFilename[13];
+ FILE_TYPE checkFiletype;
+
+ dirEntry = FAT_DirEntFromPath (path);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return -1;
+ }
+
+ // Only delete directories if the directory is entry
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ // Change to the directory temporarily
+ oldWorkDirCluster = curWorkDirCluster;
+ FAT_chdir(path);
+
+ // Search for files or directories, excluding the . and .. entries
+ checkFiletype = FAT_FindFirstFile (checkFilename);
+ while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE))
+ {
+ checkFiletype = FAT_FindNextFile (checkFilename);
+ }
+
+ // Change back to working directory
+ curWorkDirCluster = oldWorkDirCluster;
+
+ // Check that the directory is empty
+ if (checkFiletype != FT_NONE)
+ {
+ // Directory isn't empty
+ return -1;
+ }
+ }
+
+ // Refresh directory information
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+
+ // Remove Directory entry
+ disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE;
+ disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ return 0;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path)
+{
+ u32 newDirCluster;
+ u32 parentDirCluster;
+ DIR_ENT dirEntry;
+ DIR_ENT* entries = (DIR_ENT*)globalBuffer;
+ int i;
+
+ int pathPos, filePos;
+ char pathname[MAX_FILENAME_LENGTH];
+ u32 oldDirCluster;
+
+ if (FAT_FileExists(path) != FT_NONE)
+ {
+ return -1; // File or directory exists with that name
+ }
+
+ // Find filename within path and change to that directory
+ oldDirCluster = curWorkDirCluster;
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ pathname[filePos] = '\0';
+ if (FAT_chdir(pathname) == false)
+ {
+ curWorkDirCluster = oldDirCluster;
+ return -1; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ pathname[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Now grab the parent directory's cluster
+ parentDirCluster = curWorkDirCluster;
+ curWorkDirCluster = oldDirCluster;
+
+ // Get a new cluster for the file
+ newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE);
+
+ if (newDirCluster == CLUSTER_FREE)
+ {
+ return -1; // Couldn't get a new cluster for the directory
+ }
+ // Fill in directory entry's information
+ dirEntry.attrib = ATTRIB_DIR;
+ dirEntry.reserved = 0;
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (newDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF);
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (FAT_AddDirEntry (path, dirEntry) == false)
+ {
+ return -1; // Couldn't add the directory entry
+ }
+
+ // Create the new directory itself
+ memset(entries, FILE_LAST, BYTE_PER_READ);
+
+ // Create . directory entry
+ dirEntry.name[0] = '.';
+ // Fill name and extension with spaces
+ for (i = 1; i < 11; i++)
+ {
+ dirEntry.name[i] = ' ';
+ }
+
+ memcpy(entries, &dirEntry, sizeof(dirEntry));
+
+ // Create .. directory entry
+ dirEntry.name[1] = '.';
+ dirEntry.startCluster = (parentDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF);
+
+ memcpy(&entries[1], &dirEntry, sizeof(dirEntry));
+
+ // Write entry to disc
+ disc_WriteSector(FAT_ClustToSect(newDirCluster), entries);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ return 0;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file)
+{
+ char c;
+ return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file)
+{
+ return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file)
+{
+ u32 curPos;
+ u32 readLength;
+ char *returnChar;
+
+ // invalid filehandle
+ if (file == NULL)
+ {
+ return NULL ;
+ }
+
+ // end of file
+ if (FAT_feof(file)==true)
+ {
+ return NULL ;
+ }
+
+ // save current position
+ curPos = FAT_ftell(file);
+
+ // read the full buffer (max string chars is num-1 and one end of string \0
+ readLength = FAT_fread(tgtBuffer,1,num-1,file) ;
+
+ // mark least possible end of string
+ tgtBuffer[readLength] = '\0' ;
+
+ if (readLength==0) {
+ // return error
+ return NULL ;
+ }
+
+ // get position of first return '\r'
+ returnChar = strchr(tgtBuffer,'\r');
+
+ // if no return is found, search for a newline
+ if (returnChar == NULL)
+ {
+ returnChar = strchr(tgtBuffer,'\n');
+ }
+
+ // Mark the return, if existant, as end of line/string
+ if (returnChar!=NULL) {
+ *returnChar++ = 0 ;
+ if (*returnChar=='\n') { // catch newline too when jumping over the end
+ // return to location after \r\n (strlen+2)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ;
+ return tgtBuffer ;
+ } else {
+ // return to location after \r (strlen+1)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ;
+ return tgtBuffer ;
+ }
+ }
+
+ return tgtBuffer ;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file)
+{
+ u32 writtenBytes;
+ // save string except end of string '\0'
+ writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file);
+
+ // check if we had an error
+ if (writtenBytes != strlen(string))
+ {
+ // return EOF error
+ return EOF;
+ }
+
+ // return the charcount written
+ return writtenBytes ;
+}
+#endif
+
+
+
+/*
+ gba_nds_fat.c
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+// Includes
+
+#include "gba_nds_fat.h"
+#include "disc_io.h"
+#include <string.h>
+#ifdef NDS
+ #include <nds/ipc.h> // Time on the NDS
+#endif
+//----------------------------------------------------------------
+// Data types
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//----------------------------------------------------------------
+// NDS memory access control register
+#ifdef NDS
+ #ifndef WAIT_CR
+ #define WAIT_CR (*(vu16*)0x04000204)
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// Appropriate placement of CF functions and data
+#ifdef NDS
+ #define _VARS_IN_RAM
+#else
+ #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
+#endif
+
+
+//-----------------------------------------------------------------
+// FAT constants
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x0000
+#define CLUSTER_FIRST 0x0002
+
+#define FILE_LAST 0x00
+#define FILE_FREE 0xE5
+
+#define FAT16_ROOT_DIR_CLUSTER 0x00
+
+
+//-----------------------------------------------------------------
+// long file name constants
+#define LFN_END 0x40
+#define LFN_DEL 0x80
+
+//-----------------------------------------------------------------
+// Data Structures
+
+// Take care of packing for GCC - it doesn't obey pragma pack()
+// properly for ARM targets.
+#ifdef __GNUC__
+ #define __PACKED __attribute__ ((__packed__))
+#else
+ #define __PACKED
+ #pragma pack(1)
+#endif
+
+// Boot Sector - must be packed
+typedef struct
+{
+ u8 jmpBoot[3];
+ u8 OEMName[8];
+ // BIOS Parameter Block
+ u16 bytesPerSector;
+ u8 sectorsPerCluster;
+ u16 reservedSectors;
+ u8 numFATs;
+ u16 rootEntries;
+ u16 numSectorsSmall;
+ u8 mediaDesc;
+ u16 sectorsPerFAT;
+ u16 sectorsPerTrk;
+ u16 numHeads;
+ u32 numHiddenSectors;
+ u32 numSectors;
+ union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
+ {
+ struct
+ {
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[448];
+ } __PACKED fat16;
+ struct
+ {
+ // FAT32 extended block
+ u32 sectorsPerFAT32;
+ u16 extFlags;
+ u16 fsVer;
+ u32 rootClus;
+ u16 fsInfo;
+ u16 bkBootSec;
+ u8 reserved[12];
+ // Ext BIOS Parameter Block for FAT16
+ u8 driveNumber;
+ u8 reserved1;
+ u8 extBootSig;
+ u32 volumeID;
+ u8 volumeLabel[11];
+ u8 fileSysType[8];
+ // Bootcode
+ u8 bootCode[420];
+ } __PACKED fat32;
+ } __PACKED extBlock;
+
+ u16 bootSig;
+
+} __PACKED BOOT_SEC;
+
+// Directory entry - must be packed
+typedef struct
+{
+ u8 name[8];
+ u8 ext[3];
+ u8 attrib;
+ u8 reserved;
+ u8 cTime_ms;
+ u16 cTime;
+ u16 cDate;
+ u16 aDate;
+ u16 startClusterHigh;
+ u16 mTime;
+ u16 mDate;
+ u16 startCluster;
+ u32 fileSize;
+} __PACKED DIR_ENT;
+
+// Long file name directory entry - must be packed
+typedef struct
+{
+ u8 ordinal; // Position within LFN
+ u16 char0;
+ u16 char1;
+ u16 char2;
+ u16 char3;
+ u16 char4;
+ u8 flag; // Should be equal to ATTRIB_LFN
+ u8 reserved1; // Always 0x00
+ u8 checkSum; // Checksum of short file name (alias)
+ u16 char5;
+ u16 char6;
+ u16 char7;
+ u16 char8;
+ u16 char9;
+ u16 char10;
+ u16 reserved2; // Always 0x0000
+ u16 char11;
+ u16 char12;
+} __PACKED DIR_ENT_LFN;
+
+const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+// End of packed structs
+#ifdef __PACKED
+ #undef __PACKED
+#endif
+#ifndef __GNUC__
+ #pragma pack()
+#endif
+
+//-----------------------------------------------------------------
+// Global Variables
+
+// _VARS_IN_RAM variables are stored in the largest section of WRAM
+// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
+
+// Files
+_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN];
+
+// Long File names
+_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
+bool lfnExists;
+
+// Locations on card
+int filesysRootDir;
+int filesysRootDirClus;
+int filesysFAT;
+int filesysSecPerFAT;
+int filesysNumSec;
+int filesysData;
+int filesysBytePerSec;
+int filesysSecPerClus;
+int filesysBytePerClus;
+
+FS_TYPE filesysType = FS_UNKNOWN;
+u32 filesysTotalSize;
+
+// Info about FAT
+u32 fatLastCluster;
+u32 fatFirstFree;
+
+// fatBuffer used to reduce wear on the CF card from multiple writes
+_VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
+u32 fatBufferCurSector;
+
+// Current working directory
+u32 curWorkDirCluster;
+
+// Position of the directory entry last retreived with FAT_GetDirEntry
+u32 wrkDirCluster;
+int wrkDirSector;
+int wrkDirOffset;
+
+// Global sector buffer to save on stack space
+_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];
+
+//-----------------------------------------------------------------
+// Functions contained in this file - predeclarations
+char ucase (char character);
+u16 getRTCtoFileTime (void);
+u16 getRTCtoFileDate (void);
+
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
+bool FAT_ClearLinks (u32 cluster);
+DIR_ENT FAT_DirEntFromPath (const char* path);
+u32 FAT_FirstFreeCluster(void);
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
+u32 FAT_LinkFreeCluster(u32 cluster);
+u32 FAT_NextCluster(u32 cluster);
+bool FAT_WriteFatEntry (u32 cluster, u32 value);
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);
+
+bool FAT_InitFiles (void);
+bool FAT_FreeFiles (void);
+int FAT_remove (const char* path);
+bool FAT_chdir (const char* path);
+FILE_TYPE FAT_FindFirstFile (char* filename);
+FILE_TYPE FAT_FindNextFile (char* filename);
+FILE_TYPE FAT_FileExists (const char* filename);
+bool FAT_GetAlias (char* alias);
+bool FAT_GetLongFilename (char* filename);
+u32 FAT_GetFileSize (void);
+u32 FAT_GetFileCluster (void);
+
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+bool FAT_fclose (FAT_FILE* file);
+bool FAT_feof(FAT_FILE* file);
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+u32 FAT_ftell (FAT_FILE* file);
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+char FAT_fgetc (FAT_FILE* file);
+char FAT_fputc (char c, FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+ucase
+Returns the uppercase version of the given char
+char IN: a character
+char return OUT: uppercase version of character
+-----------------------------------------------------------------*/
+char ucase (char character)
+{
+ if ((character > 0x60) && (character < 0x7B))
+ character = character - 0x20;
+ return (character);
+}
+
+
+/*-----------------------------------------------------------------
+getRTCtoFileTime and getRTCtoFileDate
+Returns the time / date in Dir Entry styled format
+u16 return OUT: time / date in Dir Entry styled format
+-----------------------------------------------------------------*/
+u16 getRTCtoFileTime (void)
+{
+#ifdef NDS
+ return (
+ ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
+ ( (IPC->rtc_minutes & 0x3F) << 5) |
+ ( (IPC->rtc_seconds >> 1) & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+u16 getRTCtoFileDate (void)
+{
+#ifdef NDS
+ return (
+ ( ((IPC->rtc_year + 20) & 0x7F) <<9) |
+ ( (IPC->rtc_month & 0xF) << 5) |
+ (IPC->rtc_day & 0x1F) );
+#else
+ return 0;
+#endif
+}
+
+
+/*-----------------------------------------------------------------
+Disc level FAT routines
+-----------------------------------------------------------------*/
+#define FAT_ClustToSect(m) \
+ (((m-2) * filesysSecPerClus) + filesysData)
+
+/*-----------------------------------------------------------------
+FAT_NextCluster
+Internal function - gets the cluster linked from input cluster
+-----------------------------------------------------------------*/
+u32 FAT_NextCluster(u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster = ((u8*)fatBuffer)[offset];
+ offset++;
+
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ nextCluster |= (((u8*)fatBuffer)[offset]) << 8;
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = ((u16*)fatBuffer)[offset];
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // read the nextCluster value
+ nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntry
+Internal function - writes FAT information about a cluster
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntry (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ {
+ return false;
+ }
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ ((u16*)fatBuffer)[offset] = (value & 0xFFFF);
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ (((u32*)fatBuffer)[offset]) = value;
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+
+ return true;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ReadWriteFatEntryBuffered
+Internal function - writes FAT information about a cluster to a
+ buffer that should then be flushed to disc using
+ FAT_WriteFatEntryFlushBuffer()
+ Call FAT_WriteFatEntry first so as not to ruin the disc.
+ Also returns the entry being replaced
+-----------------------------------------------------------------*/
+u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
+{
+ u32 sector;
+ int offset;
+ u32 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return CLUSTER_FREE;
+
+
+ switch (filesysType)
+ {
+ case FS_UNKNOWN:
+ oldValue = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTE_PER_READ;
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ if (cluster & 0x01) {
+
+ oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
+ ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
+
+ } else {
+
+ oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
+ ((u8*)fatBuffer)[offset] = value & 0xFF;
+
+ offset++;
+ if (offset >= BYTE_PER_READ) {
+ offset = 0;
+ // write the buffer back to disc
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // read the next sector
+ fatBufferCurSector++;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
+ ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
+ }
+
+ if (oldValue >= 0x0FF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 1);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u16*)fatBuffer)[offset];
+ ((u16*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0xFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT32:
+ sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
+ offset = cluster % (BYTE_PER_READ >> 2);
+
+ // If FAT buffer contains wrong sector
+ if (sector != fatBufferCurSector)
+ {
+ // write the old buffer to disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ // Load correct sector to buffer
+ fatBufferCurSector = sector;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+ }
+
+ // write the value to the FAT buffer
+ oldValue = ((u32*)fatBuffer)[offset];
+ ((u32*)fatBuffer)[offset] = value;
+
+ if (oldValue >= 0x0FFFFFF7)
+ {
+ oldValue = CLUSTER_EOF;
+ }
+
+ break;
+
+ default:
+ oldValue = CLUSTER_FREE;
+ break;
+ }
+
+ return oldValue;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_WriteFatEntryFlushBuffer
+Flush the FAT buffer back to the disc
+-----------------------------------------------------------------*/
+bool FAT_WriteFatEntryFlushBuffer (void)
+{
+ // write the buffer disc
+ if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
+ {
+ disc_WriteSector(fatBufferCurSector, fatBuffer);
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FirstFreeCluster
+Internal function - gets the first available free cluster
+-----------------------------------------------------------------*/
+u32 FAT_FirstFreeCluster(void)
+{
+ // Start at first valid cluster
+ if (fatFirstFree < CLUSTER_FIRST)
+ fatFirstFree = CLUSTER_FIRST;
+
+ while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
+ {
+ fatFirstFree++;
+ }
+ if (fatFirstFree > fatLastCluster)
+ {
+ return CLUSTER_EOF;
+ }
+ return fatFirstFree;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_LinkFreeCluster
+Internal function - gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+-----------------------------------------------------------------*/
+u32 FAT_LinkFreeCluster(u32 cluster)
+{
+ u32 firstFree;
+ u32 curLink;
+
+ if (cluster > fatLastCluster)
+ {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = FAT_NextCluster (cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
+ {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = FAT_FirstFreeCluster();
+
+ // If couldn't get a free cluster then return
+ if (firstFree == CLUSTER_EOF)
+ {
+ return CLUSTER_FREE;
+ }
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
+ {
+ // Update the linked from FAT entry
+ FAT_WriteFatEntry (cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ FAT_WriteFatEntry (firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+#endif
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_ClearLinks
+Internal function - frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool FAT_ClearLinks (u32 cluster)
+{
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > fatLastCluster))
+ return false;
+
+ // Store next cluster before erasing the link
+ nextCluster = FAT_NextCluster (cluster);
+
+ // Erase the link
+ FAT_WriteFatEntry (cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
+ {
+ cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
+ }
+
+ // Flush fat write buffer
+ FAT_WriteFatEntryFlushBuffer ();
+
+ return true;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void)
+{
+ int i;
+ int bootSector;
+ BOOT_SEC* bootSec;
+
+ if (!disc_Init())
+ {
+ return (false);
+ }
+
+ // Read first sector of CF card
+ if ( !disc_ReadSector(0, globalBuffer)) {
+ return false;
+ }
+
+ // Make sure it is a valid MBR or boot sector
+ if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
+ return false;
+ }
+
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
+ {
+ bootSector = 0;
+ }
+ // Check for FAT32
+ else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
+ {
+ bootSector = 0;
+ }
+ else // This is an MBR
+ {
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE)
+ for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
+
+ // Go to first valid partition
+ if ( i != 0x1FE) // Make sure it found a partition
+ {
+ bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ bootSec = (BOOT_SEC*) globalBuffer;
+ if (!disc_ReadSector (bootSector, bootSec)) {
+ return false;
+ }
+
+ // Store required information about the file system
+ if (bootSec->sectorsPerFAT != 0)
+ {
+ filesysSecPerFAT = bootSec->sectorsPerFAT;
+ }
+ else
+ {
+ filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
+ }
+
+ if (bootSec->numSectorsSmall != 0)
+ {
+ filesysNumSec = bootSec->numSectorsSmall;
+ }
+ else
+ {
+ filesysNumSec = bootSec->numSectors;
+ }
+
+ filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes
+ filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
+ filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
+ filesysFAT = bootSector + bootSec->reservedSectors;
+
+ filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);
+ filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
+
+ filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;
+
+ // Store info about FAT
+ fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
+ fatFirstFree = CLUSTER_FIRST;
+ fatBufferCurSector = 0;
+ disc_ReadSector(fatBufferCurSector, fatBuffer);
+
+ if (fatLastCluster < 4085)
+ {
+ filesysType = FS_FAT12; // FAT12 volume - unsupported
+ }
+ else if (fatLastCluster < 65525)
+ {
+ filesysType = FS_FAT16; // FAT16 volume
+ }
+ else
+ {
+ filesysType = FS_FAT32; // FAT32 volume
+ }
+
+ if (filesysType != FS_FAT32)
+ {
+ filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
+ }
+ else // Set up for the FAT32 way
+ {
+ filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
+ // Check if FAT mirroring is enabled
+ if (!(bootSec->extBlock.fat32.extFlags & 0x80))
+ {
+ // Use the active FAT
+ filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
+ }
+ }
+
+ // Set current directory to the root
+ curWorkDirCluster = filesysRootDirClus;
+ wrkDirCluster = filesysRootDirClus;
+ wrkDirSector = 0;
+ wrkDirOffset = 0;
+
+ // Set all files to free
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ openFiles[i].inUse = false;
+ }
+
+ // No long filenames so far
+ lfnExists = false;
+ for (i = 0; i < MAX_FILENAME_LENGTH; i++)
+ {
+ lfnName[i] = '\0';
+ }
+
+ return (true);
+}
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void)
+{
+ int i;
+
+ // Close all open files
+ for (i=0; i < MAX_FILES_OPEN; i++)
+ {
+ if (openFiles[i].inUse == true)
+ {
+ FAT_fclose(&openFiles[i]);
+ }
+ }
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ // Clear card status
+ disc_ClearStatus();
+
+ // Return status of card
+ return disc_IsInserted();
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetDirEntry
+Return the file info structure of the next valid file entry
+u32 dirCluster: IN cluster of subdirectory table
+int entry: IN the desired file entry
+int origin IN: relative position of the entry
+DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
+ the entry does not exist.
+-----------------------------------------------------------------*/
+DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
+{
+ DIR_ENT dir;
+ DIR_ENT_LFN lfn;
+ int firstSector = 0;
+ bool notFound = false;
+ bool found = false;
+ int maxSectors;
+ int lfnPos, aliasPos;
+ u8 lfnChkSum, chkSum;
+
+ int i;
+
+ dir.name[0] = FILE_FREE; // default to no file found
+ dir.attrib = 0x00;
+
+ // Check if fat has been initialised
+ if (filesysBytePerSec == 0)
+ {
+ return (dir);
+ }
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ break;
+ case SEEK_CUR: // Don't change anything
+ break;
+ case SEEK_END: // Find entry signifying end of directory
+ // Subtraction will never reach 0, so it keeps going
+ // until reaches end of directory
+ wrkDirCluster = dirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ entry = -1;
+ break;
+ default:
+ return dir;
+ }
+
+ lfnChkSum = 0;
+ maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+
+ // Scan Dir for correct entry
+ firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ found = false;
+ notFound = false;
+ do {
+ wrkDirOffset++;
+ if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ wrkDirOffset = 0;
+ wrkDirSector++;
+ if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ wrkDirSector = 0;
+ wrkDirCluster = FAT_NextCluster(wrkDirCluster);
+ if (wrkDirCluster == CLUSTER_EOF)
+ {
+ notFound = true;
+ }
+ firstSector = FAT_ClustToSect(wrkDirCluster);
+ }
+ else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
+ {
+ notFound = true; // Got to end of root dir
+ }
+ disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
+ }
+ dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
+ if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
+ {
+ entry--;
+ if (lfnExists)
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 11; aliasPos++)
+ {
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
+ }
+ if (chkSum != lfnChkSum)
+ {
+ lfnExists = false;
+ lfnName[0] = '\0';
+ }
+ }
+ if (entry == 0)
+ {
+ if (!lfnExists)
+ {
+ FAT_GetFilename (dir, lfnName);
+ }
+ found = true;
+ }
+ }
+ else if (dir.name[0] == FILE_LAST)
+ {
+ if (origin == SEEK_END)
+ {
+ found = true;
+ }
+ else
+ {
+ notFound = true;
+ }
+ }
+ else if (dir.attrib == ATTRIB_LFN)
+ {
+ lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
+ if (lfn.ordinal & LFN_DEL)
+ {
+ lfnExists = false;
+ }
+ else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
+ {
+ lfnExists = true;
+ lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
+ lfnChkSum = lfn.checkSum;
+ }
+ if (lfnChkSum != lfn.checkSum)
+ {
+ lfnExists = false;
+ }
+ if (lfnExists)
+ {
+ lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
+ for (i = 0; i < 13; i++) {
+ lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/;
+ }
+ }
+ }
+ } while (!found && !notFound);
+
+ // If no file is found, return FILE_FREE
+ if (notFound)
+ {
+ dir.name[0] = FILE_FREE;
+ }
+
+ return (dir);
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile.
+ If a long name doesn't exist, it returns the short name
+ instead.
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename)
+{
+ if (filename == NULL)
+ return false;
+
+ strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
+ filename[MAX_FILENAME_LENGTH - 1] = '\0';
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_GetFilename
+Get the alias (short name) of the file or directory stored in
+ dirEntry
+DIR_ENT dirEntry: IN a valid directory table entry
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
+{
+ int i=0;
+ int j=0;
+
+ alias[0] = '\0';
+ if (dirEntry.name[0] != FILE_FREE)
+ {
+ if (dirEntry.name[0] == '.')
+ {
+ alias[0] = '.';
+ if (dirEntry.name[1] == '.')
+ {
+ alias[1] = '.';
+ alias[2] = '\0';
+ }
+ else
+ {
+ alias[1] = '\0';
+ }
+ }
+ else
+ {
+ // Copy the filename from the dirEntry to the string
+ for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++)
+ {
+ alias[i] = dirEntry.name[i];
+ }
+ // Copy the extension from the dirEntry to the string
+ if (dirEntry.ext[0] != ' ')
+ {
+ alias[i++] = '.';
+ for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
+ {
+ alias[i++] = dirEntry.ext[j];
+ }
+ }
+ alias[i] = '\0';
+ }
+ }
+
+ return (alias[0] != '\0');
+}
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias)
+{
+ if (alias == NULL)
+ {
+ return false;
+ }
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonLight
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
+{
+ // Get the file
+ if (!FAT_FileExists(filename)) {
+ return 0xff;
+ }
+
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes
+
+ disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
+}
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
+{
+ struct tm timeInfo;
+
+ timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970
+ timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january
+ timeInfo.tm_mday = fileDate & 0x1f; // Day of the month
+
+ timeInfo.tm_hour = fileTime >> 11; // hours past midnight
+ timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour
+ timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute
+
+ return mktime(&timeInfo);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void)
+{
+ // Read in the last accessed directory entry
+ disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
+
+ return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_DirEntFromPath
+Finds the directory entry for a file or directory from a path
+Path separator is a forward slash /
+const char* path: IN null terminated string of path.
+DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
+ if the file was not found
+-----------------------------------------------------------------*/
+DIR_ENT FAT_DirEntFromPath (const char* path)
+{
+ int pathPos;
+ char name[MAX_FILENAME_LENGTH];
+ char alias[13];
+ int namePos;
+ bool found, notFound;
+ DIR_ENT dirEntry;
+ u32 dirCluster;
+ bool flagLFN, dotSeen;
+
+ // Start at beginning of path
+ pathPos = 0;
+
+ if (path[pathPos] == '/')
+ {
+ dirCluster = filesysRootDirClus; // Start at root directory
+ }
+ else
+ {
+ dirCluster = curWorkDirCluster; // Start at current working dir
+ }
+
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search until can't continue
+ found = false;
+ notFound = false;
+ while (!notFound && !found)
+ {
+ flagLFN = false;
+ // Copy name from path
+ namePos = 0;
+ if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
+ // Dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
+ // Double dot entry
+ name[namePos++] = '.';
+ pathPos++;
+ name[namePos++] = '.';
+ pathPos++;
+ } else {
+ // Copy name from path
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+ dotSeen = false;
+ while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
+ {
+ name[namePos] = ucase(path[pathPos]);
+ if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (name[namePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ namePos++;
+ pathPos++;
+ }
+ // Check if a long filename was specified
+ if (namePos > 12)
+ {
+ flagLFN = true;
+ }
+ }
+
+ // Add end of string char
+ name[namePos] = '\0';
+
+ // Move through path to correct place
+ while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
+ pathPos++;
+ // Eat any slash /
+ while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
+ {
+ pathPos++;
+ }
+
+ // Search current Dir for correct entry
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
+ while ( !found && !notFound)
+ {
+ // Match filename
+ found = true;
+ for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(lfnName[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
+ {
+ found = false;
+ }
+
+ // Check against alias as well.
+ if (!found)
+ {
+ FAT_GetFilename(dirEntry, alias);
+ found = true;
+ for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
+ {
+ if (name[namePos] != ucase(alias[namePos]))
+ {
+ found = false;
+ }
+ }
+ if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
+ {
+ found = false;
+ }
+ }
+
+ if (dirEntry.name[0] == FILE_FREE)
+ // Couldn't find specified file
+ {
+ found = false;
+ notFound = true;
+ }
+ if (!found && !notFound)
+ {
+ dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
+ }
+ }
+
+ if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
+ // It has found a directory from within the path that needs to be followed
+ {
+ found = false;
+ dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ }
+ }
+
+ if (notFound)
+ {
+ dirEntry.name[0] = FILE_FREE;
+ dirEntry.attrib = 0x00;
+ }
+
+ return (dirEntry);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_AddDirEntry
+Creates a new dir entry for a file
+Path separator is a forward slash /
+const char* path: IN null terminated string of path to file.
+DIR_ENT newDirEntry IN: The directory entry to use.
+int file IN: The file being added (optional, use -1 if not used)
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
+{
+ char filename[MAX_FILENAME_LENGTH];
+ int filePos, pathPos, aliasPos;
+ char tempChar;
+ bool flagLFN, dotSeen;
+ char fileAlias[13] = {0};
+ int tailNum;
+
+ unsigned char chkSum = 0;
+
+ u32 oldWorkDirCluster;
+
+ DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
+ u32 dirCluster;
+ int secOffset;
+ int entryOffset;
+ int maxSectors;
+ u32 firstSector;
+
+ DIR_ENT_LFN lfnEntry;
+ int lfnPos = 0;
+
+ int dirEntryLength = 0;
+ int dirEntryRemain = 0;
+ u32 tempDirCluster;
+ int tempSecOffset;
+ int tempEntryOffset;
+ bool dirEndFlag = false;
+
+ int i;
+
+ // Store current working directory
+ oldWorkDirCluster = curWorkDirCluster;
+
+ // Find filename within path and change to correct directory
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+ flagLFN = false;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ filename[filePos] = '\0';
+ if (FAT_chdir(filename) == false)
+ {
+ curWorkDirCluster = oldWorkDirCluster;
+ return false; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ filename[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Skip over last slashes
+ while (path[pathPos] == '/')
+ pathPos++;
+
+ // Check if the filename has a leading "."
+ // If so, it is an LFN
+ if (path[pathPos] == '.') {
+ flagLFN = true;
+ }
+
+ // Copy name from path
+ filePos = 0;
+ dotSeen = false;
+
+ while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
+ {
+ filename[filePos] = path[pathPos];
+ if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
+ {
+ flagLFN = true;
+ }
+ if (filename[filePos] == '.') {
+ if (!dotSeen) {
+ dotSeen = true;
+ } else {
+ flagLFN = true;
+ }
+ }
+ filePos++;
+ pathPos++;
+ if ((filePos > 8) && !dotSeen) {
+ flagLFN = true;
+ }
+ }
+
+ if (filePos == 0) // No filename
+ {
+ return false;
+ }
+
+ // Check if a long filename was specified
+ if (filePos > 12)
+ {
+ flagLFN = true;
+ }
+
+ // Check if extension is > 3 characters long
+ if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
+ flagLFN = true;
+ }
+
+ lfnPos = (filePos - 1) / 13;
+
+ // Add end of string char
+ filename[filePos++] = '\0';
+ // Clear remaining chars
+ while (filePos < MAX_FILENAME_LENGTH)
+ filename[filePos++] = 0x01; // Set for LFN compatibility
+
+
+ if (flagLFN)
+ {
+ // Generate short filename - always a 2 digit number for tail
+ // Get first 5 chars of alias from LFN
+ aliasPos = 0;
+ filePos = 0;
+ if (filename[filePos] == '.') {
+ filePos++;
+ }
+ for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ // Pad Alias with underscores
+ while (aliasPos < 5)
+ fileAlias[aliasPos++] = '_';
+
+ fileAlias[5] = '~';
+ fileAlias[8] = '.';
+ fileAlias[9] = ' ';
+ fileAlias[10] = ' ';
+ fileAlias[11] = ' ';
+ if (strchr (filename, '.') != NULL) {
+ while(filename[filePos] != '\0')
+ {
+ filePos++;
+ if (filename[filePos] == '.')
+ {
+ pathPos = filePos;
+ }
+ }
+ filePos = pathPos + 1; //pathPos is used as a temporary variable
+ // Copy first 3 characters of extension
+ for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
+ {
+ tempChar = ucase(filename[filePos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos++] = tempChar;
+ }
+ } else {
+ aliasPos = 9;
+ }
+
+ // Pad Alias extension with spaces
+ while (aliasPos < 12)
+ fileAlias[aliasPos++] = ' ';
+
+ fileAlias[12] = '\0';
+
+
+ // Get a valid tail number
+ tailNum = 0;
+ do {
+ tailNum++;
+ fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit
+ fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit
+ } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));
+
+ if (tailNum < 100) // Found an alias not being used
+ {
+ // Calculate file checksum
+ chkSum = 0;
+ for (aliasPos=0; aliasPos < 12; aliasPos++)
+ {
+ // Skip '.'
+ if (fileAlias[aliasPos] == '.')
+ aliasPos++;
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
+ }
+ }
+ else // Couldn't find a valid alias
+ {
+ return false;
+ }
+
+ dirEntryLength = lfnPos + 2;
+ }
+ else // Its not a long file name
+ {
+ // Just copy alias straight from filename
+ for (aliasPos = 0; aliasPos < 13; aliasPos++)
+ {
+ tempChar = ucase(filename[aliasPos]);
+ if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
+ fileAlias[aliasPos] = tempChar;
+ }
+ fileAlias[12] = '\0';
+
+ lfnPos = -1;
+
+ dirEntryLength = 1;
+ }
+
+ // Change dirEntry name to match alias
+ for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
+ {
+ newDirEntry.name[aliasPos] = fileAlias[aliasPos];
+ }
+ while (aliasPos < 8)
+ {
+ newDirEntry.name[aliasPos++] = ' ';
+ }
+ aliasPos = 0;
+ while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
+ aliasPos++;
+ filePos = 0;
+ while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
+ {
+ tempChar = fileAlias[aliasPos++];
+ if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
+ newDirEntry.ext[filePos++] = tempChar;
+ }
+ while (filePos < 3)
+ {
+ newDirEntry.ext[filePos++] = ' ';
+ }
+
+ // Scan Dir for free entry
+ dirCluster = curWorkDirCluster;
+ secOffset = 0;
+ entryOffset = 0;
+ maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
+ firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+
+ // Search for a large enough space to fit in new directory entry
+ while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
+ {
+
+ entryOffset++;
+
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ entryOffset = 0;
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
+ {
+ dirEntryRemain--;
+ } else {
+ dirEntryRemain = dirEntryLength;
+ tempDirCluster = dirCluster;
+ tempSecOffset = secOffset;
+ tempEntryOffset = entryOffset;
+ }
+ }
+
+ // Modifying the last directory is a special case - have to erase following entries
+ if (dirEntries[entryOffset].name[0] == FILE_LAST)
+ {
+ dirEndFlag = true;
+ }
+
+ // Recall last used entry
+ dirCluster = tempDirCluster;
+ secOffset = tempSecOffset;
+ entryOffset = tempEntryOffset;
+ dirEntryRemain = dirEntryLength;
+
+ // Re-read in first sector that will be written to
+ if (dirEndFlag && (entryOffset == 0)) {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+
+ // Add new directory entry
+ while (dirEntryRemain > 0)
+ {
+ // Move to next entry, first pass advances from last used entry
+ entryOffset++;
+ if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
+ {
+ // Write out the current sector if we need to
+ entryOffset = 0;
+ if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
+ {
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+ }
+ secOffset++;
+ if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
+ {
+ secOffset = 0;
+ if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
+ {
+ dirCluster = FAT_LinkFreeCluster(dirCluster);
+ dirEntries[0].name[0] = FILE_LAST;
+ }
+ else
+ {
+ dirCluster = FAT_NextCluster(dirCluster);
+ }
+ firstSector = FAT_ClustToSect(dirCluster);
+ }
+ else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
+ {
+ return false; // Got to end of root dir - can't fit in more files
+ }
+ if (dirEndFlag)
+ {
+ memset (dirEntries, FILE_LAST, BYTE_PER_READ);
+ } else {
+ disc_ReadSector (firstSector + secOffset, dirEntries);
+ }
+ }
+
+ // Generate LFN entries
+ if (lfnPos >= 0)
+ {
+ lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
+ for (i = 0; i < 13; i++) {
+ if (filename [lfnPos * 13 + i] == 0x01) {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff;
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff;
+ } else {
+ ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i];
+ ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00;
+ }
+ }
+
+ lfnEntry.checkSum = chkSum;
+ lfnEntry.flag = ATTRIB_LFN;
+ lfnEntry.reserved1 = 0;
+ lfnEntry.reserved2 = 0;
+
+ *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
+ lfnPos --;
+ lfnEntry.ordinal = 0;
+ } // end writing long filename entries
+ else
+ {
+ dirEntries[entryOffset] = newDirEntry;
+ if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
+ dirEntries[entryOffset+1].name[0] = FILE_LAST;
+ }
+
+ dirEntryRemain--;
+ }
+
+ // Write directory back to disk
+ disc_WriteSector (firstSector + secOffset, dirEntries);
+
+ // Change back to Working DIR
+ curWorkDirCluster = oldWorkDirCluster;
+
+ return true;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile(char* filename)
+{
+ // Get the next directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile(char* filename)
+{
+ // Get the first directory entry
+ DIR_ENT file;
+ file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);
+
+ if (file.name[0] == FILE_FREE)
+ {
+ return FT_NONE; // Did not find a file
+ }
+
+ // Get the filename
+ if (filename != NULL)
+ FAT_GetFilename (file, filename);
+
+ if ((file.attrib & ATTRIB_DIR) != 0)
+ {
+ return FT_DIR; // Found a directory
+ }
+ else
+ {
+ return FT_FILE; // Found a file
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindFirstFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn)
+{
+ FILE_TYPE type;
+ type = FAT_FindNextFile(NULL);
+ FAT_GetLongFilename (lfn);
+ return type;
+}
+
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists(const char* filename)
+{
+ DIR_ENT dirEntry;
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (filename);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return FT_NONE;
+ }
+ else if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return FT_DIR;
+ }
+ else
+ {
+ return FT_FILE;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void)
+{
+ return filesysType;
+}
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void)
+{
+ return filesysTotalSize;
+}
+
+
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path)
+{
+ DIR_ENT dir;
+ if (path[0] == '/' && path[1] == '\0')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ return true;
+ }
+ if (path[0] == '\0') // Return true if changing relative to nothing
+ {
+ return true;
+ }
+
+ dir = FAT_DirEntFromPath (path);
+
+ if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE))
+ {
+ // Change directory
+ curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16);
+
+ // Move to correct cluster for root directory
+ if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER)
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ // Reset file position in directory
+ wrkDirCluster = curWorkDirCluster;
+ wrkDirSector = 0;
+ wrkDirOffset = -1;
+ return true;
+ }
+ else
+ {
+ // Couldn't change directory - wrong path specified
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns NULL if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode)
+{
+ int fileNum;
+ FAT_FILE* file;
+ DIR_ENT dirEntry;
+#ifdef CAN_WRITE_TO_DISC
+ u32 startCluster;
+ int clusCount;
+#endif
+
+ char* pchTemp;
+ // Check that a valid mode was specified
+ pchTemp = strpbrk ( mode, "rRwWaA" );
+ if (pchTemp == NULL)
+ {
+ return NULL;
+ }
+ if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL)
+ {
+ return NULL;
+ }
+
+ // Get the dirEntry for the path specified
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Check that it is not a directory
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ return NULL;
+ }
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check that it is not a read only file being openned in a writing mode
+ if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO))
+ {
+ return NULL;
+ }
+#else
+ if ( (strpbrk(mode, "wWaA+") != NULL))
+ {
+ return NULL;
+ }
+#endif
+
+ // Find a free file buffer
+ for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++);
+
+ if (fileNum == MAX_FILES_OPEN) // No free files
+ {
+ return NULL;
+ }
+
+ file = &openFiles[fileNum];
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+
+ if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // File must exist
+ {
+ return NULL;
+ }
+
+ file->read = true;
+#ifdef CAN_WRITE_TO_DISC
+ file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+#else
+ file->write = false;
+#endif
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+
+#ifdef CAN_WRITE_TO_DISC
+ // Check if file is openned for random. If it is, and currently has no cluster, one must be
+ // assigned to it.
+ if (file->write && file->firstCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+#endif
+
+ file->length = dirEntry.fileSize;
+ file->curPos = 0;
+ file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ } // mode "r"
+
+#ifdef CAN_WRITE_TO_DISC
+ if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ }
+ else // Already a file entry
+ {
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+ }
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (dirEntry.name[0] == FILE_FREE) // No file
+ {
+ // Have to create a new entry
+ if(!FAT_AddDirEntry (path, dirEntry))
+ {
+ return NULL;
+ }
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // Already a file
+ {
+ // Just modify the old entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+ }
+
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
+ file->write = true;
+ file->append = false;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = 0; // Should always have 0 bytes if openning in "w" mode
+ file->curPos = 0;
+ file->curClus = startCluster;
+ file->curSect = 0;
+ file->curByte = 0;
+
+ // Not appending
+ file->appByte = 0;
+ file->appClus = 0;
+ file->appSect = 0;
+
+ // Empty file, so empty read buffer
+ memset (file->readBuffer, 0, BYTE_PER_READ);
+ file->inUse = true; // We're using this file now
+
+ return file;
+ }
+
+ if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A')
+ {
+ if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
+ {
+ dirEntry.attrib = ATTRIB_ARCH;
+ dirEntry.reserved = 0;
+
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+
+ // The file has no data in it
+ dirEntry.fileSize = 0;
+
+ // Get a cluster to use
+ startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+ dirEntry.startCluster = (startCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
+
+ if(!FAT_AddDirEntry (path, dirEntry))
+ return NULL;
+
+ // Get the newly created dirEntry
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ // Remember where directory entry was
+ file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
+ file->dirEntOffset = wrkDirOffset;
+ }
+ else // File already exists - reuse the old directory entry
+ {
+ startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
+ // If it currently has no cluster, one must be assigned to it.
+ if (startCluster == CLUSTER_FREE)
+ {
+ file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
+ if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
+ {
+ return NULL;
+ }
+
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (file->firstCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+ ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Store append cluster
+ file->appClus = startCluster;
+
+ } else {
+
+ // Follow cluster list until last one is found
+ clusCount = dirEntry.fileSize / filesysBytePerClus;
+ file->appClus = startCluster;
+ while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF))
+ {
+ file->appClus = FAT_NextCluster (file->appClus);
+ }
+ if (clusCount >= 0) // Check if ran out of clusters
+ {
+ // Set flag to allocate new cluster when needed
+ file->appSect = filesysSecPerClus;
+ file->appByte = 0;
+ }
+ }
+ }
+
+ // Now that file is created, open it
+ file->read = ( strchr(mode, '+') != NULL );
+ file->write = false;
+ file->append = true;
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ;
+ file->appByte = dirEntry.fileSize % BYTE_PER_READ;
+
+ // Store information about position within the file, for use
+ // by FAT_fread, FAT_fseek, etc.
+ file->firstCluster = startCluster;
+ file->length = dirEntry.fileSize;
+ file->curPos = dirEntry.fileSize;
+ file->curClus = file->appClus;
+ file->curSect = file->appSect;
+ file->curByte = file->appByte;
+
+ // Read into buffer
+ disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer);
+ file->inUse = true; // We're using this file now
+ return file;
+ }
+#endif
+
+ // Can only reach here if a bad mode was specified
+ return NULL;
+}
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file)
+{
+ // Clear memory used by file information
+ if ((file != NULL) && (file->inUse == true))
+ {
+#ifdef CAN_WRITE_TO_DISC
+ if (file->write || file->append)
+ {
+ // Write new length, time and date back to directory entry
+ disc_ReadSector (file->dirEntSector, globalBuffer);
+
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length;
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate();
+ ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate();
+
+ disc_WriteSector (file->dirEntSector, globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ }
+#endif
+ file->inUse = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file)
+{
+ // Return the position as specified in the FAT_FILE structure
+ if ((file != NULL) && (file->inUse == true))
+ {
+ return file->curPos;
+ }
+ else
+ {
+ // Return -1 if no file was given
+ return -1;
+ }
+}
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+FAT_FILE* file: IN handle of an open file
+s32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin)
+{
+ u32 cluster, nextCluster;
+ int clusCount;
+ u32 position;
+ u32 curPos;
+
+ if ((file == NULL) || (file->inUse == false)) // invalid file
+ {
+ return -1;
+ }
+
+ // Can't seek in append only mode
+ if (!file->read && !file->write)
+ {
+ return -1;
+ }
+
+ curPos = file->curPos;
+
+ switch (origin)
+ {
+ case SEEK_SET:
+ if (offset >= 0)
+ {
+ position = offset;
+ } else {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ break;
+ case SEEK_CUR:
+ if (offset >= 0)
+ {
+ position = curPos + offset;
+ }
+ else if ( (u32)(offset * -1) >= curPos )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = curPos - (u32)(offset * -1);
+ }
+ break;
+ case SEEK_END:
+ if (offset >= 0)
+ {
+ // Seeking to end of file
+ position = file->length; // Fixed thanks to MoonLight
+ }
+ else if ( (u32)(offset * -1) >= file->length )
+ {
+ // Tried to seek before start of file
+ position = 0;
+ }
+ else
+ {
+ // Using u32 to maintain 32 bits of accuracy
+ position = file->length - (u32)(offset * -1);
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (position > file->length)
+ {
+ // Tried to go past end of file
+ position = file->length;
+ }
+
+ // Save position
+ file->curPos = position;
+
+
+ // Calculate where the correct cluster is
+ if (position > curPos)
+ {
+ clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ
+ cluster = file->curClus;
+ } else {
+ clusCount = position / filesysBytePerClus;
+ cluster = file->firstCluster;
+ }
+
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ;
+ file->curByte = position % BYTE_PER_READ;
+
+ // Follow cluster list until desired one is found
+ if (clusCount > 0) // Only look at next cluster if need to
+ {
+ nextCluster = FAT_NextCluster (cluster);
+ } else {
+ nextCluster = cluster;
+ }
+ while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
+ {
+ cluster = nextCluster;
+ nextCluster = FAT_NextCluster (cluster);
+ }
+ // Check if ran out of clusters, and the file is being written to
+ if ((clusCount >= 0) && (file->write || file->append))
+ {
+ // Set flag to allocate a new cluster
+ file->curSect = filesysSecPerClus;
+ file->curByte = 0;
+ }
+ file->curClus = cluster;
+
+ // Reload sector buffer for new position in file, if it is a different sector
+ if ((curPos ^ position) >= BYTE_PER_READ)
+ {
+ disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in size * count bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ char* data = (char*)buffer;
+
+ u32 length = size * count;
+ u32 remain;
+
+ bool flagNoError = true;
+
+ // Can't read non-existant files
+ if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL)
+ return 0;
+
+ // Can only read files openned for reading
+ if (!file->read)
+ return 0;
+
+ // Don't read past end of file
+ if (length + file->curPos > file->length)
+ length = file->length - file->curPos;
+
+ remain = length;
+
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(data, &(file->readBuffer[curByte]), tempVar);
+ remain -= tempVar;
+ data += tempVar;
+
+ curByte += tempVar;
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // align to cluster
+ // tempVar is number of sectors to read
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+
+ curSect += tempVar;
+ }
+
+ // Move onto next cluster
+ // It should get to here without reading anything if a cluster is due to be allocated
+ if (curSect >= filesysSecPerClus)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read in whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+
+ // Advance to next cluster
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
+ {
+ curSect = filesysSecPerClus;
+ } else {
+ curSect = 0;
+ curClus = tempNextCluster;
+ if (curClus == CLUSTER_FREE)
+ {
+ flagNoError = false;
+ }
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer);
+ if (remain > 0)
+ {
+ memcpy(data, file->readBuffer, remain);
+ curByte += remain;
+ remain = 0;
+ }
+ }
+
+ // Length read is the wanted length minus the stuff not read
+ length = length - remain;
+
+ // Update file information
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ return length;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file)
+{
+ int curByte;
+ int curSect;
+ u32 curClus;
+
+ u32 tempNextCluster;
+ int tempVar;
+ u32 length = size * count;
+ u32 remain = length;
+ char* data = (char*)buffer;
+
+ char* writeBuffer;
+
+ bool flagNoError = true;
+ bool flagAppending = false;
+
+ if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL)
+ return 0;
+
+ if (file->write)
+ {
+ // Write at current read pointer
+ curByte = file->curByte;
+ curSect = file->curSect;
+ curClus = file->curClus;
+
+ // Use read buffer as write buffer
+ writeBuffer = file->readBuffer;
+
+ // If it is writing past the current end of file, set appending flag
+ if (length + file->curPos > file->length)
+ {
+ flagAppending = true;
+ }
+ }
+ else if (file->append)
+ {
+ // Write at end of file
+ curByte = file->appByte;
+ curSect = file->appSect;
+ curClus = file->appClus;
+ flagAppending = true;
+
+ // Use global buffer as write buffer, don't touch read buffer
+ writeBuffer = (char*)globalBuffer;
+ disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer);
+ }
+ else
+ {
+ return 0;
+ }
+
+ // Move onto next cluster if needed
+ if (curSect >= filesysSecPerClus)
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ memset(writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ curClus = tempNextCluster;
+ disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Align to sector
+ tempVar = BYTE_PER_READ - curByte;
+ if (tempVar > remain)
+ tempVar = remain;
+
+ if ((tempVar < BYTE_PER_READ) && flagNoError)
+ {
+ memcpy(&(writeBuffer[curByte]), data, tempVar);
+ remain -= tempVar;
+ data += tempVar;
+ curByte += tempVar;
+
+ // Write buffer back to disk
+ disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer);
+
+ // Move onto next sector
+ if (curByte >= BYTE_PER_READ)
+ {
+ curByte = 0;
+ curSect++;
+ }
+ }
+
+ // Align to cluster
+ // tempVar is number of sectors to write
+ if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
+ {
+ tempVar = filesysSecPerClus - curSect;
+ } else {
+ tempVar = remain / BYTE_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0))
+ {
+ curSect = 0;
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= filesysBytePerClus) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
+ data += filesysBytePerClus;
+ remain -= filesysBytePerClus;
+ if (remain > 0)
+ {
+ tempNextCluster = FAT_NextCluster(curClus);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
+ {
+ // Ran out of clusters so get a new one
+ curClus = FAT_LinkFreeCluster(curClus);
+ if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
+ {
+ flagNoError = false;
+ break;
+ }
+ } else {
+ curClus = tempNextCluster;
+ }
+ } else {
+ // Allocate a new cluster when next writing the file
+ curSect = filesysSecPerClus;
+ }
+ }
+
+ // Write remaining sectors
+ tempVar = remain / BYTE_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError)
+ {
+ disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data);
+ data += tempVar * BYTE_PER_READ;
+ remain -= tempVar * BYTE_PER_READ;
+ curSect += tempVar;
+ }
+
+ // Last remaining sector
+ // Check if sector wanted is different to the one started with
+ if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError)
+ {
+ if (flagAppending)
+ {
+ // Zero sector before using it
+ memset (writeBuffer, 0, BYTE_PER_READ);
+ } else {
+ // Modify existing sector
+ disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ if (remain > 0) {
+ memcpy(writeBuffer, data, remain);
+ curByte += remain;
+ remain = 0;
+ disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
+ }
+ }
+
+ // Amount read is the originally requested amount minus stuff remaining
+ length = length - remain;
+
+ // Update file information
+ if (file->write) // Writing also shifts the read pointer
+ {
+ file->curByte = curByte;
+ file->curSect = curSect;
+ file->curClus = curClus;
+ file->curPos = file->curPos + length;
+ if (file->length < file->curPos)
+ {
+ file->length = file->curPos;
+ }
+ }
+ else if (file->append) // Appending doesn't affect the read pointer
+ {
+ file->appByte = curByte;
+ file->appSect = curSect;
+ file->appClus = curClus;
+ file->length = file->length + length;
+ }
+
+ return length;
+}
+#endif
+
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file)
+{
+ if ((file == NULL) || (file->inUse == false))
+ return true; // Return eof on invalid files
+
+ return (file->length == file->curPos);
+}
+
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path)
+{
+ DIR_ENT dirEntry;
+ u32 oldWorkDirCluster;
+ char checkFilename[13];
+ FILE_TYPE checkFiletype;
+
+ dirEntry = FAT_DirEntFromPath (path);
+
+ if (dirEntry.name[0] == FILE_FREE)
+ {
+ return -1;
+ }
+
+ // Only delete directories if the directory is entry
+ if (dirEntry.attrib & ATTRIB_DIR)
+ {
+ // Change to the directory temporarily
+ oldWorkDirCluster = curWorkDirCluster;
+ FAT_chdir(path);
+
+ // Search for files or directories, excluding the . and .. entries
+ checkFiletype = FAT_FindFirstFile (checkFilename);
+ while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE))
+ {
+ checkFiletype = FAT_FindNextFile (checkFilename);
+ }
+
+ // Change back to working directory
+ curWorkDirCluster = oldWorkDirCluster;
+
+ // Check that the directory is empty
+ if (checkFiletype != FT_NONE)
+ {
+ // Directory isn't empty
+ return -1;
+ }
+ }
+
+ // Refresh directory information
+ dirEntry = FAT_DirEntFromPath (path);
+
+ // Free any clusters used
+ FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
+
+ // Remove Directory entry
+ disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+ ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE;
+ disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+
+ return 0;
+}
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path)
+{
+ u32 newDirCluster;
+ u32 parentDirCluster;
+ DIR_ENT dirEntry;
+ DIR_ENT* entries = (DIR_ENT*)globalBuffer;
+ int i;
+
+ int pathPos, filePos;
+ char pathname[MAX_FILENAME_LENGTH];
+ u32 oldDirCluster;
+
+ if (FAT_FileExists(path) != FT_NONE)
+ {
+ return -1; // File or directory exists with that name
+ }
+
+ // Find filename within path and change to that directory
+ oldDirCluster = curWorkDirCluster;
+ if (path[0] == '/')
+ {
+ curWorkDirCluster = filesysRootDirClus;
+ }
+
+ pathPos = 0;
+ filePos = 0;
+
+ while (path[pathPos + filePos] != '\0')
+ {
+ if (path[pathPos + filePos] == '/')
+ {
+ pathname[filePos] = '\0';
+ if (FAT_chdir(pathname) == false)
+ {
+ curWorkDirCluster = oldDirCluster;
+ return -1; // Couldn't change directory
+ }
+ pathPos += filePos + 1;
+ filePos = 0;
+ }
+ pathname[filePos] = path[pathPos + filePos];
+ filePos++;
+ }
+
+ // Now grab the parent directory's cluster
+ parentDirCluster = curWorkDirCluster;
+ curWorkDirCluster = oldDirCluster;
+
+ // Get a new cluster for the file
+ newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE);
+
+ if (newDirCluster == CLUSTER_FREE)
+ {
+ return -1; // Couldn't get a new cluster for the directory
+ }
+ // Fill in directory entry's information
+ dirEntry.attrib = ATTRIB_DIR;
+ dirEntry.reserved = 0;
+ // Time and date set to system time and date
+ dirEntry.cTime_ms = 0;
+ dirEntry.cTime = getRTCtoFileTime();
+ dirEntry.cDate = getRTCtoFileDate();
+ dirEntry.aDate = getRTCtoFileDate();
+ dirEntry.mTime = getRTCtoFileTime();
+ dirEntry.mDate = getRTCtoFileDate();
+ // Store cluster position into the directory entry
+ dirEntry.startCluster = (newDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF);
+ // The file has no data in it - its over written so should be empty
+ dirEntry.fileSize = 0;
+
+ if (FAT_AddDirEntry (path, dirEntry) == false)
+ {
+ return -1; // Couldn't add the directory entry
+ }
+
+ // Create the new directory itself
+ memset(entries, FILE_LAST, BYTE_PER_READ);
+
+ // Create . directory entry
+ dirEntry.name[0] = '.';
+ // Fill name and extension with spaces
+ for (i = 1; i < 11; i++)
+ {
+ dirEntry.name[i] = ' ';
+ }
+
+ memcpy(entries, &dirEntry, sizeof(dirEntry));
+
+ // Create .. directory entry
+ dirEntry.name[1] = '.';
+ dirEntry.startCluster = (parentDirCluster & 0xFFFF);
+ dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF);
+
+ memcpy(&entries[1], &dirEntry, sizeof(dirEntry));
+
+ // Write entry to disc
+ disc_WriteSector(FAT_ClustToSect(newDirCluster), entries);
+
+ // Flush any sectors in disc cache
+ disc_CacheFlush();
+ return 0;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file)
+{
+ char c;
+ return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file)
+{
+ return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF;
+}
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file)
+{
+ u32 curPos;
+ u32 readLength;
+ char *returnChar;
+
+ // invalid filehandle
+ if (file == NULL)
+ {
+ return NULL ;
+ }
+
+ // end of file
+ if (FAT_feof(file)==true)
+ {
+ return NULL ;
+ }
+
+ // save current position
+ curPos = FAT_ftell(file);
+
+ // read the full buffer (max string chars is num-1 and one end of string \0
+ readLength = FAT_fread(tgtBuffer,1,num-1,file) ;
+
+ // mark least possible end of string
+ tgtBuffer[readLength] = '\0' ;
+
+ if (readLength==0) {
+ // return error
+ return NULL ;
+ }
+
+ // get position of first return '\r'
+ returnChar = strchr(tgtBuffer,'\r');
+
+ // if no return is found, search for a newline
+ if (returnChar == NULL)
+ {
+ returnChar = strchr(tgtBuffer,'\n');
+ }
+
+ // Mark the return, if existant, as end of line/string
+ if (returnChar!=NULL) {
+ *returnChar++ = 0 ;
+ if (*returnChar=='\n') { // catch newline too when jumping over the end
+ // return to location after \r\n (strlen+2)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ;
+ return tgtBuffer ;
+ } else {
+ // return to location after \r (strlen+1)
+ FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ;
+ return tgtBuffer ;
+ }
+ }
+
+ return tgtBuffer ;
+}
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file)
+{
+ u32 writtenBytes;
+ // save string except end of string '\0'
+ writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file);
+
+ // check if we had an error
+ if (writtenBytes != strlen(string))
+ {
+ // return EOF error
+ return EOF;
+ }
+
+ // return the charcount written
+ return writtenBytes ;
+}
+#endif
+
+
+
diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.h b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h
new file mode 100644
index 0000000000..8c44fafafb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h
@@ -0,0 +1,888 @@
+/*
+ gba_nds_fat.h
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+
+#ifndef _GBA_NDS_FAT_INCLUDED
+#define _GBA_NDS_FAT_INCLUDED
+
+//---------------------------------------------------------------
+// Customisable features
+
+// Maximum number of files open at once
+// Increase this to open more files, decrease to save memory
+#define MAX_FILES_OPEN 4
+
+// Allow file writing
+// Disable this to remove file writing support
+#define CAN_WRITE_TO_DISC
+
+// Allow file time functions
+// This adds ~ 14KB to the compiled size
+// Uncomment to enable
+// #define FILE_TIME_SUPPORT
+
+//---------------------------------------------------------------
+// Platform specific includes
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+ #include <time.h>
+#endif
+
+//---------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+//---------------------------------------------------------------
+
+//---------------------------------------------------------------
+// Important constants
+
+
+#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one
+
+// File Constants
+#ifndef EOF
+#define EOF -1
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+// File attributes
+#define ATTRIB_ARCH 0x20 // Archive
+#define ATTRIB_DIR 0x10 // Directory
+#define ATTRIB_LFN 0x0F // Long file name
+#define ATTRIB_VOL 0x08 // Volume
+#define ATTRIB_SYS 0x04 // System
+#define ATTRIB_HID 0x02 // Hidden
+#define ATTRIB_RO 0x01 // Read only
+
+
+// Directory Constants
+typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+// Open file information structure
+typedef struct
+{
+ u32 firstCluster;
+ u32 length;
+ u32 curPos;
+ u32 curClus; // Current cluster to read from
+ int curSect; // Current sector within cluster
+ int curByte; // Current byte within sector
+ char readBuffer[512]; // Buffer used for unaligned reads
+ u32 appClus; // Cluster to append to
+ int appSect; // Sector within cluster for appending
+ int appByte; // Byte within sector for appending
+ bool read; // Can read from file
+ bool write; // Can write to file
+ bool append;// Can append to file
+ bool inUse; // This file is open
+ u32 dirEntSector; // The sector where the directory entry is stored
+ int dirEntOffset; // The offset within the directory sector
+} FAT_FILE;
+
+
+//-----------------------------------------------------------------
+// CF Card functions
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias);
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonShine
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask);
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists (const char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void);
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path);
+
+
+//-----------------------------------------------------------------
+// File functions
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns -1 if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+int file: IN handle of an open file
+u32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in length number of bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path);
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* handle IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ;
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file);
+#endif
+
+//------------------------------------------------------------------
+#ifdef __cplusplus
+} // extern "C"
+#endif
+//------------------------------------------------------------------
+
+#endif // ifndef _GBA_NDS_FAT
+
+/*
+ gba_nds_fat.h
+ By chishm (Michael Chisholm)
+
+ Routines for reading a compact flash card
+ using the GBA Movie Player or M3.
+
+ Some FAT routines are based on those in fat.c, which
+ is part of avrlib by Pascal Stang.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+//---------------------------------------------------------------
+
+#ifndef _GBA_NDS_FAT_INCLUDED
+#define _GBA_NDS_FAT_INCLUDED
+
+//---------------------------------------------------------------
+// Customisable features
+
+// Maximum number of files open at once
+// Increase this to open more files, decrease to save memory
+#define MAX_FILES_OPEN 4
+
+// Allow file writing
+// Disable this to remove file writing support
+#define CAN_WRITE_TO_DISC
+
+// Allow file time functions
+// This adds ~ 14KB to the compiled size
+// Uncomment to enable
+// #define FILE_TIME_SUPPORT
+
+//---------------------------------------------------------------
+// Platform specific includes
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+ #include <time.h>
+#endif
+
+//---------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+//---------------------------------------------------------------
+
+//---------------------------------------------------------------
+// Important constants
+
+
+#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one
+
+// File Constants
+#ifndef EOF
+#define EOF -1
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+// File attributes
+#define ATTRIB_ARCH 0x20 // Archive
+#define ATTRIB_DIR 0x10 // Directory
+#define ATTRIB_LFN 0x0F // Long file name
+#define ATTRIB_VOL 0x08 // Volume
+#define ATTRIB_SYS 0x04 // System
+#define ATTRIB_HID 0x02 // Hidden
+#define ATTRIB_RO 0x01 // Read only
+
+
+// Directory Constants
+typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+// Open file information structure
+typedef struct
+{
+ u32 firstCluster;
+ u32 length;
+ u32 curPos;
+ u32 curClus; // Current cluster to read from
+ int curSect; // Current sector within cluster
+ int curByte; // Current byte within sector
+ char readBuffer[512]; // Buffer used for unaligned reads
+ u32 appClus; // Cluster to append to
+ int appSect; // Sector within cluster for appending
+ int appByte; // Byte within sector for appending
+ bool read; // Can read from file
+ bool write; // Can write to file
+ bool append;// Can append to file
+ bool inUse; // This file is open
+ u32 dirEntSector; // The sector where the directory entry is stored
+ int dirEntOffset; // The offset within the directory sector
+} FAT_FILE;
+
+
+//-----------------------------------------------------------------
+// CF Card functions
+
+/*-----------------------------------------------------------------
+FAT_InitFiles
+Reads the FAT information from the CF card.
+You need to call this before reading any files.
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_InitFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_FreeFiles
+Closes all open files then resets the CF card.
+Call this before exiting back to the GBAMP
+bool return OUT: true if successful.
+-----------------------------------------------------------------*/
+bool FAT_FreeFiles (void);
+
+/*-----------------------------------------------------------------
+FAT_GetAlias
+Get the alias (short name) of the last file or directory entry read
+ using GetDirEntry. Works for FindFirstFile and FindNextFile
+char* alias OUT: will be filled with the alias (short filename),
+ should be at least 13 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetAlias (char* alias);
+
+/*-----------------------------------------------------------------
+FAT_GetLongFilename
+Get the long name of the last file or directory retrived with
+ GetDirEntry. Also works for FindFirstFile and FindNextFile
+char* filename: OUT will be filled with the filename, should be at
+ least 256 bytes long
+bool return OUT: return true if successful
+-----------------------------------------------------------------*/
+bool FAT_GetLongFilename (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSize
+Get the file size of the last file found or openned.
+This idea is based on a modification by MoonShine
+u32 return OUT: the file size
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSize (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileCluster
+Get the first cluster of the last file found or openned.
+u32 return OUT: the file start cluster
+-----------------------------------------------------------------*/
+u32 FAT_GetFileCluster (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileAttributes
+Get the attributes of the last file found or openned.
+u8 return OUT: the file's attributes
+-----------------------------------------------------------------*/
+u8 FAT_GetFileAttributes (void);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_FAT_SetFileAttributes
+Set the attributes of a file.
+const char* filename IN: The name and path of the file to modify
+u8 attributes IN: The attribute values to assign
+u8 mask IN: Detemines which attributes are changed
+u8 return OUT: the file's new attributes
+-----------------------------------------------------------------*/
+u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask);
+#endif
+
+#ifdef FILE_TIME_SUPPORT
+/*-----------------------------------------------------------------
+FAT_GetFileCreationTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileCreationTime (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileLastWriteTime
+Get the creation time of the last file found or openned.
+time_t return OUT: the file's creation time
+-----------------------------------------------------------------*/
+time_t FAT_GetFileLastWriteTime (void);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_FindNextFile
+Gets the name of the next directory entry
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFile
+Gets the name of the first directory entry and resets the count
+ (can be a file or subdirectory)
+char* filename: OUT filename, must be at least 13 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFile (char* filename);
+
+/*-----------------------------------------------------------------
+FAT_FindFirstFileLFN
+Gets the long file name of the first directory entry and resets
+ the count (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindFirstFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FindNextFileLFN
+Gets the long file name of the next directory entry
+ (can be a file or subdirectory)
+char* lfn: OUT long file name, must be at least 256 chars long
+FILE_TYPE return: OUT returns FT_NONE if failed,
+ FT_FILE if it found a file and FT_DIR if it found a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FindNextFileLFN(char* lfn);
+
+/*-----------------------------------------------------------------
+FAT_FileExists
+Returns the type of file
+char* filename: IN filename of the file to look for
+FILE_TYPE return: OUT returns FT_NONE if there is now file with
+ that name, FT_FILE if it is a file and FT_DIR if it is a directory
+-----------------------------------------------------------------*/
+FILE_TYPE FAT_FileExists (const char* filename);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemType
+FS_TYPE return: OUT returns the current file system type
+-----------------------------------------------------------------*/
+FS_TYPE FAT_GetFileSystemType (void);
+
+/*-----------------------------------------------------------------
+FAT_GetFileSystemTotalSize
+u32 return: OUT returns the total disk space (used + free)
+-----------------------------------------------------------------*/
+u32 FAT_GetFileSystemTotalSize (void);
+
+/*-----------------------------------------------------------------
+FAT_chdir
+Changes the current working directory
+const char* path: IN null terminated string of directory separated by
+ forward slashes, / is root
+bool return: OUT returns true if successful
+-----------------------------------------------------------------*/
+bool FAT_chdir (const char* path);
+
+
+//-----------------------------------------------------------------
+// File functions
+
+/*-----------------------------------------------------------------
+FAT_fopen(filename, mode)
+Opens a file
+const char* path: IN null terminated string of filename and path
+ separated by forward slashes, / is root
+const char* mode: IN mode to open file in
+ Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
+ "b" or "t" in any mode, as all files are openned in binary mode
+FAT_FILE* return: OUT handle to open file, returns -1 if the file
+ couldn't be openned
+-----------------------------------------------------------------*/
+FAT_FILE* FAT_fopen(const char* path, const char* mode);
+
+/*-----------------------------------------------------------------
+FAT_fclose(file)
+Closes a file
+FAT_FILE* file: IN handle of the file to close
+bool return OUT: true if successful, false if not
+-----------------------------------------------------------------*/
+bool FAT_fclose (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_ftell(file)
+Returns the current position in a file
+FAT_FILE* file: IN handle of an open file
+u32 OUT: Current position
+-----------------------------------------------------------------*/
+u32 FAT_ftell (FAT_FILE* file);
+
+/*-----------------------------------------------------------------
+FAT_fseek(file, offset, origin)
+Seeks to specified byte position in file
+int file: IN handle of an open file
+u32 offset IN: position to seek to, relative to origin
+int origin IN: origin to seek from
+int OUT: Returns 0 if successful, -1 if not
+-----------------------------------------------------------------*/
+int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
+
+/*-----------------------------------------------------------------
+FAT_fread(buffer, size, count, file)
+Reads in length number of bytes into buffer from file, starting
+ from current position. It then sets the current position to the
+ byte after the last byte read. If it reaches the end of file
+ before filling the buffer then it stops reading.
+void* buffer OUT: Pointer to buffer to fill. Should be at least as
+ big as the number of bytes required
+u32 size IN: size of each item to read
+u32 count IN: number of items to read
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes read
+-----------------------------------------------------------------*/
+u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fwrite(buffer, size, count, file)
+Writes size * count bytes into file from buffer, starting
+ from current position. It then sets the current position to the
+ byte after the last byte written. If the file was openned in
+ append mode it always writes to the end of the file.
+const void* buffer IN: Pointer to buffer containing data. Should be
+ at least as big as the number of bytes to be written.
+u32 size IN: size of each item to write
+u32 count IN: number of items to write
+FAT_FILE* file IN: Handle of an open file
+u32 OUT: returns the actual number of bytes written
+-----------------------------------------------------------------*/
+u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_feof(file)
+Returns true if the end of file has been reached
+FAT_FILE* file IN: Handle of an open file
+bool return OUT: true if EOF, false if not
+-----------------------------------------------------------------*/
+bool FAT_feof(FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_remove (path)
+Deletes the file or empty directory sepecified in path
+const char* path IN: Path of item to delete
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_remove (const char* path);
+#endif
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_mkdir (path)
+Makes a new directory, so long as no other directory or file has
+ the same name.
+const char* path IN: Path and filename of directory to make
+int return OUT: zero if successful, non-zero if not
+-----------------------------------------------------------------*/
+int FAT_mkdir (const char* path);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgetc (handle)
+Gets the next character in the file
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fgetc (FAT_FILE* file);
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputc (character, handle)
+Writes the given character into the file
+char c IN: Character to be written
+FAT_FILE* handle IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+-----------------------------------------------------------------*/
+char FAT_fputc (char c, FAT_FILE* file);
+#endif
+
+/*-----------------------------------------------------------------
+FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
+Gets a up to num bytes from file, stopping at the first
+ newline.
+
+CAUTION: does not do strictly streaming. I.e. it's
+ reading more then needed bytes and seeking back.
+ shouldn't matter for random access
+
+char *tgtBuffer OUT: buffer to write to
+int num IN: size of target buffer
+FAT_FILE* file IN: Handle of open file
+bool return OUT: character if successful, EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Added check for unix style text files
+ * Removed seek when no newline is found, since it isn't necessary
+-------------------------------------------------------------------*/
+char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ;
+
+#ifdef CAN_WRITE_TO_DISC
+/*-----------------------------------------------------------------
+FAT_fputs (const char *string, FAT_FILE* file)
+Writes string to file, excluding end of string character
+const char *string IN: string to write
+FAT_FILE* file IN: Handle of open file
+bool return OUT: number of characters written if successful,
+ EOF if not
+
+ Written by MightyMax
+ Modified by Chishm - 2005-11-17
+ * Uses FAT_FILE instead of int
+ * writtenBytes is now u32 instead of int
+-------------------------------------------------------------------*/
+int FAT_fputs (const char *string, FAT_FILE* file);
+#endif
+
+//------------------------------------------------------------------
+#ifdef __cplusplus
+} // extern "C"
+#endif
+//------------------------------------------------------------------
+
+#endif // ifndef _GBA_NDS_FAT
+
diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.c b/backends/platform/ds/arm9/source/fat/io_efa2.c
new file mode 100644
index 0000000000..f3aa65cfcb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_efa2.c
@@ -0,0 +1,642 @@
+/*
+io_efa2.c by CyteX
+
+Based on io_mpfc.c by chishm (Michael Chisholm)
+
+Hardware Routines for reading the NAND flash located on
+EFA2 flash carts
+
+This software is completely free. No warranty is provided.
+If you use it, please give me credit and email me about your
+project at cytex <at> gmx <dot> de and do not forget to also
+drop chishm <at> hotmail <dot> com a line
+
+See gba_nds_fat.txt for help and license details.
+*/
+
+#include "io_efa2.h"
+
+#ifdef SUPPORT_EFA2
+
+//
+// EFA2 register addresses
+//
+
+// RTC registers
+#define REG_RTC_CLK *(vu16*)0x080000c4
+#define REG_RTC_EN *(vu16*)0x080000c8
+
+// "Magic" registers used for unlock/lock sequences
+#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000
+#define REG_EFA2_MAGIC_B *(vu16*)0x08000000
+#define REG_EFA2_MAGIC_C *(vu16*)0x08020000
+#define REG_EFA2_MAGIC_D *(vu16*)0x08040000
+#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000
+
+// NAND flash lock/unlock register
+#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000
+// NAND flash enable register
+#define REG_EFA2_NAND_EN *(vu16*)0x09400000
+// NAND flash command write register
+#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2
+// NAND flash address/data write register
+#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0
+// NAND flash data read register
+#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000
+
+// ID of Samsung K9K1G NAND flash chip
+#define EFA2_NAND_ID 0xEC79A5C0
+
+// first sector of udisk
+#define EFA2_UDSK_START 0x40
+
+//
+// EFA2 access functions
+//
+
+// deactivate RTC ports
+inline void efa2_rtc_deactivate(void) {
+ REG_RTC_EN = 0;
+}
+
+// unlock register access
+void efa2_reg_unlock(void) {
+ REG_EFA2_MAGIC_A = 0x0d200;
+ REG_EFA2_MAGIC_B = 0x01500;
+ REG_EFA2_MAGIC_C = 0x0d200;
+ REG_EFA2_MAGIC_D = 0x01500;
+}
+
+// finish/lock register access
+inline void efa2_reg_lock(void) {
+ REG_EFA2_MAGIC_E = 0x1500;
+}
+
+// global reset/init/enable/unlock ?
+void efa2_global_unlock(void) {
+ efa2_reg_unlock();
+ *(vu16*)0x09880000 = 0x08000;
+ efa2_reg_lock();
+}
+
+// global lock, stealth mode
+void efa2_global_lock(void) {
+ // quite sure there is such a sequence, but haven't had
+ // a look for it upto now
+}
+
+// unlock NAND Flash
+void efa2_nand_unlock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x01500;
+ efa2_reg_lock();
+}
+
+// lock NAND Flash
+void efa2_nand_lock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x0d200;
+ efa2_reg_lock();
+}
+
+//
+// Set NAND Flash chip enable and write protection bits ?
+//
+// val | ~CE | ~WP |
+// -----+-----+-----+
+// 0 | 0 | 0 |
+// 1 | 1 | 0 |
+// 3 | 1 | 1 |
+// -----+-----+-----+
+//
+void efa2_nand_enable(u16 val) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_EN = val;
+ efa2_reg_lock();
+}
+
+//
+// Perform NAND reset
+// NAND has to be unlocked and enabled when called
+//
+inline void efa2_nand_reset(void) {
+ REG_EFA2_NAND_CMD = 0xff; // write reset command
+}
+
+//
+// Read out NAND ID information, could be used for card detection
+//
+// | EFA2 1GBit |
+// ------------------+------------+
+// maker code | 0xEC |
+// device code | 0x79 |
+// don't care | 0xA5 |
+// multi plane code | 0xC0 |
+// ------------------+------------+
+//
+u32 efa2_nand_id(void) {
+ u8 byte;
+ u32 id;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+
+ REG_EFA2_NAND_CMD = 0x90; // write id command
+ REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle
+ byte = REG_EFA2_NAND_RD; // read maker code
+ id = byte;
+ byte = REG_EFA2_NAND_RD; // read device code
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read don't care
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read multi plane code
+ id = (id << 8) | byte;
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return (id);
+}
+
+//
+// Start of gba_nds_fat block device description
+//
+
+/*-----------------------------------------------------------------
+EFA2_ClearStatus
+Reads and checks NAND status information
+bool return OUT: true if NAND is idle
+-----------------------------------------------------------------*/
+bool EFA2_ClearStatus (void)
+{
+ // tbd: currently there is no write support, so always return
+ // true, there is no possibility for pending operations
+ return true;
+}
+
+/*-----------------------------------------------------------------
+EFA2_IsInserted
+Checks to see if the NAND chip used by the EFA2 is present
+bool return OUT: true if the correct NAND chip is found
+-----------------------------------------------------------------*/
+bool EFA2_IsInserted (void)
+{
+ EFA2_ClearStatus();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+EFA2_ReadSectors
+Read "numSecs" 512 byte sectors starting from "sector" into "buffer"
+No error correction, no use of spare cells, no use of R/~B signal
+u32 sector IN: number of first 512 byte sector to be read
+u8 numSecs IN: number of 512 byte sectors to read,
+1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+
+#ifndef _CF_ALLOW_UNALIGNED
+ u8 byte;
+ u16 word;
+#endif
+
+ // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the
+ // udisk and thus is sector 0. The original EFA2 firmware
+ // does never look at this, it only watches page 0x60, which
+ // contains the boot block of the FAT16 partition. That is
+ // fixed, so the EFA2 udisk must not be reformated, else
+ // the ARK Octopus and also the original Firmware won't be
+ // able to access the udisk anymore and I have to write a
+ // recovery tool.
+ u32 page = EFA2_UDSK_START + sector;
+
+ // future enhancement: wait for possible write operations to
+ // be finisched
+ if (!EFA2_ClearStatus()) return false;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+ efa2_nand_reset();
+
+ // set NAND to READ1 operation mode and transfer page address
+ REG_EFA2_NAND_CMD = 0x00; // write READ1 command
+ REG_EFA2_NAND_WR = 0x00; // write address [7:0]
+ REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8]
+ REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16]
+ REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24]
+
+ // Due to a bug in EFA2 design there is need to waste some cycles
+ // "by hand" instead the possibility to check the R/~B port of
+ // the NAND flash via a register. The RTC deactivation is only
+ // there to make sure the loop won't be optimized by the compiler
+ for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate();
+
+ while (j--)
+ {
+ // read page data
+#ifdef _CF_ALLOW_UNALIGNED
+ // slow byte access to RAM, but works in principle
+ for (i=0 ; i < 512 ; i++)
+ ((u8*)buffer)[i] = REG_EFA2_NAND_RD;
+#else
+ // a bit faster, but DMA is not possible
+ for (i=0 ; i < 256 ; i++) {
+ byte = REG_EFA2_NAND_RD; // read lo-byte
+ word = byte;
+ byte = REG_EFA2_NAND_RD; // read hi-byte
+ word = word | (byte << 8);
+ ((u16*)buffer)[i] = word;
+ }
+#endif
+ }
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+EFA2_WriteSectors
+Write "numSecs" 512 byte sectors starting at "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on card to write
+u8 numSecs IN: number of 512 byte sectors to write
+1 to 256 sectors can be written, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ // Upto now I focused on reading NAND, write operations
+ // will follow
+ return false;
+}
+
+/*-----------------------------------------------------------------
+EFA2_Shutdown
+unload the EFA2 interface
+-----------------------------------------------------------------*/
+bool EFA2_Shutdown(void)
+{
+ return EFA2_ClearStatus();
+}
+
+/*-----------------------------------------------------------------
+EFA2_StartUp
+initializes the EFA2 card, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool EFA2_StartUp(void)
+{
+ efa2_global_unlock();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_efa2 = {
+ DEVICE_TYPE_EFA2,
+ FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&EFA2_StartUp,
+ (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted,
+ (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown
+};
+
+/*-----------------------------------------------------------------
+EFA2_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE EFA2_GetInterface(void) {
+ return &io_efa2;
+}
+
+#endif // SUPPORT_EFA2
+/*
+io_efa2.c by CyteX
+
+Based on io_mpfc.c by chishm (Michael Chisholm)
+
+Hardware Routines for reading the NAND flash located on
+EFA2 flash carts
+
+This software is completely free. No warranty is provided.
+If you use it, please give me credit and email me about your
+project at cytex <at> gmx <dot> de and do not forget to also
+drop chishm <at> hotmail <dot> com a line
+
+See gba_nds_fat.txt for help and license details.
+*/
+
+#include "io_efa2.h"
+
+#ifdef SUPPORT_EFA2
+
+//
+// EFA2 register addresses
+//
+
+// RTC registers
+#define REG_RTC_CLK *(vu16*)0x080000c4
+#define REG_RTC_EN *(vu16*)0x080000c8
+
+// "Magic" registers used for unlock/lock sequences
+#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000
+#define REG_EFA2_MAGIC_B *(vu16*)0x08000000
+#define REG_EFA2_MAGIC_C *(vu16*)0x08020000
+#define REG_EFA2_MAGIC_D *(vu16*)0x08040000
+#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000
+
+// NAND flash lock/unlock register
+#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000
+// NAND flash enable register
+#define REG_EFA2_NAND_EN *(vu16*)0x09400000
+// NAND flash command write register
+#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2
+// NAND flash address/data write register
+#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0
+// NAND flash data read register
+#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000
+
+// ID of Samsung K9K1G NAND flash chip
+#define EFA2_NAND_ID 0xEC79A5C0
+
+// first sector of udisk
+#define EFA2_UDSK_START 0x40
+
+//
+// EFA2 access functions
+//
+
+// deactivate RTC ports
+inline void efa2_rtc_deactivate(void) {
+ REG_RTC_EN = 0;
+}
+
+// unlock register access
+void efa2_reg_unlock(void) {
+ REG_EFA2_MAGIC_A = 0x0d200;
+ REG_EFA2_MAGIC_B = 0x01500;
+ REG_EFA2_MAGIC_C = 0x0d200;
+ REG_EFA2_MAGIC_D = 0x01500;
+}
+
+// finish/lock register access
+inline void efa2_reg_lock(void) {
+ REG_EFA2_MAGIC_E = 0x1500;
+}
+
+// global reset/init/enable/unlock ?
+void efa2_global_unlock(void) {
+ efa2_reg_unlock();
+ *(vu16*)0x09880000 = 0x08000;
+ efa2_reg_lock();
+}
+
+// global lock, stealth mode
+void efa2_global_lock(void) {
+ // quite sure there is such a sequence, but haven't had
+ // a look for it upto now
+}
+
+// unlock NAND Flash
+void efa2_nand_unlock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x01500;
+ efa2_reg_lock();
+}
+
+// lock NAND Flash
+void efa2_nand_lock(void) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_LOCK = 0x0d200;
+ efa2_reg_lock();
+}
+
+//
+// Set NAND Flash chip enable and write protection bits ?
+//
+// val | ~CE | ~WP |
+// -----+-----+-----+
+// 0 | 0 | 0 |
+// 1 | 1 | 0 |
+// 3 | 1 | 1 |
+// -----+-----+-----+
+//
+void efa2_nand_enable(u16 val) {
+ efa2_reg_unlock();
+ REG_EFA2_NAND_EN = val;
+ efa2_reg_lock();
+}
+
+//
+// Perform NAND reset
+// NAND has to be unlocked and enabled when called
+//
+inline void efa2_nand_reset(void) {
+ REG_EFA2_NAND_CMD = 0xff; // write reset command
+}
+
+//
+// Read out NAND ID information, could be used for card detection
+//
+// | EFA2 1GBit |
+// ------------------+------------+
+// maker code | 0xEC |
+// device code | 0x79 |
+// don't care | 0xA5 |
+// multi plane code | 0xC0 |
+// ------------------+------------+
+//
+u32 efa2_nand_id(void) {
+ u8 byte;
+ u32 id;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+
+ REG_EFA2_NAND_CMD = 0x90; // write id command
+ REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle
+ byte = REG_EFA2_NAND_RD; // read maker code
+ id = byte;
+ byte = REG_EFA2_NAND_RD; // read device code
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read don't care
+ id = (id << 8) | byte;
+ byte = REG_EFA2_NAND_RD; // read multi plane code
+ id = (id << 8) | byte;
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return (id);
+}
+
+//
+// Start of gba_nds_fat block device description
+//
+
+/*-----------------------------------------------------------------
+EFA2_ClearStatus
+Reads and checks NAND status information
+bool return OUT: true if NAND is idle
+-----------------------------------------------------------------*/
+bool EFA2_ClearStatus (void)
+{
+ // tbd: currently there is no write support, so always return
+ // true, there is no possibility for pending operations
+ return true;
+}
+
+/*-----------------------------------------------------------------
+EFA2_IsInserted
+Checks to see if the NAND chip used by the EFA2 is present
+bool return OUT: true if the correct NAND chip is found
+-----------------------------------------------------------------*/
+bool EFA2_IsInserted (void)
+{
+ EFA2_ClearStatus();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+EFA2_ReadSectors
+Read "numSecs" 512 byte sectors starting from "sector" into "buffer"
+No error correction, no use of spare cells, no use of R/~B signal
+u32 sector IN: number of first 512 byte sector to be read
+u8 numSecs IN: number of 512 byte sectors to read,
+1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+
+#ifndef _CF_ALLOW_UNALIGNED
+ u8 byte;
+ u16 word;
+#endif
+
+ // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the
+ // udisk and thus is sector 0. The original EFA2 firmware
+ // does never look at this, it only watches page 0x60, which
+ // contains the boot block of the FAT16 partition. That is
+ // fixed, so the EFA2 udisk must not be reformated, else
+ // the ARK Octopus and also the original Firmware won't be
+ // able to access the udisk anymore and I have to write a
+ // recovery tool.
+ u32 page = EFA2_UDSK_START + sector;
+
+ // future enhancement: wait for possible write operations to
+ // be finisched
+ if (!EFA2_ClearStatus()) return false;
+
+ efa2_nand_unlock();
+ efa2_nand_enable(1);
+ efa2_nand_reset();
+
+ // set NAND to READ1 operation mode and transfer page address
+ REG_EFA2_NAND_CMD = 0x00; // write READ1 command
+ REG_EFA2_NAND_WR = 0x00; // write address [7:0]
+ REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8]
+ REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16]
+ REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24]
+
+ // Due to a bug in EFA2 design there is need to waste some cycles
+ // "by hand" instead the possibility to check the R/~B port of
+ // the NAND flash via a register. The RTC deactivation is only
+ // there to make sure the loop won't be optimized by the compiler
+ for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate();
+
+ while (j--)
+ {
+ // read page data
+#ifdef _CF_ALLOW_UNALIGNED
+ // slow byte access to RAM, but works in principle
+ for (i=0 ; i < 512 ; i++)
+ ((u8*)buffer)[i] = REG_EFA2_NAND_RD;
+#else
+ // a bit faster, but DMA is not possible
+ for (i=0 ; i < 256 ; i++) {
+ byte = REG_EFA2_NAND_RD; // read lo-byte
+ word = byte;
+ byte = REG_EFA2_NAND_RD; // read hi-byte
+ word = word | (byte << 8);
+ ((u16*)buffer)[i] = word;
+ }
+#endif
+ }
+
+ efa2_nand_enable(0);
+ efa2_nand_lock();
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+EFA2_WriteSectors
+Write "numSecs" 512 byte sectors starting at "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on card to write
+u8 numSecs IN: number of 512 byte sectors to write
+1 to 256 sectors can be written, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ // Upto now I focused on reading NAND, write operations
+ // will follow
+ return false;
+}
+
+/*-----------------------------------------------------------------
+EFA2_Shutdown
+unload the EFA2 interface
+-----------------------------------------------------------------*/
+bool EFA2_Shutdown(void)
+{
+ return EFA2_ClearStatus();
+}
+
+/*-----------------------------------------------------------------
+EFA2_StartUp
+initializes the EFA2 card, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool EFA2_StartUp(void)
+{
+ efa2_global_unlock();
+ return (efa2_nand_id() == EFA2_NAND_ID);
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_efa2 = {
+ DEVICE_TYPE_EFA2,
+ FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&EFA2_StartUp,
+ (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted,
+ (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown
+};
+
+/*-----------------------------------------------------------------
+EFA2_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE EFA2_GetInterface(void) {
+ return &io_efa2;
+}
+
+#endif // SUPPORT_EFA2
diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.h b/backends/platform/ds/arm9/source/fat/io_efa2.h
new file mode 100644
index 0000000000..27c4e9beb8
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_efa2.h
@@ -0,0 +1,56 @@
+/*
+ io_efa2.h by CyteX
+
+ Based on io_mpfc.h by chishm (Michael Chisholm)
+
+ Hardware Routines for reading the NAND flash located on
+ EFA2 flash carts
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at cytex <at> gmx <dot> de and do not forget to also
+ drop chishm <at> hotmail <dot> com a line
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_EFA2_H
+#define IO_EFA2_H
+
+// 'EFA2'
+#define DEVICE_TYPE_EFA2 0x32414645
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE EFA2_GetInterface(void);
+
+#endif // define IO_EFA2_H
+/*
+ io_efa2.h by CyteX
+
+ Based on io_mpfc.h by chishm (Michael Chisholm)
+
+ Hardware Routines for reading the NAND flash located on
+ EFA2 flash carts
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at cytex <at> gmx <dot> de and do not forget to also
+ drop chishm <at> hotmail <dot> com a line
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_EFA2_H
+#define IO_EFA2_H
+
+// 'EFA2'
+#define DEVICE_TYPE_EFA2 0x32414645
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE EFA2_GetInterface(void);
+
+#endif // define IO_EFA2_H
diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.c b/backends/platform/ds/arm9/source/fat/io_fcsr.c
new file mode 100644
index 0000000000..8ca311ac92
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_fcsr.c
@@ -0,0 +1,658 @@
+/*
+ io_fcsr.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for using a GBA Flash Cart and SRAM as a
+ block device.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ The file system must be 512 byte aligned, in cart address space.
+ SRAM is supported.
+*/
+
+
+#include "io_fcsr.h"
+
+#ifdef SUPPORT_FCSR
+#include <string.h>
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+#ifdef NDS
+ #define SRAM_START 0x0A000000
+#else
+ #define SRAM_START 0x0E000000
+#endif
+
+#define NO_SRAM 0xFFFFFFFF
+
+#define FCSR 0x52534346
+const char FCSR_LabelString[] = " Chishm FAT";
+
+u8* FCSR_FileSysPointer = 0;
+u8* FCSR_SramSectorPointer[4] = {0,0,0,0};
+u32 FCSR_SramSectorStart[4] = {0,0,0,0};
+u32 FCSR_SramSectorEnd[4] = {0,0,0,0};
+
+/*-----------------------------------------------------------------
+FCSR_IsInserted
+Is a GBA Flash Cart with a valid file system inserted?
+bool return OUT: true if a GBA FC card is inserted
+-----------------------------------------------------------------*/
+bool FCSR_IsInserted (void)
+{
+ bool flagFoundFileSys = false;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ return flagFoundFileSys;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ClearStatus
+Finish any pending operations
+bool return OUT: always true for GBA FC
+-----------------------------------------------------------------*/
+bool FCSR_ClearStatus (void)
+{
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int numSectors = (numSecs > 0 ? numSecs : 256);
+ int readLength = numSectors * BYTE_PER_READ;
+ u8* src;;
+ u8* dst;
+
+ // Find which region this read is in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ // Make sure read will be completely in SRAM range if it is partially there
+ if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i]))
+ return false;
+
+ // Copy data to buffer
+ if (flagSramSector)
+ {
+ src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ } else {
+ src = FCSR_FileSysPointer + sector * BYTE_PER_READ;
+ }
+ dst = (u8*)buffer;
+
+ if (flagSramSector)
+ {
+ while (readLength--)
+ {
+ *dst++ = *src++;
+ }
+ } else { // Reading from Cart ROM
+
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ #ifdef ARM9
+ DC_FlushRange( buffer, readLength);
+ #endif // ARM9
+ DMA3_SRC = (u32)src;
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS;
+ #else // ! NDS
+ DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE);
+ #endif // NDS
+#else // !_CF_USE_DMA
+ memcpy (buffer, src, readLength);
+#endif // _CF_USE_DMA
+
+ } // if (flagSramSector)
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ;
+ u8* src = (u8*) buffer;
+ u8* dst;
+
+ // Find which region this sector belongs in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ if (!flagSramSector)
+ return false;
+
+ // Entire write must be within an SRAM region
+ if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i])
+ return false;
+
+ // Copy data to SRAM
+ dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ while (writeLength--)
+ {
+ *dst++ = *src++;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_Shutdown
+unload the Flash Cart interface
+-----------------------------------------------------------------*/
+bool FCSR_Shutdown(void)
+{
+ int i;
+ if (FCSR_ClearStatus() == false)
+ return false;
+
+ FCSR_FileSysPointer = 0;
+
+ for (i=0; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = 0;
+ FCSR_SramSectorStart[i] = 0;
+ FCSR_SramSectorEnd[i] = 0;
+ }
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_StartUp
+initializes the Flash Cart interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool FCSR_StartUp(void)
+{
+ bool flagFoundFileSys = false;
+ int i;
+ int SramRegionSize[4];
+ u8* srcByte;
+ u8* destByte;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ if (!flagFoundFileSys)
+ return false;
+
+ // Flash cart file system pointer has been found
+ FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40);
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ FCSR_SramSectorStart[i] = fileSysPointer[i+4];
+ SramRegionSize[i] = fileSysPointer[i+8];
+ FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i];
+ }
+
+ // Calculate SRAM region pointers
+ FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4);
+ for (i = 1; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ);
+ }
+
+ // Initialise SRAM with overlay if it hasn't been done so
+ if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') )
+ {
+ *((u8*)SRAM_START) = 'F';
+ *((u8*)(SRAM_START+1)) = 'C';
+ *((u8*)(SRAM_START+2)) = 'S';
+ *((u8*)(SRAM_START+3)) = 'R';
+
+ for (i = 0; i < 4; i++)
+ {
+ srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ);
+ destByte = FCSR_SramSectorPointer[i];
+ while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) )
+ *destByte++ = *srcByte++;
+ }
+ }
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ if (SramRegionSize[i] == 0)
+ {
+ FCSR_SramSectorStart[i] = NO_SRAM;
+ FCSR_SramSectorEnd[i] = NO_SRAM;
+ }
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_fcsr = {
+ DEVICE_TYPE_FCSR, // 'FCSR'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&FCSR_StartUp,
+ (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted,
+ (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+FCSR_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE FCSR_GetInterface(void) {
+ return &io_fcsr ;
+} ;
+
+#endif // SUPPORT_FCSR
+/*
+ io_fcsr.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for using a GBA Flash Cart and SRAM as a
+ block device.
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ The file system must be 512 byte aligned, in cart address space.
+ SRAM is supported.
+*/
+
+
+#include "io_fcsr.h"
+
+#ifdef SUPPORT_FCSR
+#include <string.h>
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+#ifdef NDS
+ #define SRAM_START 0x0A000000
+#else
+ #define SRAM_START 0x0E000000
+#endif
+
+#define NO_SRAM 0xFFFFFFFF
+
+#define FCSR 0x52534346
+const char FCSR_LabelString[] = " Chishm FAT";
+
+u8* FCSR_FileSysPointer = 0;
+u8* FCSR_SramSectorPointer[4] = {0,0,0,0};
+u32 FCSR_SramSectorStart[4] = {0,0,0,0};
+u32 FCSR_SramSectorEnd[4] = {0,0,0,0};
+
+/*-----------------------------------------------------------------
+FCSR_IsInserted
+Is a GBA Flash Cart with a valid file system inserted?
+bool return OUT: true if a GBA FC card is inserted
+-----------------------------------------------------------------*/
+bool FCSR_IsInserted (void)
+{
+ bool flagFoundFileSys = false;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ return flagFoundFileSys;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ClearStatus
+Finish any pending operations
+bool return OUT: always true for GBA FC
+-----------------------------------------------------------------*/
+bool FCSR_ClearStatus (void)
+{
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+FCSR_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int numSectors = (numSecs > 0 ? numSecs : 256);
+ int readLength = numSectors * BYTE_PER_READ;
+ u8* src;;
+ u8* dst;
+
+ // Find which region this read is in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ // Make sure read will be completely in SRAM range if it is partially there
+ if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i]))
+ return false;
+
+ // Copy data to buffer
+ if (flagSramSector)
+ {
+ src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ } else {
+ src = FCSR_FileSysPointer + sector * BYTE_PER_READ;
+ }
+ dst = (u8*)buffer;
+
+ if (flagSramSector)
+ {
+ while (readLength--)
+ {
+ *dst++ = *src++;
+ }
+ } else { // Reading from Cart ROM
+
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ #ifdef ARM9
+ DC_FlushRange( buffer, readLength);
+ #endif // ARM9
+ DMA3_SRC = (u32)src;
+ DMA3_DEST = (u32)buffer;
+ DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS;
+ #else // ! NDS
+ DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE);
+ #endif // NDS
+#else // !_CF_USE_DMA
+ memcpy (buffer, src, readLength);
+#endif // _CF_USE_DMA
+
+ } // if (flagSramSector)
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on Flash Cart to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ bool flagSramSector = false;
+ int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ;
+ u8* src = (u8*) buffer;
+ u8* dst;
+
+ // Find which region this sector belongs in
+ for (i = 0; (i < 4) && !flagSramSector; i++)
+ {
+ if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i]))
+ {
+ flagSramSector = true;
+ break;
+ }
+ }
+
+ if (!flagSramSector)
+ return false;
+
+ // Entire write must be within an SRAM region
+ if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i])
+ return false;
+
+ // Copy data to SRAM
+ dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ;
+ while (writeLength--)
+ {
+ *dst++ = *src++;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_Shutdown
+unload the Flash Cart interface
+-----------------------------------------------------------------*/
+bool FCSR_Shutdown(void)
+{
+ int i;
+ if (FCSR_ClearStatus() == false)
+ return false;
+
+ FCSR_FileSysPointer = 0;
+
+ for (i=0; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = 0;
+ FCSR_SramSectorStart[i] = 0;
+ FCSR_SramSectorEnd[i] = 0;
+ }
+ return true;
+}
+
+/*-----------------------------------------------------------------
+FCSR_StartUp
+initializes the Flash Cart interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool FCSR_StartUp(void)
+{
+ bool flagFoundFileSys = false;
+ int i;
+ int SramRegionSize[4];
+ u8* srcByte;
+ u8* destByte;
+
+ u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string
+
+ // Search for file system
+ while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space
+ {
+ while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000))
+ fileSysPointer += 0x40;
+ if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000))
+ {
+ flagFoundFileSys = true;
+ } else {
+ fileSysPointer += 0x80;
+ }
+ }
+
+ if (!flagFoundFileSys)
+ return false;
+
+ // Flash cart file system pointer has been found
+ FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40);
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ FCSR_SramSectorStart[i] = fileSysPointer[i+4];
+ SramRegionSize[i] = fileSysPointer[i+8];
+ FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i];
+ }
+
+ // Calculate SRAM region pointers
+ FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4);
+ for (i = 1; i < 4; i++)
+ {
+ FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ);
+ }
+
+ // Initialise SRAM with overlay if it hasn't been done so
+ if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') )
+ {
+ *((u8*)SRAM_START) = 'F';
+ *((u8*)(SRAM_START+1)) = 'C';
+ *((u8*)(SRAM_START+2)) = 'S';
+ *((u8*)(SRAM_START+3)) = 'R';
+
+ for (i = 0; i < 4; i++)
+ {
+ srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ);
+ destByte = FCSR_SramSectorPointer[i];
+ while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) )
+ *destByte++ = *srcByte++;
+ }
+ }
+
+ // Get SRAM sector regions from header block
+ for (i = 0; i < 4; i++)
+ {
+ if (SramRegionSize[i] == 0)
+ {
+ FCSR_SramSectorStart[i] = NO_SRAM;
+ FCSR_SramSectorEnd[i] = NO_SRAM;
+ }
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_fcsr = {
+ DEVICE_TYPE_FCSR, // 'FCSR'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&FCSR_StartUp,
+ (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted,
+ (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+FCSR_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE FCSR_GetInterface(void) {
+ return &io_fcsr ;
+} ;
+
+#endif // SUPPORT_FCSR
diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.h b/backends/platform/ds/arm9/source/fat/io_fcsr.h
new file mode 100644
index 0000000000..2f87c1c8aa
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_fcsr.h
@@ -0,0 +1,48 @@
+/*
+ io_fcsr.h
+
+ Hardware Routines for using a GBA Flash Cart with SRAM
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_FCSR_H
+#define IO_FCSR_H
+
+// 'FCSR'
+#define DEVICE_TYPE_FCSR 0x52534346
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE FCSR_GetInterface(void) ;
+
+#endif // define IO_FCSR_H
+/*
+ io_fcsr.h
+
+ Hardware Routines for using a GBA Flash Cart with SRAM
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_FCSR_H
+#define IO_FCSR_H
+
+// 'FCSR'
+#define DEVICE_TYPE_FCSR 0x52534346
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE FCSR_GetInterface(void) ;
+
+#endif // define IO_FCSR_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.c b/backends/platform/ds/arm9/source/fat/io_m3cf.c
new file mode 100644
index 0000000000..238be7e311
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3cf.c
@@ -0,0 +1,734 @@
+/*
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3cf.h"
+
+#ifdef SUPPORT_M3CF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control
+#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return
+#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features
+
+#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer
+#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address
+#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address
+#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address
+#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0
+
+#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+M3CF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3CF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED;
+ return ((M3_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3CF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ M3_REG_SEC = numSecs;
+
+ // Set read sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ M3_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)M3_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *M3_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *M3_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *M3_DATA;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+M3CF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ M3_REG_SEC = numSecs;
+
+ // Set write sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ M3_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)M3_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *M3_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *M3_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *M3_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3_Unlock(void)
+{
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ tmp = M3_REG_LBA1;
+ M3_REG_LBA1 = (~tmp & 0xFF);
+ tmp = (~tmp & 0xFF);
+ // did it change?
+ return (M3_REG_LBA1 == tmp) ;
+}
+
+bool M3CF_Shutdown(void) {
+ return M3CF_ClearStatus() ;
+} ;
+
+bool M3CF_StartUp(void) {
+ return M3_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3cf = {
+ DEVICE_TYPE_M3CF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&M3CF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3CF_GetInterface(void) {
+ return &io_m3cf ;
+} ;
+
+#endif // SUPPORT_M3CF
+/*
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3cf.h"
+
+#ifdef SUPPORT_M3CF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control
+#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return
+#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features
+
+#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer
+#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address
+#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address
+#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address
+#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0
+
+#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+M3CF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3CF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED;
+ return ((M3_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3CF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3CF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ M3_REG_SEC = numSecs;
+
+ // Set read sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ M3_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)M3_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *M3_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *M3_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *M3_DATA;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+M3CF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ M3_REG_SEC = numSecs;
+
+ // Set write sector
+ M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ M3_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)M3_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *M3_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *M3_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *M3_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3_Unlock(void)
+{
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ tmp = M3_REG_LBA1;
+ M3_REG_LBA1 = (~tmp & 0xFF);
+ tmp = (~tmp & 0xFF);
+ // did it change?
+ return (M3_REG_LBA1 == tmp) ;
+}
+
+bool M3CF_Shutdown(void) {
+ return M3CF_ClearStatus() ;
+} ;
+
+bool M3CF_StartUp(void) {
+ return M3_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3cf = {
+ DEVICE_TYPE_M3CF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&M3CF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3CF_GetInterface(void) {
+ return &io_m3cf ;
+} ;
+
+#endif // SUPPORT_M3CF
diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.h b/backends/platform/ds/arm9/source/fat/io_m3cf.h
new file mode 100644
index 0000000000..bade53f511
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3cf.h
@@ -0,0 +1,50 @@
+/*
+ io_m3cf.h
+
+ Hardware Routines for reading a compact flash card
+ using the M3 CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3CF_H
+#define IO_M3CF_H
+
+// 'M3CF'
+#define DEVICE_TYPE_M3CF 0x4643334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3CF_GetInterface(void) ;
+
+#endif // define IO_M3CF_H
+/*
+ io_m3cf.h
+
+ Hardware Routines for reading a compact flash card
+ using the M3 CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3CF_H
+#define IO_M3CF_H
+
+// 'M3CF'
+#define DEVICE_TYPE_M3CF 0x4643334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3CF_GetInterface(void) ;
+
+#endif // define IO_M3CF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.c b/backends/platform/ds/arm9/source/fat/io_m3sd.c
new file mode 100644
index 0000000000..897f65bd6e
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd.c
@@ -0,0 +1,750 @@
+/*
+ io_m3sd.c based on io_m3cf.c by SaTa.
+
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3sd.h"
+
+#ifdef SUPPORT_M3SD
+
+//SD dir control bit cmddir=bit0 clken=bit1
+//output
+#define SDDIR (*(volatile u16*)0x8800000)
+
+//SD send get control bit send=bit0 get=bit1
+//output
+#define SDCON (*(volatile u16*)0x9800000)
+
+//SD output data obyte[7:0]=AD[7:0]
+//output
+#define SDODA (*(volatile u16*)0x9000000)
+
+//SD input data AD[7:0]=ibyte[7:0]
+//input
+#define SDIDA (*(volatile u16*)0x9000000)
+
+//readsector data1
+#define SDIDA1 (*(volatile u16*)0x9200000)
+
+//readsector data2
+#define SDIDA2 (*(volatile u16*)0x9400000)
+
+//readsector data3
+#define SDIDA3 (*(volatile u16*)0x9600000)
+
+//SD stutas cmdneg=bit0 cmdpos=bit1 issend=bit2 isget=bit3
+//input
+#define SDSTA (*(volatile u16*)0x9800000)
+
+//#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+#define CARD_TIMEOUT (500*100) // M3SD timeout nomal:500
+
+//======================================================
+bool M3SD_read1sector(u32 sectorn,u32 TAddr)
+{
+ u32 i;
+ int w;
+
+ SDCON=0x8; // bit3:コマンドモード?
+ SDIDA1=0x40+17; // コマンド CMD17
+ SDIDA2=(sectorn>>7);// セクタH 9ビット=アドレスH 16ビット
+ SDIDA3=(sectorn<<9);// セクタL 7ビット=アドレスL 16ビット
+ SDDIR=0x29; // コマンド送信?
+ i=0;
+
+ while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+ i=0;
+ SDDIR=0x49;
+ while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+
+ SDDIR=0x8;//cmd input clken=0 datadir input clock=0
+ SDCON=0x4;//send=0 get=0 en25=1 cmd1=0
+
+ w = SDDIR;
+ for(w=0;w<0x100;w++)
+ {
+ u16 d16;
+ u8 *d8=(u8 *)&d16;
+// *(u16*)(TAddr+w*2) = SDDIR; // 16bit
+ d16 = SDDIR; // 16bit
+ *(u8 *)(TAddr+w*2) =d8[0];
+ *(u8 *)(TAddr+w*2+1) =d8[1];
+
+ }
+ w = SDDIR;
+ w = SDDIR;
+
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+
+}
+//==================================================
+
+
+//======================================================
+void SD_crc16(u16* buff,u16 num,u16* crc16buff);
+void SD_data_write(u16 *buff,u16* crc16buff);
+
+u16 Hal4ATA_StatusByte;
+
+void Hal4ATA_GetStatus(void)
+{
+ Hal4ATA_StatusByte = SDSTA;
+}
+
+bool Hal4ATA_WaitOnBusy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte & 0x01) != 0x1)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+bool Hal4ATA_WaitOnBusyNDrdy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte&0x40) !=0x40)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+
+void SendCommand(u16 command, u32 sectorn)
+{
+ SDCON=0x8;
+ SDIDA1=0x40+command;
+ SDIDA2=(sectorn>>7);
+ SDIDA3=(sectorn<<9);
+
+ SDDIR=0x29;
+ Hal4ATA_WaitOnBusy();
+ SDDIR=0x09;
+}
+
+
+#define DMA3SAD *(u32*)0x040000D4
+#define DMA3DAD *(u32*)0x040000D8
+#define DMA3CNT *(u32*)0x040000DC
+
+void DMA3(u32 src, u32 dst, u32 cnt)
+{
+ DMA3SAD=src;
+ DMA3DAD=dst;
+ DMA3CNT=cnt;
+}
+
+
+
+void PassRespond(u32 num)
+{
+ u32 i,dmanum;
+
+ dmanum=(64+(num<<3))>>2;
+ SDDIR=0x8;
+ SDCON=0x4;
+ DMA3(0x8800000,(u32)&i,0x80400000+dmanum);
+}
+
+//bool M3SD_write1sector(u32 sectorn,u16 * p)
+bool M3SD_write1sector(u32 sectorn,u32 p)
+{
+ u16 crc[4];
+
+ SendCommand(24,sectorn);
+ PassRespond(6);
+
+ SDDIR=0x4;
+ SDCON=0x0;
+
+ SD_crc16((u16 *)p,512,crc);
+ SD_data_write((u16 *)p,crc);
+ return true;
+}
+//==================================================
+
+
+// GBAMP CF Addresses
+
+#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control
+
+#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED1 0x20
+#define CF_STS_INSERTED2 0x30
+
+/*-----------------------------------------------------------------
+M3SD_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3SD_IsInserted (void)
+{
+ int i;
+ u16 sta;
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED1;
+
+ for(i=0;i<CARD_TIMEOUT;i++)
+ {
+ sta=M3_REG_STS;
+ if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2))
+ {
+ return true;
+ //break;
+ }
+ }
+ return false;
+
+// return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) );
+// return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3SD_ClearStatus (void)
+{
+
+// int i=SDDIR;
+ int i;
+ u16 sta;
+
+ i = 0;
+ M3_REG_STS = CF_STS_INSERTED1;
+ while (i < CARD_TIMEOUT)
+ {
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )break;
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+
+ //void M3SD_read1sector(u32 sectorn,u32 TAddr)
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+}
+
+
+
+/*-----------------------------------------------------------------
+M3SD_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+// return false;
+
+
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3SD_Unlock(void)
+{
+
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ vu16 sta;
+ sta=M3_REG_STS;
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )return true;
+
+ return false;
+}
+
+bool M3SD_Shutdown(void) {
+ return M3SD_ClearStatus() ;
+} ;
+
+bool M3SD_StartUp(void) {
+ return M3SD_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3sd = {
+ DEVICE_TYPE_M3SD,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&M3SD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3SD_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3SD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3SD_GetInterface(void) {
+ return &io_m3sd ;
+} ;
+
+#endif // SUPPORT_M3CF
+/*
+ io_m3sd.c based on io_m3cf.c by SaTa.
+
+ io_m3cf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the M3 Perfect CF Adapter
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_m3sd.h"
+
+#ifdef SUPPORT_M3SD
+
+//SD dir control bit cmddir=bit0 clken=bit1
+//output
+#define SDDIR (*(volatile u16*)0x8800000)
+
+//SD send get control bit send=bit0 get=bit1
+//output
+#define SDCON (*(volatile u16*)0x9800000)
+
+//SD output data obyte[7:0]=AD[7:0]
+//output
+#define SDODA (*(volatile u16*)0x9000000)
+
+//SD input data AD[7:0]=ibyte[7:0]
+//input
+#define SDIDA (*(volatile u16*)0x9000000)
+
+//readsector data1
+#define SDIDA1 (*(volatile u16*)0x9200000)
+
+//readsector data2
+#define SDIDA2 (*(volatile u16*)0x9400000)
+
+//readsector data3
+#define SDIDA3 (*(volatile u16*)0x9600000)
+
+//SD stutas cmdneg=bit0 cmdpos=bit1 issend=bit2 isget=bit3
+//input
+#define SDSTA (*(volatile u16*)0x9800000)
+
+//#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+#define CARD_TIMEOUT (500*100) // M3SD timeout nomal:500
+
+//======================================================
+bool M3SD_read1sector(u32 sectorn,u32 TAddr)
+{
+ u32 i;
+ int w;
+
+ SDCON=0x8; // bit3:コマンドモード?
+ SDIDA1=0x40+17; // コマンド CMD17
+ SDIDA2=(sectorn>>7);// セクタH 9ビット=アドレスH 16ビット
+ SDIDA3=(sectorn<<9);// セクタL 7ビット=アドレスL 16ビット
+ SDDIR=0x29; // コマンド送信?
+ i=0;
+
+ while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+ i=0;
+ SDDIR=0x49;
+ while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) )
+ {
+ i++;
+ }
+ SDDIR=0x09;
+
+ SDDIR=0x8;//cmd input clken=0 datadir input clock=0
+ SDCON=0x4;//send=0 get=0 en25=1 cmd1=0
+
+ w = SDDIR;
+ for(w=0;w<0x100;w++)
+ {
+ u16 d16;
+ u8 *d8=(u8 *)&d16;
+// *(u16*)(TAddr+w*2) = SDDIR; // 16bit
+ d16 = SDDIR; // 16bit
+ *(u8 *)(TAddr+w*2) =d8[0];
+ *(u8 *)(TAddr+w*2+1) =d8[1];
+
+ }
+ w = SDDIR;
+ w = SDDIR;
+
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+
+}
+//==================================================
+
+
+//======================================================
+void SD_crc16(u16* buff,u16 num,u16* crc16buff);
+void SD_data_write(u16 *buff,u16* crc16buff);
+
+u16 Hal4ATA_StatusByte;
+
+void Hal4ATA_GetStatus(void)
+{
+ Hal4ATA_StatusByte = SDSTA;
+}
+
+bool Hal4ATA_WaitOnBusy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte & 0x01) != 0x1)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+bool Hal4ATA_WaitOnBusyNDrdy(void)
+{
+ Hal4ATA_GetStatus();
+ while ( (Hal4ATA_StatusByte&0x40) !=0x40)
+ {
+ Hal4ATA_GetStatus();
+ }
+ return TRUE;
+}
+
+
+void SendCommand(u16 command, u32 sectorn)
+{
+ SDCON=0x8;
+ SDIDA1=0x40+command;
+ SDIDA2=(sectorn>>7);
+ SDIDA3=(sectorn<<9);
+
+ SDDIR=0x29;
+ Hal4ATA_WaitOnBusy();
+ SDDIR=0x09;
+}
+
+
+#define DMA3SAD *(u32*)0x040000D4
+#define DMA3DAD *(u32*)0x040000D8
+#define DMA3CNT *(u32*)0x040000DC
+
+void DMA3(u32 src, u32 dst, u32 cnt)
+{
+ DMA3SAD=src;
+ DMA3DAD=dst;
+ DMA3CNT=cnt;
+}
+
+
+
+void PassRespond(u32 num)
+{
+ u32 i,dmanum;
+
+ dmanum=(64+(num<<3))>>2;
+ SDDIR=0x8;
+ SDCON=0x4;
+ DMA3(0x8800000,(u32)&i,0x80400000+dmanum);
+}
+
+//bool M3SD_write1sector(u32 sectorn,u16 * p)
+bool M3SD_write1sector(u32 sectorn,u32 p)
+{
+ u16 crc[4];
+
+ SendCommand(24,sectorn);
+ PassRespond(6);
+
+ SDDIR=0x4;
+ SDCON=0x0;
+
+ SD_crc16((u16 *)p,512,crc);
+ SD_data_write((u16 *)p,crc);
+ return true;
+}
+//==================================================
+
+
+// GBAMP CF Addresses
+
+#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control
+
+#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED1 0x20
+#define CF_STS_INSERTED2 0x30
+
+/*-----------------------------------------------------------------
+M3SD_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool M3SD_IsInserted (void)
+{
+ int i;
+ u16 sta;
+ // Change register, then check if value did change
+ M3_REG_STS = CF_STS_INSERTED1;
+
+ for(i=0;i<CARD_TIMEOUT;i++)
+ {
+ sta=M3_REG_STS;
+ if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2))
+ {
+ return true;
+ //break;
+ }
+ }
+ return false;
+
+// return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) );
+// return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool M3SD_ClearStatus (void)
+{
+
+// int i=SDDIR;
+ int i;
+ u16 sta;
+
+ i = 0;
+ M3_REG_STS = CF_STS_INSERTED1;
+ while (i < CARD_TIMEOUT)
+ {
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )break;
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+M3SD_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+
+ //void M3SD_read1sector(u32 sectorn,u32 TAddr)
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+}
+
+
+
+/*-----------------------------------------------------------------
+M3SD_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+
+ bool r=true;
+ int i;
+ for(i=0;i<numSecs;i++)
+ {
+ if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false)
+ {
+ r=false;
+ break;
+ }
+ }
+ return r;
+
+// return false;
+
+
+}
+
+
+/*-----------------------------------------------------------------
+M3_Unlock
+Returns true if M3 was unlocked, false if failed
+Added by MightyMax
+-----------------------------------------------------------------*/
+bool M3SD_Unlock(void)
+{
+
+ // run unlock sequence
+ volatile unsigned short tmp ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08E00002 ;
+ tmp = *(volatile unsigned short *)0x0800000E ;
+ tmp = *(volatile unsigned short *)0x08801FFC ;
+ tmp = *(volatile unsigned short *)0x0800104A ;
+ tmp = *(volatile unsigned short *)0x08800612 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ tmp = *(volatile unsigned short *)0x08801B66 ;
+ tmp = *(volatile unsigned short *)0x08800006 ;
+ tmp = *(volatile unsigned short *)0x08000000 ;
+ // test that we have register access
+ vu16 sta;
+ sta=M3_REG_STS;
+ sta=M3_REG_STS;
+ if( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) )return true;
+
+ return false;
+}
+
+bool M3SD_Shutdown(void) {
+ return M3SD_ClearStatus() ;
+} ;
+
+bool M3SD_StartUp(void) {
+ return M3SD_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_m3sd = {
+ DEVICE_TYPE_M3SD,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&M3SD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&M3SD_IsInserted,
+ (FN_MEDIUM_READSECTORS)&M3SD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown
+} ;
+
+
+LPIO_INTERFACE M3SD_GetInterface(void) {
+ return &io_m3sd ;
+} ;
+
+#endif // SUPPORT_M3CF
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.h b/backends/platform/ds/arm9/source/fat/io_m3sd.h
new file mode 100644
index 0000000000..bd6b2644cc
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd.h
@@ -0,0 +1,50 @@
+/*
+ io_m3sd.h by SaTa.
+
+ Hardware Routines for reading an SD card
+ using the M3 SD
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3SD_H
+#define IO_M3SD_H
+
+// 'M3SD'
+#define DEVICE_TYPE_M3SD 0x4453334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3SD_GetInterface(void) ;
+
+#endif // define IO_M3SD_H
+/*
+ io_m3sd.h by SaTa.
+
+ Hardware Routines for reading an SD card
+ using the M3 SD
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_M3SD_H
+#define IO_M3SD_H
+
+// 'M3SD'
+#define DEVICE_TYPE_M3SD 0x4453334D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE M3SD_GetInterface(void) ;
+
+#endif // define IO_M3SD_H
diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s
new file mode 100644
index 0000000000..18bbee75ce
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s
@@ -0,0 +1,392 @@
+ .TEXT
+@ AREA M3SD, CODE, READONLY
+@ ENTRY
+@-----------------------------------
+@ EXPORT SD_crc16
+@ EXPORT SD_data_write
+
+@CSYNC EQU 0x9800000
+@SDDIR EQU 0x8800000
+@SDCON EQU 0x9800000
+@SDODA EQU 0x9000000
+.equ CSYNC,0x9800000
+.equ SDDIR,0x8800000
+.equ SDCON,0x9800000
+.equ SDODA,0x9000000
+
+ .ALIGN
+ .CODE 32
+
+clkout:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x4
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0xc
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+clkin:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x0
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0x8
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+wait_ready:
+ stmfd r13!,{r0-r2}
+ mov r2,#32
+ mov r1,#SDODA
+sd_write_loop2:
+ mov r0,#0xff @end bit
+ strh r0,[r1]
+ bl clkout
+ subs r2, r2, #1
+ bne sd_write_loop2
+
+sd_write_busy:
+ bl clkin
+ ldrh r0,[r1]
+ tst r0,#0x100
+ beq sd_write_busy
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+@------void SD_crc16(u16* buff,u16 num,u16* crc16buff)
+
+ .GLOBAL SD_crc16
+
+SD_crc16:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end-----------------------------------
+
+@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)-------------------
+ .GLOBAL SD_data_write
+SD_data_write:
+ stmfd r13!,{r4-r5,r14}
+ mov r5,#512
+ mov r2,#SDODA
+sd_data_write_busy:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+ bl clkout
+
+sd_data_write_loop:
+ ldrh r4,[r0],#2
+ mov r3,r4,lsr#4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#12
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#8
+ strh r3,[r2]
+ bl clkout
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r5,#32
+sd_data_write_loop2:
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+ bl clkout
+ subs r5, r5, #1
+ bne sd_data_write_loop2
+
+sd_data_write_busy2:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy2
+
+ ldmfd r13!,{r4-r5,r15}
+@-----------------end-------------------
+
+ .TEXT
+@ AREA M3SD, CODE, READONLY
+@ ENTRY
+@-----------------------------------
+@ EXPORT SD_crc16
+@ EXPORT SD_data_write
+
+@CSYNC EQU 0x9800000
+@SDDIR EQU 0x8800000
+@SDCON EQU 0x9800000
+@SDODA EQU 0x9000000
+.equ CSYNC,0x9800000
+.equ SDDIR,0x8800000
+.equ SDCON,0x9800000
+.equ SDODA,0x9000000
+
+ .ALIGN
+ .CODE 32
+
+clkout:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x4
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0xc
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+clkin:
+ stmfd r13!,{r0-r1}
+ ldr r1,=SDDIR
+ mov r0,#0x0
+ strh r0,[r1]
+ mov r0,r0
+ mov r0,r0
+ mov r0,#0x8
+ strh r0,[r1]
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+wait_ready:
+ stmfd r13!,{r0-r2}
+ mov r2,#32
+ mov r1,#SDODA
+sd_write_loop2:
+ mov r0,#0xff @end bit
+ strh r0,[r1]
+ bl clkout
+ subs r2, r2, #1
+ bne sd_write_loop2
+
+sd_write_busy:
+ bl clkin
+ ldrh r0,[r1]
+ tst r0,#0x100
+ beq sd_write_busy
+ ldmfd r13!,{r0-r1}
+ bx r14
+
+@------void SD_crc16(u16* buff,u16 num,u16* crc16buff)
+
+ .GLOBAL SD_crc16
+
+SD_crc16:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end-----------------------------------
+
+@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)-------------------
+ .GLOBAL SD_data_write
+SD_data_write:
+ stmfd r13!,{r4-r5,r14}
+ mov r5,#512
+ mov r2,#SDODA
+sd_data_write_busy:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+ bl clkout
+
+sd_data_write_loop:
+ ldrh r4,[r0],#2
+ mov r3,r4,lsr#4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#12
+ strh r3,[r2]
+ bl clkout
+ mov r3,r4,lsr#8
+ strh r3,[r2]
+ bl clkout
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r5,#32
+sd_data_write_loop2:
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+ bl clkout
+ subs r5, r5, #1
+ bne sd_data_write_loop2
+
+sd_data_write_busy2:
+ bl clkin
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy2
+
+ ldmfd r13!,{r4-r5,r15}
+@-----------------end-------------------
+
diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.c b/backends/platform/ds/arm9/source/fat/io_mpcf.c
new file mode 100644
index 0000000000..c55eef73eb
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_mpcf.c
@@ -0,0 +1,714 @@
+/*
+ io_mpcf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_mpcf.h"
+
+#ifdef SUPPORT_MPCF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control
+#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return
+#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features
+
+#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer
+#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address
+#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address
+#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address
+#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0
+
+#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+MPCF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool MPCF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ MP_REG_STS = CF_STS_INSERTED;
+ return ((MP_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool MPCF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9)
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ MP_REG_SEC = numSecs;
+
+ // Set read sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ MP_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)MP_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *MP_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *MP_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *MP_DATA;
+#endif
+ }
+#if (defined _CF_USE_DMA) && (defined NDS)
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+MPCF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ MP_REG_SEC = numSecs;
+
+ // Set write sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ MP_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)MP_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *MP_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *MP_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *MP_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+MPCF_Shutdown
+unload the GBAMP CF interface
+-----------------------------------------------------------------*/
+bool MPCF_Shutdown(void)
+{
+ return MPCF_ClearStatus() ;
+}
+
+/*-----------------------------------------------------------------
+MPCF_StartUp
+initializes the CF interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool MPCF_StartUp(void)
+{
+ u8 temp = MP_REG_LBA1;
+ MP_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (MP_REG_LBA1 == temp) ;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_mpcf = {
+ DEVICE_TYPE_MPCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&MPCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+MPCF_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE MPCF_GetInterface(void) {
+ return &io_mpcf ;
+} ;
+
+#endif // SUPPORT_MPCF
+/*
+ io_mpcf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_mpcf.h"
+
+#ifdef SUPPORT_MPCF
+
+//---------------------------------------------------------------
+// DMA
+#ifdef _CF_USE_DMA
+ #ifndef NDS
+ #include "gba_dma.h"
+ #else
+ #include <nds/dma.h>
+ #ifdef ARM9
+ #include <nds/arm9/cache.h>
+ #endif
+ #endif
+#endif
+
+//---------------------------------------------------------------
+// CF Addresses & Commands
+
+#define GAME_PAK 0x08000000 // Game pack start address
+
+// GBAMP CF Addresses
+#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control
+#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return
+#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features
+
+#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer
+#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address
+#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address
+#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address
+#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0
+
+#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card
+
+// CF Card status
+#define CF_STS_INSERTED 0x50
+#define CF_STS_REMOVED 0x00
+#define CF_STS_READY 0x58
+
+#define CF_STS_DRQ 0x08
+#define CF_STS_BUSY 0x80
+
+// CF Card commands
+#define CF_CMD_LBA 0xE0
+#define CF_CMD_READ 0x20
+#define CF_CMD_WRITE 0x30
+
+#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write
+
+
+/*-----------------------------------------------------------------
+MPCF_IsInserted
+Is a compact flash card inserted?
+bool return OUT: true if a CF card is inserted
+-----------------------------------------------------------------*/
+bool MPCF_IsInserted (void)
+{
+ // Change register, then check if value did change
+ MP_REG_STS = CF_STS_INSERTED;
+ return ((MP_REG_STS & 0xff) == CF_STS_INSERTED);
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ClearStatus
+Tries to make the CF card go back to idle mode
+bool return OUT: true if a CF card is idle
+-----------------------------------------------------------------*/
+bool MPCF_ClearStatus (void)
+{
+ int i;
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+
+/*-----------------------------------------------------------------
+MPCF_ReadSectors
+Read 512 byte sector numbered "sector" into "buffer"
+u32 sector IN: address of first 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer OUT: pointer to 512 byte buffer to store data in
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9)
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to read
+ MP_REG_SEC = numSecs;
+
+ // Set read sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to read
+ MP_REG_CMD = CF_CMD_READ;
+
+
+ while (j--)
+ {
+ // Wait until card is ready for reading
+ i = 0;
+ while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Read data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)MP_DATA;
+ DMA3_DEST = (u32)buff;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX;
+ #else
+ DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *MP_DATA;
+ *buff_u8++ = temp & 0xFF;
+ *buff_u8++ = temp >> 8;
+ }
+ } else {
+ while(i--)
+ *buff++ = *MP_DATA;
+ }
+#else
+ i=256;
+ while(i--)
+ *buff++ = *MP_DATA;
+#endif
+ }
+#if (defined _CF_USE_DMA) && (defined NDS)
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+ return true;
+}
+
+
+
+/*-----------------------------------------------------------------
+MPCF_WriteSectors
+Write 512 byte sector numbered "sector" from "buffer"
+u32 sector IN: address of 512 byte sector on CF card to read
+u8 numSecs IN: number of 512 byte sectors to read,
+ 1 to 256 sectors can be read, 0 = 256
+void* buffer IN: pointer to 512 byte buffer to read data from
+bool return OUT: true if successful
+-----------------------------------------------------------------*/
+bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ int i;
+ int j = (numSecs > 0 ? numSecs : 256);
+ u16 *buff = (u16*)buffer;
+#ifdef _CF_ALLOW_UNALIGNED
+ u8 *buff_u8 = (u8*)buffer;
+ int temp;
+#endif
+
+#if defined _CF_USE_DMA && defined NDS && defined ARM9
+ DC_FlushRange( buffer, j * BYTE_PER_READ);
+#endif
+
+ // Wait until CF card is finished previous commands
+ i=0;
+ while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+
+ // Wait until card is ready for commands
+ i = 0;
+ while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Set number of sectors to write
+ MP_REG_SEC = numSecs;
+
+ // Set write sector
+ MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number
+ MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number
+ MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number
+ MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number
+
+ // Set command to write
+ MP_REG_CMD = CF_CMD_WRITE;
+
+ while (j--)
+ {
+ // Wait until card is ready for writing
+ i = 0;
+ while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT))
+ {
+ i++;
+ }
+ if (i >= CARD_TIMEOUT)
+ return false;
+
+ // Write data
+#ifdef _CF_USE_DMA
+ #ifdef NDS
+ DMA3_SRC = (u32)buff;
+ DMA3_DEST = (u32)MP_DATA;
+ DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX;
+ #else
+ DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED);
+ #endif
+ buff += BYTE_PER_READ / 2;
+#elif defined _CF_ALLOW_UNALIGNED
+ i=256;
+ if ((u32)buff_u8 & 0x01) {
+ while(i--)
+ {
+ temp = *buff_u8++;
+ temp |= *buff_u8++ << 8;
+ *MP_DATA = temp;
+ }
+ } else {
+ while(i--)
+ *MP_DATA = *buff++;
+ }
+#else
+ i=256;
+ while(i--)
+ *MP_DATA = *buff++;
+#endif
+ }
+#if defined _CF_USE_DMA && defined NDS
+ // Wait for end of transfer before returning
+ while(DMA3_CR & DMA_BUSY);
+#endif
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+MPCF_Shutdown
+unload the GBAMP CF interface
+-----------------------------------------------------------------*/
+bool MPCF_Shutdown(void)
+{
+ return MPCF_ClearStatus() ;
+}
+
+/*-----------------------------------------------------------------
+MPCF_StartUp
+initializes the CF interface, returns true if successful,
+otherwise returns false
+-----------------------------------------------------------------*/
+bool MPCF_StartUp(void)
+{
+ u8 temp = MP_REG_LBA1;
+ MP_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (MP_REG_LBA1 == temp) ;
+}
+
+/*-----------------------------------------------------------------
+the actual interface structure
+-----------------------------------------------------------------*/
+IO_INTERFACE io_mpcf = {
+ DEVICE_TYPE_MPCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&MPCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown
+} ;
+
+/*-----------------------------------------------------------------
+MPCF_GetInterface
+returns the interface structure to host
+-----------------------------------------------------------------*/
+LPIO_INTERFACE MPCF_GetInterface(void) {
+ return &io_mpcf ;
+} ;
+
+#endif // SUPPORT_MPCF
diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.h b/backends/platform/ds/arm9/source/fat/io_mpcf.h
new file mode 100644
index 0000000000..58cab41b4c
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_mpcf.h
@@ -0,0 +1,50 @@
+/*
+ io_mpcf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_MPCF_H
+#define IO_MPCF_H
+
+// 'MPCF'
+#define DEVICE_TYPE_MPCF 0x4643504D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE MPCF_GetInterface(void) ;
+
+#endif // define IO_MPCF_H
+/*
+ io_mpcf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_MPCF_H
+#define IO_MPCF_H
+
+// 'MPCF'
+#define DEVICE_TYPE_MPCF 0x4643504D
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE MPCF_GetInterface(void) ;
+
+#endif // define IO_MPCF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.c b/backends/platform/ds/arm9/source/fat/io_nmmc.c
new file mode 100644
index 0000000000..2e6b99de24
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_nmmc.c
@@ -0,0 +1,716 @@
+/*
+ io_nmmc.c
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ Submit bug reports for this device to the NeoFlash forums
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ 2006-02-09 - www.neoflash.com:
+ * First stable release
+
+ 2006-02-13 - Chishm
+ * Added ReadMK2Config function
+ * Added read config test to init function so no unnecessary card commands are sent
+ * Changed data read and write functions to use multiple block commands
+*/
+
+#include "io_nmmc.h"
+
+#ifdef SUPPORT_NMMC
+
+#include <nds/card.h>
+
+int spi_freq = 3;
+
+#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5)
+#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0))
+//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1))
+//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4)
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_READ_BLOCK 17
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_WRITE_BLOCK 24
+#define MMC_STOP_TRANSMISSION 12
+#define MMC_SET_BLOCKLEN 16
+#define MMC_SET_BLOCK_COUNT 23
+#define MMC_SEND_CSD 9
+
+// SPI functions
+
+static inline void Neo_OpenSPI( u8 frequency )
+{
+ CARD_CR1 = 0x0000A040 | frequency;
+}
+
+static inline u8 Neo_SPI( u8 dataByte )
+{
+ CARD_EEPDATA = dataByte;
+ while (CARD_CR1 & 0x80); // card busy
+ return CARD_EEPDATA;
+}
+
+static inline void Neo_CloseSPI ( void )
+{
+ CARD_CR1 = 0;
+}
+
+static inline void Neo_MK2GameMode() {
+ Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port
+ Neo_SPI(0xF1); // Switch to game mode
+ Neo_CloseSPI(); // Disable DS Card's SPI port
+}
+
+static inline void Neo_EnableEEPROM( bool enable ) {
+ Neo_OpenSPI(spi_freq);
+ if(enable) Neo_SPI(0x06);
+ else Neo_SPI(0x0E);
+ Neo_CloseSPI();
+}
+
+void Neo_WriteMK2Config(u8 config) {
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xFA); // Send mem conf write command
+ Neo_SPI(0x01); // Send high byte (0x01)
+ Neo_SPI(config); // Send low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+}
+
+u8 Neo_ReadMK2Config(void)
+{
+ u8 config;
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xf8); // Send mem conf read command
+ Neo_SPI(0x01); // Send high byte
+ config = Neo_SPI(0x00); // Get low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+ return config;
+}
+
+// Low level functions
+
+u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3};
+
+void Neo_SelectMMC (u8 dataByte)
+{
+ selectMMC_command[1] = dataByte; // Set enable / disable byte
+ cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card"
+ CARD_CR2 = CARD_ACTIVATE | CARD_nRESET;
+ while (CARD_CR2 & CARD_BUSY);
+ return;
+}
+
+void Neo_EnableMMC( bool enable )
+{
+ if ( enable == false) {
+ Neo_CloseSPI ();
+ Neo_SelectMMC (0);
+ Neo_SelectMMC (0);
+ } else {
+ Neo_SelectMMC (1);
+ Neo_SelectMMC (1);
+ Neo_OpenSPI (spi_freq);
+ }
+ return;
+}
+
+void Neo_SendMMCCommand( u8 command, u32 argument )
+{
+ Neo_SPI (0xFF);
+ Neo_SPI (command | 0x40);
+ Neo_SPI ((argument >> 24) & 0xff);
+ Neo_SPI ((argument >> 16) & 0xff);
+ Neo_SPI ((argument >> 8) & 0xff) ;
+ Neo_SPI (argument & 0xff);
+ Neo_SPI (0x95);
+ Neo_SPI (0xFF);
+ return;
+}
+
+bool Neo_CheckMMCResponse( u8 response, u8 mask ) {
+ u32 i;
+ for(i=0;i<256;i++) {
+ if( ( Neo_SPI( 0xFF ) & mask ) == response )
+ return true;
+ }
+ return false;
+}
+
+// Neo MMC functions
+
+bool Neo_InitMMC() {
+ Neo_MK2GameMode();
+ Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE);
+
+ // Make sure the configuration was accepted
+ if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) {
+ return false; // If not, then it wasn't initialised properly
+ }
+
+ return true;
+}
+
+// Neo MMC driver functions
+
+bool NMMC_IsInserted(void) {
+ int i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // consume data from card, and send clocks.
+ for (i = 0; i < 28; i++) {
+ Neo_SPI(0xff);
+ }
+
+ return true;
+}
+
+bool NMMC_ClearStatus (void) {
+ u32 i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ for (i = 0; i < 10; i++) {
+ Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card
+ }
+ Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command
+ if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error)
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for(i=0;i<256;i++) {
+ Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND
+ if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true; // Card is now idle
+ }
+ }
+ Neo_EnableMMC( false );
+ return false;
+}
+
+bool NMMC_Shutdown(void) {
+ return NMMC_ClearStatus();
+}
+
+bool NMMC_StartUp(void) {
+ int i;
+ int transSpeed;
+ if (Neo_InitMMC() == false) {
+ return false;
+ }
+ if (NMMC_ClearStatus() == false) {
+ return false;
+ }
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ // Set block length
+ Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // Check if we can use a higher SPI frequency
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for (i = 0; i < 3; i++) {
+ Neo_SPI(0xFF);
+ }
+ transSpeed = Neo_SPI (0xFF);
+ for (i = 0; i < 24; i++) {
+ Neo_SPI(0xFF);
+ }
+ if ((transSpeed & 0xf0) >= 0x30) {
+ spi_freq = 0;
+ }
+
+ Neo_EnableMMC( false );
+ return true;
+}
+
+
+bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand( 25, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ while (totalSecs--) {
+ Neo_SPI( 0xFC ); // Send Start Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data
+ Neo_SPI( *p++ );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted
+ Neo_EnableMMC( false );
+ return false;
+ }
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written
+ }
+
+ // Stop transmission block
+ Neo_SPI( 0xFD ); // Send Stop Transmission Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data
+ Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ Neo_SPI (0xFF); // Send 8 clocks
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear
+
+
+ for ( i = 0; i < 0x10; i++) {
+ Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ while (totalSecs--) {
+ Neo_SendMMCCommand(MMC_READ_BLOCK, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data
+ *p++ = Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ sector += BYTE_PER_READ;
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+
+IO_INTERFACE io_nmmc = {
+ DEVICE_TYPE_NMMC,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&NMMC_StartUp,
+ (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted,
+ (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown
+} ;
+
+
+LPIO_INTERFACE NMMC_GetInterface(void) {
+ return &io_nmmc ;
+}
+
+#endif // #ifdef SUPPORT_NMMC
+/*
+ io_nmmc.c
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ Submit bug reports for this device to the NeoFlash forums
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+
+ 2006-02-09 - www.neoflash.com:
+ * First stable release
+
+ 2006-02-13 - Chishm
+ * Added ReadMK2Config function
+ * Added read config test to init function so no unnecessary card commands are sent
+ * Changed data read and write functions to use multiple block commands
+*/
+
+#include "io_nmmc.h"
+
+#ifdef SUPPORT_NMMC
+
+#include <nds/card.h>
+
+int spi_freq = 3;
+
+#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5)
+#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0))
+//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1))
+//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4)
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_READ_BLOCK 17
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_WRITE_BLOCK 24
+#define MMC_STOP_TRANSMISSION 12
+#define MMC_SET_BLOCKLEN 16
+#define MMC_SET_BLOCK_COUNT 23
+#define MMC_SEND_CSD 9
+
+// SPI functions
+
+static inline void Neo_OpenSPI( u8 frequency )
+{
+ CARD_CR1 = 0x0000A040 | frequency;
+}
+
+static inline u8 Neo_SPI( u8 dataByte )
+{
+ CARD_EEPDATA = dataByte;
+ while (CARD_CR1 & 0x80); // card busy
+ return CARD_EEPDATA;
+}
+
+static inline void Neo_CloseSPI ( void )
+{
+ CARD_CR1 = 0;
+}
+
+static inline void Neo_MK2GameMode() {
+ Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port
+ Neo_SPI(0xF1); // Switch to game mode
+ Neo_CloseSPI(); // Disable DS Card's SPI port
+}
+
+static inline void Neo_EnableEEPROM( bool enable ) {
+ Neo_OpenSPI(spi_freq);
+ if(enable) Neo_SPI(0x06);
+ else Neo_SPI(0x0E);
+ Neo_CloseSPI();
+}
+
+void Neo_WriteMK2Config(u8 config) {
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xFA); // Send mem conf write command
+ Neo_SPI(0x01); // Send high byte (0x01)
+ Neo_SPI(config); // Send low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+}
+
+u8 Neo_ReadMK2Config(void)
+{
+ u8 config;
+ Neo_EnableEEPROM(true);
+ Neo_OpenSPI(spi_freq);
+ Neo_SPI(0xf8); // Send mem conf read command
+ Neo_SPI(0x01); // Send high byte
+ config = Neo_SPI(0x00); // Get low byte
+ Neo_CloseSPI();
+ Neo_EnableEEPROM(false);
+ return config;
+}
+
+// Low level functions
+
+u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3};
+
+void Neo_SelectMMC (u8 dataByte)
+{
+ selectMMC_command[1] = dataByte; // Set enable / disable byte
+ cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card"
+ CARD_CR2 = CARD_ACTIVATE | CARD_nRESET;
+ while (CARD_CR2 & CARD_BUSY);
+ return;
+}
+
+void Neo_EnableMMC( bool enable )
+{
+ if ( enable == false) {
+ Neo_CloseSPI ();
+ Neo_SelectMMC (0);
+ Neo_SelectMMC (0);
+ } else {
+ Neo_SelectMMC (1);
+ Neo_SelectMMC (1);
+ Neo_OpenSPI (spi_freq);
+ }
+ return;
+}
+
+void Neo_SendMMCCommand( u8 command, u32 argument )
+{
+ Neo_SPI (0xFF);
+ Neo_SPI (command | 0x40);
+ Neo_SPI ((argument >> 24) & 0xff);
+ Neo_SPI ((argument >> 16) & 0xff);
+ Neo_SPI ((argument >> 8) & 0xff) ;
+ Neo_SPI (argument & 0xff);
+ Neo_SPI (0x95);
+ Neo_SPI (0xFF);
+ return;
+}
+
+bool Neo_CheckMMCResponse( u8 response, u8 mask ) {
+ u32 i;
+ for(i=0;i<256;i++) {
+ if( ( Neo_SPI( 0xFF ) & mask ) == response )
+ return true;
+ }
+ return false;
+}
+
+// Neo MMC functions
+
+bool Neo_InitMMC() {
+ Neo_MK2GameMode();
+ Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE);
+
+ // Make sure the configuration was accepted
+ if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) {
+ return false; // If not, then it wasn't initialised properly
+ }
+
+ return true;
+}
+
+// Neo MMC driver functions
+
+bool NMMC_IsInserted(void) {
+ int i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // consume data from card, and send clocks.
+ for (i = 0; i < 28; i++) {
+ Neo_SPI(0xff);
+ }
+
+ return true;
+}
+
+bool NMMC_ClearStatus (void) {
+ u32 i;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ for (i = 0; i < 10; i++) {
+ Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card
+ }
+ Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command
+ if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error)
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for(i=0;i<256;i++) {
+ Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND
+ if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true; // Card is now idle
+ }
+ }
+ Neo_EnableMMC( false );
+ return false;
+}
+
+bool NMMC_Shutdown(void) {
+ return NMMC_ClearStatus();
+}
+
+bool NMMC_StartUp(void) {
+ int i;
+ int transSpeed;
+ if (Neo_InitMMC() == false) {
+ return false;
+ }
+ if (NMMC_ClearStatus() == false) {
+ return false;
+ }
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ // Set block length
+ Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ // Check if we can use a higher SPI frequency
+ Neo_SendMMCCommand(MMC_SEND_CSD, 0);
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for (i = 0; i < 3; i++) {
+ Neo_SPI(0xFF);
+ }
+ transSpeed = Neo_SPI (0xFF);
+ for (i = 0; i < 24; i++) {
+ Neo_SPI(0xFF);
+ }
+ if ((transSpeed & 0xf0) >= 0x30) {
+ spi_freq = 0;
+ }
+
+ Neo_EnableMMC( false );
+ return true;
+}
+
+
+bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+ Neo_SendMMCCommand( 25, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ while (totalSecs--) {
+ Neo_SPI( 0xFC ); // Send Start Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data
+ Neo_SPI( *p++ );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted
+ Neo_EnableMMC( false );
+ return false;
+ }
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written
+ }
+
+ // Stop transmission block
+ Neo_SPI( 0xFD ); // Send Stop Transmission Block token
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data
+ Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Send fake CRC16
+ Neo_SPI( 0xFF ); // Send fake CRC16
+
+ Neo_SPI (0xFF); // Send 8 clocks
+ while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear
+
+
+ for ( i = 0; i < 0x10; i++) {
+ Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer)
+{
+ u32 i;
+ u8 *p=buffer;
+
+ int totalSecs = (numSecs == 0) ? 256 : numSecs;
+ sector *= BYTE_PER_READ;
+
+ Neo_EnableMMC( true ); // Open SPI port to MMC card
+
+ while (totalSecs--) {
+ Neo_SendMMCCommand(MMC_READ_BLOCK, sector );
+ if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured
+ Neo_EnableMMC( false );
+ return false;
+ }
+
+ if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token
+ Neo_EnableMMC( false );
+ return false;
+ }
+ for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data
+ *p++ = Neo_SPI( 0xFF );
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ Neo_SPI( 0xFF ); // Ignore CRC16
+ sector += BYTE_PER_READ;
+ }
+
+ Neo_EnableMMC( false ); // Close SPI port to MMC card
+ return true;
+}
+
+
+IO_INTERFACE io_nmmc = {
+ DEVICE_TYPE_NMMC,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&NMMC_StartUp,
+ (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted,
+ (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown
+} ;
+
+
+LPIO_INTERFACE NMMC_GetInterface(void) {
+ return &io_nmmc ;
+}
+
+#endif // #ifdef SUPPORT_NMMC
diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.h b/backends/platform/ds/arm9/source/fat/io_nmmc.h
new file mode 100644
index 0000000000..5244d21868
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_nmmc.h
@@ -0,0 +1,54 @@
+/*
+ io_NMMC.h
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_NMMC_H
+#define IO_NMMC_H
+
+// 'NMMC'
+#define DEVICE_TYPE_NMMC 0x434D4D4E
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE NMMC_GetInterface(void) ;
+
+#endif // define IO_NMMC_H
+/*
+ io_NMMC.h
+
+ Hardware Routines for reading an SD or MMC card using
+ a Neoflash MK2 or MK3.
+
+ Written by www.neoflash.com
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_NMMC_H
+#define IO_NMMC_H
+
+// 'NMMC'
+#define DEVICE_TYPE_NMMC 0x434D4D4E
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE NMMC_GetInterface(void) ;
+
+#endif // define IO_NMMC_H
diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.c b/backends/platform/ds/arm9/source/fat/io_sccf.c
new file mode 100644
index 0000000000..336387e9d7
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_sccf.c
@@ -0,0 +1,174 @@
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_sccf.h"
+
+#ifdef SUPPORT_SCCF
+
+#ifndef SUPPORT_MPCF
+ #error Supercard CF support requires GBAMP CF support
+#endif // SUPPORT_MPCF
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+
+/*-----------------------------------------------------------------
+SCCF_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCCF_Unlock(void)
+{
+#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000
+ unsigned char temp;
+ volatile short *unlockAddress = (volatile short *)0x09FFFFFE;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0x3 ;
+ *unlockAddress = 0x3 ;
+ // provoke a ready reply
+ temp = CF_REG_LBA1;
+ CF_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (CF_REG_LBA1 == temp);
+#undef CF_REG_LBA1
+}
+
+bool SCCF_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCCF_StartUp(void) {
+ return SCCF_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_sccf = {
+ DEVICE_TYPE_SCCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&SCCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCCF_GetInterface(void) {
+ return &io_sccf ;
+} ;
+
+#endif // SUPPORT_SCCF
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_sccf.h"
+
+#ifdef SUPPORT_SCCF
+
+#ifndef SUPPORT_MPCF
+ #error Supercard CF support requires GBAMP CF support
+#endif // SUPPORT_MPCF
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+
+/*-----------------------------------------------------------------
+SCCF_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCCF_Unlock(void)
+{
+#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000
+ unsigned char temp;
+ volatile short *unlockAddress = (volatile short *)0x09FFFFFE;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0xA55A ;
+ *unlockAddress = 0x3 ;
+ *unlockAddress = 0x3 ;
+ // provoke a ready reply
+ temp = CF_REG_LBA1;
+ CF_REG_LBA1 = (~temp & 0xFF);
+ temp = (~temp & 0xFF);
+ return (CF_REG_LBA1 == temp);
+#undef CF_REG_LBA1
+}
+
+bool SCCF_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCCF_StartUp(void) {
+ return SCCF_Unlock() ;
+} ;
+
+
+IO_INTERFACE io_sccf = {
+ DEVICE_TYPE_SCCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA,
+ (FN_MEDIUM_STARTUP)&SCCF_StartUp,
+ (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted,
+ (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCCF_GetInterface(void) {
+ return &io_sccf ;
+} ;
+
+#endif // SUPPORT_SCCF
diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.h b/backends/platform/ds/arm9/source/fat/io_sccf.h
new file mode 100644
index 0000000000..961909fbce
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_sccf.h
@@ -0,0 +1,50 @@
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the Supercard CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCCF_H
+#define IO_SCCF_H
+
+// 'SCCF'
+#define DEVICE_TYPE_SCCF 0x46434353
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCCF_GetInterface(void) ;
+
+#endif // define IO_SCCF_H
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the Supercard CF
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCCF_H
+#define IO_SCCF_H
+
+// 'SCCF'
+#define DEVICE_TYPE_SCCF 0x46434353
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCCF_GetInterface(void) ;
+
+#endif // define IO_SCCF_H
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.c b/backends/platform/ds/arm9/source/fat/io_scsd.c
new file mode 100644
index 0000000000..a2fccf45d2
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd.c
@@ -0,0 +1,186 @@
+/*
+ io_scsd.c by SaTa.
+ based on io_sccf.c
+
+
+*/
+
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_scsd.h"
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+// add by SaTa.
+extern void InitSCMode(void); // CFと同じ
+extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber);
+extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber);
+extern bool MemoryCard_IsInserted(void); // CFと違う
+//
+
+/*-----------------------------------------------------------------
+SCSD_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCSD_Unlock(void)
+{
+ InitSCMode();
+ return MemoryCard_IsInserted();
+}
+
+bool SCSD_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCSD_StartUp(void) {
+ return SCSD_Unlock() ;
+} ;
+
+bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff)
+{
+ ReadSector((u16 *)buff,sector,ReadNumber);
+ return true;
+}
+
+bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff)
+{
+ WriteSector((u16 *)buff,sector,writeNumber);
+ return true;
+}
+
+
+IO_INTERFACE io_scsd = {
+ 0x44534353, // 'SCSD'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&SCSD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&SCSD_Unlock,
+ (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCSD_GetInterface(void) {
+ return &io_scsd ;
+} ;
+/*
+ io_scsd.c by SaTa.
+ based on io_sccf.c
+
+
+*/
+
+/*
+ io_sccf.c based on
+
+ compact_flash.c
+ By chishm (Michael Chisholm)
+
+ Hardware Routines for reading a compact flash card
+ using the Super Card CF
+
+ CF routines modified with help from Darkfader
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+
+#include "io_scsd.h"
+
+/*-----------------------------------------------------------------
+Since all CF addresses and commands are the same for the GBAMP,
+simply use it's functions instead.
+-----------------------------------------------------------------*/
+
+extern bool MPCF_IsInserted (void);
+extern bool MPCF_ClearStatus (void);
+extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer);
+extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer);
+
+// add by SaTa.
+extern void InitSCMode(void); // CFと同じ
+extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber);
+extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber);
+extern bool MemoryCard_IsInserted(void); // CFと違う
+//
+
+/*-----------------------------------------------------------------
+SCSD_Unlock
+Returns true if SuperCard was unlocked, false if failed
+Added by MightyMax
+Modified by Chishm
+-----------------------------------------------------------------*/
+bool SCSD_Unlock(void)
+{
+ InitSCMode();
+ return MemoryCard_IsInserted();
+}
+
+bool SCSD_Shutdown(void) {
+ return MPCF_ClearStatus() ;
+} ;
+
+bool SCSD_StartUp(void) {
+ return SCSD_Unlock() ;
+} ;
+
+bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff)
+{
+ ReadSector((u16 *)buff,sector,ReadNumber);
+ return true;
+}
+
+bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff)
+{
+ WriteSector((u16 *)buff,sector,writeNumber);
+ return true;
+}
+
+
+IO_INTERFACE io_scsd = {
+ 0x44534353, // 'SCSD'
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&SCSD_StartUp,
+ (FN_MEDIUM_ISINSERTED)&SCSD_Unlock,
+ (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown
+} ;
+
+
+LPIO_INTERFACE SCSD_GetInterface(void) {
+ return &io_scsd ;
+} ;
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.h b/backends/platform/ds/arm9/source/fat/io_scsd.h
new file mode 100644
index 0000000000..1e4e17dbb8
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd.h
@@ -0,0 +1,58 @@
+/*
+ io_scsd.h by SaTa.
+ based on io_sccf.h
+
+
+*/
+
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCSD_H
+#define IO_SCSD_H
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCSD_GetInterface(void) ;
+
+#endif // define IO_SCSD_H
+/*
+ io_scsd.h by SaTa.
+ based on io_sccf.h
+
+
+*/
+
+/*
+ io_sccf.h
+
+ Hardware Routines for reading a compact flash card
+ using the GBA Movie Player
+
+ This software is completely free. No warranty is provided.
+ If you use it, please give me credit and email me about your
+ project at chishm@hotmail.com
+
+ See gba_nds_fat.txt for help and license details.
+*/
+
+#ifndef IO_SCSD_H
+#define IO_SCSD_H
+
+#include "disc_io.h"
+
+// export interface
+extern LPIO_INTERFACE SCSD_GetInterface(void) ;
+
+#endif // define IO_SCSD_H
diff --git a/backends/platform/ds/arm9/source/fat/io_scsd_asm.s b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s
new file mode 100644
index 0000000000..f138c07205
--- /dev/null
+++ b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s
@@ -0,0 +1,1042 @@
+ .TEXT
+@--------------------------------sd data--------------------------------
+.equ sd_comadd,0x9800000
+.equ sd_dataadd,0x9000000
+.equ sd_dataradd,0x9100000
+@-----------------viod sd_data_write_s(u16 *buff,u16* crc16buff)-------------------
+ .ALIGN
+ .GLOBAL sd_data_write_s
+ .CODE 32
+sd_data_write_s:
+ stmfd r13!,{r4-r5}
+ mov r5,#512
+ mov r2,#sd_dataadd
+sd_data_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ ldrh r3,[r2]
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+sd_data_write_loop:
+ ldrh r3,[r0],#2
+ add r3,r3,r3,lsl #20
+ mov r4,r3,lsl #8
+ stmia r2,{r3-r4}
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+sd_data_write_loop2:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ bne sd_data_write_loop2
+
+ ldmia r2,{r3-r4}
+
+ ldmfd r13!,{r4-r5}
+ bx r14
+@-----------------end sd_data_write_s-------------------
+
+@----------void sd_data_read_s(u16 *buff)-------------
+ .ALIGN
+ .GLOBAL sd_data_read_s
+ .CODE 32
+sd_data_read_s:
+ stmfd r13!,{r4}
+ mov r1,#sd_dataradd
+sd_data_read_loop1:
+ ldrh r3,[r1] @star bit
+ tst r3,#0x100
+ bne sd_data_read_loop1
+ mov r2,#512
+sd_data_read_loop:
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ subs r2, r2, #16
+ bne sd_data_read_loop
+
+ ldmia r1,{r3-r4} @crc 16
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldrh r3,[r1] @end bit
+ ldmfd r13!,{r4}
+ bx r14
+@----------end sd_data_read_s-------------
+
+@------void sd_com_crc16_s(u16* buff,u16 num,u16* crc16buff)
+ .ALIGN
+ .GLOBAL sd_crc16_s
+ .CODE 32
+sd_crc16_s:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end sd_com_crc16_s-----------------------------------
+
+@--------------u8 sd_crc7_s(u16* buff,u16 num)----------------------------
+ .ALIGN
+ .GLOBAL sd_crc7_s
+ .CODE 32
+sd_crc7_s:
+ stmfd r13!,{r4}
+
+ mov r3,#0
+ ldr r4,=0x80808080
+ mov r1,r1,lsl #3 @ *8
+sd_crc7_loop:
+ tst r4,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+
+ tst r3,#0x80
+ eorne r3,r3,#0x9
+
+ tst r2,r4,lsr #24
+ eorne r3,r3,#0x9
+
+ mov r4,r4,ror #1
+ subs r1,r1,#1
+ bne sd_crc7_loop
+
+ mov r3,r3,lsl #1
+ add r0,r3,#1
+ ldmfd r13!,{r4}
+ bx r14
+@--------------end sd_crc7_s----------------------------
+
+@--------------sd_com_read_s(u16* buff,u32 num)------------------
+ .ALIGN
+ .GLOBAL sd_com_read_s
+ .CODE 32
+
+sd_com_read_s:
+ stmfd r13!,{r4-r6}
+ mov r2,#sd_comadd
+sd_com_read_loop1:
+ ldrh r3,[r2] @star bit
+ tst r3,#1
+ bne sd_com_read_loop1
+
+sd_com_read_loop:
+ ldmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_read_loop
+ ldmfd r13!,{r4-r6}
+ bx r14
+@--------------end sd_com_read_s------------------
+
+@-------------------void sd_com_write_s(u16* buff,u32 num)-----------------
+
+ .ALIGN
+ .GLOBAL sd_com_write_s
+ .CODE 32
+sd_com_write_s:
+ stmfd r13!,{r4-r6}
+
+ mov r2,#sd_comadd
+sd_com_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x1
+ beq sd_com_write_busy
+
+ ldrh r3,[r2]
+
+sd_com_write_loop:
+ ldrb r3,[r0],#1
+ add r3,r3,r3,lsl #17
+ mov r4,r3,lsl #2
+ mov r5,r4,lsl #2
+ mov r6,r5,lsl #2
+ stmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_write_loop
+ ldmfd r13!,{r4-r6}
+
+ bx r14
+@-------------------end sd_com_write_s-----------------
+
+@-----------------void send_clk(u32 num)---------
+ .ALIGN
+ .GLOBAL send_clk
+ .CODE 32
+
+send_clk:
+ mov r1,#sd_comadd
+send_clk_loop1:
+ ldrh r3,[r1]
+ subs r0,r0,#1
+ bne send_clk_loop1
+ bx r14
+@-----------------end send_clk---------
+
+@---------------------------void SDCommand(u8 command,u8 num,u32 sector)--------------------
+ .ALIGN
+ .GLOBAL SDCommand
+ .CODE 32
+@void SDCommand(u8 command,u8 num,u32 sector )
+@{
+@ u8 databuff[6];
+@ register u8 *char1;
+@ register u8 *char2;
+@
+@ char1=(u8*)(((u32)&sector)+3);
+@ char2=(u8*)databuff;
+@ *char2++=coma+0x40;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1;
+@ *char2=sd_crc7_s((u32)databuff,5);
+@
+@ sd_com_write_s((u32)databuff,6);
+@
+@}
+SDCommand:
+ stmfd r13!,{r14}
+
+ sub r13,r13,#16
+ add r0,r0,#0x40
+ strb r0,[r13,#4]
+
+ mov r1,r2,lsr #24
+ strb r1,[r13,#5]
+ mov r1,r2,lsr #16
+ strb r1,[r13,#6]
+ mov r1,r2,lsr #8
+ strb r1,[r13,#7]
+ strb r2,[r13,#8]
+ add r0,r13,#4
+ mov r1,#5
+ bl sd_crc7_s
+ strb r0,[r13,#9]
+ add r0,r13,#4
+ mov r1,#6
+ bl sd_com_write_s
+
+ add r13,r13,#16
+ ldmfd r13!,{r15}
+@ bx r14
+@---------------------------end SDCommand--------------------
+
+@----------void ReadSector(u16 *buff,u32 sector,u8 readnum)-------------
+ .ALIGN
+ .GLOBAL ReadSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void ReadSector(u16 *buff,u32 sector,u8 readnum)
+@{
+@ register u16 i,j;
+@ i=readnum;
+@ sectno<<=9;
+@ SDCommand(18,0,sector);
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_data_read_s((u32)buff+j*512);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@
+@}
+ReadSector:
+ stmfd r13!,{r4-r6,r14}
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#18
+ mov r1,#0
+ bl SDCommand
+ mov r6,#0
+beginforj_ReadSector:
+ cmp r6,r5
+ bge endforj_ReadSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ bl sd_data_read_s
+ add r6,r6,#1
+ b beginforj_ReadSector
+endforj_ReadSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r0,#1
+ ldmfd r13!,{r4-r6,r15}
+@ bx r14
+@----------end ReadSector------------
+
+@-----------void get_resp(void)-------------------
+
+
+ .ALIGN
+ .GLOBAL get_resp @ r0:Srcp r1:num ok
+ .CODE 32
+get_resp:
+
+ stmfd r13!,{r14}
+ mov r1,#6
+ bl sd_com_read_s
+ ldmfd r13!,{r15}
+@-----------end get_resp-------------------
+
+
+@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)---------------------
+ .ALIGN
+ .GLOBAL WriteSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void WriteSector(u16 *buff,u32 sector,u8 writenum)
+@{
+@ register u16 i,j;
+@ u16 crc16[5];
+@ i=writenum;
+@
+@ sectno<<=9;
+@
+@ SDCommand(25,0,sector);
+@ get_resp();
+@ send_clk(0x10);
+@
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16);
+@ sd_data_write_s((u32)buff+j*512,(u32)crc16);
+@ send_clk(0x10);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@ while((*(u16*)sd_dataadd &0x0100)==0);
+@
+@}
+@
+WriteSector:
+ stmfd r13!,{r4-r6,r14}
+
+ sub r13,r13,#20
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#25
+ mov r1,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r6,#0
+
+beginforj_WriteSector:
+ cmp r6,r5
+ bge endforj_WriteSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ mov r1,#512
+ add r2,r13,#4
+ bl sd_crc16_s
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ add r1,r13,#4
+ bl sd_data_write_s
+ mov r0,#0x10
+ bl send_clk
+ add r6,r6,#1
+ b beginforj_WriteSector
+endforj_WriteSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ ldr r0,=sd_dataadd
+beginwhile_WriteSector:
+ ldrh r1,[r0]
+ tst r1,#0x0100
+ beq beginwhile_WriteSector
+ mov r0,#1
+ add r13,r13,#20
+ ldmfd r13!,{r4-r6,r15}
+@---------------end WriteSector--------------------
+
+@----------------void InitSCMode(void)---------------
+ .ALIGN
+ .GLOBAL InitSCMode
+ .CODE 32
+InitSCMode:
+ mvn r0,#0x0F6000000
+ sub r0,r0,#0x01
+ mov r1,#0x0A500
+ add r1,r1,#0x5A
+ strh r1,[r0]
+ strh r1,[r0]
+ mov r2,#3
+ strh r2,[r0]
+ strh r2,[r0]
+ bx r14
+@----------------end InitSCMode ---------------
+
+@----------------bool MemoryCard_IsInserted(void)---------------
+ .ALIGN
+ .GLOBAL MemoryCard_IsInserted
+ .CODE 32
+
+MemoryCard_IsInserted:
+ ldr r0,=sd_comadd
+ ldrh r1,[r0]
+ tst r1,#0x300
+ moveq r0,#1
+ movne r0,#0
+ bx r14
+@----------------end MemoryCard_IsInserted---------------
+
+ .END
+
+
+
+
+
+
+
+
+
+
+
+ .TEXT
+@--------------------------------sd data--------------------------------
+.equ sd_comadd,0x9800000
+.equ sd_dataadd,0x9000000
+.equ sd_dataradd,0x9100000
+@-----------------viod sd_data_write_s(u16 *buff,u16* crc16buff)-------------------
+ .ALIGN
+ .GLOBAL sd_data_write_s
+ .CODE 32
+sd_data_write_s:
+ stmfd r13!,{r4-r5}
+ mov r5,#512
+ mov r2,#sd_dataadd
+sd_data_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ beq sd_data_write_busy
+
+ ldrh r3,[r2]
+
+ mov r3,#0 @star bit
+ strh r3,[r2]
+sd_data_write_loop:
+ ldrh r3,[r0],#2
+ add r3,r3,r3,lsl #20
+ mov r4,r3,lsl #8
+ stmia r2,{r3-r4}
+
+ subs r5, r5, #2
+ bne sd_data_write_loop
+
+ cmp r1,#0
+ movne r0,r1
+ movne r1,#0
+ movne r5,#8
+ bne sd_data_write_loop
+
+ mov r3,#0xff @end bit
+ strh r3,[r2]
+sd_data_write_loop2:
+ ldrh r3,[r2]
+ tst r3,#0x100
+ bne sd_data_write_loop2
+
+ ldmia r2,{r3-r4}
+
+ ldmfd r13!,{r4-r5}
+ bx r14
+@-----------------end sd_data_write_s-------------------
+
+@----------void sd_data_read_s(u16 *buff)-------------
+ .ALIGN
+ .GLOBAL sd_data_read_s
+ .CODE 32
+sd_data_read_s:
+ stmfd r13!,{r4}
+ mov r1,#sd_dataradd
+sd_data_read_loop1:
+ ldrh r3,[r1] @star bit
+ tst r3,#0x100
+ bne sd_data_read_loop1
+ mov r2,#512
+sd_data_read_loop:
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ ldmia r1,{r3-r4}
+ mov r3,r4,lsr #16
+ strh r3 ,[r0],#2
+
+ subs r2, r2, #16
+ bne sd_data_read_loop
+
+ ldmia r1,{r3-r4} @crc 16
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldmia r1,{r3-r4}
+ ldrh r3,[r1] @end bit
+ ldmfd r13!,{r4}
+ bx r14
+@----------end sd_data_read_s-------------
+
+@------void sd_com_crc16_s(u16* buff,u16 num,u16* crc16buff)
+ .ALIGN
+ .GLOBAL sd_crc16_s
+ .CODE 32
+sd_crc16_s:
+ stmfd r13!,{r4-r9}
+ mov r9,r2
+
+ mov r3,#0
+ mov r4,#0
+ mov r5,#0
+ mov r6,#0
+
+ ldr r7,=0x80808080
+ ldr r8,=0x1021
+ mov r1,r1,lsl #3
+sd_crc16_loop:
+
+ tst r7,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+ tst r3,#0x10000
+ eorne r3,r3,r8
+ tst r2,r7,lsr #24
+ eorne r3,r3,r8
+
+ mov r4,r4,lsl #1
+ tst r4,#0x10000
+ eorne r4,r4,r8
+ tst r2,r7,lsr #25
+ eorne r4,r4,r8
+
+ mov r5,r5,lsl #1
+ tst r5,#0x10000
+ eorne r5,r5,r8
+ tst r2,r7,lsr #26
+ eorne r5,r5,r8
+
+ mov r6,r6,lsl #1
+ tst r6,#0x10000
+ eorne r6,r6,r8
+ tst r2,r7,lsr #27
+ eorne r6,r6,r8
+
+ mov r7,r7,ror #4
+ subs r1,r1,#4
+ bne sd_crc16_loop
+
+ mov r2,r9
+ mov r8,#16
+sd_crc16_write_data:
+ mov r7,r7,lsl #4
+ tst r3,#0x8000
+ orrne r7,r7,#8
+ tst r4,#0x8000
+ orrne r7,r7,#4
+ tst r5,#0x8000
+ orrne r7,r7,#2
+ tst r6,#0x8000
+ orrne r7,r7,#1
+
+ mov r3,r3,lsl #1
+ mov r4,r4,lsl #1
+ mov r5,r5,lsl #1
+ mov r6,r6,lsl #1
+
+ sub r8,r8,#1
+ tst r8,#1
+ streqb r7,[r2],#1
+ cmp r8,#0
+ bne sd_crc16_write_data
+
+ ldmfd r13!,{r4-r9}
+ bx r14
+@------end sd_com_crc16_s-----------------------------------
+
+@--------------u8 sd_crc7_s(u16* buff,u16 num)----------------------------
+ .ALIGN
+ .GLOBAL sd_crc7_s
+ .CODE 32
+sd_crc7_s:
+ stmfd r13!,{r4}
+
+ mov r3,#0
+ ldr r4,=0x80808080
+ mov r1,r1,lsl #3 @ *8
+sd_crc7_loop:
+ tst r4,#0x80
+ ldrneb r2,[r0],#1
+
+ mov r3,r3,lsl #1
+
+ tst r3,#0x80
+ eorne r3,r3,#0x9
+
+ tst r2,r4,lsr #24
+ eorne r3,r3,#0x9
+
+ mov r4,r4,ror #1
+ subs r1,r1,#1
+ bne sd_crc7_loop
+
+ mov r3,r3,lsl #1
+ add r0,r3,#1
+ ldmfd r13!,{r4}
+ bx r14
+@--------------end sd_crc7_s----------------------------
+
+@--------------sd_com_read_s(u16* buff,u32 num)------------------
+ .ALIGN
+ .GLOBAL sd_com_read_s
+ .CODE 32
+
+sd_com_read_s:
+ stmfd r13!,{r4-r6}
+ mov r2,#sd_comadd
+sd_com_read_loop1:
+ ldrh r3,[r2] @star bit
+ tst r3,#1
+ bne sd_com_read_loop1
+
+sd_com_read_loop:
+ ldmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_read_loop
+ ldmfd r13!,{r4-r6}
+ bx r14
+@--------------end sd_com_read_s------------------
+
+@-------------------void sd_com_write_s(u16* buff,u32 num)-----------------
+
+ .ALIGN
+ .GLOBAL sd_com_write_s
+ .CODE 32
+sd_com_write_s:
+ stmfd r13!,{r4-r6}
+
+ mov r2,#sd_comadd
+sd_com_write_busy:
+ ldrh r3,[r2]
+ tst r3,#0x1
+ beq sd_com_write_busy
+
+ ldrh r3,[r2]
+
+sd_com_write_loop:
+ ldrb r3,[r0],#1
+ add r3,r3,r3,lsl #17
+ mov r4,r3,lsl #2
+ mov r5,r4,lsl #2
+ mov r6,r5,lsl #2
+ stmia r2,{r3-r6}
+ subs r1, r1, #1
+ bne sd_com_write_loop
+ ldmfd r13!,{r4-r6}
+
+ bx r14
+@-------------------end sd_com_write_s-----------------
+
+@-----------------void send_clk(u32 num)---------
+ .ALIGN
+ .GLOBAL send_clk
+ .CODE 32
+
+send_clk:
+ mov r1,#sd_comadd
+send_clk_loop1:
+ ldrh r3,[r1]
+ subs r0,r0,#1
+ bne send_clk_loop1
+ bx r14
+@-----------------end send_clk---------
+
+@---------------------------void SDCommand(u8 command,u8 num,u32 sector)--------------------
+ .ALIGN
+ .GLOBAL SDCommand
+ .CODE 32
+@void SDCommand(u8 command,u8 num,u32 sector )
+@{
+@ u8 databuff[6];
+@ register u8 *char1;
+@ register u8 *char2;
+@
+@ char1=(u8*)(((u32)&sector)+3);
+@ char2=(u8*)databuff;
+@ *char2++=coma+0x40;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1--;
+@ *char2++=*char1;
+@ *char2=sd_crc7_s((u32)databuff,5);
+@
+@ sd_com_write_s((u32)databuff,6);
+@
+@}
+SDCommand:
+ stmfd r13!,{r14}
+
+ sub r13,r13,#16
+ add r0,r0,#0x40
+ strb r0,[r13,#4]
+
+ mov r1,r2,lsr #24
+ strb r1,[r13,#5]
+ mov r1,r2,lsr #16
+ strb r1,[r13,#6]
+ mov r1,r2,lsr #8
+ strb r1,[r13,#7]
+ strb r2,[r13,#8]
+ add r0,r13,#4
+ mov r1,#5
+ bl sd_crc7_s
+ strb r0,[r13,#9]
+ add r0,r13,#4
+ mov r1,#6
+ bl sd_com_write_s
+
+ add r13,r13,#16
+ ldmfd r13!,{r15}
+@ bx r14
+@---------------------------end SDCommand--------------------
+
+@----------void ReadSector(u16 *buff,u32 sector,u8 readnum)-------------
+ .ALIGN
+ .GLOBAL ReadSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void ReadSector(u16 *buff,u32 sector,u8 readnum)
+@{
+@ register u16 i,j;
+@ i=readnum;
+@ sectno<<=9;
+@ SDCommand(18,0,sector);
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_data_read_s((u32)buff+j*512);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@
+@}
+ReadSector:
+ stmfd r13!,{r4-r6,r14}
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#18
+ mov r1,#0
+ bl SDCommand
+ mov r6,#0
+beginforj_ReadSector:
+ cmp r6,r5
+ bge endforj_ReadSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ bl sd_data_read_s
+ add r6,r6,#1
+ b beginforj_ReadSector
+endforj_ReadSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r0,#1
+ ldmfd r13!,{r4-r6,r15}
+@ bx r14
+@----------end ReadSector------------
+
+@-----------void get_resp(void)-------------------
+
+
+ .ALIGN
+ .GLOBAL get_resp @ r0:Srcp r1:num ok
+ .CODE 32
+get_resp:
+
+ stmfd r13!,{r14}
+ mov r1,#6
+ bl sd_com_read_s
+ ldmfd r13!,{r15}
+@-----------end get_resp-------------------
+
+
+@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)---------------------
+ .ALIGN
+ .GLOBAL WriteSector @ r0:Srcp r1:num ok
+ .CODE 32
+
+@void WriteSector(u16 *buff,u32 sector,u8 writenum)
+@{
+@ register u16 i,j;
+@ u16 crc16[5];
+@ i=writenum;
+@
+@ sectno<<=9;
+@
+@ SDCommand(25,0,sector);
+@ get_resp();
+@ send_clk(0x10);
+@
+@ for (j=0;j<i ; j++)
+@ {
+@ sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16);
+@ sd_data_write_s((u32)buff+j*512,(u32)crc16);
+@ send_clk(0x10);
+@ }
+@ SDCommand(12,0,0);
+@ get_resp();
+@ send_clk(0x10);
+@ while((*(u16*)sd_dataadd &0x0100)==0);
+@
+@}
+@
+WriteSector:
+ stmfd r13!,{r4-r6,r14}
+
+ sub r13,r13,#20
+
+ mov r4,r0
+ mov r5,r2
+
+ mov r2,r1,lsl #9
+ mov r0,#25
+ mov r1,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ mov r6,#0
+
+beginforj_WriteSector:
+ cmp r6,r5
+ bge endforj_WriteSector
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ mov r1,#512
+ add r2,r13,#4
+ bl sd_crc16_s
+ mov r0,r4
+ add r0,r0,r6,lsl #9
+ add r1,r13,#4
+ bl sd_data_write_s
+ mov r0,#0x10
+ bl send_clk
+ add r6,r6,#1
+ b beginforj_WriteSector
+endforj_WriteSector:
+ mov r0,#12
+ mov r1,#0
+ mov r2,#0
+ bl SDCommand
+ bl get_resp
+ mov r0,#0x10
+ bl send_clk
+ ldr r0,=sd_dataadd
+beginwhile_WriteSector:
+ ldrh r1,[r0]
+ tst r1,#0x0100
+ beq beginwhile_WriteSector
+ mov r0,#1
+ add r13,r13,#20
+ ldmfd r13!,{r4-r6,r15}
+@---------------end WriteSector--------------------
+
+@----------------void InitSCMode(void)---------------
+ .ALIGN
+ .GLOBAL InitSCMode
+ .CODE 32
+InitSCMode:
+ mvn r0,#0x0F6000000
+ sub r0,r0,#0x01
+ mov r1,#0x0A500
+ add r1,r1,#0x5A
+ strh r1,[r0]
+ strh r1,[r0]
+ mov r2,#3
+ strh r2,[r0]
+ strh r2,[r0]
+ bx r14
+@----------------end InitSCMode ---------------
+
+@----------------bool MemoryCard_IsInserted(void)---------------
+ .ALIGN
+ .GLOBAL MemoryCard_IsInserted
+ .CODE 32
+
+MemoryCard_IsInserted:
+ ldr r0,=sd_comadd
+ ldrh r1,[r0]
+ tst r1,#0x300
+ moveq r0,#1
+ movne r0,#0
+ bx r14
+@----------------end MemoryCard_IsInserted---------------
+
+ .END
+
+
+
+
+
+
+
+
+
+
+