+/* STORAGE OF SETTINGS */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include "klavirko-ui.h"
+#include "fs.h"
+#include "debug.h"
+#include "main.h"
+#include "gui.h"
+#include "wave.h"
+
+int16_t fs_index0 = -1;
+int16_t fs_index1 = -1;
+int16_t fs_newindex0 = -1;
+int16_t fs_newindex1 = -1;
+int16_t fs_index = -1;
+int16_t fs_index_block = -1;
+int16_t fs_index_record = -1;
+int16_t fs_index_sector = -1;
+int16_t fs_index_fileid = -1;
+
+int16_t fs_page = -1;
+uint8_t fs_page_flags = 0;
+int16_t fs_page_sectors[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_fileids[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+uint8_t fs_page_names[N_FILES_ON_PAGE][N_NAME+1] = {[0 ... (N_FILES_ON_PAGE-1)] = {[0 ... N_NAME] = 0}};
+int16_t fs_page_indexes[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_indexblocks[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_indexrecords[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+
+volatile uint16_t fs_timeout;
+
+inline void fs_watchdog (void)
+{
+ ++fs_timeout;
+}
+
+
+/* SPI FLASH HANDLING */
+
+inline uint8_t fs_spi_1b (const uint8_t data) // send & receive single byte
+{
+ FS__SDR = data;
+ while (FS__SSR & 0x0040)
+ {}
+ return (uint8_t)(FS__SDR );
+}
+
+void fs_spi( // execute single command
+ const uint8_t opcode,
+ const uint16_t address_bytes,
+ const uint16_t dummy_bytes,
+ const uint16_t data_bytes,
+ const uint32_t address,
+ uint8_t * const read_data,
+ const uint8_t * const write_data,
+ const uint8_t flag
+)
+{
+ uint16_t i;
+ uint8_t io = 0;
+
+ // clear errors
+ if (FS__SSR & 0b00000111)
+ FS__SIR = FS__SSR;
+
+ // Chip Select & LED indicator
+ LED_U = 1;
+ FS_CS = 0;
+
+ io = fs_spi_1b(opcode);
+
+ for (i = address_bytes ? 2 : -1; i<3; --i) //assume 3 or 0 bytes
+ {
+ io = fs_spi_1b(*(((uint8_t *)(&address))+i));
+ }
+
+ for (i=0; i<dummy_bytes; ++i)
+ {
+ io = fs_spi_1b(io);
+ }
+
+ for (i=0; i<data_bytes; ++i)
+ {
+ if (flag & FS_WRITE_FLAG)
+ io = write_data[i];
+ else
+ io = 0;
+ io = fs_spi_1b(io);
+ if (flag & FS_READ_FLAG)
+ read_data[i] = io;
+ }
+
+ // Chip Select & LED indicator
+ FS_CS = 1;
+ LED_U = 0;
+}
+
+/* SPI FLASH COMMANDS */
+
+inline void fs_spi_read2 (
+ const uint32_t address,
+ const uint16_t n,
+ uint8_t * const data
+)
+{
+ fs_spi(FS_READ_2, 3, 2, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_read1 (
+ const uint32_t address,
+ const uint16_t n,
+ uint8_t * const data
+)
+{
+ fs_spi(FS_READ_1, 3, 1, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_read (
+ const uint32_t address,
+ const uint16_t n,
+ uint8_t * const data
+)
+{
+ fs_spi(FS_READ, 3, 0, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_erase_4k (const uint32_t address)
+{
+ fs_spi(FS_ERASE_4K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_32k (const uint32_t address)
+{
+ fs_spi(FS_ERASE_32K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_64k (const uint32_t address)
+{
+ fs_spi(FS_ERASE_64K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_chip (void)
+{
+ fs_spi(FS_ERASE_CHIP, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_program (
+ const uint32_t address,
+ const uint16_t n,
+ const uint8_t * const data
+)
+{
+ fs_spi(FS_PROGRAM, 3, 0, n, address, FS_NULL, data, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_enable (void)
+{
+ fs_spi(FS_WRITE_ENABLE, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_write_disable (void)
+{
+ fs_spi(FS_WRITE_DISABLE, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_protect_sector (const uint32_t address)
+{
+ fs_spi(FS_PROTECT_SECTOR, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_unprotect_sector (const uint32_t address)
+{
+ fs_spi(FS_UNPROTECT_SECTOR, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline uint8_t fs_spi_read_sector_protection (const uint32_t address)
+{
+ uint8_t status;
+ fs_spi(FS_READ_SECTOR_PROTECTION, 3, 0, 1, address, &status, FS_NULL, FS_READ_FLAG);
+ return status;
+}
+
+inline uint16_t fs_spi_read_status (void)
+{
+ uint16_t status;
+ fs_spi(FS_READ_STATUS, 0, 0, 2, 0, (uint8_t*)&status, FS_NULL, FS_READ_FLAG);
+ return status;
+}
+
+inline void fs_spi_write_status_low (const uint8_t status)
+{
+ fs_spi(FS_WRITE_STATUS_1, 0, 0, 1, 0, FS_NULL, &status, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_status_high (const uint8_t status)
+{
+ fs_spi(FS_WRITE_STATUS_2, 0, 0, 1, 0, FS_NULL, &status, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_status (const uint16_t status)
+{
+ fs_spi_write_status_low((uint8_t) status);
+ fs_spi_write_status_high(*(((uint8_t*)(&status))+1));
+}
+
+inline void fs_spi_reset (void)
+{
+ uint8_t confirm = FS_RESET_CONFIRM;
+ fs_spi(FS_RESET, 0, 0, 1, 0, FS_NULL, &confirm, FS_WRITE_FLAG);
+}
+
+inline uint32_t fs_spi_read_id (void) // TODO: fix this if ever needed
+{
+ uint32_t id = 0;
+ fs_spi(FS_READ_ID, 0, 0, 3, 0, (uint8_t*)&id, FS_NULL, FS_READ_FLAG);
+ return id;
+}
+
+inline void fs_wait_ready (const uint16_t timeout)
+{
+ uint16_t status;
+
+ fs_timeout = 0;
+ do
+ {
+ if (fs_timeout > timeout)
+ {
+ fs_index = -1;
+ fs_index0 = -1;
+ fs_index1 = -1;
+ debug_string("FS TIMEOUT!\r\n",1);
+ gui_trigger_error(GUI_ERRF_FS);
+ break;
+ }
+ status = fs_spi_read_status();
+ }while(status & FS_BSY);
+}
+
+/* support for filesystem operations */
+
+inline uint32_t fs_expand_address (
+ const uint8_t sector,
+ const uint8_t id,
+ const uint16_t offset
+)
+{
+ return
+ ((((uint32_t)sector)<<(FILEID_BITS+FILE_BITS)) |
+ (((uint32_t)id)<<FILE_BITS))
+ +offset;
+}
+
+inline uint32_t fs_expand_record_address (
+ const uint8_t index,
+ const uint8_t block,
+ const uint8_t record
+)
+{
+ uint8_t index_sector;
+
+ if (index & NEW_INDEX)
+ index_sector = (index & 1) ? fs_newindex1 : fs_newindex0;
+ else
+ index_sector = (index & 1) ? fs_index1 : fs_index0;
+
+ return fs_expand_address(
+ index_sector,
+ block,
+ ((uint16_t)record) * N_RECORD_BYTES + INDEX_OFFSET_RECORDS
+ );
+}
+
+inline uint8_t fs_is_sector_free (const uint8_t sector)
+{
+ uint32_t address;
+ uint8_t data;
+
+ address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+ fs_spi_read (address, 1, &data);
+
+ return (data == 0xff) ? 0xff : 0x00;
+}
+
+inline uint8_t fs_is_file_free (
+ const uint8_t sector,
+ const uint8_t id
+)
+{
+ uint32_t address;
+ uint8_t data;
+
+ address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+ fs_spi_read (address, 1, &data);
+
+ return data & (1<<id);
+}
+
+uint16_t fs_get_file_type (
+ const uint8_t sector,
+ const uint8_t id
+)
+{
+ uint32_t address;
+ uint16_t type = 0;
+ uint8_t free;
+
+ free = fs_is_file_free(sector, id);
+ address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+ fs_spi_read (address, 1, (uint8_t*)&type);
+
+ if(free)
+ {
+ if(type != FILE_FREE)
+ type |= FILE_INVALID;
+ }
+ else
+ {
+ if(type == FILE_FREE)
+ type |= FILE_INVALID;
+ }
+ return type;
+}
+
+inline uint16_t fs_get_index_validation (const uint8_t sector)
+{
+ uint16_t type;
+
+ type = fs_get_file_type(sector, 0);
+ if ((type != FILE_INDEX_0)&&(type != FILE_INDEX_1))
+ return 0;
+ if ((fs_get_file_type(sector, 1)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 2)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 3)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 4)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 5)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 6)!=type))
+ return 0;
+ if ((fs_get_file_type(sector, 7)!=type))
+ return 0;
+ return type;
+}
+
+/* filesystem indexing */
+
+inline void fs_locate_index (void)
+{
+ int16_t i;
+ uint8_t found0 = 0;
+ uint8_t found1 = 0;
+ uint16_t type;
+
+ for (i=0; i< N_SECTORS; ++i)
+ {
+ type = fs_get_index_validation(i);
+
+ if (type == FILE_INDEX_0)
+ {
+ found0 = 1;
+ fs_index0 = i;
+ }
+ else if (type == FILE_INDEX_1)
+ {
+ found1 = 1;
+ fs_index1 = i;
+ }
+
+ if (found0 && found1)
+ return;
+ }
+ fs_index0 = -1;
+ fs_index1 = -1;
+}
+
+void fs_scan_index (void)
+{
+ uint8_t index, block, record;
+ uint8_t status, page, id, sector=fs_index0, fileid=N_FILES_IN_SECTOR-1;
+ uint8_t lastsector, lastfileid;
+
+ for (index = 0; index<2; ++index)
+ {
+ for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+ {
+ for (record = 0; record < N_INDEX_RECORDS; ++record)
+ {
+ lastsector = sector;
+ lastfileid = fileid;
+ status = fs_read_record(
+ index, block, record,
+ &page, &id,
+ §or, &fileid
+ );
+ if (status == RECORD_FREE)
+ goto fs_scan_index__after_scan;
+ }
+ }
+ }
+ fs_scan_index__after_scan:
+
+ fs_index = index;
+ fs_index_block = block;
+ fs_index_record = record;
+ fs_index_sector = lastsector;
+ fs_index_fileid = lastfileid;
+ fs_index_next_file();
+}
+
+inline void fs_index_next_record (void)
+{
+ if (fs_index_record < N_INDEX_RECORDS-1)
+ ++fs_index_record;
+ else
+ {
+ fs_index_record = 0;
+ if (fs_index_block < N_FILES_IN_SECTOR-1)
+ ++fs_index_block;
+ else
+ {
+ fs_index_block = 0;
+ if(fs_index < 1)
+ ++fs_index;
+ else
+ {
+ fs_index = -1;
+ fs_index0 = -1;
+ fs_index1 = -1;
+ debug_string("FS OVERFLOW!\r\n", 1);
+ gui_trigger_error(GUI_ERRF_FS);
+ }
+ }
+ }
+}
+
+inline void fs_index_next_sector (void)
+{
+ fs_index_fileid = 0;
+ if (fs_index_sector < N_SECTORS - 1)
+ ++fs_index_sector;
+ else
+ fs_index_sector = 0;
+}
+
+inline void fs_index_next_file (void)
+{
+ if (fs_index_fileid < N_FILES_IN_SECTOR-1)
+ ++fs_index_fileid;
+ else
+ fs_index_next_sector();
+}
+
+/* filesystem operations, low level */
+
+uint8_t fs_create_file (
+ const uint8_t sector,
+ const uint8_t id,
+ const uint8_t type
+)
+{
+ uint32_t sector_address;
+ uint32_t file_id_address;
+ uint8_t flag;
+
+ // debug_string("create file ",1);
+ // debug_hex(type,1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(sector,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(id,1,1,0);
+ // debug_string("\r\n",1);
+
+ if (!fs_is_file_free(sector, id))
+ return 0;
+
+ sector_address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+ file_id_address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+ flag = ~(1<<id);
+
+ // debug_string("sector_addr: ",1);
+ // debug_hex(sector_address,1,8,1);
+ // debug_string(" flag: ",1);
+ // debug_hex(flag,1,2,0);
+ // debug_string("\r\nfile_id_addr: ",1);
+ // debug_hex(file_id_address,1,8,1);
+ // debug_string(" type: ",1);
+ // debug_hex(type,1,2,0);
+ // debug_string("\r\n",1);
+
+ fs_spi_write_enable();
+ fs_spi_program(sector_address, 1, &flag);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+ fs_spi_write_enable();
+ fs_spi_program(file_id_address, 1, &type);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+
+ return 0xff;
+}
+
+uint8_t fs_delete_file (
+ const uint8_t sector,
+ const uint8_t id
+)
+{
+ uint32_t sector_address;
+ uint32_t file_id_address;
+ uint8_t flag;
+ uint8_t type = FILE_DELETED;
+
+ sector_address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+ file_id_address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+ flag = ~(1<<id);
+
+ // debug_string("delete file ",1);
+ // debug_hex(type,1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(sector,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(id,1,1,0);
+ // debug_string("\r\nsector_addr: ",1);
+ // debug_hex(sector_address,1,8,1);
+ // debug_string(" flag: ",1);
+ // debug_hex(flag,1,2,0);
+ // debug_string("\r\nfile_id_addr: ",1);
+ // debug_hex(file_id_address,1,8,1);
+ // debug_string(" type: ",1);
+ // debug_hex(type,1,2,0);
+ // debug_string("\r\n",1);
+
+ fs_spi_write_enable();
+ fs_spi_program(sector_address, 1, &flag);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+ fs_spi_write_enable();
+ fs_spi_program(file_id_address, 1, &type);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+
+ return 0xff;
+}
+
+uint8_t fs_duplicate_file (
+ const uint8_t src_sector,
+ const uint8_t src_id,
+ const uint8_t dst_sector,
+ const uint8_t dst_id
+)
+{
+ uint32_t src_address_0, src_address_1;
+ uint32_t dst_address_0, dst_address_1;
+ uint32_t dst_sector_address;
+ uint8_t data[256];
+
+ // debug_string("duplicate file ",1);
+ // debug_byte(' ',1);
+ // debug_hex(src_sector,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(src_id,1,1,0);
+ // debug_byte(' ',1);
+ // debug_hex(dst_sector,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(dst_id,1,1,0);
+ // debug_string("\r\n",1);
+
+ if (!fs_is_file_free(dst_sector, dst_id))
+ return 0;
+
+ src_address_0 = fs_expand_address(src_sector, src_id, 0);
+ src_address_1 = src_address_0 + 256;
+ dst_address_0 = fs_expand_address(dst_sector, dst_id, 0);
+ dst_address_1 = dst_address_0 + 256;
+ dst_sector_address = fs_expand_address(dst_sector, 0, FILE_OFFSET_FLAG);
+
+ // debug_string("src_addr: ",1);
+ // debug_hex(src_address_0,1,8,1);
+ // debug_byte(' ',1);
+ // debug_hex(src_address_1,1,8,1);
+ // debug_string("\r\ndst_addr: ",1);
+ // debug_hex(dst_address_0,1,8,1);
+ // debug_byte(' ',1);
+ // debug_hex(dst_address_1,1,8,1);
+ // debug_string("\r\ndst_sect_addr: ",1);
+ // debug_hex(dst_sector_address,1,8,1);
+ // debug_string("\r\n",1);
+
+ fs_spi_read(src_address_0, 256, data);
+ data[FILE_OFFSET_FLAG] = ~ (1<<dst_id);
+ fs_spi_write_enable();
+ fs_spi_program(dst_sector_address, 1, data+FILE_OFFSET_FLAG);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+ fs_spi_write_enable();
+ fs_spi_program(dst_address_0, 256, data);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+ fs_spi_read(src_address_1, 256, data);
+ fs_spi_write_enable();
+ fs_spi_program(dst_address_1, 256, data);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+ fs_spi_write_disable();
+
+ return 0xff;
+}
+
+inline void fs_erase_sector(const uint8_t sector)
+{
+ uint32_t address;
+
+ address = fs_expand_address(sector, 0, 0);
+
+ // debug_string("erase sector ",1);
+ // debug_hex(sector,1,2,0);
+ // debug_string("\r\n",1);
+
+ fs_spi_write_enable();
+ fs_spi_erase_4k(address);
+ fs_wait_ready(TIMEOUT_ERASE);
+}
+
+void fs_format (void)
+{
+ uint16_t status;
+
+ fs_spi_write_enable ();
+ fs_spi_erase_chip();
+ fs_wait_ready(TIMEOUT_FORMAT);
+
+ if(fs_index0 < 0)
+ {
+ fs_index0 = 0;
+ fs_index1 = 1;
+ }
+ else
+ {
+ ++fs_index0;
+ if (fs_index0 >= N_SECTORS - 1)
+ fs_index0 = 0;
+ fs_index1 = fs_index0 + 1;
+ }
+
+ fs_create_file(fs_index0, 0, FILE_INDEX_0);
+ fs_create_file(fs_index0, 1, FILE_INDEX_0);
+ fs_create_file(fs_index0, 2, FILE_INDEX_0);
+ fs_create_file(fs_index0, 3, FILE_INDEX_0);
+ fs_create_file(fs_index0, 4, FILE_INDEX_0);
+ fs_create_file(fs_index0, 5, FILE_INDEX_0);
+ fs_create_file(fs_index0, 6, FILE_INDEX_0);
+ fs_create_file(fs_index0, 7, FILE_INDEX_0);
+
+ fs_create_file(fs_index1, 0, FILE_INDEX_1);
+ fs_create_file(fs_index1, 1, FILE_INDEX_1);
+ fs_create_file(fs_index1, 2, FILE_INDEX_1);
+ fs_create_file(fs_index1, 3, FILE_INDEX_1);
+ fs_create_file(fs_index1, 4, FILE_INDEX_1);
+ fs_create_file(fs_index1, 5, FILE_INDEX_1);
+ fs_create_file(fs_index1, 6, FILE_INDEX_1);
+ fs_create_file(fs_index1, 7, FILE_INDEX_1);
+}
+
+uint8_t fs_read_record (
+ const uint8_t index,
+ const uint8_t block,
+ const uint8_t record,
+ uint8_t * const page,
+ uint8_t * const id,
+ uint8_t * const sector,
+ uint8_t * const fileid
+)
+{
+ uint8_t status;
+ uint32_t address;
+ uint8_t data[N_RECORD_BYTES];
+
+ address = fs_expand_record_address(index, block, record);
+
+ fs_spi_read(address, N_RECORD_BYTES, data);
+
+ status = data[RECORD_OFFSET_ID] & RECORD_FLAGS;
+ if (status == RECORD_ACTIVE)
+ {
+ *page = data[RECORD_OFFSET_PAGE];
+ *id =(data[RECORD_OFFSET_ID] & PAGEID_MASK) >> PAGEID_SHIFT;
+ *sector= data[RECORD_OFFSET_SECTOR];
+ *fileid=(data[RECORD_OFFSET_ID] & SECTRID_MASK) >> SECTRID_SHIFT;
+ }
+ return status;
+}
+
+void fs_write_record (
+ const uint8_t index,
+ const uint8_t block,
+ const uint8_t record,
+ const uint8_t page,
+ const uint8_t id,
+ const uint8_t sector,
+ const uint8_t fileid
+)
+{
+ uint32_t address;
+ uint8_t data[N_RECORD_BYTES];
+
+ // debug_string("write record ",1);
+ // debug_hex(index,1,1,0);
+ // debug_byte(':',1);
+ // debug_hex(block,1,1,0);
+ // debug_byte(':',1);
+ // debug_hex(record,1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(page,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(id,1,1,0);
+ // debug_byte(' ',1);
+ // debug_hex(sector,1,2,0);
+ // debug_byte(':',1);
+ // debug_hex(fileid,1,1,0);
+ // debug_string("\r\n",1);
+
+ address = fs_expand_record_address(index, block, record);
+
+ data [RECORD_OFFSET_ID] =
+ ((id << PAGEID_SHIFT) & PAGEID_MASK) |
+ ((fileid << SECTRID_SHIFT) & SECTRID_MASK) |
+ (RECORD_FLAGS & RECORD_ACTIVE);
+ data [RECORD_OFFSET_SECTOR] = sector;
+ data [RECORD_OFFSET_PAGE] = page;
+
+ // debug_string("record_addr: ",1);
+ // debug_hex(address,1,8,1);
+ // debug_string(" data: ",1);
+ // debug_hex(data[0],1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(data[1],1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(data[2],1,2,0);
+ // debug_string("\r\n",1);
+
+ fs_spi_write_enable();
+ fs_spi_program(address, N_RECORD_BYTES, data);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+}
+
+void fs_delete_record (
+ const uint8_t index,
+ const uint8_t block,
+ const uint8_t record
+)
+{
+ uint32_t address;
+ uint8_t data[N_RECORD_BYTES] = {[0 ... (N_RECORD_BYTES-1)] = RECORD_DELETED};
+
+ // debug_string("delete record ",1);
+ // debug_hex(index,1,1,0);
+ // debug_byte(':',1);
+ // debug_hex(block,1,1,0);
+ // debug_byte(':',1);
+ // debug_hex(record,1,2,0);
+ // debug_string("\r\n",1);
+
+ address = fs_expand_record_address(index, block, record);
+
+ // debug_string("record_addr: ",1);
+ // debug_hex(address,1,8,1);
+ // debug_string(" data: ",1);
+ // debug_hex(data[0],1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(data[1],1,2,0);
+ // debug_byte(' ',1);
+ // debug_hex(data[2],1,2,0);
+ // debug_string("\r\n",1);
+
+ fs_spi_write_enable();
+ fs_spi_program(address, N_RECORD_BYTES, data);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+}
+
+/* filesystem operations, high level */
+
+void fs_get_file_name (
+ const uint8_t sector,
+ const uint8_t id,
+ uint8_t * const name
+)
+{
+ uint32_t address;
+
+ address = fs_expand_address(sector, id, FILE_OFFSET_NAME);
+ fs_spi_read(address, N_NAME, name);
+}
+
+void fs_load_page (const uint8_t new_page)
+{
+ uint8_t index, block, record;
+ uint8_t status, page, id, sector, fileid;
+
+ // debug_string("FS LOAD PAGE\r\n",1);
+
+ if (new_page == fs_page)
+ {
+ // debug_string("no change\r\n",1);
+ return;
+ }
+
+ fs_page = -1;
+ fs_page_flags = 0;
+ for (id = 0; id < N_FILES_ON_PAGE; ++id)
+ fs_page_names[id][0] = '\0';
+
+ if ((fs_index0 < 0) || (fs_index1 < 0))
+ {
+ // debug_string("no index\r\n",1);
+ return;
+ }
+
+ for (index = 0; index<2; ++index)
+ {
+ for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+ {
+ for (record = 0; record < N_INDEX_RECORDS; ++record)
+ {
+ // debug_string("i=",1);
+ // debug_dec(index,1,1);
+ // debug_string(" b=",1);
+ // debug_dec(block,1,1);
+ // debug_string(" r=",1);
+ // debug_dec(record,1,3);
+ // debug_string(" : ",1);
+
+ status = fs_read_record(
+ index, block, record,
+ &page, &id,
+ §or, &fileid
+ );
+ // debug_hex(status,1,2,0);
+ // debug_string("\r\n",1);
+
+ if (status == RECORD_FREE)
+ goto fs_load_page__after_scan;
+ if (status != RECORD_ACTIVE)
+ continue;
+ if (page == new_page)
+ {
+ fs_page_indexes[id] = index;
+ fs_page_indexblocks[id] = block;
+ fs_page_indexrecords[id] = record;
+ fs_page_sectors[id] = sector;
+ fs_page_fileids[id] = fileid;
+ fs_get_file_name(sector, fileid, fs_page_names[id]);
+ fs_page_flags |= 1<<id;
+ if (fs_page_flags == PAGE_FLAGS)
+ goto fs_load_page__after_scan;
+ }
+ }
+ }
+ }
+ fs_load_page__after_scan:
+ // for (id = 0; id < FILES_ON_PAGE; ++i)
+ // {
+ // if (!(fs_page_flags & (1<<id)))
+ // fs_page_names[id][0] = '\0';
+ // }
+ fs_page = new_page;
+}
+
+void fs_store_voice (
+ const uint8_t id,
+ const uint8_t * const name,
+ const uint32_t A,
+ const uint32_t D,
+ const uint32_t S,
+ const uint32_t R,
+ const uint8_t * const wave
+)
+{
+ uint8_t overwrite;
+ int16_t i;
+ uint32_t address;
+ uint8_t data[FILE_OFFSET_NAME - FILE_OFFSET_START];
+ uint8_t opt_sector;
+
+ if ((fs_index0 < 0) || (fs_index1 < 0))
+ return;
+
+ overwrite = fs_page_flags & (1<<id);
+
+ for (i=0; ;++i)
+ {
+ if (i >= (N_SECTORS * N_FILES_IN_SECTOR))
+ {
+ fs_index = -1;
+ fs_index0 = -1;
+ fs_index1 = -1;
+ debug_string("FS FULL!\r\n", 1);
+ gui_trigger_error(GUI_ERRF_FS);
+ return;
+ }
+ if (fs_create_file(fs_index_sector, fs_index_fileid, FILE_NORMAL))
+ break;
+ fs_index_next_file();
+ }
+ address = fs_expand_address(fs_index_sector, fs_index_fileid, 0);
+
+ data[FILE_OFFSET_PAGE - FILE_OFFSET_START] = fs_page;
+ data[FILE_OFFSET_ID - FILE_OFFSET_START] = id;
+ *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_A - FILE_OFFSET_START])) = A;
+ *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_D - FILE_OFFSET_START])) = D;
+ *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_S - FILE_OFFSET_START])) = S;
+ *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_R - FILE_OFFSET_START])) = R;
+
+ fs_spi_write_enable();
+ fs_spi_program(address + FILE_OFFSET_START, FILE_OFFSET_NAME - FILE_OFFSET_START, data);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+
+ fs_spi_write_enable();
+ fs_spi_program(address + FILE_OFFSET_NAME, N_NAME, name);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+
+ fs_spi_write_enable();
+ fs_spi_program(address + FILE_OFFSET_WAVE, N_SAMPLE, wave);
+ fs_wait_ready(TIMEOUT_PROGRAM);
+
+
+ if (overwrite)
+ {
+ fs_delete_record(
+ fs_page_indexes[id],
+ fs_page_indexblocks[id],
+ fs_page_indexrecords[id]
+ );
+ fs_delete_file(
+ fs_page_sectors[id],
+ fs_page_fileids[id]
+ );
+ opt_sector = fs_page_sectors[id];
+ }
+
+ fs_write_record(
+ fs_index,
+ fs_index_block,
+ fs_index_record,
+ fs_page,
+ id,
+ fs_index_sector,
+ fs_index_fileid
+ );
+
+ fs_page_flags |= 1<<id;
+ fs_page_indexes[id] = fs_index;
+ fs_page_indexblocks[id] = fs_index_block;
+ fs_page_indexrecords[id] = fs_index_record;
+ fs_page_sectors[id] = fs_index_sector;
+ fs_page_fileids[id] = fs_index_fileid;
+ memcpy(fs_page_names[id],name, N_NAME);
+
+ fs_index_next_file();
+ fs_index_next_record();
+
+ if (overwrite)
+ fs_optimise_sector(opt_sector);
+
+ fs_optimise_index();
+}
+
+uint8_t fs_load_voice (
+ const uint8_t id,
+ uint32_t * const A,
+ uint32_t * const D,
+ uint32_t * const S,
+ uint32_t * const R,
+ uint8_t * const wave
+)
+{
+ int16_t i;
+ uint32_t address;
+ uint8_t data[FILE_OFFSET_NAME - FILE_OFFSET_ADSR];
+
+ if ((fs_index0 < 0) || (fs_index1 < 0))
+ return 0;
+
+ if (!(fs_page_flags & (1<<id)))
+ return 0;
+
+ address = fs_expand_address(fs_page_sectors[id], fs_page_fileids[id], 0);
+
+ fs_spi_read(address + FILE_OFFSET_ADSR, FILE_OFFSET_NAME - FILE_OFFSET_ADSR, data);
+
+ *A = *((uint32_t*)&(data[FILE_OFFSET_ADSR_A]));
+ *D = *((uint32_t*)&(data[FILE_OFFSET_ADSR_D]));
+ *S = *((uint32_t*)&(data[FILE_OFFSET_ADSR_S]));
+ *R = *((uint32_t*)&(data[FILE_OFFSET_ADSR_R]));
+
+ fs_spi_read(address + FILE_OFFSET_WAVE, N_SAMPLE, wave);
+
+ return 0xff;
+}
+
+void fs_optimise_sector (const uint8_t opt_sector)
+{
+ uint8_t found = 0;
+ uint8_t deleted = 0;
+ uint8_t index, block, record;
+ uint8_t status, page, id, sector, fileid;
+ uint16_t type, i;
+
+ uint8_t pages[N_FILES_IN_SECTOR];
+ uint8_t ids[N_FILES_IN_SECTOR];
+ uint8_t types[N_FILES_IN_SECTOR];
+ uint8_t indexes[N_FILES_IN_SECTOR];
+ uint8_t indexblocks[N_FILES_IN_SECTOR];
+ uint8_t indexrecords[N_FILES_IN_SECTOR];
+
+ uint32_t address;
+
+ // debug_string("optimise_sector ", 1);
+ // debug_hex(opt_sector, 1, 2, 0);
+ // debug_string("\r\n",1);
+
+ for (fileid = 0; fileid < N_FILES_IN_SECTOR; ++fileid)
+ {
+ type = fs_get_file_type(opt_sector, fileid);
+ // debug_hex(fileid, 1,1,0);
+ // debug_byte(':',1);
+ // debug_hex(type, 1,2,0);
+ if (type != FILE_NORMAL)
+ {
+ types[fileid] = FILE_DELETED;
+ found |= (1<<fileid);
+ if(type != FILE_FREE)
+ {
+ ++deleted;
+ // debug_byte('+',1);
+ }
+ }
+ else
+ types[fileid] = type;
+ // debug_byte(' ',1);
+ }
+
+ // debug_string("\r\n deleted=",1);
+ // debug_hex(deleted,1,1,0);
+ // debug_string("\r\n",1);
+
+ if (deleted <= N_MAX_DELETED)
+ return;
+
+ if (fs_page >= 0)
+ {
+ for (id = 0; id < N_FILES_ON_PAGE; ++id)
+ {
+ if (fs_page_sectors[id] == opt_sector)
+ {
+ fileid = fs_page_fileids[id];
+ found |= (1<<fileid);
+ pages[fileid] = fs_page;
+ ids[fileid] = id;
+ indexes[fileid] = fs_page_indexes[id];
+ indexblocks[fileid] = fs_page_indexblocks[id];
+ indexrecords[fileid] = fs_page_indexrecords[id];
+ }
+ }
+ }
+
+ if (found == SECTOR_FLAGS)
+ goto fs_optimise_sector__after_scan;
+
+ for (index = 0; index < 2; ++index)
+ {
+ for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+ {
+ for (record = 0; record < N_INDEX_RECORDS; ++record)
+ {
+ status = fs_read_record(
+ index, block, record,
+ &page, &id,
+ §or, &fileid
+ );
+ if (status == RECORD_FREE)
+ goto fs_optimise_sector__after_scan;
+ else if ((status == RECORD_ACTIVE) && (sector == opt_sector))
+ {
+ found |= (1<<fileid);
+ pages[fileid] = page;
+ ids[fileid] = id;
+ indexes[fileid] = index;
+ indexblocks[fileid] = block;
+ indexrecords[fileid] = record;
+ if (found == SECTOR_FLAGS)
+ goto fs_optimise_sector__after_scan;
+ }
+ }
+ }
+ }
+ fs_optimise_sector__after_scan:
+
+ // debug_string("found=",1);
+ // debug_hex(found,1,2,0);
+ // debug_string("\r\n",1);
+
+ if (opt_sector == fs_index_sector)
+ fs_index_next_sector();
+
+ for (fileid = 0; fileid < N_FILES_IN_SECTOR; ++fileid)
+ {
+ if ((found & (1<<fileid)) && (types[fileid]==FILE_NORMAL))
+ {
+ for (i = 0; ; ++i)
+ {
+ if (i >= (N_SECTORS * N_FILES_IN_SECTOR))
+ {
+ fs_index = -1;
+ fs_index0 = -1;
+ fs_index1 = -1;
+ debug_string("FS FULL!\r\n", 1);
+ gui_trigger_error(GUI_ERRF_FS);
+ return;
+ }
+ if (fs_duplicate_file(opt_sector, fileid, fs_index_sector, fs_index_fileid))
+ break;
+ fs_index_next_file();
+ }
+ fs_write_record(
+ fs_index, fs_index_block, fs_index_record,
+ pages[fileid], ids[fileid],
+ fs_index_sector, fs_index_fileid
+ );
+ fs_delete_record(indexes[fileid], indexblocks[fileid], indexrecords[fileid]);
+ if (pages[fileid] == fs_page)
+ {
+ id = ids[fileid];
+ fs_page_indexes[id] = fs_index;
+ fs_page_indexblocks[id] = fs_index_block;
+ fs_page_indexrecords[id] = fs_index_record;
+ fs_page_sectors[id] = fs_index_sector;
+ fs_page_fileids[id] = fs_index_fileid;
+ }
+ fs_index_next_record();
+ fs_index_next_file();
+ }
+ }
+
+ fs_erase_sector(opt_sector);
+}
+
+void fs_optimise_index (void)
+{
+ uint16_t i;
+ uint8_t index=0, block=0, record=0;
+ uint8_t newindex=0, newblock=0, newrecord=0;
+ uint8_t status, page, id, sector, fileid;
+
+ if (!(
+ (fs_index == 1) &&
+ (fs_index_block == N_FILES_IN_SECTOR-1) &&
+ (fs_index_record >= N_INDEX_RECORDS - N_INDEX_MARGIN)
+ ))
+ return;
+ if ((fs_index0 < 0) || (fs_index1 < 0))
+ return;
+
+ // debug_string("optimise_index\r\n", 1);
+
+ fs_newindex0 = -1;
+ fs_newindex1 = -1;
+ sector = fs_index_sector;
+
+ for (i = 0; i< N_SECTORS ; ++i)
+ {
+ if (fs_is_sector_free(sector))
+ {
+ if (fs_newindex0 <0)
+ fs_newindex0 = sector;
+ else
+ {
+ fs_newindex1 = sector;
+ break;
+ }
+ }
+ if (sector >= N_SECTORS-1)
+ sector = 0;
+ else
+ ++sector;
+ }
+ if (fs_newindex1 < 0)
+ {
+ fs_index = -1;
+ fs_index0 = -1;
+ fs_index1 = -1;
+ debug_string("FS FULL!\r\n", 1);
+ gui_trigger_error(GUI_ERRF_FS);
+ return;
+ }
+
+ for (id =0; id<N_FILES_IN_SECTOR; ++id)
+ {
+ fs_create_file(fs_newindex0, id, FILE_INDEX_0);
+ fs_create_file(fs_newindex1, id, FILE_INDEX_1);
+ }
+
+ for (index=0; index<2; ++i)
+ {
+ for (block=0; block<N_FILES_IN_SECTOR; ++block)
+ {
+ for (record=0; record<N_INDEX_RECORDS; ++record)
+ {
+ status = fs_read_record(
+ index, block, record,
+ &page, &id,
+ §or, &fileid
+ );
+ if (status == RECORD_FREE)
+ goto fs_optimise_index__after_scan;
+ if (status != RECORD_ACTIVE)
+ continue;
+ fs_write_record(
+ newindex | NEW_INDEX, newblock, newrecord,
+ page, id,
+ sector, fileid
+ );
+ //todo: extract to sub?
+ if (newrecord < N_INDEX_RECORDS-1)
+ ++newrecord;
+ else
+ {
+ newrecord=0;
+ if(newblock < N_FILES_IN_SECTOR)
+ ++newblock;
+ else
+ {
+ newblock = 0;
+ ++newindex;
+ }
+ }
+ }
+ }
+ }
+ fs_optimise_index__after_scan:
+
+ fs_erase_sector(fs_index0);
+ fs_erase_sector(fs_index1);
+
+ fs_index0 = fs_newindex0;
+ fs_index1 = fs_newindex1;
+ fs_index = newindex;
+ fs_index_block = newblock;
+ fs_index_record = newrecord;
+}
+
+inline void setup_fs (void)
+{
+ FS_HOLD = 1;
+ FS_HOLD__PM = 0;
+ FS_HOLD = 1;
+
+ FS_WP = 1;
+ FS_WP__PM = 0;
+ FS_WP = 1;
+
+ FS_CS = 1;
+ FS_CS__PM = 0;
+ FS_CS__PU = 0;
+ FS_CS = 1;
+
+ LED_U = 0;
+ LED_U__PM = 0;
+
+ PER0 |= 0b00001000; //enable serial array unit 1
+
+ SPS1 = (SPS1 & 0xff0f) | 0x0000; //CK01 = fclk/1 = 32MHz
+
+ FS__SMR = 0b1000000000100000; //CK01, int. on tx end
+ FS__SCR = 0b1111000000000111; //T/R enable, mode 0, MSB first, 8 bit
+ FS__SDR = 0x0400; //32MHz / 6 = 5+1/3 MHz
+
+ SO1 &= ~0b0000001000000010; //ck, so = 0 (mode^!)
+
+ FS__SOE = 1;
+
+ FS_SCK__PM = 0;
+ FS_SCK__PU = 0;
+ FS_SCK = 1;
+
+ FS_MOSI__PM = 0;
+ FS_MOSI__PU = 0;
+ FS_MOSI = 1;
+
+ FS_MISO__PM = 1;
+ FS_MISO__POM = 0;
+ FS_MISO__PU = 0;
+
+ FS__SS = 1;
+}
+
+inline void init_fs (void)
+{
+ uint8_t index, block, record;
+ uint8_t status, page, id, sector, fileid;
+
+ fs_spi_write_disable ();
+
+ fs_locate_index();
+ if((fs_index0<0)||(fs_index1<0))
+ {
+ fs_index = -1;
+ debug_string("FS INVALID!\r\n", 1);
+ gui_trigger_error(GUI_ERRF_FS);
+ return;
+ }
+ fs_scan_index();
+
+ debug_string("INIT_FS\r\nindex0=",1);
+ debug_dec(fs_index0,1,1);
+ debug_string("\r\nindex1=",1);
+ debug_dec(fs_index1,1,1);
+ debug_string("\r\nindex=",1);
+ debug_dec(fs_index,1,1);
+ debug_string("\r\nblock=",1);
+ debug_dec(fs_index_block,1,1);
+ debug_string("\r\nrecord=",1);
+ debug_dec(fs_index_record,1,1);
+ debug_string("\r\nsector=",1);
+ debug_dec(fs_index_sector,1,1);
+ debug_string("\r\nfileid=",1);
+ debug_dec(fs_index_fileid,1,1);
+ debug_string("\r\n",1);
+
+ // debug_sector(fs_index0);
+ // debug_sector(fs_index1);
+}
+
+void debug_file (
+ const uint8_t sector,
+ const uint8_t id
+)
+{
+ uint32_t address;
+ uint16_t i, j;
+ uint8_t data[16];
+
+ address = fs_expand_address(sector,id,0);
+
+ debug_string ("file ",1);
+ debug_hex(sector,1,2,0);
+ debug_byte(':',1);
+ debug_hex(id,1,1,0);
+ debug_string("\r\n",1);
+ debug_hex(address,1,5,1);
+ debug_string("\r\n",1);
+
+ for(i=0; i<FILE_OFFSET_NEXT; i+=16, address+=16)
+ {
+ fs_spi_read(address,16,data);
+ for(j=0; j<16; ++j)
+ {
+ if(j)
+ debug_byte(' ',1);
+ debug_hex(data[j],1,2,0);
+ }
+ debug_string("\r\n",1);
+ }
+}
+
+void debug_sector (const uint8_t sector)
+{
+ uint8_t id;
+
+ for(id = 0; id < N_FILES_IN_SECTOR; ++id)
+ debug_file(sector,id);
+}