juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

sdlog.c (11753B)


      1 /* sdlog.c - log de diagnostico y partida guardada en la tarjeta SD de la PicoCalc.
      2  *
      3  * Para no meter un driver de FAT completo, los archivos de log y de partida
      4  * (APP_SD_LOG y APP_SD_SAV de app_config.h, ej. CATAN.LOG y CATAN.SAV)
      5  * se crean de antemano en la PC (tamano fijo) y aca solo se escribe DENTRO de
      6  * ellos: se buscan en el directorio raiz FAT32, se sigue su cadena de clusters
      7  * y se reescriben sectores. Nunca se toca la FAT ni el directorio.
      8  *
      9  * Log: cada arranque agrega texto despues de lo ultimo escrito; el resto
     10  * del archivo queda en espacios.
     11  *
     12  * SD por SPI0: MISO GP16, CS GP17, SCK GP18, MOSI GP19 (esquematico PicoCalc). */
     13 #include <string.h>
     14 #include "pico/stdlib.h"
     15 #include "hardware/spi.h"
     16 #include "sdlog.h"
     17 #include "app_config.h"
     18 
     19 #define SD_SPI  spi0
     20 #define SD_MISO 16
     21 #define SD_CS   17
     22 #define SD_SCK  18
     23 #define SD_MOSI 19
     24 
     25 static bool sd_hc;               /* direccionamiento por bloque (SDHC/SDXC) */
     26 static bool mounted, ready;
     27 static uint8_t sec[512];         /* sectores de FAT y directorio */
     28 static uint32_t fat_lba, data_lba, spc, root_cluster;
     29 static uint32_t log_cluster, log_size;
     30 static uint32_t cur_sector_idx;  /* indice de sector dentro del log */
     31 static int cur_off;              /* bytes usados en ese sector */
     32 static uint8_t wbuf[512];        /* sector del log en curso */
     33 
     34 /* ------------------------------------------------------------ SPI SD */
     35 
     36 static uint8_t xfer(uint8_t b)
     37 {
     38     uint8_t r;
     39     spi_write_read_blocking(SD_SPI, &b, &r, 1);
     40     return r;
     41 }
     42 
     43 static void cs(bool on) { gpio_put(SD_CS, on ? 0 : 1); xfer(0xFF); }
     44 
     45 static uint8_t cmd(uint8_t c, uint32_t arg, uint8_t crc)
     46 {
     47     xfer(0xFF);
     48     xfer(0x40 | c);
     49     xfer((uint8_t)(arg >> 24)); xfer((uint8_t)(arg >> 16)); xfer((uint8_t)(arg >> 8)); xfer((uint8_t)arg);
     50     xfer(crc);
     51     uint8_t r = 0xFF;
     52     for (int i = 0; i < 10 && (r & 0x80); i++) r = xfer(0xFF);
     53     return r;
     54 }
     55 
     56 static bool sd_init(void)
     57 {
     58     spi_init(SD_SPI, 400000);
     59     gpio_set_function(SD_MISO, GPIO_FUNC_SPI);
     60     gpio_set_function(SD_SCK, GPIO_FUNC_SPI);
     61     gpio_set_function(SD_MOSI, GPIO_FUNC_SPI);
     62     gpio_init(SD_CS);
     63     gpio_set_dir(SD_CS, GPIO_OUT);
     64     gpio_put(SD_CS, 1);
     65     gpio_pull_up(SD_MISO);
     66     for (int i = 0; i < 10; i++) xfer(0xFF);            /* >74 clocks con CS alto */
     67 
     68     cs(true);
     69     uint8_t r = 0;
     70     for (int i = 0; i < 20 && r != 1; i++) r = cmd(0, 0, 0x95);
     71     if (r != 1) { cs(false); return false; }
     72     r = cmd(8, 0x1AA, 0x87);
     73     bool v2 = r == 1;
     74     if (v2) for (int i = 0; i < 4; i++) xfer(0xFF);
     75     uint32_t t0 = to_ms_since_boot(get_absolute_time());
     76     do {
     77         cmd(55, 0, 0x65);
     78         r = cmd(41, v2 ? 0x40000000 : 0, 0x77);
     79     } while (r != 0 && to_ms_since_boot(get_absolute_time()) - t0 < 1500);
     80     if (r != 0) { cs(false); return false; }
     81     sd_hc = false;
     82     if (v2 && cmd(58, 0, 0xFD) == 0) {
     83         uint8_t ocr = xfer(0xFF);
     84         xfer(0xFF); xfer(0xFF); xfer(0xFF);
     85         sd_hc = (ocr & 0x40) != 0;
     86     }
     87     cmd(16, 512, 0x15);
     88     cs(false);
     89     spi_set_baudrate(SD_SPI, 12000000);
     90     return true;
     91 }
     92 
     93 static bool sd_read(uint32_t lba, uint8_t *buf)
     94 {
     95     cs(true);
     96     if (cmd(17, sd_hc ? lba : lba * 512, 0xFF) != 0) { cs(false); return false; }
     97     uint8_t t = 0xFF;
     98     for (int i = 0; i < 100000 && t == 0xFF; i++) t = xfer(0xFF);
     99     if (t != 0xFE) { cs(false); return false; }
    100     uint8_t ff = 0xFF;
    101     for (int i = 0; i < 512; i++) spi_write_read_blocking(SD_SPI, &ff, &buf[i], 1);
    102     xfer(0xFF); xfer(0xFF);
    103     cs(false);
    104     return true;
    105 }
    106 
    107 static bool sd_write(uint32_t lba, const uint8_t *buf)
    108 {
    109     cs(true);
    110     if (cmd(24, sd_hc ? lba : lba * 512, 0xFF) != 0) { cs(false); return false; }
    111     xfer(0xFF);
    112     xfer(0xFE);
    113     spi_write_blocking(SD_SPI, buf, 512);
    114     xfer(0xFF); xfer(0xFF);
    115     uint8_t resp = xfer(0xFF);
    116     uint8_t b = 0;
    117     for (int i = 0; i < 500000 && b != 0xFF; i++) b = xfer(0xFF);   /* esperar fin de escritura */
    118     cs(false);
    119     return (resp & 0x1F) == 0x05;
    120 }
    121 
    122 /* ------------------------------------------------------------ FAT32 minimo */
    123 
    124 static uint32_t rd32(const uint8_t *p) { return p[0] | (p[1] << 8) | (p[2] << 16) | ((uint32_t)p[3] << 24); }
    125 static uint16_t rd16(const uint8_t *p) { return (uint16_t)(p[0] | (p[1] << 8)); }
    126 
    127 static uint32_t fat_next(uint32_t cl)
    128 {
    129     if (!sd_read(fat_lba + cl / 128, sec)) return 0x0FFFFFFF;
    130     return rd32(sec + (cl % 128) * 4) & 0x0FFFFFFF;
    131 }
    132 
    133 static uint32_t cluster_lba(uint32_t cl) { return data_lba + (cl - 2) * spc; }
    134 
    135 /* sector numero idx del archivo -> LBA (siguiendo la cadena de clusters) */
    136 static uint32_t file_lba(uint32_t first, uint32_t idx)
    137 {
    138     uint32_t cl = first, n = idx / spc;
    139     while (n-- && cl >= 2 && cl < 0x0FFFFFF8) cl = fat_next(cl);
    140     if (cl < 2 || cl >= 0x0FFFFFF8) return 0;
    141     return cluster_lba(cl) + idx % spc;
    142 }
    143 
    144 static bool mount(void)
    145 {
    146     if (mounted) return true;
    147     if (!sd_init() || !sd_read(0, sec)) return false;
    148     uint32_t part = 0;
    149     if (sec[510] == 0x55 && sec[511] == 0xAA && (sec[0x1C2] == 0x0B || sec[0x1C2] == 0x0C))
    150         part = rd32(sec + 0x1C6);
    151     if (!sd_read(part, sec)) return false;
    152     if (rd16(sec + 11) != 512 || rd16(sec + 22) != 0) return false;   /* solo FAT32 */
    153     spc = sec[13];
    154     uint32_t rsv = rd16(sec + 14), nfat = sec[16], fatsz = rd32(sec + 36);
    155     root_cluster = rd32(sec + 44);
    156     fat_lba = part + rsv;
    157     data_lba = fat_lba + nfat * fatsz;
    158     mounted = true;
    159     return true;
    160 }
    161 
    162 /* name en formato de directorio FAT: 8+3 en mayusculas, con espacios */
    163 static bool find_file(const char name[11], uint32_t *first, uint32_t *size)
    164 {
    165     if (!mount()) return false;
    166     uint32_t cl = root_cluster;
    167     for (int guard = 0; guard < 64 && cl >= 2 && cl < 0x0FFFFFF8; guard++) {
    168         for (uint32_t s = 0; s < spc; s++) {
    169             if (!sd_read(cluster_lba(cl) + s, sec)) return false;
    170             for (int e = 0; e < 16; e++) {
    171                 const uint8_t *d = sec + e * 32;
    172                 if (d[0] == 0) return false;
    173                 if (d[0] == 0xE5 || (d[11] & 0x0F) == 0x0F || (d[11] & 0x18)) continue;
    174                 if (!memcmp(d, name, 11)) {
    175                     *first = ((uint32_t)rd16(d + 20) << 16) | rd16(d + 26);
    176                     *size = rd32(d + 28);
    177                     return *first >= 2;
    178                 }
    179             }
    180         }
    181         cl = fat_next(cl);
    182     }
    183     return false;
    184 }
    185 
    186 /* ------------------------------------------------------------ log */
    187 
    188 bool sdlog_open(void)
    189 {
    190     ready = false;
    191     if (!find_file(APP_SD_LOG83, &log_cluster, &log_size) || log_size < 1024) return false;
    192     /* primer sector que todavia esta en blanco (arranca con espacio) */
    193     uint32_t nsec = log_size / 512;
    194     uint32_t idx = 0;
    195     for (; idx < nsec; idx++) {
    196         uint32_t lba = file_lba(log_cluster, idx);
    197         if (!lba || !sd_read(lba, sec)) return false;
    198         if (sec[0] == ' ') break;
    199     }
    200     if (idx >= nsec) idx = 0;             /* lleno: vuelve a empezar */
    201     cur_sector_idx = idx;
    202     cur_off = 0;
    203     memset(wbuf, ' ', sizeof wbuf);
    204     ready = true;
    205     return true;
    206 }
    207 
    208 bool sdlog_ready(void) { return ready; }
    209 
    210 void sdlog(const char *line)
    211 {
    212     if (!ready) return;
    213     int n = (int)strlen(line);
    214     for (int i = 0; i <= n; i++) {
    215         char c = i < n ? line[i] : '\n';
    216         wbuf[cur_off++] = (uint8_t)c;
    217         if (cur_off == 512) {
    218             uint32_t lba = file_lba(log_cluster, cur_sector_idx);
    219             if (!lba || !sd_write(lba, wbuf)) { ready = false; return; }
    220             cur_sector_idx++;
    221             if (cur_sector_idx >= log_size / 512) cur_sector_idx = 0;
    222             cur_off = 0;
    223             memset(wbuf, ' ', sizeof wbuf);
    224         }
    225     }
    226     /* escribir el sector parcial ya, por si despues se cuelga */
    227     uint32_t lba = file_lba(log_cluster, cur_sector_idx);
    228     if (!lba || !sd_write(lba, wbuf)) ready = false;
    229 }
    230 
    231 /* ------------------------------------------------------------ partida guardada
    232  *
    233  * La partida se guarda en un archivo que se divide en dos mitades ("slots"). Cada slot arranca con una
    234  * cabecera de 16 bytes: "CSV1", numero de secuencia, largo y checksum de los
    235  * datos. Se guarda siempre en el slot que NO tiene el ultimo guardado, y el
    236  * primer sector (el de la cabecera) se escribe al final: si se corta la
    237  * energia a mitad de camino, el guardado anterior sigue entero. Al cargar se
    238  * usa el slot valido con la secuencia mas alta. */
    239 
    240 #define SAV_HDR 16
    241 
    242 static uint8_t sbuf[512];        /* sector de la partida (aparte de wbuf: el log queda intacto) */
    243 static uint32_t sav_cluster, sav_size;
    244 static bool sav_found;
    245 
    246 static bool sav_open(void)
    247 {
    248     if (!sav_found)
    249         sav_found = find_file(APP_SD_SAV83, &sav_cluster, &sav_size) && sav_size >= 4 * 512;
    250     return sav_found;
    251 }
    252 
    253 static uint32_t slot_sectors(void) { return sav_size / 512 / 2; }
    254 
    255 static uint32_t sum_step(uint32_t h, uint8_t b) { return (h ^ b) * 16777619u; }   /* FNV-1a */
    256 
    257 /* Lee el slot (hasta max bytes en buf, puede ser NULL) y verifica el checksum.
    258  * Devuelve el largo guardado, o -1 si el slot no es valido. */
    259 static int slot_read(int slot, char *buf, int max, uint32_t *seq)
    260 {
    261     uint32_t base = (uint32_t)slot * slot_sectors();
    262     uint32_t lba = file_lba(sav_cluster, base);
    263     if (!lba || !sd_read(lba, sbuf) || memcmp(sbuf, "CSV1", 4)) return -1;
    264     uint32_t len = rd32(sbuf + 8), want = rd32(sbuf + 12);
    265     *seq = rd32(sbuf + 4);
    266     if (len > slot_sectors() * 512 - SAV_HDR) return -1;
    267     uint32_t h = 2166136261u;
    268     for (uint32_t k = 0, i = SAV_HDR; k < len; k++, i++) {
    269         if (i == 512) {
    270             i = 0;
    271             lba = file_lba(sav_cluster, base + (SAV_HDR + k) / 512);
    272             if (!lba || !sd_read(lba, sbuf)) return -1;
    273         }
    274         h = sum_step(h, sbuf[i]);
    275         if (buf && (int)k < max) buf[k] = (char)sbuf[i];
    276     }
    277     return h == want ? (int)len : -1;
    278 }
    279 
    280 /* slot con el ultimo guardado valido (-1 ninguno) y su secuencia */
    281 static int sav_latest(uint32_t *seq)
    282 {
    283     int best = -1;
    284     uint32_t s;
    285     for (int slot = 0; slot < 2; slot++)
    286         if (slot_read(slot, NULL, 0, &s) >= 0 && (best < 0 || (int32_t)(s - *seq) > 0)) { best = slot; *seq = s; }
    287     return best;
    288 }
    289 
    290 bool sdsave_available(void) { return sav_open(); }
    291 
    292 int sdsave_load(char *buf, int max)
    293 {
    294     uint32_t seq = 0;
    295     if (!sav_open()) return 0;
    296     int slot = sav_latest(&seq);
    297     if (slot < 0) return 0;
    298     int n = slot_read(slot, buf, max, &seq);
    299     if (n < 0) return 0;
    300     return n < max ? n : max;
    301 }
    302 
    303 static void wr32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); }
    304 
    305 bool sdsave_save(const char *data, int n)
    306 {
    307     if (!sav_open() || n < 0 || (uint32_t)n > slot_sectors() * 512 - SAV_HDR) return false;
    308     uint32_t seq = 0;
    309     int last = sav_latest(&seq);
    310     int slot = last < 0 ? 0 : 1 - last;
    311     uint32_t base = (uint32_t)slot * slot_sectors();
    312     uint32_t h = 2166136261u;
    313     for (int k = 0; k < n; k++) h = sum_step(h, (uint8_t)data[k]);
    314 
    315     /* sectores 1.. primero, el de la cabecera al final */
    316     uint32_t nsec = (uint32_t)(SAV_HDR + n + 511) / 512;
    317     for (uint32_t s = nsec; s-- > 0;) {
    318         memset(sbuf, 0, sizeof sbuf);
    319         int from = (int)s * 512 - SAV_HDR;              /* byte de data en sbuf[0] */
    320         for (int i = 0; i < 512; i++) {
    321             int k = from + i;
    322             if (k >= 0 && k < n) sbuf[i] = (uint8_t)data[k];
    323         }
    324         if (s == 0) {
    325             memcpy(sbuf, "CSV1", 4);
    326             wr32(sbuf + 4, seq + 1);
    327             wr32(sbuf + 8, (uint32_t)n);
    328             wr32(sbuf + 12, h);
    329         }
    330         uint32_t lba = file_lba(sav_cluster, base + s);
    331         if (!lba || !sd_write(lba, sbuf)) return false;
    332     }
    333     return true;
    334 }