PicoPAC/VSC-PicoPAC/flash_fs.c
2024-08-07 20:45:50 +02:00

254 lines
8.5 KiB
C

/*
// PicoPAC MultiCART by Andrea Ottaviani 2024
//
// VIDEOPAC multicart based on Raspberry Pico board -
//
// More info on https://github.com/aotta/
//
// parts of code are directly from the A8PicoCart project by Robin Edwards 2023
//
// Needs to be a release NOT debug build for the cartridge emulation to work
//
// Edit myboard.h depending on the type of flash memory on the pico clone//
//
// v. 1.0 2024-08-05 : Initial version for Pi Pico
//
*/
#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include <stdio.h>
#include <string.h>
#include "flash_fs.h"
// Implements 512 byte FAT sectors on 4096 byte flash sectors.
// Doesn't really implement wear levelling (e.g. the fs_map) so not for heavy use but should be
// fine for the intended use case.
#define HW_FLASH_STORAGE_BASE (1024 * 1024)
#define MAGIC_8_BYTES "RHE!FS30"
#define NUM_FAT_SECTORS 30716 // 15megs / 512bytes = 30720, but we used 4 records for the header (8 bytes)
#define NUM_FLASH_SECTORS 3840 // 15megs / 4096bytes = 3840
typedef struct {
uint8_t header[8];
uint16_t sectors[NUM_FAT_SECTORS]; // map FAT sectors -> flash sectors
} sector_map;
sector_map fs_map;
bool fs_map_needs_written[15];
uint8_t used_bitmap[NUM_FLASH_SECTORS]; // we will use 256 flash sectors for 2048 fat sectors
uint16_t write_sector = 0; // which flash sector we are writing to
uint8_t write_sector_bitmap = 0; // 1 for each free 512 byte page on the sector
// each sector entry in the sector map is:
// 13 bits of sector (indexing 8192 4k flash sectors)
// 3 bits of offset (0->7 512 byte FAT sectors in each 4k flash sector)
uint16_t getMapSector(uint16_t mapEntry) { return (mapEntry & 0xFFF8) >> 3; }
uint8_t getMapOffset(uint16_t mapEntry) { return mapEntry & 0x7; }
uint16_t makeMapEntry(uint16_t sector, uint8_t offset) { return (sector << 3) | offset; };
// forward declns
void flash_read_sector(uint16_t sector, uint8_t offset, void *buffer, uint16_t size);
void flash_erase_sector(uint16_t sector);
void flash_write_sector(uint16_t sector, uint8_t offset, const void *buffer, uint16_t size);
void flash_erase_with_copy_sector(uint16_t sector, uint8_t preserve_bitmap);
void debug_print_in_use() {
return;
// just shows first 1meg
printf("IN USE-----------------------------------\n");
for (int i=0; i<16; i++) {
printf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
used_bitmap[i*16+0], used_bitmap[i*16+1], used_bitmap[i*16+2], used_bitmap[i*16+3],
used_bitmap[i*16+4], used_bitmap[i*16+5], used_bitmap[i*16+6], used_bitmap[i*16+7],
used_bitmap[i*16+8], used_bitmap[i*16+9], used_bitmap[i*16+10], used_bitmap[i*16+11],
used_bitmap[i*16+12], used_bitmap[i*16+13], used_bitmap[i*16+14], used_bitmap[i*16+15]);
}
printf("END--------------------------------------\n");
}
void write_fs_map()
{
debug_print_in_use();
for (int i=0; i<15; i++) {
if (fs_map_needs_written[i]) {
// printf("Writing FS Map %d\n", i);
flash_erase_sector(i);
flash_write_sector(i, 0, (uint8_t*)&fs_map+(4096*i), 4096);
fs_map_needs_written[i] = false;
}
}
}
uint16_t getNextWriteSector()
{
static uint16_t search_start_pos = 0;
int i;
if (write_sector == 0 || write_sector_bitmap == 0)
{ // first try to find a completely free sector
for (i=0; i<NUM_FLASH_SECTORS; i++) {
if (used_bitmap[(i + search_start_pos) % NUM_FLASH_SECTORS] == 0)
break;
}
if (i < NUM_FLASH_SECTORS) {
write_sector = (i + search_start_pos) % NUM_FLASH_SECTORS;
write_sector_bitmap = 0xFF;
flash_erase_sector(write_sector);
}
else
{ // no completely free sector, just return the first sector with space
for (i=0; i<NUM_FLASH_SECTORS; i++) {
if (used_bitmap[(i + search_start_pos) % NUM_FLASH_SECTORS] != 0xFF)
break;
}
write_sector = (i + search_start_pos) % NUM_FLASH_SECTORS;
write_sector_bitmap = ~used_bitmap[write_sector];
flash_erase_with_copy_sector(write_sector, used_bitmap[write_sector]);
}
search_start_pos = (i + search_start_pos) % NUM_FLASH_SECTORS;
}
// if we get here, then at least one 512 byte page is free on the write_sector
for (i=0; i<8; i++) {
if (write_sector_bitmap & (1 << i))
break;
}
// mark the offset used
write_sector_bitmap &= ~(1 << i);
return makeMapEntry(write_sector, i);
}
void init_used_bitmap() {
memset(used_bitmap, 0, NUM_FLASH_SECTORS);
for (int i=0; i<15; i++)
used_bitmap[i] = 0xFF; // first 15 flash sectors used by fs map
for (int i=0; i<NUM_FAT_SECTORS; i++) {
uint16_t mapEntry = fs_map.sectors[i];
if (mapEntry)
used_bitmap[getMapSector(mapEntry)] |= (1 << getMapOffset(mapEntry));
}
write_sector = 0;
}
int flash_fs_mount()
{
for (int i=0; i<15; i++)
fs_map_needs_written[i] = false;
// read the first sector, with header
flash_read_sector(0, 0, &fs_map, 4096);
if (memcmp(fs_map.header, MAGIC_8_BYTES, 8) != 0) {
printf("mountFlashFS() - magic bytes not found\n");
return 1;
}
// read the remaining 14 sectors without headers
for (int i=1; i<15; i++)
flash_read_sector(i, 0, (uint8_t*)&fs_map+(4096*i), 4096);
init_used_bitmap();
debug_print_in_use();
return 0;
}
void flash_fs_create()
{
printf("flash_fs_create()\n");
memset(&fs_map, 0, sizeof(fs_map));
strcpy(fs_map.header, MAGIC_8_BYTES);
for (int i=0; i<15; i++)
fs_map_needs_written[i] = true;
write_fs_map();
init_used_bitmap();
}
void flash_fs_sync()
{
write_fs_map();
}
void flash_fs_read_FAT_sector(uint16_t fat_sector, void *buffer)
{
int mapEntry = fs_map.sectors[fat_sector];
if (mapEntry)
flash_read_sector(getMapSector(mapEntry), getMapOffset(mapEntry), buffer, 512);
else
memset(buffer, 0, 512);
return;
}
void flash_fs_write_FAT_sector(uint16_t fat_sector, const void *buffer)
{
uint16_t mapEntry = fs_map.sectors[fat_sector];
if (mapEntry)
{ // mark any previous flash allocated as unused
used_bitmap[getMapSector(mapEntry)] &= ~(1 << getMapOffset(mapEntry));
}
mapEntry = getNextWriteSector();
fs_map.sectors[fat_sector] = mapEntry;
if (fat_sector < 2044)
fs_map_needs_written[0] = true;
else
fs_map_needs_written[1+((fat_sector-2044)/2048)] = true;
used_bitmap[getMapSector(mapEntry)] |= (1 << getMapOffset(mapEntry));
flash_write_sector(getMapSector(mapEntry), getMapOffset(mapEntry), buffer, 512);
}
bool flash_fs_verify_FAT_sector(uint16_t fat_sector, const void *buffer)
{
uint8_t read_buf[512];
flash_fs_read_FAT_sector(fat_sector, read_buf);
if (memcmp(buffer, read_buf, 512) == 0) return true;
return false;
}
/* Low level flash functions */
void flash_read_sector(uint16_t sector, uint8_t offset, void *buffer, uint16_t size)
{
// printf("[FS] READ: %d, %d (%d)\n", sector, offset, size);
uint32_t fs_start = XIP_BASE + HW_FLASH_STORAGE_BASE;
uint32_t addr = fs_start + (sector * FLASH_SECTOR_SIZE) + (offset * 512);
memcpy(buffer, (unsigned char *)addr, size);
}
void flash_erase_sector(uint16_t sector)
{
// printf("[FS] ERASE: %d\n", sector);
uint32_t fs_start = HW_FLASH_STORAGE_BASE;
uint32_t offset = fs_start + (sector * FLASH_SECTOR_SIZE);
uint32_t ints = save_and_disable_interrupts();
flash_range_erase(offset, FLASH_SECTOR_SIZE);
restore_interrupts(ints);
}
void flash_write_sector(uint16_t sector, uint8_t offset, const void *buffer, uint16_t size)
{
// printf("[FS] WRITE: %d, %d (%d)\n", sector, offset, size);
uint32_t fs_start = HW_FLASH_STORAGE_BASE;
uint32_t addr = fs_start + (sector * FLASH_SECTOR_SIZE) + (offset * 512);
uint32_t ints = save_and_disable_interrupts();
flash_range_program(addr, (const uint8_t *)buffer, size);
restore_interrupts(ints);
}
void flash_erase_with_copy_sector(uint16_t sector, uint8_t preserve_bitmap)
{
// printf("[FS] ERASE with COPY: %d\n", sector);
uint8_t buf[FLASH_SECTOR_SIZE];
flash_read_sector(sector, 0, buf, FLASH_SECTOR_SIZE);
flash_erase_sector(sector);
for (int i=0; i<8; i++) {
if (preserve_bitmap & (1 << i))
flash_write_sector(sector, i, buf + (i * 512), 512);
}
}