// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // FAT formmatted SD card access // ========== P A R T 1 ====================================== // This has the buffers and low level sector handeling. Use this for // mounting the device and reserving space in RAM for the buffers // Unusually in this file there are a lot of non colon words that are // compiled as the file is read // This file will compile stand alone as there are no duplicated // words used // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // REQUIRES: // #URL-lib "http://pin1.org/forthlib/flb/General/soft1.flb" sid=99 // #URL-lib "http://pin1.org/forthlib/flb/General/pinsel.flb" sid=100 // #URL-lib "http://pin1.org/forthlib/flb/General/SPI.flb" sid=101 // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/MMC.flb" sid=102 // CONSTANTS: // The main access is via static buffers which simplifies file handleing // operations. Set the constant below to the number of buffers required, // one buffer is required for each file open but beware thay take // about 300 bytes each. 1 constant devices // number of devices 2 constant fileBuffers // number of file buffers 2 constant rclust // always 2 don't know why or how to determine otherwise 512 vspace$ dbuff // buffer used for system // // These are of major importance and simplyfy coding considerably // the last sector read is always known so that it can be easily // updated - I got a long way before realising this integer d-sec // current sector being worked on for Dbuff integer f-sec // current sector being worked on for fatb integer error# // error number integer d# // this is the current file handle makes access much easier integer f# // this is the current file handle makes access much easier // // <><><><><><><><><><><><><><><<><><><><><><><><><><><><> // This is an array to store the device geometry, is is an array so that // more than one device is possible. Note that *array uses the variable // space so this can be saved to flash withou loosing the buffer 0 constant MBR // address of start of MBR 4 constant FATTable1 // address of start of FAT1 8 constant FATTable2 // address of start of FAT1 12 constant Dir // address of start of root dir 16 constant RootEntries // number of directory entries 20 constant FileSpace // address of start of filespace - rclust 24 constant BytesSec // number of bytes per sector 28 constant SectClust // number of sectors per cluster 32 constant SectFat // number of sectors per fat 36 constant ReservedSectors // 40 constant TotalSec // Total size in sectors 44 constant Fat# // fat 12 or fat 16 48 constant csel // cs line for this device -ve is port 1 devices 56 *array device# // <><><><><><><><><><><><><><><<><><><><><><><><><><><><> // // <><><><><><><><><><><><><><><<><><><><><><><><><><><><> // The buffers are accessed via a file hanlde that simply // points to the array index // A buffer can access more than one device 4 constant EOF // kept as part of array to prevent reading past end 8 constant device // when open points to a particular device 12 constant isOpen // -1 if file is open // has a directory connection so that a file can be associated to // a directory that can be wriiten back only at file close // the whole 32 byte directory entry is kept in this buffer 16 constant dirSec // sector where this entry is stored 20 constant en# // entry number for wtiting back 24 constant dirEntry // copy of root entry 60 constant fatbb // note space for dir-entry fileBuffers 572 *array file# // data buffer 512 + 60 // <><><><><><><><><><><><><><><<><><><><><><><><><><><><> // general error handling, over 100 is is an abort, this is a method // for testing to save having always to return -1 or 0 // still unsure if this is worh it or not : error ( error-number -- [error#] ) dup => error# 100 > if cr ." error number " error# . abort then ; // Device access, Device# is an array so that more than one device // can be used, to access a device parameter set d# first and use d // example to get the buyes per sector for device 0 do this: // 0 => d# // BytesPerSec d @ : dd ( const device -- ) device# + ; : d ( const -- ) d# device# + ; // The data buffer is defined in array file#, array is used so there can // be more than one data buffer, to access set the file buffer number f# first // access to file through this word, example // to get Dir: Dir f @ // to store 55 Dir f ! : f ( const -- ) f# file# + ; // access to the fat buffer address // this is used all the time so it is given its own word here : fatb ( --- address ) fatbb f ; // Utility for reading 16 bit words from the address given // buffer, address points to the low (first) byte (little endian) // ( address ---) : word16 dup c@ // low byte swap 1+ c@ &100 * + ; // this is the oposite to word16 defined in fat-1 // write 16 word in intel format to the address : word16! ( word16 address -- ) >r // address dup &ff and r@ c! // store low byte 8 rshift &ff and r> 1+ c! // high byte ; // primitave for sec@, bps is bytes per sector. All this really // does is translate sector to a device address : (sec@) ( sector buffer bps -- ) csel d @ mmc.cs // select correct card (d# set by sec@) rot over * mmc.read // read device -1 <> abort" Can't read MMC Card" ; // Gets sector into the device buffer rather than the file buffer // this is used in diectory and device operations : Dsec@ ( sector -- [dbuff]) dup => d-sec // keep sector up to date dbuff BytesSec d @ (sec@) ; // read from the device at a given sector, the address is calculated // from the sector * bytes per sec // ( sector --- [fatb]) : secfetch // read sector dup => f-sec // keep sector up to date device f @ => d# // select correct device fatb // buffer BytesSec d @ // bps (sec@) ; // given a sector number will write the contents of buf to it : (sec!) ( buf sector -- ) BytesSec d @ swap over * // actual physical address swap // buff, addr, bytes mmc.write -1 <> abort" Can't write MMC Card" ; // The last sector read is ALEWAYS stored in d-sec or f-sec // and so there is no need to provide a sector when writing the same // sector back, but for copying etc. a sector is needed hence it is // included here although most of the time this d-sec Dsec! will be used. // stores fatb at the supplied sector : secstore ( sector -- [error] ) fatb swap (sec!) ; // stores decvice buffer at the supplied sector : Dsec! ( sector -- [error] ) dbuff swap (sec!) ; // determines master boot record address, this is not always // at 0, found that it can be an offset determined // by location 454 but not checked this fact on all cards // aborts if card cannot be read // (--- sector) : MBRsec // this is a one time operation for each device mounted // here is borrowed as a buffer 512 BytesSec d ! // always 512 ast this stage 0 Dsec@ // get device contents sector 0 dbuff c@ &eb = if 0 else dbuff 454 + c@ then ; // ( offfset --- contents16 ) : Hword16 dbuff + word16 ; // utility for fat-Vload // Loads variables with addresses of the major blocks // used in the FAT system, tested with fat12 and fat16 // This must be run first, all required parameters are discovered // here ** i.e. determines the device geometry // d# and csel must be set first, this should be part of mound : Geo MBRSec dup MBR d ! // save for later // claculate current address, given the address of this MBR // and at this stage the default bytes per sec Dsec@ // load from new sector given by MBR // save new Bytes per sector &b Hword16 // Bytes per sec, check against constant BytesSec d ! // never come accross anything other than 512 // set FAT system dbuff &39 + c@ 48 - 10 * // convert ASCII to number, tens part dbuff &3a + c@ 48 - // convert ASCII to number, units part + // add tens + units fat# d ! // indicates 12 or 16 &e Hword16 dup ReservedSectors d ! mbr d @ + FatTable1 d ! // number of reserved sectors + MBR &16 Hword16 dup SectFat d ! // number of sectors / FAT FatTable1 d @ + FatTable2 d ! &16 Hword16 FatTable2 d @ + Dir d ! // dir follows this dbuff &d + c@ SectClust d ! // sectors / cluster &11 Hword16 dup RootEntries d ! 32 * // 32 is bytes taken up by Dir entry BytesSec d @ / Dir d @ + // add Dir SectClust d @ rclust * - // removeve reserved clusters FileSpace d ! &13 Hword16 dup 0= if drop &20 Hword16 &22 Hword16 16 lshift + then // diffferent if large TotalSec d ! // size ; : tab 9 emit ; : (geo.) dup ph. tab pd. ; // This simply prints out the device geometry, could be useful for // debugging : geodot cr ." name " tab ." size/sector Device: " d# . cr ." MBR " tab mbr d @ (geo.) cr ." FatTable1 " tab FatTable1 d @ (geo.) cr ." FatTable2 " tab FatTable2 d @ (geo.) cr ." Dir " tab Dir d @ (geo.) cr ." RootEntries" tab RootEntries d @ (geo.) cr ." FileSpace " tab FileSpace d @ (geo.) cr ." Bytes/Sec " tab Bytessec d @ (geo.) cr ." Sect/Clust" tab Sectclust d @ (geo.) cr ." Sect/Fat " tab SectFat d @ (geo.) cr ." ReservedSect" tab ReservedSectors d @ (geo.) cr ." Fat type/#" tab fat# d @ . cr cr ." Sectors Total " tab TotalSec d @ (geo.) cr ." Size MB " tab TotalSec d @ BytesSec d @ * 1000000 / (geo.) ; // --------------------- Public interface ------------------------ // This is the only public interface for the above code. At the time // of mount the csel pin is decided, this is +ve for port - and -ve for port1 // mmc.start sets this pin to an output. The card speed should be set // before calling this. Make sure that if more then one device is needed then // the devices constant is set apropriately. : <0>mmc.mount ( csel device -- ) // dup devices > if abort" Set devices constant to a bigger vlaue" then => d# // sets device array to use dup csel d ! // csel into device array mmc.start // start card - does all of initialisation if geo // get geometry else cr ." can't start mmc card " 101 error then ; : <0>fb ( -- file-buffer ) fatb ; : <0>geo. ( -- ) geodot ; // showes disk geometry : <0>sec@ ( sector -- ) secfetch ; // gets sector into fb : <0>sec! ( sector -- ) secstore ; // stores fb to sector