§ Дискета

Так получилось, что с дискеты раньше пользовались колоссальной популярностью в 1980-х годах. Как вы догадались, сейчас идет 2022 год (11 декабря), и дискеты вообще забыли, даже не знают, как они выглядят. Это — нормально, поэтому надо сделать так, чтобы все что можно, загрузилось с дискеты.
Ладно. Это лирическое отступление. А теперь код.
Компиляция кода g++ boot.c -o boot, запуск кода boot. Код создает образ дискеты a.img, беря за основу бутсектор boot.bin и добавляет в корень файл demo.bin.

§ Код

#include <stdio.h>
#include <stdlib.h>

int size = 1440*1024;
int clst = 3;
int file_id = 0;

unsigned char* disk;

// Дата и время, при создании четко заданные

// Точное время 18:54:30
static const int crt_time = (18 << 11) | (54 << 5) | (30 >> 1);

// 10 дек 2022 года
static const int crt_date = ((2022 - 1980) << 9) | (12<<5) | 10;

// Запись в 2 таблицы FAT
void fat_write(int n, int v) {

    n *= 3;

    int m = 0x0200 + (n >> 1);
    int w = disk[m] + 256*disk[m];

    if (n & 1) {
        w = (w & 0x000F) | (v << 4);
    } else {
        w = (w & 0xF000) | v;
    }

    // FAT-1
    disk[m  ] = w & 255;
    disk[m+1] = w >> 8;

    // FAT-2
    disk[0x1200 + m] = disk[m];
    disk[0x1201 + m] = disk[m+1];
}

// Запись байта
void writeb(int offset, int v) {
    disk[offset  ] = v & 255;
}

// Запись слова
void writew(int offset, int v) {

    disk[offset  ] = v & 255;
    disk[offset+1] = (v >> 8) & 255;
}

// Создать root запись в корне, cluster-начальный кластер; fsize-размер файла
void set_root_entry(const char* name, int id, int cluster, int fsize) {

    // Найти имя и расширение
    int i = 0;
    int n = 0;

    int ptr = 0x2600 + 32*id;

    // Подготовка имени
    for (int j = 0; j < 11; j++) disk[ptr + j] = ' ';

    // Выстраивание имени и расширения
    while (name[i] && n < 11) {

        int ch = name[i];
        if (ch >= 'a' && ch <= 'z') ch += ('A'-'a');

        if (ch == '.') {
            n = 8;
        } else {
            disk[ptr + n] = ch;
            n++;
        }

        i++;
    }

    // Атрибуты файла
    disk[ptr + 0x0B] = 0x20; // Archived
    disk[ptr + 0x0C] = 0x00; // NULL

    // Create Time
    // 100 * 10ms = 1 сек, время создания файла, уточненное
    disk[ptr + 0x0D] = 0x64;
    disk[ptr + 0x0E] = crt_time & 255;
    disk[ptr + 0x0F] = crt_time >> 8;

    // Create Date
    disk[ptr + 0x10] = crt_date & 255;
    disk[ptr + 0x11] = crt_date >> 8;

    // Last Access Date
    disk[ptr + 0x12] = crt_date & 255;
    disk[ptr + 0x13] = crt_date >> 8;

    // Старший кластер для FAT32
    disk[ptr + 0x14] = 0;
    disk[ptr + 0x15] = 0;

    // Last Modified Time
    disk[ptr + 0x16] = crt_time & 255;
    disk[ptr + 0x17] = crt_time >> 8;

    // Last Modified Date
    disk[ptr + 0x18] = crt_date & 255;
    disk[ptr + 0x19] = crt_date >> 8;

    // Номер кластера
    disk[ptr + 0x1A] = cluster & 255;
    disk[ptr + 0x1B] = cluster >> 8;

    // Номер кластера
    disk[ptr + 0x1C] = fsize & 255;
    disk[ptr + 0x1D] = (fsize >> 8)  & 255;
    disk[ptr + 0x1E] = (fsize >> 16) & 255;
    disk[ptr + 0x1F] = (fsize >> 24) & 255;
}

