Library(Beta) for Gameduino 2

Dependencies:   arduino

Dependents:   aa_gd_jw

Utils.h

Committer:
aluqard
Date:
2014-04-11
Revision:
0:9c211972beb2

File content as of revision 0:9c211972beb2:

#ifndef _UTILS_H_
#define _UTILS_H_

#include "arduino.h"

void loop();

#define LOOP    while(1) loop()

typedef struct {
    byte handle;
    uint16_t w, h;
    uint16_t size;
} shape_t;

struct direntry
{
    char name[8];
    char ext[3];
    uint8_t attribute;
    uint8_t reserved[8];
    uint16_t cluster_hi;  // FAT32 only
    uint16_t time;
    uint16_t date;
    uint16_t cluster;
    uint32_t size;
};

// https://www.sdcard.org/downloads/pls/simplified_specs/Part_1_Physical_Layer_Simplified_Specification_Ver_3.01_Final_100518.pdf
// page 22
// http://mac6.ma.psu.edu/space2008/RockSat/microController/sdcard_appnote_foust.pdf
// http://elm-chan.org/docs/mmc/mmc_e.html
// http://www.pjrc.com/tech/8051/ide/fat32.html

#define FAT16 0
#define FAT32 1

class sdcard 
{
public:
    SPI* _spi;
    DigitalOut _cs;
    uint8_t ccs;

    uint8_t type;
    uint16_t sectors_per_cluster;
    uint16_t reserved_sectors;
    uint16_t max_root_dir_entries;
    uint16_t sectors_per_fat;
    uint16_t cluster_size;
    uint32_t root_dir_first_cluster;

    // These are all linear addresses, hence the o_ prefix
    uint32_t o_partition;
    uint32_t o_fat;
    uint32_t o_root;
    uint32_t o_data;
public:
    sdcard(SPI* spi, PinName cs) : _cs(cs)
    {
        _spi = spi;
    }
    
    void sel() 
    {
        _cs = 0;
        delay(1);
    }
    void desel() 
    {
        _cs = 1;
        _spi->write(0xff); // force DO release
    }
    void sd_delay(uint8_t n) 
    {
        while (n--)
            _spi->write(0xff);
    }

    void cmd(uint8_t cmd, uint32_t lba = 0, uint8_t crc = 0x95) 
    {
        sel();
        _spi->write(0xff);
        _spi->write(0x40 | cmd);
        _spi->write(0xff & (lba >> 24));
        _spi->write(0xff & (lba >> 16));
        _spi->write(0xff & (lba >> 8));
        _spi->write(0xff & (lba));
        _spi->write(crc);
        _spi->write(0xff);
    }

    uint8_t R1() 
    {   // read response R1
        uint8_t r;
        while ((r = _spi->write(0xff)) & 0x80)
            ;
        desel();
        _spi->write(0xff);   // trailing uint8_t
        return r;
    }

    uint8_t sdR3(uint32_t &ocr) 
    {  // read response R3
        uint32_t r;
        while ((r = _spi->write(0xff)) & 0x80)
            ;
        for (uint8_t i = 4; i; i--)
            ocr = (ocr << 8) | _spi->write(0xff);
        _spi->write(0xff);   // trailing uint8_t

        desel();
        return r;
    }

    uint8_t sdR7() 
    {  // read response R3
        uint32_t r;
        while ((r = _spi->write(0xff)) & 0x80)
            ;
        for (uint8_t i = 4; i; i--)
            // Serial.println(____spi->write(0xff), HEX);
            _spi->write(0xff);
        desel();

        return r;
    }

    void appcmd(uint8_t cc, uint32_t lba = 0) 
    {
        cmd(55); R1();
        cmd(cc, lba);
    }

