Страница 1 из 4 Мы будем писать загрузочный сектор для трехдюймовой дискеты с файловой системой FAT12. После окончания начальной загрузки программа POST находит активное устройство и загружает с него короткую программу загрузки ОС - загрузочный сектор. Загрузочный сектор это первый физический сектор устройства, в данном случае дискеты и его размет равен всего ничего 512 байт. С помощью этих 512 байт кода мы должны найти основную часть загрузчика операционной системы, загрузить его в память и передать ему управление. Заголовок файловой системы FAT находится в первом секторе дискеты, благодаря чему этот заголовок, содержащий всю необходимую информацию о файловой системе, загружается вместе нашим загрузчиком.
Наш загрузочный сектор будет искать в корневом каталоге некоторый файл - загрузчик, загрузит его в память и передаст ему управление на его начало. А загрузчик уже сам разберется, что ему делать дальше. Я использую NASM, т.к. считаю, что он больше подходит для наших целей. И так, приступим. Как я уже говорил, в начале нашего загрузочного сектора располагается заголовок FAT, опишем его: ; Общая часть для всех типов FAT BS_jmpBoot: jmpshort BootStart; Переходим на код загрузчика nop BS_OEMNamedb '*-v4VIHC'; 8 байт, что было на моей дискете, то и написал BPB_BytsPerSecdw 0x200; Байт на сектор BPB_SecPerClusdb 1; Секторов на кластер BPB_RsvdSecCntdw 1; Число резервных секторов BPB_NumFATsdb 2; Количектво копий FAT BPB_RootEntCntdw 224; Элементов в корневом катологе (max) BPB_TotSec16dw 2880; Всего секторов или 0 BPB_Mediadb 0xF0; код типа устройства BPB_FATsz16dw 9; Секторов на элемент таблицы FAT BPB_SecPerTrkdw 18; Секторов на дорожку BPB_NumHeadsdw 2; Число головок BPB_HiddSecdd 0; Скрытых секторов BPB_TotSec32dd 0; Всего секторов или 0 ; Заголовок для FAT12 и FAT16 BS_DrvNumdb 0; Номер дика для прерывания int 0x13 BS_ResNTdb 0; Зарезервировано для Windows NT BS_BootSigdb 29h; Сигнатура расширения BS_VolIDdd 2a876CE1h; Серийный номер тома BS_VolLabdb 'X boot disk'; 11 байт, метка тома BS_FilSysTypedb 'FAT12 '; 8 байт, тип ФС ; Структура элемента каталога strucDirItem DIR_Name:resb 11 DIR_Attr:resb 1 DIR_ResNT:resb 1 DIR_CrtTimeTenthresb 1 DIR_CrtTime:resw 1 DIR_CrtDate:resw 1 DIR_LstAccDate:resw 1 DIR_FstClusHi:resw 1 DIR_WrtTime:resw 1 DIR_WrtDate:resw 1 DIR_FstClusLow:resw 1 DIR_FileSize:resd 1 endstruc ;DirItem Большинство полей мы использовать не будем, и так мало места для полета. Загрузчик BIOS передает нам управление на начало загрузочного сектора, т.е. на BS_jmpBoot, поэтому в начале заголовка FAT на отводится 3 байта для короткой или длинной инструкции jmp. Мы в данном случае использовали короткую, указав модификатор short, и в третьем байте просто разместили однобайтовую инструкцию nop. По инструкции jmp short BootStart мы переходим на наш код. Проведем небольшую инициализацию: ; Наши не инициализированные переменные ; При инициализации они затрут не нужные нам ; поля заголовка FAT: BS_jmpBoot и BS_OEMName strucNotInitData SysSize:resd 1; Размер системной области FAT fails:resd 1; Число неудачных попыток при чтении fat:resd 1; Номер загруженного сектора с элементами FAT endstruc ;NotInitData ; По этому адресу мы будем загружать загрузчик %define SETUP_ADDR0x1000 ; А по этому адресу нас должны были загрузить %define BOOT_ADDR0x7C00 %define BUF0x500 BootStart: cld xorcx, cx movss, cx moves, cx movds, cx movsp, BOOT_ADDR movbp, sp ; Сообщим о том что мы загружаемся movsi, BOOT_ADDR + mLoading callprint Все сегментные регистры настраиваем на начало физической памяти. Вершину стека настраиваем на начало нашего сектора, стек растет вниз (т.е. в сторону младших адресов), так что проблем быть не должно. Туда же указывает регистр bp - нам нужно обращаться к полям заголовка FAT и паре наших переменных. Мы используем базовую адресацию со смещением, для чего используем регистр bp т.к. в этом случае можно использовать однобайтовые смещения, вместо двухбайтовых адресов, что позволяет сократить код. Процедуру print, выводящую сообщение на экран, рассмотрим позже. Теперь нам нужно вычислить номера первых секторов корневого каталога и данных файлов. moval, [byte bp+BPB_NumFATs] cbw mulword [byte bp+BPB_FATsz16] addax, [byte bp+BPB_HiddSec] adcdx, [byte bp+BPB_HiddSec+2] addax, [byte bp+BPB_RsvdSecCnt] adcdx, cx movsi, [byte bp+BPB_RootEntCnt] ; dx:ax - Номер первого сектора корневого каталога ; si - Количество элементов в корневом каталоге pusha ; Вычислим размер системной области FAT = резервные сектора + ; все копии FAT + корневой каталог mov[bp+SysSize], ax; осталось добавить размер каталога mov[bp+SysSize+2], dx ; Вычислим размер корневого каталога movax, 32 mulsi ; dx:ax - размер корневого каталога в байтах, а надо в секторах movbx, [byte bp+BPB_BytsPerSec] addax, bx decax divbx ; ax - размер корневого каталога в секторах add[bp+SysSize], ax; Теперь мы знаем размер системной adc[bp+SysSize+2], cx; области FAT, и начало области данных popa ; В dx:ax - снова номер первого сектора корневого каталога ; si - количество элементов в корневом каталоге Теперь мы будем просматривать корневой каталог в поисках нужного нам файла NextDirSector: ; Загрузим очередной сектор каталога во временный буфер movbx, 700h; es:bx - буфер для считываемого сектора movdi, bx; указатель текущего элемента каталога movcx, 1; количество секторов для чтения callReadSectors jcnear DiskError; ошибка при чтении RootDirLoop: ; Ищем наш файл ; cx = 0 после функции ReadSectors cmp[di], ch; byte ptr [di] = 0? jznear NotFound; Да, это последний элемент в каталоге ; Нет, не последний, сравним имя файла pusha movcl, 11; длина имени файла с расширением movsi, BOOT_ADDR + LoaderName; указатель на имя искомого файла repcmpsb; сравниваем popa jzshort Found; Нашли, выходим из цикла ; Нет, ищем дальше decsi; RootEntCnt jznear NotFound; Это был последний элемент каталога adddi, 32; Переходим к следующему элементу каталога ; bx указывает на конец прочтенного сектора после call ReadSectors cmpdi, bx; Последний элемент в буфере? jbshort RootDirLoop; Нет, проверим следующий элемент jmpshort NextDirSector; Да последний, загрузим следующий сектор
Из этого кода мы можем выйти одну из трех точек: ошибка при чтении DiskError, файл наден Found или файл не найден NotFound. |