// Записать весь диск
void writedisk() {

    FILE* fp = fopen("a.img", "wb");
    if (fp) {

        disk[510] = 0x55;
        disk[511] = 0xAA;

        fwrite(disk, 1, size, fp);
        fclose(fp);
    }
}

// Добавить файл с fn, имя файла name
void add_file(const char* fn, const char* name = NULL) {

    FILE* fp = fopen(fn, "rb");

    if (fp) {

        // Определить размер файла и прочитать его в диск целиком
        fseek(fp, 0, SEEK_END);
        int fsize = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        fread(disk + (0x4200 + 512*(clst - 2)), 1, fsize, fp);
        fclose(fp);

        // Количество кластеров выделить
        int reserve = fsize >> 9;
        if (fsize % 512) reserve++;

        // Добавление CHAIN
        for (int i = 0; i < reserve; i++) {

            // Указатель на последний кластер будет FFFh
            fat_write(clst + i, reserve == i + 1 ? 0xFFF : clst + i + 1);
        }

        // Добавить описание файла
        set_root_entry(name ? name : fn, file_id, clst, fsize);
        file_id++;

        // К следуюшим кластерам
        clst += reserve;

    } else {

        printf("File not found %s\n", fn);
        exit(1);
    }
}

// Запись бут-сектора
void update_boot() {

    FILE* fp = fopen("boot.bin", "rb");
    if (fp) {

        fread(disk, 1, 512, fp);
        fclose(fp);

    } else {
        printf("Can't file file boot.bin\n");
        exit(1);
    }
}

int main(int argc, char* argv[]) {

    // Создать диск в памяти и очистить его в 0
    disk = (unsigned char*) malloc(size);
    for (int i = 0; i < size; i++) disk[i] = 0;

    // Обновление первого сектора update_bpb();
    update_boot();

    // Записать маркеры
    fat_write(0, 0xFF0);
    fat_write(1, 0xFFF);

    // Пока что только единственный файл
    add_file("demo.bin");

    writedisk();
    free(disk);
    return 0;
}

§ Необязательные процедуры

Записать структуру BPB:
void update_bpb() {

    int i;
    char sig1[] = "mkfs.fat";
    char sig2[] = "BOOT    BIN";
    char sig3[] = "FAT12    ";

    // JMP $+3C
    disk[0x00] = 0xEB; disk[0x01] = 0x3D; disk[0x02] = 0x00;

    // Сигнатура
    for (i = 0; i < 8; i++) disk[0x03 + i] = sig1[i];

    // Лейбл диска
    for (i = 0; i < 11; i++) disk[0x2B + i] = sig2[i];

    // Тип системы
    for (i = 0; i < 8; i++) disk[0x36 + i] = sig3[i];

    writew(0x0B, 0x0200);   // Bytes in sector
    writeb(0x0D, 0x01);     // Sectors by cluster
    writew(0x0E, 0x0001);   // Count reserver sectors
    writeb(0x10, 0x02);     // Count of FAT
    writew(0x11, 0x00E0);   // Count of Root Entries (224)
    writew(0x13, 0x0B40);   // Total count of sectors
    writeb(0x15, 0xF0);     // Media
    writew(0x16, 0x0009);   // Sectors in FAT
    writew(0x18, 0x0012);   // Sectors on track
    writew(0x1A, 0x0002);   // Count of heads
    writew(0x1C, 0x0000);   // Hidden Sectors (DD)
    writew(0x1E, 0x0000);
    writew(0x20, 0x0000);   // Total Sectors
    writew(0x22, 0x0000);
    writeb(0x24, 0x00);     // Number of Phys.
    writeb(0x25, 0x01);     // Flags
    writeb(0x26, 0x29);     // Ext Sig
    writew(0x27, 0x0000);   // Serial Numbers ES:BX
    writew(0x29, 0x07E0);
}
Чтение из FAT

// Чтение значения из FAT
int fat_read(int n) {

    n *= 3;

    int m = 0x0200 + (n >> 1);
    int w = disk[m] + 256*disk[m];

    return (n & 1 ? (w >> 4) : w) & 0xFFF;
}