|
Страница 1 из 4 Мы будем писать загрузочный сектор для трехдюймовой дискеты с файловой системой FAT12. После окончания начальной загрузки программа POST находит активное устройство и загружает с него короткую программу загрузки ОС - загрузочный сектор. Загрузочный сектор это первый физический сектор устройства, в данном случае дискеты и его размет равен всего ничего 512 байт. С помощью этих 512 байт кода мы должны найти основную часть загрузчика операционной системы, загрузить его в память и передать ему управление. Заголовок файловой системы FAT находится в первом секторе дискеты, благодаря чему этот заголовок, содержащий всю необходимую информацию о файловой системе, загружается вместе нашим загрузчиком.
Наш загрузочный сектор будет искать в корневом каталоге некоторый файл - загрузчик, загрузит его в память и передаст ему управление на его начало. А загрузчик уже сам разберется, что ему делать дальше. Я использую NASM, т.к. считаю, что он больше подходит для наших целей. И так, приступим. Как я уже говорил, в начале нашего загрузочного сектора располагается заголовок FAT, опишем его: ; Общая часть для всех типов FAT BS_jmpBoot: jmp short BootStart ; Переходим на код загрузчика nop BS_OEMName db '*-v4VIHC' ; 8 байт, что было на моей дискете, то и написал BPB_BytsPerSec dw 0x200 ; Байт на сектор BPB_SecPerClus db 1 ; Секторов на кластер BPB_RsvdSecCnt dw 1 ; Число резервных секторов BPB_NumFATs db 2 ; Количектво копий FAT BPB_RootEntCnt dw 224 ; Элементов в корневом катологе (max) BPB_TotSec16 dw 2880 ; Всего секторов или 0 BPB_Media db 0xF0 ; код типа устройства BPB_FATsz16 dw 9 ; Секторов на элемент таблицы FAT BPB_SecPerTrk dw 18 ; Секторов на дорожку BPB_NumHeads dw 2 ; Число головок BPB_HiddSec dd 0 ; Скрытых секторов BPB_TotSec32 dd 0 ; Всего секторов или 0 ; Заголовок для FAT12 и FAT16 BS_DrvNum db 0 ; Номер дика для прерывания int 0x13 BS_ResNT db 0 ; Зарезервировано для Windows NT BS_BootSig db 29h ; Сигнатура расширения BS_VolID dd 2a876CE1h ; Серийный номер тома BS_VolLab db 'X boot disk' ; 11 байт, метка тома BS_FilSysType db 'FAT12 ' ; 8 байт, тип ФС ; Структура элемента каталога struc DirItem DIR_Name: resb 11 DIR_Attr: resb 1 DIR_ResNT: resb 1 DIR_CrtTimeTenth resb 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 struc NotInitData SysSize: resd 1 ; Размер системной области FAT fails: resd 1 ; Число неудачных попыток при чтении fat: resd 1 ; Номер загруженного сектора с элементами FAT endstruc ;NotInitData ; По этому адресу мы будем загружать загрузчик %define SETUP_ADDR 0x1000 ; А по этому адресу нас должны были загрузить %define BOOT_ADDR 0x7C00 %define BUF 0x500 BootStart: cld xor cx, cx mov ss, cx mov es, cx mov ds, cx mov sp, BOOT_ADDR mov bp, sp ; Сообщим о том что мы загружаемся mov si, BOOT_ADDR + mLoading call print Все сегментные регистры настраиваем на начало физической памяти. Вершину стека настраиваем на начало нашего сектора, стек растет вниз (т.е. в сторону младших адресов), так что проблем быть не должно. Туда же указывает регистр bp - нам нужно обращаться к полям заголовка FAT и паре наших переменных. Мы используем базовую адресацию со смещением, для чего используем регистр bp т.к. в этом случае можно использовать однобайтовые смещения, вместо двухбайтовых адресов, что позволяет сократить код. Процедуру print, выводящую сообщение на экран, рассмотрим позже. Теперь нам нужно вычислить номера первых секторов корневого каталога и данных файлов. mov al, [byte bp+BPB_NumFATs] cbw mul word [byte bp+BPB_FATsz16] add ax, [byte bp+BPB_HiddSec] adc dx, [byte bp+BPB_HiddSec+2] add ax, [byte bp+BPB_RsvdSecCnt] adc dx, cx mov si, [byte bp+BPB_RootEntCnt] ; dx:ax - Номер первого сектора корневого каталога ; si - Количество элементов в корневом каталоге pusha ; Вычислим размер системной области FAT = резервные сектора + ; все копии FAT + корневой каталог mov [bp+SysSize], ax ; осталось добавить размер каталога mov [bp+SysSize+2], dx ; Вычислим размер корневого каталога mov ax, 32 mul si ; dx:ax - размер корневого каталога в байтах, а надо в секторах mov bx, [byte bp+BPB_BytsPerSec] add ax, bx dec ax div bx ; ax - размер корневого каталога в секторах add [bp+SysSize], ax ; Теперь мы знаем размер системной adc [bp+SysSize+2], cx ; области FAT, и начало области данных popa ; В dx:ax - снова номер первого сектора корневого каталога ; si - количество элементов в корневом каталоге Теперь мы будем просматривать корневой каталог в поисках нужного нам файла NextDirSector: ; Загрузим очередной сектор каталога во временный буфер mov bx, 700h ; es:bx - буфер для считываемого сектора mov di, bx ; указатель текущего элемента каталога mov cx, 1 ; количество секторов для чтения call ReadSectors jc near DiskError ; ошибка при чтении RootDirLoop: ; Ищем наш файл ; cx = 0 после функции ReadSectors cmp [di], ch ; byte ptr [di] = 0? jz near NotFound ; Да, это последний элемент в каталоге ; Нет, не последний, сравним имя файла pusha mov cl, 11 ; длина имени файла с расширением mov si, BOOT_ADDR + LoaderName ; указатель на имя искомого файла rep cmpsb ; сравниваем popa jz short Found ; Нашли, выходим из цикла ; Нет, ищем дальше dec si ; RootEntCnt jz near NotFound ; Это был последний элемент каталога add di, 32 ; Переходим к следующему элементу каталога ; bx указывает на конец прочтенного сектора после call ReadSectors cmp di, bx ; Последний элемент в буфере? jb short RootDirLoop ; Нет, проверим следующий элемент jmp short NextDirSector ; Да последний, загрузим следующий сектор
Из этого кода мы можем выйти одну из трех точек: ошибка при чтении DiskError, файл наден Found или файл не найден NotFound.
|