    void begin() 
    {
        uint8_t type_code;
        uint8_t sdhc;

        desel();

        delay(10);      // wait for boot
        sd_delay(10);   // deselected, 80 pulses

        // Tty.printf("Attempting card reset... ");
        // attempt reset
        uint8_t r1;
        int attempts = 0;
        do 
        {       // reset, enter idle
            cmd(0);
            while ((r1 = _spi->write(0xff)) & 0x80)
                if (++attempts == 1000)
                    return;
            desel();
            _spi->write(0xff);   // trailing uint8_t
        } while (r1 != 1);
        // Tty.printf("reset ok\n");

        sdhc = 0;
        cmd(8, 0x1aa, 0x87);
        r1 = sdR7();
        sdhc = (r1 == 1);

        // Tty.printf("card %s SDHC\n", sdhc ? "is" : "is not");

        // Tty.printf("Sending card init command... ");
        while (1) 
        {
            appcmd(41, sdhc ? (1UL << 30) : 0); // card init
            r1 = R1();
            if ((r1 & 1) == 0)
                break;
            delay(100);
        }
        // Tty.printf("OK\n");

        if (sdhc) 
        {
            cmd(58);
            uint32_t OCR = 0;
            sdR3(OCR);
            ccs = 1UL & (OCR >> 30);
            // Tty.printf("OCR register is %#010lx\n", long(OCR));
        }

        else 
        {
            ccs = 0;
        }
        // Tty.printf("ccs = %d\n", ccs);
        // REPORT(ccs);

        type_code = rd(0x1be + 0x4);
        
        switch (type_code) 
        {
            default:
                type = FAT16;
                break;
            case 0x0b:
            case 0x0c:
                type = FAT32;
                break;
        }
        // REPORT(type_code);
        // Tty.printf("Type code %#02x means FAT%d\n", type_code, (type == FAT16) ? 16 : 32);
#if VERBOSE
        DEBUGOUT("Type ");
        DEBUGOUT("%x", type_code);
        DEBUGOUT(" so FAT");
        DEBUGOUT("%d\r\n", (type == FAT16) ? 16 : 32);
#endif

        o_partition = 512L * rd4(0x1be + 0x8);
        sectors_per_cluster = rd(o_partition + 0xd);
        reserved_sectors = rd2(o_partition + 0xe);
        cluster_size = 512L * sectors_per_cluster;
        // REPORT(sectors_per_cluster);

        // Tty.printf("Bytes per sector:    %d\n", rd2(o_partition + 0xb));
        // Tty.printf("Sectors per cluster: %d\n", sectors_per_cluster);

        if (type == FAT16) 
        {
            max_root_dir_entries = rd2(o_partition + 0x11);
            sectors_per_fat = rd2(o_partition + 0x16);
            o_fat = o_partition + 512L * reserved_sectors;
            o_root = o_fat + (2 * 512L * sectors_per_fat);
            // data area starts with cluster 2, so offset it here
            o_data = o_root + (max_root_dir_entries * 32L) - (2L * cluster_size);
        }

        else 
        {
            uint32_t sectors_per_fat = rd4(o_partition + 0x24);
            root_dir_first_cluster = rd4(o_partition + 0x2c);
            uint32_t fat_begin_lba = (o_partition >> 9) + reserved_sectors;
            uint32_t cluster_begin_lba = (o_partition >> 9) + reserved_sectors + (2 * sectors_per_fat);

            o_fat = 512L * fat_begin_lba;
            o_root = (512L * (cluster_begin_lba + (root_dir_first_cluster - 2) * sectors_per_cluster));
            o_data = (512L * (cluster_begin_lba - 2 * sectors_per_cluster));
        }
    }
    
    static char toupper(char sv)
    {
        char c = sv;
        if( sv >= 'a' && sv <= 'z')
            c = sv - ('a' - 'A');
        
        return c;
    }
    
    void cmd17(uint32_t off) 
    {
        if (ccs)
            cmd(17, off >> 9);
        else
            cmd(17, off & ~511L);
        R1();
        sel();
        while (_spi->write(0xff) != 0xfe)
            ;
    }

