§ Дискета

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

§ Код

1#include <stdio.h>
2#include <stdlib.h>
3
4int size = 1440*1024;
5int clst = 3;
6int file_id = 0;
7
8unsigned char* disk;
9
10// Дата и время, при создании четко заданные
11
12// Точное время 18:54:30
13static const int crt_time = (18 << 11) | (54 << 5) | (30 >> 1);
14
15// 10 дек 2022 года
16static const int crt_date = ((2022 - 1980) << 9) | (12<<5) | 10;
17
18// Запись в 2 таблицы FAT
19void fat_write(int n, int v) {
20
21    n *= 3;
22
23    int m = 0x0200 + (n >> 1);
24    int w = disk[m] + 256*disk[m];
25
26    if (n & 1) {
27        w = (w & 0x000F) | (v << 4);
28    } else {
29        w = (w & 0xF000) | v;
30    }
31
32    // FAT-1
33    disk[m  ] = w & 255;
34    disk[m+1] = w >> 8;
35
36    // FAT-2
37    disk[0x1200 + m] = disk[m];
38    disk[0x1201 + m] = disk[m+1];
39}
40
41// Запись байта
42void writeb(int offset, int v) {
43    disk[offset  ] = v & 255;
44}
45
46// Запись слова
47void writew(int offset, int v) {
48
49    disk[offset  ] = v & 255;
50    disk[offset+1] = (v >> 8) & 255;
51}
52
53// Создать root запись в корне, cluster-начальный кластер; fsize-размер файла
54void set_root_entry(const char* name, int id, int cluster, int fsize) {
55
56    // Найти имя и расширение
57    int i = 0;
58    int n = 0;
59
60    int ptr = 0x2600 + 32*id;
61
62    // Подготовка имени
63    for (int j = 0; j < 11; j++) disk[ptr + j] = ' ';
64
65    // Выстраивание имени и расширения
66    while (name[i] && n < 11) {
67
68        int ch = name[i];
69        if (ch >= 'a' && ch <= 'z') ch += ('A'-'a');
70
71        if (ch == '.') {
72            n = 8;
73        } else {
74            disk[ptr + n] = ch;
75            n++;
76        }
77
78        i++;
79    }
80
81    // Атрибуты файла
82    disk[ptr + 0x0B] = 0x20; // Archived
83    disk[ptr + 0x0C] = 0x00; // NULL
84
85    // Create Time
86    // 100 * 10ms = 1 сек, время создания файла, уточненное
87    disk[ptr + 0x0D] = 0x64;
88    disk[ptr + 0x0E] = crt_time & 255;
89    disk[ptr + 0x0F] = crt_time >> 8;
90
91    // Create Date
92    disk[ptr + 0x10] = crt_date & 255;
93    disk[ptr + 0x11] = crt_date >> 8;
94
95    // Last Access Date
96    disk[ptr + 0x12] = crt_date & 255;
97    disk[ptr + 0x13] = crt_date >> 8;
98
99    // Старший кластер для FAT32
100    disk[ptr + 0x14] = 0;
101    disk[ptr + 0x15] = 0;
102
103    // Last Modified Time
104    disk[ptr + 0x16] = crt_time & 255;
105    disk[ptr + 0x17] = crt_time >> 8;
106
107    // Last Modified Date
108    disk[ptr + 0x18] = crt_date & 255;
109    disk[ptr + 0x19] = crt_date >> 8;
110
111    // Номер кластера
112    disk[ptr + 0x1A] = cluster & 255;
113    disk[ptr + 0x1B] = cluster >> 8;
114
115    // Номер кластера
116    disk[ptr + 0x1C] = fsize & 255;
117    disk[ptr + 0x1D] = (fsize >> 8)  & 255;
118    disk[ptr + 0x1E] = (fsize >> 16) & 255;
119    disk[ptr + 0x1F] = (fsize >> 24) & 255;
120}
121
122// Записать весь диск
123void writedisk() {
124
125    FILE* fp = fopen("a.img", "wb");
126    if (fp) {
127
128        disk[510] = 0x55;
129        disk[511] = 0xAA;
130
131        fwrite(disk, 1, size, fp);
132        fclose(fp);
133    }
134}
135
136// Добавить файл с fn, имя файла name
137void add_file(const char* fn, const char* name = NULL) {
138
139    FILE* fp = fopen(fn, "rb");
140
141    if (fp) {
142
143        // Определить размер файла и прочитать его в диск целиком
144        fseek(fp, 0, SEEK_END);
145        int fsize = ftell(fp);
146        fseek(fp, 0, SEEK_SET);
147        fread(disk + (0x4200 + 512*(clst - 2)), 1, fsize, fp);
148        fclose(fp);
149
150        // Количество кластеров выделить
151        int reserve = fsize >> 9;
152        if (fsize % 512) reserve++;
153
154        // Добавление CHAIN
155        for (int i = 0; i < reserve; i++) {
156
157            // Указатель на последний кластер будет FFFh
158            fat_write(clst + i, reserve == i + 1 ? 0xFFF : clst + i + 1);
159        }
160
161        // Добавить описание файла
162        set_root_entry(name ? name : fn, file_id, clst, fsize);
163        file_id++;
164
165        // К следуюшим кластерам
166        clst += reserve;
167
168    } else {
169
170        printf("File not found %s\n", fn);
171        exit(1);
172    }
173}
174
175// Запись бут-сектора
176void update_boot() {
177
178    FILE* fp = fopen("boot.bin", "rb");
179    if (fp) {
180
181        fread(disk, 1, 512, fp);
182        fclose(fp);
183
184    } else {
185        printf("Can't file file boot.bin\n");
186        exit(1);
187    }
188}
189
190int main(int argc, char* argv[]) {
191
192    // Создать диск в памяти и очистить его в 0
193    disk = (unsigned char*) malloc(size);
194    for (int i = 0; i < size; i++) disk[i] = 0;
195
196    // Обновление первого сектора update_bpb();
197    update_boot();
198
199    // Записать маркеры
200    fat_write(0, 0xFF0);
201    fat_write(1, 0xFFF);
202
203    // Пока что только единственный файл
204    add_file("demo.bin");
205
206    writedisk();
207    free(disk);
208    return 0;
209}

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

