diff options
author | Max Horn | 2006-07-09 11:47:17 +0000 |
---|---|---|
committer | Max Horn | 2006-07-09 11:47:17 +0000 |
commit | bea72e9514a5b3ced091d952762a5fa633e27740 (patch) | |
tree | c697df47b449a0952c2bb18f10502cae8ae3d939 /backends/platform/ds/arm9/source/fat | |
parent | 51ad5aa7197b3ced348ae37e2bc1586cb25dff3e (diff) | |
download | scummvm-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')
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:�R�}���h���[�h�H + SDIDA1=0x40+17; // �R�}���h CMD17 + SDIDA2=(sectorn>>7);// �Z�N�^H 9�r�b�g=�A�h���XH �P�U�r�b�g + SDIDA3=(sectorn<<9);// �Z�N�^L 7�r�b�g=�A�h���XL �P�U�r�b�g + SDDIR=0x29; // �R�}���h���M�H + 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:�R�}���h���[�h�H + SDIDA1=0x40+17; // �R�}���h CMD17 + SDIDA2=(sectorn>>7);// �Z�N�^H 9�r�b�g=�A�h���XH �P�U�r�b�g + SDIDA3=(sectorn<<9);// �Z�N�^L 7�r�b�g=�A�h���XL �P�U�r�b�g + SDDIR=0x29; // �R�}���h���M�H + 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)§or)+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)§or)+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 + + + + + + + + + + + |