Userdata export (FAT16): Optimize code size.

Reorganize the and optimize the code to avoid redundant code and thus
reduce code size, while making sure to zero-initialize the used areas
on the SD card. These are the "clean" part of this change.

The "dirty" part:
Optimize the FAT16 export code further by introducing small deviations
from the FAT16 specification. These deviations should not be harmful
at all, unless the SD card is used for something requiring the jump
instruction and/or boot code in the boot sector to be valid. This is
typically only required when booting off the filesystem.

After these changes, a considerable reduction in code size can be
observed for sys_controller.elf and sys_onchip_memory2_0.bin:

sys_controller.elf:
   text	   data	    bss	    dec	    hex	filename
  32392	   2936	   2652	  37980	   945c	sys_controller.elf

sys_onchip_memory2_0.bin:
size: 35328 bytes

This reduces the cost of the FAT16 export feature to 446 bytes.
This commit is contained in:
Ari Sundholm 2020-11-17 23:31:00 +02:00
parent 12436a3d3f
commit 760be1738e
5 changed files with 5588 additions and 5616 deletions

File diff suppressed because it is too large Load Diff

View File

@ -23,29 +23,35 @@
#include "fat16_export.h"
/*
* The beginning of the boot sector. Will be followed by the BPB.
* Offsets 0x000 to 0x00a, inclusive.
* The beginning of the boot sector, along with the BPB.
* Volume offsets 0x003 to 0x01a, inclusive.
* The BPB spans volume offsets 0x00b to 0x01c, inclusive.
*
* The jump instruction at volume offsets 0x000 to 0x002, inclusive,
* is left zeroed out to save a tiny bit of space.
*/
static const alt_u8 bootsec_before_bpb_16[11] = {
0xeb, 0x00, 0x90, 0x4d, 0x53, 0x57, 0x49, 0x4e, 0x34, 0x2e, 0x31,
/* ^-----------^--- Maybe zero these? */
static const alt_u8 bootsec_beg_bpb_16[24] = {
/* Three zeros */ 0x4d, 0x53, 0x57, 0x49, 0x4e, /* 0x003...0x007 */
0x34, 0x2e, 0x31, 0x00, 0x02, 0x04, 0x80, 0x00, /* 0x008...0x00f */
0x02, 0x00, 0x08, 0x00, 0x80, 0xf8, 0x20, 0x00, /* 0x010...0x017 */
0x3f, 0x00, 0xff, /* Zeros until 0x024 */ /* 0x018...0x01a */
};
/* The BPB will span offsets 0x00b to 0x23, inclusive. Will be generated. */
/*
* The rest of the boot sector before the boot code and terminator.
* Offsets 0x024 to 0x03d, inclusive.
*/
static const alt_u8 bootsec_after_bpb_16[26] = {
0x80, 0x00, 0x29, 0xf4, 0xcf, 0xc6, 0x04, 0x4f, 0x53, 0x53, 0x43, 0x50,
0x52, 0x4f, 0x46, 0x49, 0x4c, 0x53, 0x46, 0x41, 0x54, 0x31, 0x36, 0x20,
0x20, 0x20,
/* Zeros */ 0x80, 0x00, 0x29, 0xf4, /* 0x024...0x027 */
0xcf, 0xc6, 0x04, 0x4f, 0x53, 0x53, 0x43, 0x50, /* 0x028...0x02f */
0x52, 0x4f, 0x46, 0x49, 0x4c, 0x53, 0x46, 0x41, /* 0x030...0x037 */
0x54, 0x31, 0x36, 0x20, 0x20, 0x20, /* Zeros */ /* 0x038...0x03d */
};
/*
* After this, we have the boot code (448 bytes) and sector terminator
* (2 bytes). These will be generated.
* (2 bytes). The former will be left zeroed-out and the latter will
* be generated.
*/
/* Generates a FAT16 boot sector.
@ -53,35 +59,25 @@ static const alt_u8 bootsec_after_bpb_16[26] = {
* and is assumed to be pre-zeroed.
*/
void generate_boot_sector_16(alt_u8 *const buf) {
/* Initial FAT16 boot sector contents. */
memcpy(buf, bootsec_before_bpb_16, 11);
/* BPB */
buf[12] = 0x02;
buf[13] = 0x04;
buf[14] = 0x80;
buf[16] = 0x02;
buf[18] = 0x08;
buf[20] = 0x80;
buf[21] = 0xf8;
buf[22] = 0x20;
buf[24] = 0x3f;
buf[26] = 0xff;
/* Initial FAT16 boot sector contents + the BPB. */
memcpy(buf + 3, bootsec_beg_bpb_16, 24);
/*
* Then the rest of the boot sector.
* The boot code is just 448 bytes of 0xf4.
*
* The boot code is supposed to be 448 bytes filled with 0xf4,
* but leave it zeroed out to keep the code smaller. This may
* be a deviation from the FAT16 spec, but should be harmless
* for our purposes.
*/
memcpy(buf + 36, bootsec_after_bpb_16, 26);
memset(buf + 62, 0xf4, 448);
buf[510] = 0x55;
buf[511] = 0xaa;
/* RISC-V is little-endian, so do a 16-bit write instead. */
*((alt_u16*)(buf + 510)) = 0xaa55U;
}
/* The fixed 'preamble' of a FAT on a FAT16 volume. */
static const alt_u8 fat16_preamble[4] = {
0xf8, 0xff, 0xff, 0xff,
};
static const alt_u32 fat16_preamble = 0xfffffff8U;
/*
* Generate a FAT.
@ -93,9 +89,10 @@ static const alt_u8 fat16_preamble[4] = {
* The intention is to be able to generate and write the FAT in chunks
* that do not exhaust all the remaining RAM.
*/
alt_u16 generate_fat16(alt_u8 *const fat, const alt_u16 written) {
alt_u16 generate_fat16(void *const buf, const alt_u16 written) {
alt_u16 cur_ofs = 0;
const alt_u16 start_cluster = 3U + written;
alt_u16 *const fat = buf;
/*
* The total number of FAT entries to write consists of:
@ -111,24 +108,24 @@ alt_u16 generate_fat16(alt_u8 *const fat, const alt_u16 written) {
? FAT16_ENTRIES_PER_SECTOR
: clusters_remaining) - preamble_compensation;
const alt_u16 end_cluster = start_cluster + clusters_to_write;
static const alt_u16 last_fat_cluster = PROF_16_CLUSTER_COUNT + 2U;
const alt_u16 last_fat_cluster = PROF_16_CLUSTER_COUNT + 2U;
if (!written) {
memcpy(fat, fat16_preamble, sizeof(fat16_preamble));
cur_ofs += sizeof(fat16_preamble);
*((alt_u32*)fat) = fat16_preamble;
cur_ofs += sizeof(fat16_preamble)/sizeof(alt_u16);
}
for (alt_u16 cluster = start_cluster; cluster < end_cluster; ++cluster) {
alt_u16 *const cur_entry = fat + cur_ofs;
/* FAT16 entries are 16-bit little-endian. */
if (cluster == last_fat_cluster) {
/* At the last cluster, write the chain terminator. */
fat[cur_ofs++] = 0xff;
fat[cur_ofs++] = 0xff;
*cur_entry = 0xffffU;
}
else {
fat[cur_ofs++] = cluster & 0xffU;
fat[cur_ofs++] = (cluster >> 8U) & 0xffU;
*cur_entry = cluster;
}
++cur_ofs;
}
return end_cluster - 3U;

View File

@ -43,6 +43,12 @@
/* On this volume, each FAT will be 16 kiB in size. */
#define FAT16_SIZE 0x04000UL
/* The first sector of the root directory. */
#define FAT16_ROOT_DIR_FIRST_SECTOR 192U
/* The length of the root directory in sectors. */
#define FAT16_ROOT_DIR_SECTORS 128U
/*
* Define the properties and contents of the directory entry for the
* settings file.
@ -72,6 +78,6 @@ void generate_boot_sector_16(alt_u8 *buf);
* The number of clusters already written are given as an argument.
* The function returns the total number of clusters written so far.
*/
alt_u16 generate_fat16(alt_u8 *fat, alt_u16 written);
alt_u16 generate_fat16(void *buf, alt_u16 written);
#endif // FAT16_EXPORT_H_

View File

@ -416,31 +416,29 @@ eval_button:
strncpy(menu_row2, "Exporting...", LCD_ROW_LEN+1);
ui_disp_menu(2);
/* Generate and write the boot sector. */
/* Zero out the boot sector, FATs and root directory. */
memset(databuf, 0, SD_BLK_SIZE);
for (alt_u32 sector = 0;
sector < (FAT16_ROOT_DIR_FIRST_SECTOR + FAT16_ROOT_DIR_SECTORS);
++sector)
{
retval = SD_Write(&sdcard_dev, databuf, sector);
if (retval)
goto out;
}
/* Generate and write the boot sector. */
generate_boot_sector_16(databuf);
retval = SD_Write(&sdcard_dev, databuf, 0);
if (retval)
goto out;
/* Zero out the FAT area */
/* TODO: a proper erase would be more ideal... */
memset(databuf, 0, SD_BLK_SIZE);
for (alt_u32 fat_sector = FAT16_1_OFS/SD_BLK_SIZE;
fat_sector < (FAT16_2_OFS + FAT16_SIZE)/SD_BLK_SIZE; ++fat_sector)
{
retval = SD_Write(&sdcard_dev, databuf, fat_sector);
if (retval)
goto out;
}
/* Generate and write the file allocation tables. */
for (alt_u16 clusters_written = 0, sd_blk_idx = 0;
clusters_written < (PROF_16_DATA_SIZE/FAT16_CLUSTER_SIZE);)
{
alt_u16 count;
memset(databuf, 0, SD_BLK_SIZE);
count = generate_fat16(databuf, clusters_written);
clusters_written = generate_fat16(databuf, clusters_written);
retval = SD_Write(&sdcard_dev, databuf,
(FAT16_1_OFS/SD_BLK_SIZE) + sd_blk_idx);
if (retval)
@ -452,7 +450,6 @@ eval_button:
goto out;
++sd_blk_idx;
clusters_written = count;
}
/* Write the directory entry of the settings file. */