Записать структуру BPB:
1void update_bpb() {
2
3    int i;
4    char sig1[] = "mkfs.fat";
5    char sig2[] = "BOOT    BIN";
6    char sig3[] = "FAT12    ";
7
8    // JMP $+3C
9    disk[0x00] = 0xEB; disk[0x01] = 0x3D; disk[0x02] = 0x00;
10
11    // Сигнатура
12    for (i = 0; i < 8; i++) disk[0x03 + i] = sig1[i];
13
14    // Лейбл диска
15    for (i = 0; i < 11; i++) disk[0x2B + i] = sig2[i];
16
17    // Тип системы
18    for (i = 0; i < 8; i++) disk[0x36 + i] = sig3[i];
19
20    writew(0x0B, 0x0200);   // Bytes in sector
21    writeb(0x0D, 0x01);     // Sectors by cluster
22    writew(0x0E, 0x0001);   // Count reserver sectors
23    writeb(0x10, 0x02);     // Count of FAT
24    writew(0x11, 0x00E0);   // Count of Root Entries (224)
25    writew(0x13, 0x0B40);   // Total count of sectors
26    writeb(0x15, 0xF0);     // Media
27    writew(0x16, 0x0009);   // Sectors in FAT
28    writew(0x18, 0x0012);   // Sectors on track
29    writew(0x1A, 0x0002);   // Count of heads
30    writew(0x1C, 0x0000);   // Hidden Sectors (DD)
31    writew(0x1E, 0x0000);
32    writew(0x20, 0x0000);   // Total Sectors
33    writew(0x22, 0x0000);
34    writeb(0x24, 0x00);     // Number of Phys.
35    writeb(0x25, 0x01);     // Flags
36    writeb(0x26, 0x29);     // Ext Sig
37    writew(0x27, 0x0000);   // Serial Numbers ES:BX
38    writew(0x29, 0x07E0);
39}
Чтение из FAT
1
2// Чтение значения из FAT
3int fat_read(int n) {
4
5    n *= 3;
6
7    int m = 0x0200 + (n >> 1);
8    int w = disk[m] + 256*disk[m];
9
10    return (n & 1 ? (w >> 4) : w) & 0xFFF;
11}