Skip to content

Instantly share code, notes, and snippets.

@danneu
Forked from RickKimball/diskio.h
Created February 23, 2021 10:05
Show Gist options
  • Save danneu/ae31a3835e6db5dc0f68137824b9ffbe to your computer and use it in GitHub Desktop.
Save danneu/ae31a3835e6db5dc0f68137824b9ffbe to your computer and use it in GitHub Desktop.

Revisions

  1. @RickKimball RickKimball created this gist Apr 7, 2012.
    38 changes: 38 additions & 0 deletions diskio.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    /*-----------------------------------------------------------------------
    / PFF - Low level disk interface modlue include file (C)ChaN, 2009
    /-----------------------------------------------------------------------*/

    #ifndef _DISKIO

    #include "integer.h"

    /* Status of Disk Functions */
    typedef BYTE DSTATUS;

    /* Results of Disk Functions */
    typedef enum {
    RES_OK = 0, /* 0: Function succeeded */
    RES_ERROR, /* 1: Disk error */
    RES_NOTRDY, /* 2: Not ready */
    RES_PARERR /* 3: Invalid parameter */
    } DRESULT;

    /*---------------------------------------*/
    /* Prototypes for disk control functions */

    DRESULT disk_initialize(void);
    DRESULT disk_readp(BYTE*, DWORD, WORD, WORD);
    DRESULT disk_writep(const BYTE*, DWORD);

    #define STA_NOINIT 0x01 /* Drive not initialized */
    #define STA_NODISK 0x02 /* No medium in the drive */

    /* Card type flags (CardType) */
    #define CT_MMC 0x01 /* MMC ver 3 */
    #define CT_SD1 0x02 /* SD ver 1 */
    #define CT_SD2 0x04 /* SD ver 2 */
    #define CT_SDC (CT_SD1|CT_SD2) /* SD */
    #define CT_BLOCK 0x08 /* Block addressing */

    #define _DISKIO
    #endif
    37 changes: 37 additions & 0 deletions integer.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    /*-------------------------------------------*/
    /* Integer type definitions for FatFs module */
    /*-------------------------------------------*/

    #ifndef _INTEGER
    #define _INTEGER

    #ifdef _WIN32 /* FatFs development platform */

    #include <windows.h>
    #include <tchar.h>

    #else /* Embedded platform */

    /* These types must be 16-bit, 32-bit or larger integer */
    typedef int INT;
    typedef unsigned int UINT;

    /* These types must be 8-bit integer */
    typedef char CHAR;
    typedef unsigned char UCHAR;
    typedef unsigned char BYTE;

    /* These types must be 16-bit integer */
    typedef short SHORT;
    typedef unsigned short USHORT;
    typedef unsigned short WORD;
    typedef unsigned short WCHAR;

    /* These types must be 32-bit integer */
    typedef long LONG;
    typedef unsigned long ULONG;
    typedef unsigned long DWORD;

    #endif

    #endif
    296 changes: 296 additions & 0 deletions main.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,296 @@
    /*---------------------------------------------------------------*/
    /* Petit FAT file system module test program R0.02 (C)ChaN, 2009 */
    /*---------------------------------------------------------------*/

    /*---------------------------------------------------------------
    * 04-06-2012 rkimball - Initial msp430g2553 implementation done
    * for msp430-gcc using USCI UART(USCA0) and SPI(USCB0)
    *---------------------------------------------------------------
    */

    #include <msp430.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <string.h>

    #include "spi.h"
    #include "diskio.h"
    #include "pff.h"
    #include "serial.h"

    const unsigned long SMCLK_FREQ = 16000000;
    const unsigned long BAUD_RATE = 9600;

    /*---------------------------------------------------------------*/
    /* Work Area */
    /*---------------------------------------------------------------*/

    char Line[128];

    static
    void put_rc(FRESULT rc) {
    const char *p;
    static const char str[] =
    "OK\0" "DISK_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
    "NOT_OPENED\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0";
    FRESULT i;

    for (p = str, i = 0; i != rc && *p; i++) {
    while (*p++)
    ;
    }
    printf("rc=%lu FR_%s\n", (long int) rc, p);
    }

    static
    void put_drc(long int res) {
    printf("rc=%lu\n", res);
    }

    static
    void get_line(char *buff, BYTE len) {
    BYTE c, i;

    i = 0;
    for (;;) {
    c = getchar();
    if (c == '\r')
    break;
    if ((c == '\b') && i)
    i--;
    if ((c >= ' ') && (i < len - 1))
    buff[i++] = c;
    }
    buff[i] = 0;
    putchar('\n');
    }

    #if _USE_READ
    static
    void put_dump(const BYTE* buff, /* Pointer to the byte array to be dumped */
    DWORD addr, /* Heading address value */
    WORD len /* Number of bytes to be dumped */
    ) {
    int n;

    printf("%08lX ", addr); /* address */

    for (n = 0; n < len; n++) { /* data (hexdecimal) */
    printf(" %02X", buff[n]);
    }

    printf(" ");

    for (n = 0; n < len; n++) { /* data (ascii) */
    putchar(((buff[n] < 0x20) || (buff[n] >= 0x7F)) ? '.' : buff[n]);
    ;
    }

    putchar('\n');
    }
    #endif

    /**
    * setup() - configure DCO clock system, UART and SPI.
    */

    void setup() {
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    DCOCTL = 0x00; // Set DCOCLK to 16MHz
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;
    //DCOCTL += 6; // the default calibrated clock on my MCU is a little slow

    P1DIR |= BIT4;
    P1SEL |= BIT4; // output SMCLK for measurement on P1.4

    /*
    * Configure USCI serial TX on P1.2, RX on P1.1. You need to change
    * turn your Launchpad TX/RX jumpers 90 degrees from the factory settings.
    */

    serial_initialize((SMCLK_FREQ + (BAUD_RATE >> 1)) / BAUD_RATE);

    /*
    * configure USCI SPI B
    */
    spi_initialize();
    }

    int main(void) {
    char *ptr;
    FRESULT res;
    #if _USE_READ
    WORD s1, s2;
    DWORD ofs;
    #endif
    #if _USE_DIR
    DIR dir; /* Directory object */
    FILINFO fno; /* File information */
    #endif
    #if _USE_WRITE
    WORD bw;
    WORD n = 0;
    #endif
    #if _USE_LSEEK
    DWORD p1;
    #endif
    FATFS fs; /* File system object */

    setup();

    __enable_interrupt(); // don't really need this we do blocking reads

    printf("\nPFF test monitor\n");

    for (;;) {
    printf("> ");
    get_line(Line, sizeof(Line));
    ptr = Line;
    switch (*ptr++) {
    case 'd':
    switch (*ptr++) {
    case 'i': /* di - Initialize physical drive */
    printf("Disk Initialize...\n");
    res = disk_initialize();
    put_drc(res);
    break;
    }
    break;
    case 'f':
    switch (*ptr++) {
    case 'i': /* fi - Mount the volume */
    put_rc(pf_mount(&fs));
    break;

    case 'o': /* fo <file> - Open a file */
    while (*ptr == ' ')
    ptr++;
    put_rc(pf_open(ptr));
    break;

    #if _USE_READ
    case 'd': /* fd - Read the file 128 bytes and dump it */
    ofs = fs.fptr;
    res = pf_read(Line, sizeof(Line), &s1);
    if (res != FR_OK) {
    put_rc(res);
    break;
    }
    ptr = Line;
    while (s1) {
    s2 = (s1 >= 16) ? 16 : s1;
    s1 -= s2;
    put_dump((BYTE*) ptr, ofs, s2);
    ptr += 16;
    ofs += 16;
    }
    break;

    case 't': /* ft - Type the file data via dreadp function */
    do {
    res = pf_read(0, 32768, &s1);
    if (res != FR_OK) {
    put_rc(res);
    break;
    }
    } while (s1 == 32768);
    break;
    #endif

    #if _USE_LSEEK
    case 'e': /* fe - Move file pointer of the file */
    p1 = atol(ptr);
    res = pf_lseek(p1);
    put_rc(res);
    if (res == FR_OK) {
    printf("fptr = %lu(0x%lX)\n", fs.fptr, fs.fptr);
    }
    break;
    #endif

    #if _USE_DIR
    case 'l': /* fl [<path>] - Directory listing */
    while (*ptr == ' ')
    ptr++;
    res = pf_opendir(&dir, ptr);
    if (res) {
    put_rc(res);
    break;
    }
    s1 = 0;
    for (;;) {
    res = pf_readdir(&dir, &fno);
    if (res != FR_OK) {
    put_rc(res);
    break;
    }
    if (!fno.fname[0])
    break;
    if (fno.fattrib & AM_DIR)
    printf(" <DIR> %s\n", fno.fname);
    else
    printf("%9lu %s\n", fno.fsize, fno.fname);
    s1++;
    }
    printf("%u item(s)\n", s1);
    break;
    #endif

    #if _USE_WRITE
    case 'p': /* fp - Write console input to the file */
    printf(
    "Enter lines to write. A blank line finalize the write operation.\n");
    for (;;) {
    get_line(Line, sizeof(Line));
    if (!Line[0])
    break;
    strcat(Line, "\r\n");
    res = pf_write(Line, strlen(Line), &bw); /* Write a line to the file */
    if (res)
    break;
    }
    res = pf_write(0, 0, &bw); /* Finalize the write process */
    put_rc(res);
    break;

    case 'w': /* fw - write into write.txt, see write limitation in Petit FatFS documentation */
    printf(
    "\nTrying to open an existing file for writing (write.txt)...\n");
    res = pf_open("WRITE.TXT");
    if (res != FR_OK) {
    put_rc(res);
    } else {
    printf("Writing 100 lines of text data\n");
    do {
    ltoa(++n, Line, 10);
    strcat(Line,
    " The quick brown fox jumps over the lazy dog. 1234567890\r\n");
    res = pf_write(Line, strlen(Line), &bw);
    if (res != FR_OK) {
    put_rc(res);
    break;
    }
    if (n & 0x10) {
    printf(".");
    }
    } while (n < 100);
    if (res == FR_OK) {
    printf("\nFinalizing the file write process.\n");
    res = pf_write(0, 0, &bw);
    if (res != FR_OK)
    put_rc(res);
    }
    }
    break;
    #endif

    }
    break;
    } /* end switch */
    } /* end for */

    return 0;
    }
    247 changes: 247 additions & 0 deletions mmc.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,247 @@
    /*-----------------------------------------------------------------------*/
    /* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2009 */
    /*-----------------------------------------------------------------------*/

    /*-----------------------------------------------------------------------*/
    /* msp430 USCI support routines */
    /*-----------------------------------------------------------------------*/

    #include <msp430.h>
    #include <stdint.h>
    #include <stdio.h>
    #include "diskio.h"
    #include "pff.h"
    #include "spi.h"

    #define DELAY_100US() __delay_cycles(1600) /* ( 100us/(1/16Mhz) ) = 1600 ticks */
    #define SELECT() P2OUT &= ~BIT0 /* CS = L */
    #define DESELECT() P2OUT |= BIT0 /* CS = H */
    #define MMC_SEL !(P2OUT & BIT0) /* CS status (true:CS == L) */
    #define FORWARD(d) putchar(d) /* Data forwarding function (Console out in this example) */

    /* Definitions for MMC/SDC command */
    #define CMD0 (0x40+0) /* GO_IDLE_STATE */
    #define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
    #define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
    #define CMD8 (0x40+8) /* SEND_IF_COND */
    #define CMD16 (0x40+16) /* SET_BLOCKLEN */
    #define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
    #define CMD24 (0x40+24) /* WRITE_BLOCK */
    #define CMD55 (0x40+55) /* APP_CMD */
    #define CMD58 (0x40+58) /* READ_OCR */

    /* Card type flags (CardType) */
    #define CT_MMC 0x01 /* MMC ver 3 */
    #define CT_SD1 0x02 /* SD ver 1 */
    #define CT_SD2 0x04 /* SD ver 2 */
    #define CT_BLOCK 0x08 /* Block addressing */

    static BYTE CardType;

    /*-----------------------------------------------------------------------*/
    /* Send a command packet to MMC */
    /*-----------------------------------------------------------------------*/

    static BYTE send_cmd(BYTE cmd, /* 1st byte (Start + Index) */
    DWORD arg /* Argument (32 bits) */
    ) {
    BYTE n, res;

    if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
    cmd &= 0x7F;
    res = send_cmd(CMD55, 0);
    if (res > 1)
    return res;
    }

    /* Select the card */DESELECT();
    spi_receive();
    SELECT();
    spi_receive();

    /* Send a command packet */
    spi_send((BYTE) cmd); /* Start + Command index */
    spi_send((BYTE) (arg >> 24)); /* Argument[31..24] */
    spi_send((BYTE) (arg >> 16)); /* Argument[23..16] */
    spi_send((BYTE) (arg >> 8)); /* Argument[15..8] */
    spi_send((BYTE) arg); /* Argument[7..0] */
    n = 0x01; /* Dummy CRC + Stop */
    if (cmd == CMD0)
    n = 0x95; /* Valid CRC for CMD0(0) */
    if (cmd == CMD8)
    n = 0x87; /* Valid CRC for CMD8(0x1AA) */
    spi_send(n);

    /* Receive a command response */
    n = 10; /* Wait for a valid response in timeout of 10 attempts */
    do {
    res = spi_receive();
    } while ((res & 0x80) && --n);

    return res; /* Return with the response value */
    }

    /*-----------------------------------------------------------------------*/
    /* Initialize Disk Drive */
    /*-----------------------------------------------------------------------*/

    DRESULT disk_initialize(void) {
    BYTE n, cmd, ty, ocr[4];
    UINT tmr;

    #if _USE_WRITE
    if (CardType && MMC_SEL)
    disk_writep(0, 0); /* Finalize write process if it is in progress */
    #endif
    spi_set_divisor(SPI_250kHz);

    DESELECT();
    for (n = 10; n; n--)
    spi_receive(); /* 80 dummy clocks with CS=H */

    ty = 0;
    if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
    if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2 */
    for (n = 0; n < 4; n++)
    ocr[n] = spi_receive(); /* Get trailing return value of R7 resp */
    if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
    for (tmr = 10000; tmr && send_cmd(ACMD41, 1UL << 30); tmr--)
    DELAY_100US(); /* Wait for leaving idle state (ACMD41 with HCS bit) */
    if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
    for (n = 0; n < 4; n++)
    ocr[n] = spi_receive();
    ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 (HC or SC) */
    }
    }
    } else { /* SDv1 or MMCv3 */
    if (send_cmd(ACMD41, 0) <= 1) {
    ty = CT_SD1;
    cmd = ACMD41; /* SDv1 */
    } else {
    ty = CT_MMC;
    cmd = CMD1; /* MMCv3 */
    }
    for (tmr = 10000; tmr && send_cmd(cmd, 0); tmr--)
    DELAY_100US(); /* Wait for leaving idle state */
    if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
    ty = 0;
    }
    }
    CardType = ty;
    spi_set_divisor(SPI_DEFAULT_SPEED);
    DESELECT();
    spi_receive();

    return ty ? 0 : STA_NOINIT;
    }

    /*-----------------------------------------------------------------------*/
    /* Read partial sector */
    /*-----------------------------------------------------------------------*/

    DRESULT disk_readp(BYTE *buff, /* Pointer to the read buffer (NULL:Read bytes are forwarded to the stream) */
    DWORD lba, /* Sector number (LBA) */
    WORD ofs, /* Byte offset to read from (0..511) */
    WORD cnt /* Number of bytes to read (ofs + cnt mus be <= 512) */
    ) {
    DRESULT res;
    BYTE rc;
    WORD bc;

    if (!(CardType & CT_BLOCK))
    lba *= 512; /* Convert to byte address if needed */

    res = RES_ERROR;
    if (send_cmd(CMD17, lba) == 0) { /* READ_SINGLE_BLOCK */

    bc = 40000;
    do { /* Wait for data packet */
    rc = spi_receive();
    } while (rc == 0xFF && --bc);

    if (rc == 0xFE) { /* A data packet arrived */
    bc = 514 - ofs - cnt;

    /* Skip leading bytes */
    if (ofs) {
    do
    spi_receive();
    while (--ofs);
    }

    /* Receive a part of the sector */
    if (buff) { /* Store data to the memory */
    do {
    *buff++ = spi_receive();
    } while (--cnt);
    } else { /* Forward data to the outgoing stream (depends on the project) */
    do {
    FORWARD(spi_receive());
    } while (--cnt);
    }

    /* Skip trailing bytes and CRC */
    do
    spi_receive();
    while (--bc);

    res = RES_OK;
    }
    }

    DESELECT();
    spi_receive();

    return res;
    }

    /*-----------------------------------------------------------------------*/
    /* Write partial sector */
    /*-----------------------------------------------------------------------*/

    #if _USE_WRITE
    DRESULT disk_writep(const BYTE *buff, /* Pointer to the bytes to be written (NULL:Initiate/Finalize sector write) */
    DWORD sa /* Number of bytes to send, Sector number (LBA) or zero */
    ) {
    DRESULT res;
    WORD bc;
    static WORD wc;

    res = RES_ERROR;

    if (buff) { /* Send data bytes */
    bc = (WORD) sa;
    while (bc && wc) { /* Send data bytes to the card */
    spi_send(*buff++);
    wc--;
    bc--;
    }
    res = RES_OK;
    } else {
    if (sa) { /* Initiate sector write process */
    if (!(CardType & CT_BLOCK))
    sa *= 512; /* Convert to byte address if needed */
    if (send_cmd(CMD24, sa) == 0) { /* WRITE_SINGLE_BLOCK */
    spi_send(0xFF);
    spi_send(0xFE); /* Data block header */
    wc = 512; /* Set byte counter */
    res = RES_OK;
    }
    } else { /* Finalize sector write process */
    bc = wc + 2;
    while (bc--)
    spi_send(0); /* Fill left bytes and CRC with zeros */
    if ((spi_receive() & 0x1F) == 0x05) { /* Receive data resp and wait for end of write process in timeout of 500ms */
    for (bc = 5000; spi_receive() != 0xFF && bc; bc--)
    DELAY_100US(); /* Wait ready */
    if (bc)
    res = RES_OK;
    }
    DESELECT();
    spi_receive();
    }
    }

    return res;
    }
    #endif
    1,114 changes: 1,114 additions & 0 deletions pff.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1114 @@
    /*----------------------------------------------------------------------------/
    / Petit FatFs - FAT file system module R0.02a (C)ChaN, 2010
    /-----------------------------------------------------------------------------/
    / Petit FatFs module is an open source software to implement FAT file system to
    / small embedded systems. This is a free software and is opened for education,
    / research and commercial developments under license policy of following trems.
    /
    / Copyright (C) 2010, ChaN, all right reserved.
    /
    / * The Petit FatFs module is a free software and there is NO WARRANTY.
    / * No restriction on use. You can use, modify and redistribute it for
    / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
    / * Redistributions of source code must retain the above copyright notice.
    /
    /-----------------------------------------------------------------------------/
    / Jun 15,'09 R0.01a First release. (Branched from FatFs R0.07b.)
    /
    / Dec 14,'09 R0.02 Added multiple code page support.
    / Added write funciton.
    / Changed stream read mode interface.
    / Dec 07,'10 R0.02a Added some configuration options.
    / Fixed fails to open objects with DBCS character.
    /----------------------------------------------------------------------------*/

    #include "pff.h" /* Petit FatFs configurations and declarations */
    #include "diskio.h" /* Declarations of low level disk I/O functions */



    /*--------------------------------------------------------------------------
    Module Private Definitions
    ---------------------------------------------------------------------------*/


    #if _FS_FAT32
    #define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO))
    #else
    #define LD_CLUST(dir) LD_WORD(dir+DIR_FstClusLO)
    #endif


    /*--------------------------------------------------------*/
    /* DBCS code ranges and SBCS extend char conversion table */

    #if _CODE_PAGE == 932 /* Japanese Shift-JIS */
    #define _DF1S 0x81 /* DBC 1st byte range 1 start */
    #define _DF1E 0x9F /* DBC 1st byte range 1 end */
    #define _DF2S 0xE0 /* DBC 1st byte range 2 start */
    #define _DF2E 0xFC /* DBC 1st byte range 2 end */
    #define _DS1S 0x40 /* DBC 2nd byte range 1 start */
    #define _DS1E 0x7E /* DBC 2nd byte range 1 end */
    #define _DS2S 0x80 /* DBC 2nd byte range 2 start */
    #define _DS2E 0xFC /* DBC 2nd byte range 2 end */

    #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
    #define _DF1S 0x81
    #define _DF1E 0xFE
    #define _DS1S 0x40
    #define _DS1E 0x7E
    #define _DS2S 0x80
    #define _DS2E 0xFE

    #elif _CODE_PAGE == 949 /* Korean */
    #define _DF1S 0x81
    #define _DF1E 0xFE
    #define _DS1S 0x41
    #define _DS1E 0x5A
    #define _DS2S 0x61
    #define _DS2E 0x7A
    #define _DS3S 0x81
    #define _DS3E 0xFE

    #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
    #define _DF1S 0x81
    #define _DF1E 0xFE
    #define _DS1S 0x40
    #define _DS1E 0x7E
    #define _DS2S 0xA1
    #define _DS2E 0xFE

    #elif _CODE_PAGE == 437 /* U.S. (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 720 /* Arabic (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 737 /* Greek (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
    0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 775 /* Baltic (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
    0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
    0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}

    #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
    0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
    0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 857 /* Turkish (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
    0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
    0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 862 /* Hebrew (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 866 /* Russian (OEM) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}

    #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
    0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}

    #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
    0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}

    #elif _CODE_PAGE == 1253 /* Greek (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
    0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}

    #elif _CODE_PAGE == 1254 /* Turkish (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
    0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}

    #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 1256 /* Arabic (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}

    #elif _CODE_PAGE == 1257 /* Baltic (Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}

    #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
    #define _DF1S 0
    #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
    0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}

    #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */
    #define _DF1S 0

    #else
    #error Unknown code page

    #endif



    /* Character code support macros */

    #define IsUpper(c) (((c)>='A')&&((c)<='Z'))
    #define IsLower(c) (((c)>='a')&&((c)<='z'))

    #if _DF1S /* DBCS configuration */

    #ifdef _DF2S /* Two 1st byte areas */
    #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
    #else /* One 1st byte area */
    #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
    #endif

    #ifdef _DS3S /* Three 2nd byte areas */
    #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
    #else /* Two 2nd byte areas */
    #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
    #endif

    #else /* SBCS configuration */

    #define IsDBCS1(c) 0
    #define IsDBCS2(c) 0

    #endif /* _DF1S */


    /* FatFs refers the members in the FAT structures with byte offset instead
    / of structure member because there are incompatibility of the packing option
    / between various compilers. */

    #define BS_jmpBoot 0
    #define BS_OEMName 3
    #define BPB_BytsPerSec 11
    #define BPB_SecPerClus 13
    #define BPB_RsvdSecCnt 14
    #define BPB_NumFATs 16
    #define BPB_RootEntCnt 17
    #define BPB_TotSec16 19
    #define BPB_Media 21
    #define BPB_FATSz16 22
    #define BPB_SecPerTrk 24
    #define BPB_NumHeads 26
    #define BPB_HiddSec 28
    #define BPB_TotSec32 32
    #define BS_55AA 510

    #define BS_DrvNum 36
    #define BS_BootSig 38
    #define BS_VolID 39
    #define BS_VolLab 43
    #define BS_FilSysType 54

    #define BPB_FATSz32 36
    #define BPB_ExtFlags 40
    #define BPB_FSVer 42
    #define BPB_RootClus 44
    #define BPB_FSInfo 48
    #define BPB_BkBootSec 50
    #define BS_DrvNum32 64
    #define BS_BootSig32 66
    #define BS_VolID32 67
    #define BS_VolLab32 71
    #define BS_FilSysType32 82

    #define MBR_Table 446

    #define DIR_Name 0
    #define DIR_Attr 11
    #define DIR_NTres 12
    #define DIR_CrtTime 14
    #define DIR_CrtDate 16
    #define DIR_FstClusHI 20
    #define DIR_WrtTime 22
    #define DIR_WrtDate 24
    #define DIR_FstClusLO 26
    #define DIR_FileSize 28



    /*--------------------------------------------------------------------------
    Private Functions
    ---------------------------------------------------------------------------*/


    static
    FATFS *FatFs; /* Pointer to the file system object (logical drive) */


    /* Fill memory */
    static
    void mem_set (void* dst, int val, int cnt) {
    char *d = (char*)dst;
    while (cnt--) *d++ = (char)val;
    }

    /* Compare memory to memory */
    static
    int mem_cmp (const void* dst, const void* src, int cnt) {
    const char *d = (const char *)dst, *s = (const char *)src;
    int r = 0;
    while (cnt-- && (r = *d++ - *s++) == 0) ;
    return r;
    }



    /*-----------------------------------------------------------------------*/
    /* FAT access - Read value of a FAT entry */
    /*-----------------------------------------------------------------------*/

    static
    CLUST get_fat ( /* 1:IO error, Else:Cluster status */
    CLUST clst /* Cluster# to get the link information */
    )
    {
    #if _FS_FAT12
    WORD wc, bc, ofs;
    #endif
    BYTE buf[4];
    FATFS *fs = FatFs;

    if (clst < 2 || clst >= fs->n_fatent) /* Range check */
    return 1;

    switch (fs->fs_type) {
    #if _FS_FAT12
    case FS_FAT12 :
    bc = (WORD)clst; bc += bc / 2;
    ofs = bc % 512; bc /= 512;
    if (ofs != 511) {
    if (disk_readp(buf, fs->fatbase + bc, ofs, 2)) break;
    } else {
    if (disk_readp(buf, fs->fatbase + bc, 511, 1)) break;
    if (disk_readp(buf+1, fs->fatbase + bc + 1, 0, 1)) break;
    }
    wc = LD_WORD(buf);
    return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
    #endif
    case FS_FAT16 :
    if (disk_readp(buf, fs->fatbase + clst / 256, (WORD)(((WORD)clst % 256) * 2), 2)) break;
    return LD_WORD(buf);
    #if _FS_FAT32
    case FS_FAT32 :
    if (disk_readp(buf, fs->fatbase + clst / 128, (WORD)(((WORD)clst % 128) * 4), 4)) break;
    return LD_DWORD(buf) & 0x0FFFFFFF;
    #endif
    }

    return 1; /* An error occured at the disk I/O layer */
    }




    /*-----------------------------------------------------------------------*/
    /* Get sector# from cluster# */
    /*-----------------------------------------------------------------------*/

    static
    DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */
    CLUST clst /* Cluster# to be converted */
    )
    {
    FATFS *fs = FatFs;


    clst -= 2;
    if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */
    return (DWORD)clst * fs->csize + fs->database;
    }




    /*-----------------------------------------------------------------------*/
    /* Directory handling - Rewind directory index */
    /*-----------------------------------------------------------------------*/

    static
    FRESULT dir_rewind (
    DIR *dj /* Pointer to directory object */
    )
    {
    CLUST clst;
    FATFS *fs = FatFs;


    dj->index = 0;
    clst = dj->sclust;
    if (clst == 1 || clst >= fs->n_fatent) /* Check start cluster range */
    return FR_DISK_ERR;
    if (_FS_FAT32 && !clst && fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */
    clst = (CLUST)fs->dirbase;
    dj->clust = clst; /* Current cluster */
    dj->sect = clst ? clust2sect(clst) : fs->dirbase; /* Current sector */

    return FR_OK; /* Seek succeeded */
    }




    /*-----------------------------------------------------------------------*/
    /* Directory handling - Move directory index next */
    /*-----------------------------------------------------------------------*/

    static
    FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table */
    DIR *dj /* Pointer to directory object */
    )
    {
    CLUST clst;
    WORD i;
    FATFS *fs = FatFs;


    i = dj->index + 1;
    if (!i || !dj->sect) /* Report EOT when index has reached 65535 */
    return FR_NO_FILE;

    if (!(i % 16)) { /* Sector changed? */
    dj->sect++; /* Next sector */

    if (dj->clust == 0) { /* Static table */
    if (i >= fs->n_rootdir) /* Report EOT when end of table */
    return FR_NO_FILE;
    }
    else { /* Dynamic table */
    if (((i / 16) & (fs->csize-1)) == 0) { /* Cluster changed? */
    clst = get_fat(dj->clust); /* Get next cluster */
    if (clst <= 1) return FR_DISK_ERR;
    if (clst >= fs->n_fatent) /* When it reached end of dynamic table */
    return FR_NO_FILE; /* Report EOT */
    dj->clust = clst; /* Initialize data for new cluster */
    dj->sect = clust2sect(clst);
    }
    }
    }

    dj->index = i;

    return FR_OK;
    }




    /*-----------------------------------------------------------------------*/
    /* Directory handling - Find an object in the directory */
    /*-----------------------------------------------------------------------*/

    static
    FRESULT dir_find (
    DIR *dj, /* Pointer to the directory object linked to the file name */
    BYTE *dir /* 32-byte working buffer */
    )
    {
    FRESULT res;
    BYTE c;


    res = dir_rewind(dj); /* Rewind directory object */
    if (res != FR_OK) return res;

    do {
    res = disk_readp(dir, dj->sect, (WORD)((dj->index % 16) * 32), 32) /* Read an entry */
    ? FR_DISK_ERR : FR_OK;
    if (res != FR_OK) break;
    c = dir[DIR_Name]; /* First character */
    if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
    if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
    break;
    res = dir_next(dj); /* Next entry */
    } while (res == FR_OK);

    return res;
    }




    /*-----------------------------------------------------------------------*/
    /* Read an object from the directory */
    /*-----------------------------------------------------------------------*/
    #if _USE_DIR
    static
    FRESULT dir_read (
    DIR *dj, /* Pointer to the directory object to store read object name */
    BYTE *dir /* 32-byte working buffer */
    )
    {
    FRESULT res;
    BYTE a, c;


    res = FR_NO_FILE;
    while (dj->sect) {
    res = disk_readp(dir, dj->sect, (WORD)((dj->index % 16) * 32), 32) /* Read an entry */
    ? FR_DISK_ERR : FR_OK;
    if (res != FR_OK) break;
    c = dir[DIR_Name];
    if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
    a = dir[DIR_Attr] & AM_MASK;
    if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */
    break;
    res = dir_next(dj); /* Next entry */
    if (res != FR_OK) break;
    }

    if (res != FR_OK) dj->sect = 0;

    return res;
    }
    #endif



    /*-----------------------------------------------------------------------*/
    /* Pick a segment and create the object name in directory form */
    /*-----------------------------------------------------------------------*/

    #ifdef _EXCVT
    static const BYTE cvt[] = _EXCVT;
    #endif

    static
    FRESULT create_name (
    DIR *dj, /* Pointer to the directory object */
    const char **path /* Pointer to pointer to the segment in the path string */
    )
    {
    BYTE c, d, ni, si, i, *sfn;
    const char *p;

    /* Create file name in directory form */
    sfn = dj->fn;
    mem_set(sfn, ' ', 11);
    si = i = 0; ni = 8;
    p = *path;
    for (;;) {
    c = p[si++];
    if (c <= ' ' || c == '/') break; /* Break on end of segment */
    if (c == '.' || i >= ni) {
    if (ni != 8 || c != '.') break;
    i = 8; ni = 11;
    continue;
    }
    #ifdef _EXCVT
    if (c >= 0x80) /* To upper extended char (SBCS) */
    c = cvt[c - 0x80];
    #endif
    if (IsDBCS1(c) && i < ni - 1) { /* DBC 1st byte? */
    d = p[si++]; /* Get 2nd byte */
    sfn[i++] = c;
    sfn[i++] = d;
    } else { /* Single byte code */
    if (IsLower(c)) c -= 0x20; /* toupper */
    sfn[i++] = c;
    }
    }
    *path = &p[si]; /* Rerurn pointer to the next segment */

    sfn[11] = (c <= ' ') ? 1 : 0; /* Set last segment flag if end of path */

    return FR_OK;
    }




    /*-----------------------------------------------------------------------*/
    /* Get file information from directory entry */
    /*-----------------------------------------------------------------------*/
    #if _USE_DIR
    static
    void get_fileinfo ( /* No return code */
    DIR *dj, /* Pointer to the directory object */
    BYTE *dir, /* 32-byte working buffer */
    FILINFO *fno /* Pointer to store the file information */
    )
    {
    BYTE i, c;
    char *p;


    p = fno->fname;
    if (dj->sect) {
    for (i = 0; i < 8; i++) { /* Copy file name body */
    c = dir[i];
    if (c == ' ') break;
    if (c == 0x05) c = 0xE5;
    *p++ = c;
    }
    if (dir[8] != ' ') { /* Copy file name extension */
    *p++ = '.';
    for (i = 8; i < 11; i++) {
    c = dir[i];
    if (c == ' ') break;
    *p++ = c;
    }
    }
    fno->fattrib = dir[DIR_Attr]; /* Attribute */
    fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */
    fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */
    fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */
    }
    *p = 0;
    }
    #endif /* _USE_DIR */



    /*-----------------------------------------------------------------------*/
    /* Follow a file path */
    /*-----------------------------------------------------------------------*/

    static
    FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
    DIR *dj, /* Directory object to return last directory and found object */
    BYTE *dir, /* 32-byte working buffer */
    const char *path /* Full-path string to find a file or directory */
    )
    {
    FRESULT res;


    while (*path == ' ') path++; /* Skip leading spaces */
    if (*path == '/') path++; /* Strip heading separator */
    dj->sclust = 0; /* Set start directory (always root dir) */

    if ((BYTE)*path <= ' ') { /* Null path means the root directory */
    res = dir_rewind(dj);
    dir[0] = 0;

    } else { /* Follow path */
    for (;;) {
    res = create_name(dj, &path); /* Get a segment */
    if (res != FR_OK) break;
    res = dir_find(dj, dir); /* Find it */
    if (res != FR_OK) { /* Could not find the object */
    if (res == FR_NO_FILE && !*(dj->fn+11))
    res = FR_NO_PATH;
    break;
    }
    if (*(dj->fn+11)) break; /* Last segment match. Function completed. */
    if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
    res = FR_NO_PATH; break;
    }
    dj->sclust = LD_CLUST(dir);
    }
    }

    return res;
    }




    /*-----------------------------------------------------------------------*/
    /* Check a sector if it is an FAT boot record */
    /*-----------------------------------------------------------------------*/

    static
    BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
    BYTE *buf, /* Working buffer */
    DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */
    )
    {
    if (disk_readp(buf, sect, 510, 2)) /* Read the boot sector */
    return 3;
    if (LD_WORD(buf) != 0xAA55) /* Check record signature */
    return 2;

    if (!disk_readp(buf, sect, BS_FilSysType, 2) && LD_WORD(buf) == 0x4146) /* Check FAT12/16 */
    return 0;
    if (_FS_FAT32 && !disk_readp(buf, sect, BS_FilSysType32, 2) && LD_WORD(buf) == 0x4146) /* Check FAT32 */
    return 0;
    return 1;
    }




    /*--------------------------------------------------------------------------
    Public Functions
    --------------------------------------------------------------------------*/



    /*-----------------------------------------------------------------------*/
    /* Mount/Unmount a Locical Drive */
    /*-----------------------------------------------------------------------*/

    FRESULT pf_mount (
    FATFS *fs /* Pointer to new file system object (NULL: Unmount) */
    )
    {
    BYTE fmt, buf[36];
    DWORD bsect, fsize, tsect, mclst;


    FatFs = 0;
    if (!fs) return FR_OK; /* Unregister fs object */

    if (disk_initialize() & STA_NOINIT) /* Check if the drive is ready or not */
    return FR_NOT_READY;

    /* Search FAT partition on the drive */
    bsect = 0;
    fmt = check_fs(buf, bsect); /* Check sector 0 as an SFD format */
    if (fmt == 1) { /* Not an FAT boot record, it may be FDISK format */
    /* Check a partition listed in top of the partition table */
    if (disk_readp(buf, bsect, MBR_Table, 16)) { /* 1st partition entry */
    fmt = 3;
    } else {
    if (buf[4]) { /* Is the partition existing? */
    bsect = LD_DWORD(&buf[8]); /* Partition offset in LBA */
    fmt = check_fs(buf, bsect); /* Check the partition */
    }
    }
    }
    if (fmt == 3) return FR_DISK_ERR;
    if (fmt) return FR_NO_FILESYSTEM; /* No valid FAT patition is found */

    /* Initialize the file system object */
    if (disk_readp(buf, bsect, 13, sizeof(buf))) return FR_DISK_ERR;

    fsize = LD_WORD(buf+BPB_FATSz16-13); /* Number of sectors per FAT */
    if (!fsize) fsize = LD_DWORD(buf+BPB_FATSz32-13);

    fsize *= buf[BPB_NumFATs-13]; /* Number of sectors in FAT area */
    fs->fatbase = bsect + LD_WORD(buf+BPB_RsvdSecCnt-13); /* FAT start sector (lba) */
    fs->csize = buf[BPB_SecPerClus-13]; /* Number of sectors per cluster */
    fs->n_rootdir = LD_WORD(buf+BPB_RootEntCnt-13); /* Nmuber of root directory entries */
    tsect = LD_WORD(buf+BPB_TotSec16-13); /* Number of sectors on the file system */
    if (!tsect) tsect = LD_DWORD(buf+BPB_TotSec32-13);
    mclst = (tsect /* Last cluster# + 1 */
    - LD_WORD(buf+BPB_RsvdSecCnt-13) - fsize - fs->n_rootdir / 16
    ) / fs->csize + 2;
    fs->n_fatent = (CLUST)mclst;

    fmt = FS_FAT16; /* Determine the FAT sub type */
    if (mclst < 0xFF7) /* Number of clusters < 0xFF5 */
    #if _FS_FAT12
    fmt = FS_FAT12;
    #else
    return FR_NO_FILESYSTEM;
    #endif
    if (mclst >= 0xFFF7) /* Number of clusters >= 0xFFF5 */
    #if _FS_FAT32
    fmt = FS_FAT32;
    #else
    return FR_NO_FILESYSTEM;
    #endif

    fs->fs_type = fmt; /* FAT sub-type */
    if (_FS_FAT32 && fmt == FS_FAT32)
    fs->dirbase = LD_DWORD(buf+(BPB_RootClus-13)); /* Root directory start cluster */
    else
    fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */
    fs->database = fs->fatbase + fsize + fs->n_rootdir / 16; /* Data start sector (lba) */

    fs->flag = 0;
    FatFs = fs;

    return FR_OK;
    }




    /*-----------------------------------------------------------------------*/
    /* Open or Create a File */
    /*-----------------------------------------------------------------------*/

    FRESULT pf_open (
    const char *path /* Pointer to the file name */
    )
    {
    FRESULT res;
    DIR dj;
    BYTE sp[12], dir[32];
    FATFS *fs = FatFs;


    if (!fs) /* Check file system */
    return FR_NOT_ENABLED;

    fs->flag = 0;
    dj.fn = sp;
    res = follow_path(&dj, dir, path); /* Follow the file path */
    if (res != FR_OK) return res; /* Follow failed */
    if (!dir[0] || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */
    return FR_NO_FILE;

    fs->org_clust = LD_CLUST(dir); /* File start cluster */
    fs->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
    fs->fptr = 0; /* File pointer */
    fs->flag = FA_OPENED;

    return FR_OK;
    }




    /*-----------------------------------------------------------------------*/
    /* Read File */
    /*-----------------------------------------------------------------------*/
    #if _USE_READ

    FRESULT pf_read (
    void* buff, /* Pointer to the read buffer (NULL:Forward data to the stream)*/
    WORD btr, /* Number of bytes to read */
    WORD* br /* Pointer to number of bytes read */
    )
    {
    DRESULT dr;
    CLUST clst;
    DWORD sect, remain;
    WORD rcnt;
    BYTE cs, *rbuff = buff;
    FATFS *fs = FatFs;


    *br = 0;
    if (!fs) return FR_NOT_ENABLED; /* Check file system */
    if (!(fs->flag & FA_OPENED)) /* Check if opened */
    return FR_NOT_OPENED;

    remain = fs->fsize - fs->fptr;
    if (btr > remain) btr = (WORD)remain; /* Truncate btr by remaining bytes */

    while (btr) { /* Repeat until all data transferred */
    if ((fs->fptr % 512) == 0) { /* On the sector boundary? */
    cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */
    if (!cs) { /* On the cluster boundary? */
    clst = (fs->fptr == 0) ? /* On the top of the file? */
    fs->org_clust : get_fat(fs->curr_clust);
    if (clst <= 1) goto fr_abort;
    fs->curr_clust = clst; /* Update current cluster */
    }
    sect = clust2sect(fs->curr_clust); /* Get current sector */
    if (!sect) goto fr_abort;
    fs->dsect = sect + cs;
    }
    rcnt = (WORD)(512 - (fs->fptr % 512)); /* Get partial sector data from sector buffer */
    if (rcnt > btr) rcnt = btr;
    dr = disk_readp(!buff ? 0 : rbuff, fs->dsect, (WORD)(fs->fptr % 512), rcnt);
    if (dr) goto fr_abort;
    fs->fptr += rcnt; rbuff += rcnt; /* Update pointers and counters */
    btr -= rcnt; *br += rcnt;
    }

    return FR_OK;

    fr_abort:
    fs->flag = 0;
    return FR_DISK_ERR;
    }
    #endif



    /*-----------------------------------------------------------------------*/
    /* Write File */
    /*-----------------------------------------------------------------------*/
    #if _USE_WRITE

    FRESULT pf_write (
    const void* buff, /* Pointer to the data to be written */
    WORD btw, /* Number of bytes to write (0:Finalize the current write operation) */
    WORD* bw /* Pointer to number of bytes written */
    )
    {
    CLUST clst;
    DWORD sect, remain;
    const BYTE *p = buff;
    BYTE cs;
    WORD wcnt;
    FATFS *fs = FatFs;


    *bw = 0;
    if (!fs) return FR_NOT_ENABLED; /* Check file system */
    if (!(fs->flag & FA_OPENED)) /* Check if opened */
    return FR_NOT_OPENED;

    if (!btw) { /* Finalize request */
    if ((fs->flag & FA__WIP) && disk_writep(0, 0)) goto fw_abort;
    fs->flag &= ~FA__WIP;
    return FR_OK;
    } else { /* Write data request */
    if (!(fs->flag & FA__WIP)) /* Round-down fptr to the sector boundary */
    fs->fptr &= 0xFFFFFE00;
    }
    remain = fs->fsize - fs->fptr;
    if (btw > remain) btw = (WORD)remain; /* Truncate btw by remaining bytes */

    while (btw) { /* Repeat until all data transferred */
    if (((WORD)fs->fptr % 512) == 0) { /* On the sector boundary? */
    cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */
    if (!cs) { /* On the cluster boundary? */
    clst = (fs->fptr == 0) ? /* On the top of the file? */
    fs->org_clust : get_fat(fs->curr_clust);
    if (clst <= 1) goto fw_abort;
    fs->curr_clust = clst; /* Update current cluster */
    }
    sect = clust2sect(fs->curr_clust); /* Get current sector */
    if (!sect) goto fw_abort;
    fs->dsect = sect + cs;
    if (disk_writep(0, fs->dsect)) goto fw_abort; /* Initiate a sector write operation */
    fs->flag |= FA__WIP;
    }
    wcnt = 512 - ((WORD)fs->fptr % 512); /* Number of bytes to write to the sector */
    if (wcnt > btw) wcnt = btw;
    if (disk_writep(p, wcnt)) goto fw_abort; /* Send data to the sector */
    fs->fptr += wcnt; p += wcnt; /* Update pointers and counters */
    btw -= wcnt; *bw += wcnt;
    if (((WORD)fs->fptr % 512) == 0) {
    if (disk_writep(0, 0)) goto fw_abort; /* Finalize the currtent secter write operation */
    fs->flag &= ~FA__WIP;
    }
    }

    return FR_OK;

    fw_abort:
    fs->flag = 0;
    return FR_DISK_ERR;
    }
    #endif



    /*-----------------------------------------------------------------------*/
    /* Seek File R/W Pointer */
    /*-----------------------------------------------------------------------*/
    #if _USE_LSEEK

    FRESULT pf_lseek (
    DWORD ofs /* File pointer from top of file */
    )
    {
    CLUST clst;
    DWORD bcs, sect, ifptr;
    FATFS *fs = FatFs;


    if (!fs) return FR_NOT_ENABLED; /* Check file system */
    if (!(fs->flag & FA_OPENED)) /* Check if opened */
    return FR_NOT_OPENED;

    if (ofs > fs->fsize) ofs = fs->fsize; /* Clip offset with the file size */
    ifptr = fs->fptr;
    fs->fptr = 0;
    if (ofs > 0) {
    bcs = (DWORD)fs->csize * 512; /* Cluster size (byte) */
    if (ifptr > 0 &&
    (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
    fs->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */
    ofs -= fs->fptr;
    clst = fs->curr_clust;
    } else { /* When seek to back cluster, */
    clst = fs->org_clust; /* start from the first cluster */
    fs->curr_clust = clst;
    }
    while (ofs > bcs) { /* Cluster following loop */
    clst = get_fat(clst); /* Follow cluster chain */
    if (clst <= 1 || clst >= fs->n_fatent) goto fe_abort;
    fs->curr_clust = clst;
    fs->fptr += bcs;
    ofs -= bcs;
    }
    fs->fptr += ofs;
    sect = clust2sect(clst); /* Current sector */
    if (!sect) goto fe_abort;
    fs->dsect = sect + (fs->fptr / 512 & (fs->csize - 1));
    }

    return FR_OK;

    fe_abort:
    fs->flag = 0;
    return FR_DISK_ERR;
    }
    #endif



    /*-----------------------------------------------------------------------*/
    /* Create a Directroy Object */
    /*-----------------------------------------------------------------------*/
    #if _USE_DIR

    FRESULT pf_opendir (
    DIR *dj, /* Pointer to directory object to create */
    const char *path /* Pointer to the directory path */
    )
    {
    FRESULT res;
    BYTE sp[12], dir[32];
    FATFS *fs = FatFs;


    if (!fs) { /* Check file system */
    res = FR_NOT_ENABLED;
    } else {
    dj->fn = sp;
    res = follow_path(dj, dir, path); /* Follow the path to the directory */
    if (res == FR_OK) { /* Follow completed */
    if (dir[0]) { /* It is not the root dir */
    if (dir[DIR_Attr] & AM_DIR) /* The object is a directory */
    dj->sclust = LD_CLUST(dir);
    else /* The object is not a directory */
    res = FR_NO_PATH;
    }
    if (res == FR_OK)
    res = dir_rewind(dj); /* Rewind dir */
    }
    if (res == FR_NO_FILE) res = FR_NO_PATH;
    }

    return res;
    }




    /*-----------------------------------------------------------------------*/
    /* Read Directory Entry in Sequense */
    /*-----------------------------------------------------------------------*/

    FRESULT pf_readdir (
    DIR *dj, /* Pointer to the open directory object */
    FILINFO *fno /* Pointer to file information to return */
    )
    {
    FRESULT res;
    BYTE sp[12], dir[32];
    FATFS *fs = FatFs;


    if (!fs) { /* Check file system */
    res = FR_NOT_ENABLED;
    } else {
    dj->fn = sp;
    if (!fno) {
    res = dir_rewind(dj);
    } else {
    res = dir_read(dj, dir);
    if (res == FR_NO_FILE) {
    dj->sect = 0;
    res = FR_OK;
    }
    if (res == FR_OK) { /* A valid entry is found */
    get_fileinfo(dj, dir, fno); /* Get the object information */
    res = dir_next(dj); /* Increment index for next */
    if (res == FR_NO_FILE) {
    dj->sect = 0;
    res = FR_OK;
    }
    }
    }
    }

    return res;
    }

    #endif /* _USE_DIR */
    192 changes: 192 additions & 0 deletions pff.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,192 @@
    /*---------------------------------------------------------------------------/
    / Petit FatFs - FAT file system module include file R0.02a (C)ChaN, 2010
    /----------------------------------------------------------------------------/
    / Petit FatFs module is an open source software to implement FAT file system to
    / small embedded systems. This is a free software and is opened for education,
    / research and commercial developments under license policy of following trems.
    /
    / Copyright (C) 2010, ChaN, all right reserved.
    /
    / * The Petit FatFs module is a free software and there is NO WARRANTY.
    / * No restriction on use. You can use, modify and redistribute it for
    / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
    / * Redistributions of source code must retain the above copyright notice.
    /
    /----------------------------------------------------------------------------*/

    #include "integer.h"

    /*---------------------------------------------------------------------------/
    / Petit FatFs Configuration Options
    /
    / CAUTION! Do not forget to make clean the project after any changes to
    / the configuration options.
    /
    /----------------------------------------------------------------------------*/
    #ifndef _FATFS
    #define _FATFS

    #define _USE_READ 1 /* 1:Enable pf_read() */

    #define _USE_DIR 1 /* 1:Enable pf_opendir() and pf_readdir() */

    #define _USE_LSEEK 1 /* 1:Enable pf_lseek() */

    #define _USE_WRITE 1 /* 1:Enable pf_write() */

    #define _FS_FAT12 0 /* 1:Enable FAT12 support */
    #define _FS_FAT32 1 /* 1:Enable FAT32 support */


    #define _CODE_PAGE 1
    /* Defines which code page is used for path name. Supported code pages are:
    / 932, 936, 949, 950, 437, 720, 737, 775, 850, 852, 855, 857, 858, 862, 866,
    / 874, 1250, 1251, 1252, 1253, 1254, 1255, 1257, 1258 and 1 (ASCII only).
    / SBCS code pages except for 1 requiers a case conversion table. This
    / might occupy 128 bytes on the RAM on some platforms, e.g. avr-gcc. */


    #define _WORD_ACCESS 0
    /* The _WORD_ACCESS option defines which access method is used to the word
    / data in the FAT structure.
    /
    / 0: Byte-by-byte access. Always compatible with all platforms.
    / 1: Word access. Do not choose this unless following condition is met.
    /
    / When the byte order on the memory is big-endian or address miss-aligned
    / word access results incorrect behavior, the _WORD_ACCESS must be set to 0.
    / If it is not the case, the value can also be set to 1 to improve the
    / performance and code efficiency. */


    /* End of configuration options. Do not change followings without care. */
    /*--------------------------------------------------------------------------*/



    #if _FS_FAT32
    #define CLUST DWORD
    #else
    #define CLUST WORD
    #endif


    /* File system object structure */

    typedef struct {
    BYTE fs_type; /* FAT sub type */
    BYTE flag; /* File status flags */
    BYTE csize; /* Number of sectors per cluster */
    BYTE pad1;
    WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */
    CLUST n_fatent; /* Number of FAT entries (= number of clusters + 2) */
    DWORD fatbase; /* FAT start sector */
    DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */
    DWORD database; /* Data start sector */
    DWORD fptr; /* File R/W pointer */
    DWORD fsize; /* File size */
    CLUST org_clust; /* File start cluster */
    CLUST curr_clust; /* File current cluster */
    DWORD dsect; /* File current data sector */
    } FATFS;



    /* Directory object structure */

    typedef struct {
    WORD index; /* Current read/write index number */
    BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
    CLUST sclust; /* Table start cluster (0:Static table) */
    CLUST clust; /* Current cluster */
    DWORD sect; /* Current sector */
    } DIR;



    /* File status structure */

    typedef struct {
    DWORD fsize; /* File size */
    WORD fdate; /* Last modified date */
    WORD ftime; /* Last modified time */
    BYTE fattrib; /* Attribute */
    char fname[13]; /* File name */
    } FILINFO;



    /* File function return code (FRESULT) */

    typedef enum {
    FR_OK = 0, /* 0 */
    FR_DISK_ERR, /* 1 */
    FR_NOT_READY, /* 2 */
    FR_NO_FILE, /* 3 */
    FR_NO_PATH, /* 4 */
    FR_NOT_OPENED, /* 5 */
    FR_NOT_ENABLED, /* 6 */
    FR_NO_FILESYSTEM /* 7 */
    } FRESULT;



    /*--------------------------------------------------------------*/
    /* Petit FatFs module application interface */

    FRESULT pf_mount (FATFS*); /* Mount/Unmount a logical drive */
    FRESULT pf_open (const char*); /* Open a file */
    FRESULT pf_read (void*, WORD, WORD*); /* Read data from the open file */
    FRESULT pf_write (const void*, WORD, WORD*); /* Write data to the open file */
    FRESULT pf_lseek (DWORD); /* Move file pointer of the open file */
    FRESULT pf_opendir (DIR*, const char*); /* Open a directory */
    FRESULT pf_readdir (DIR*, FILINFO*); /* Read a directory item from the open directory */



    /*--------------------------------------------------------------*/
    /* Flags and offset address */

    /* File status flag (FATFS.flag) */

    #define FA_OPENED 0x01
    #define FA_WPRT 0x02
    #define FA__WIP 0x40


    /* FAT sub type (FATFS.fs_type) */

    #define FS_FAT12 1
    #define FS_FAT16 2
    #define FS_FAT32 3


    /* File attribute bits for directory entry */

    #define AM_RDO 0x01 /* Read only */
    #define AM_HID 0x02 /* Hidden */
    #define AM_SYS 0x04 /* System */
    #define AM_VOL 0x08 /* Volume label */
    #define AM_LFN 0x0F /* LFN entry */
    #define AM_DIR 0x10 /* Directory */
    #define AM_ARC 0x20 /* Archive */
    #define AM_MASK 0x3F /* Mask of defined bits */


    /*--------------------------------*/
    /* Multi-byte word access macros */

    #if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
    #define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
    #define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
    #define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
    #define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
    #else /* Use byte-by-byte access to the FAT structure */
    #define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
    #define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
    #define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
    #define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
    #endif


    #endif /* _FATFS */
    20 changes: 20 additions & 0 deletions serial.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    //-----------------------------------------------------------------------------
    // serial.h - common function declarations for different serial implementations
    //-----------------------------------------------------------------------------

    #ifndef SERIAL_H_
    #define SERIAL_H_
    #ifdef __cplusplus
    extern "C" {
    #endif
    static const int VER = 0x0101;

    void serial_initialize(const uint32_t baud_rate_divisor);
    int getchar(void);
    int putchar(int);

    #ifdef __cplusplus
    } /* extern "C" */
    #endif

    #endif /* SERIAL_H_ */
    27 changes: 27 additions & 0 deletions spi.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    //-----------------------------------------------------------------------------
    // spi.h - common function declarations for different SPI implementations
    //-----------------------------------------------------------------------------
    #ifndef SPI_H_
    #define SPI_H_

    /**
    * SMCLK divider arguments for spi_set_divisor
    * assumes 16MHz SMCLK. You need to change if you
    * use a different frequency
    */
    #define SPI_250kHz 64 /* 16MHz/250000 */
    #define SPI_400kHz 40 /* 16MHz/400000 */
    #define SPI_1MHz 16 /* 16MHz/1MHz */
    #define SPI_2MHz 8 /* 16MHz/2MHz */
    #define SPI_4MHz 4 /* 16MHz/4MHz */
    #define SPI_8MHz 2 /* 16MHz/8MHz */
    #define SPI_16MHz 1 /* 16MHz/16Mhz */

    #define SPI_DEFAULT_SPEED SPI_8MHz

    void spi_initialize(void);
    uint8_t spi_send(const uint8_t);
    uint8_t spi_receive(void);
    uint16_t spi_set_divisor(const uint16_t clkdivider);

    #endif /*SPI_H_*/
    79 changes: 79 additions & 0 deletions usci_serial.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,79 @@
    /*
    * usci_serial.c - USCI implementations of serial_initialize(), getchar(), putchar() and puts()
    *
    * Created on: Oct 30, 2011
    * Author: kimballr - [email protected]
    *
    * 04-06-2012 - switched to oPossum style bps setting.
    */

    #include <msp430.h>
    #include <stdint.h>
    #include "serial.h"

    /**
    * Really simple use of the hardware UART so that it will be
    * compatible with our equally simple software ones. We setup the
    * hardware UART using the pins and bit duration provided.
    *
    * This code doesn't use any interrupts. Just simple polling
    * to decide when to receive or transmit.
    */

    /**
    * serial_initialize(bitclk_divisor) - configure USCI UCA0 as async serial port
    * uses SMCLK as bitclock source and fractional divisor with over sampling mode.
    *
    * @params:
    * uint32_t bitclk_divisor - should be (SMCLK_FREQ + (BPS >> 1)) / BPS
    *
    * Thanks to Kevin for original code from 43oh.com:
    * @see http://www.43oh.com/forum/viewtopic.php?f=10&t=2493
    *
    * P1.1 - RX
    * P1.2 - TX
    *
    */
    void serial_initialize(const uint32_t bitclk_divisor) {
    UCA0CTL1 = UCSWRST; // Hold USCI in reset to allow configuration
    UCA0CTL0 = 0; // No parity, LSB first, 8 bits, one stop bit, UART (async)
    UCA0BR1 = (bitclk_divisor >> 12) & 0xFF; // High byte of whole divisor
    UCA0BR0 = (bitclk_divisor >> 4) & 0xFF; // Low byte of whole divisor
    UCA0MCTL = ((bitclk_divisor << 4) & 0xF0) | UCOS16; // Fractional divisor, over sampling mode
    UCA0CTL1 = UCSSEL_2; // Use SMCLK for bit rate generator, release reset

    P1SEL = BIT1 | BIT2; // P1.1=RXD, P1.2=TXD
    P1SEL2 = BIT1 | BIT2; // P1.1=RXD, P1.2=TXD
    }

    /**
    * int getchar() - read next char from serial port rx pin
    *
    *
    */
    int getchar(void) {
    // sit and spin waiting for baudot heh I make myself laugh

    while (!(IFG2 & UCA0RXIFG)) {
    ; // busywait until UCA0 RX buffer is ready
    }

    return UCA0RXBUF; // return RXed character
    }

    /**
    * putchar(int c) - write char to serial port
    *
    */
    int putchar(int c) {

    // make sure previous character has been sent
    // before trying to send another character
    while (!(IFG2 & UCA0TXIFG)) {
    ; // busywait until UCA0 TX buffer is ready
    }

    UCA0TXBUF = (uint8_t) c; // TX character

    return 0;
    }
    109 changes: 109 additions & 0 deletions usci_spi.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,109 @@
    /**
    * File: usci_spi.c - msp430 USCI SPI implementation
    *
    */
    #include <msp430.h>
    #include <stdint.h>
    #include "spi.h"

    #ifndef __MSP430_HAS_USCI__
    #error "Error! This MCU doesn't have a USCI peripheral"
    #endif

    /**
    * USCI flags for various the SPI MODEs
    *
    * Note: The msp430 UCCKPL tracks the CPOL value. However,
    * the UCCKPH flag is inverted when compared to the CPHA
    * value described in Motorola documentation.
    */

    #define SPI_MODE_0 (UCMSB | UCMST | UCSYNC | UCCKPH) /* CPOL=0 CPHA=0 */
    #define SPI_MODE_1 (UCMSB | UCMST | UCSYNC) /* CPOL=0 CPHA=1 */
    #define SPI_MODE_2 (UCMSB | UCMST | UCSYNC | UCCKPL | UCCKPH) /* CPOL=1 CPHA=0 */
    #define SPI_MODE_3 (UCMSB | UCMST | UCSYNC | UCCKPL) /* CPOL=1 CPHA=1 */

    /**
    * utility macros for extracting hi/lo byte data from a word value
    */
    #ifndef LOBYTE
    #define LOBYTE(w) ((w)&0xFF)
    #define HIBYTE(w) ((w)>>8)
    #endif

    /**
    * spi_initialize() - Configure USCI UCB0 for SPI mode
    *
    * P2.0 - CS (active low)
    * P1.5 - SCLK
    * P1.6 - SIMO/MOSI
    * P1.7 - SOMI/MISO
    */
    void spi_initialize(void) {
    UCB0CTL1 = UCSWRST | UCSSEL_2; // Put USCI in reset mode, source USCI clock from SMCLK
    UCB0CTL0 = SPI_MODE_0; // Use SPI MODE 0 - CPOL=0 CPHA=0

    P1SEL |= BIT5 | BIT6 | BIT7; // configure P1.5, P1.6, P1.7 for USCI
    P1SEL2 |= BIT5 | BIT6 | BIT7;

    UCB0BR0 = LOBYTE(SPI_400kHz); // set initial speed to 400kHz (16MHz/400000)
    UCB0BR1 = HIBYTE(SPI_400kHz);

    P2OUT |= BIT0; // CS on P2.0. start out disabled
    P2DIR |= BIT0; // CS configured as output

    UCB0CTL1 &= ~UCSWRST; // release USCI for operation
    }

    /**
    * spi_send() - send a byte and recv response
    */
    uint8_t spi_send(const uint8_t c) {
    while (!(IFG2 & UCB0TXIFG))
    ; // wait for previous tx to complete

    UCB0TXBUF = c; // setting TXBUF clears the TXIFG flag

    while (!(IFG2 & UCB0RXIFG))
    ; // wait for an rx character?

    return UCB0RXBUF; // reading clears RXIFG flag
    }

    /**
    * spi_receive() - send dummy btye then recv response
    */

    uint8_t spi_receive(void) {

    while (!(IFG2 & UCB0TXIFG))
    ; // wait for any previous xmits to complete

    UCB0TXBUF = 0xFF; // Send dummy packet to get data back.

    while (!(IFG2 & UCB0RXIFG))
    ; // wait to recv a character?

    return UCB0RXBUF; // reading clears RXIFG flag
    }

    /**
    * spi_set_divisor() - set new clock divider for USCI
    *
    * USCI speed is based on the SMCLK divided by BR0 and BR1
    * initially we start slow (400kHz) to conform to SDCard
    * specifications then we speed up once initialized (SPI_DEFAULT_SPEED)
    *
    * returns the previous setting
    */

    uint16_t spi_set_divisor(const uint16_t clkdiv) {
    uint16_t prev_clkdiv = UCB0BR1 << 8 | UCB0BR0;

    UCB0CTL1 |= UCSWRST; // go into reset state
    UCB0BR0 = LOBYTE(clkdiv);
    UCB0BR1 = HIBYTE(clkdiv);
    UCB0CTL1 &= ~UCSWRST; // release for operation

    return prev_clkdiv;
    }