    void rdn(uint8_t *d, uint32_t off, uint16_t n) 
    {
        cmd17(off);
        uint16_t i;
        uint16_t bo = (off & 511);
        for (i = 0; i < bo; i++)
            _spi->write(0xff);
        for (i = 0; i < n; i++)
            *d++ = _spi->write(0xff);
        for (i = 0; i < (514 - bo - n); i++)
            _spi->write(0xff);
        desel();
    }

    uint32_t rd4(uint32_t off) 
    {
        uint32_t r;
        rdn((uint8_t*)&r, off, sizeof(r));
        return r;
    }

    uint16_t rd2(uint32_t off) 
    {
        uint16_t r;
        rdn((uint8_t*)&r, off, sizeof(r));
        return r;
    }

    uint8_t rd(uint32_t off) 
    {
        uint8_t r;
        rdn((uint8_t*)&r, off, sizeof(r));
        return r;
    }
};

static void dos83(uint8_t dst[11], const char *ps)
{
    uint8_t i = 0;
    while (*ps) 
    {
        if (*ps != '.')
            dst[i++] = sdcard::toupper(*ps);
        else 
        {
            while (i < 8)
                dst[i++] = ' ';
        }
        ps++;
    }
    while (i < 11)
        dst[i++] = ' ';
}

class Reader 
{
    SPI* _spi;
    sdcard* _sd;
public:
    Reader(SPI* spi, sdcard* sd)
    {
        _spi = spi;
        _sd = sd;
    }

public:
    int openfile(const char *filename) 
    {
        int i = 0;
        uint8_t dosname[11] = {0, };
        direntry de = {0, };

        dos83(dosname, filename);
        
        do {
            _sd->rdn((uint8_t*)&de, _sd->o_root + i * 32, sizeof(de));
            if (0 == memcmp(de.name, dosname, 11)) 
            {
                DEBUGOUT("begin(de)\r\n");
                begin(de);
                return 1;
            }
            i++;
        } while (de.name[0]);
        return 0;
    }

    void begin(direntry &de) 
    {
        size = de.size;
        cluster = de.cluster;
        if (_sd->type == FAT32)
            cluster |= ((long)de.cluster_hi << 16);
        sector = 0;
        offset = 0;
    }

    void nextcluster() 
    {
        if (_sd->type == FAT16)
            cluster = _sd->rd2(_sd->o_fat + 2 * cluster);
        else
            cluster = _sd->rd4(_sd->o_fat + 4 * cluster);
#if VERBOSE
        DEBUGOUT("nextcluster=");
        DEBUGOUT("%d\r\n", cluster);
#endif
    }

    void skipcluster() 
    {
        nextcluster();
        offset += _sd->cluster_size;
    }
    void skipsector() 
    {
        if (sector == _sd->sectors_per_cluster) {
            sector = 0;
            nextcluster();
        }
        sector++;
        offset += 512;
    }

    void seek(uint32_t o) 
    {
        while (offset < o) 
        {
            if ((sector == _sd->sectors_per_cluster) && ((o - offset) > (long)_sd->cluster_size))
                skipcluster();
            else
                skipsector();
        }
    }
    void readsector() 
    {
        if (sector == _sd->sectors_per_cluster) 
        {
            sector = 0;
            nextcluster();
        }
        uint32_t off = _sd->o_data + ((long)_sd->cluster_size * cluster) + (512L * sector);
#if VERBOSE
        DEBUGOUT("off=0x");
        DEBUGOUT("%x", off);
#endif
        _sd->cmd17(off & ~511L);
        // Serial.println(2 * (micros() - t0), DEC);
        sector++;
        offset += 512;
    }
    void readsector(uint8_t *dst) 
    {
        readsector();
        for (int i = 0; i < 64; i++) {
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
            *dst++ = _spi->write(0xff);
        }
        _spi->write(0xff);   // consume CRC
        _spi->write(0xff);
        _sd->desel();
    }
    uint32_t cluster;
    uint32_t offset;
    uint32_t size;
    uint8_t sector;
};

