To use this file copy and paste this:    // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/fat-1.flb"   into BV Terminal 3 or here to download.

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 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


Full Contents of File

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// 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 )
: Hword16dbuff  +  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
;                   

: tab9  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