class Poly 
{
public:
    GDClass* _gd;
    Poly(GDClass* gd)
    {
        _gd = gd;
    }
    
    int x0, y0, x1, y1;
    int x[8], y[8];
    uint8_t n;

    void restart() 
    {
        n = 0;
        x0 = 16 * 480;
        x1 = 0;
        y0 = 16 * 272;
        y1 = 0;
    }

    void perim() 
    {
        for (uint8_t i = 0; i < n; i++)
            _gd->Vertex2f(x[i], y[i]);
        _gd->Vertex2f(x[0], y[0]);
    }
public:
    void begin() 
    {
        restart();

        _gd->ColorMask(0,0,0,0);
        _gd->StencilOp(KEEP, INVERT);
        _gd->StencilFunc(ALWAYS, 255, 255);
    }

    void v(int _x, int _y) 
    {
        x0 = min(x0, _x >> 4);
        x1 = max(x1, _x >> 4);
        y0 = min(y0, _y >> 4);
        y1 = max(y1, _y >> 4);
        x[n] = _x;
        y[n] = _y;
        n++;
    }

    void paint() 
    {
        x0 = max(0, x0);
        y0 = max(0, y0);
        x1 = min(16 * 480, x1);
        y1 = min(16 * 272, y1);
        _gd->ScissorXY(x0, y0);
        _gd->ScissorSize(x1 - x0 + 1, y1 - y0 + 1);
        _gd->Begin(EDGE_STRIP_B);
        perim();
    }

    void finish() 
    {
        _gd->ColorMask(1,1,1,1);
        _gd->StencilFunc(EQUAL, 255, 255);

        _gd->Begin(EDGE_STRIP_B);
        _gd->Vertex2ii(0, 0);
        _gd->Vertex2ii(511, 0);
    }

    void draw() 
    {
        paint();
        finish();
    }

    void outline() 
    {
        _gd->Begin(LINE_STRIP);
        perim();
    }
};

class Streamer 
{
public:
    GDClass* _gd;
    
    Streamer(GDClass* gd, sdcard* sd)
    {
        r = new Reader(gd->GDTR.SPI(), sd);
        _gd = gd;
    }
    void begin(const char *rawsamples,
        uint16_t freq = 44100,
        uint8_t format = ADPCM_SAMPLES,
        uint32_t _base = (0x40000UL - 8192), uint16_t size = 8192) 
    {
            r->openfile(rawsamples);

            base = _base;
            mask = size - 1;
            wp = 0;

            for (uint8_t i = 10; i; i--)
                feed();

            _gd->sample(base, size, freq, format, 1);
    }

    int feed() 
    {
        uint16_t rp = _gd->rd32(REG_PLAYBACK_READPTR) - base;
        uint16_t freespace = mask & ((rp - 1) - wp);
        if (freespace >= 512) {
            // REPORT(base);
            // REPORT(rp);
            // REPORT(wp);
            // REPORT(freespace);
            // DEBUGOUT("\r\n");
            uint8_t buf[512];
            // uint16_t n = min(512, r->size - r->offset);
            // n = (n + 3) & ~3;   // force 32-bit alignment
            _gd->__end();
            r->readsector(buf);
            _gd->resume();
            _gd->cmd_memwrite(base + wp, 512);
            _gd->copyram(buf, 512);
            wp = (wp + 512) & mask;
        }
        return r->offset < r->size;
    }

    void progress(uint16_t &val, uint16_t &range) 
    {
        uint32_t m = r->size;
        uint32_t p = min(r->offset, m);
        while (m > 0x10000) {
            m >>= 1;
            p >>= 1;
        }
        val = p;
        range = m;
    }
private:
    Reader* r;
    uint32_t base;
    uint16_t mask;
    uint16_t wp;
};


static uint8_t sinus(GDClass* gd, uint8_t x)
{
    return 128 + gd->rsin(128, -16384 + (x << 7));
}

#endif