2010-10-01 11:21:55 +02:00
|
|
|
|
; Copyright (c) 2008-2009, diamond
|
|
|
|
|
; All rights reserved.
|
|
|
|
|
;
|
|
|
|
|
; Redistribution and use in source and binary forms, with or without
|
|
|
|
|
; modification, are permitted provided that the following conditions are met:
|
|
|
|
|
; * Redistributions of source code must retain the above copyright
|
|
|
|
|
; notice, this list of conditions and the following disclaimer.
|
|
|
|
|
; * Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
; notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
; documentation and/or other materials provided with the distribution.
|
|
|
|
|
; * Neither the name of the <organization> nor the
|
|
|
|
|
; names of its contributors may be used to endorse or promote products
|
|
|
|
|
; derived from this software without specific prior written permission.
|
|
|
|
|
;
|
|
|
|
|
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
|
|
|
|
|
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
|
|
|
|
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
;*****************************************************************************
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Встречаются вирус и FAT.
|
|
|
|
|
- Привет, ты кто?
|
|
|
|
|
- Я? Вирус.
|
|
|
|
|
- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
|
|
|
|
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
|
|
|
|
Требования для работы:
|
|
|
|
|
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
|
|
|
|
должны быть читабельны.
|
|
|
|
|
2) Минимальный процессор - 80186.
|
|
|
|
|
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Документация в тему (ссылки валидны на момент написания этого файла, 15.05.2008):
|
|
|
|
|
официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
|
|
|
|
|
в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf
|
|
|
|
|
русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip
|
|
|
|
|
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
|
|
|
|
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
|
|
|
|
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
|
|
|
|
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
|
|
|
|
|
занимает 12 бит в таблице FAT, так что общий размер не превосходит
|
|
|
|
|
0x17EE = 6126 байт. Вся таблица помещается в памяти.
|
|
|
|
|
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
|
|
|
|
|
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
|
|
|
|
|
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
|
|
|
|
|
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
|
|
|
|
|
на практике нужна только небольшая её часть. Поэтому место в памяти
|
|
|
|
|
резервируется, но данные считываются только в момент, когда к ним
|
|
|
|
|
действительно идёт обращение.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Схема используемой памяти:
|
|
|
|
|
...-7C00 стек
|
|
|
|
|
7C00-7E00 код бутсектора
|
|
|
|
|
7E00-8200 вспомогательный файл загрузчика (kordldr.f1x)
|
|
|
|
|
8200-8300 список загруженных секторов таблицы FAT16
|
|
|
|
|
(1 = соответствующий сектор загружен)
|
|
|
|
|
60000-80000 загруженная таблица FAT12 / место для таблицы FAT16
|
|
|
|
|
80000-90000 текущий кластер текущей рассматриваемой папки
|
|
|
|
|
90000-92000 кэш для корневой папки
|
|
|
|
|
92000-... кэш для некорневых папок (каждой папке отводится
|
|
|
|
|
2000h байт = 100h входов, одновременно в кэше
|
|
|
|
|
может находиться не более 7 папок;
|
|
|
|
|
точный размер определяется размером доступной
|
|
|
|
|
физической памяти - как правило, непосредственно
|
|
|
|
|
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Основной процесс загрузки.
|
|
|
|
|
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
|
|
|
|
dl содержит идентификатор диска, с которого идёт загрузка
|
|
|
|
|
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
|
|
|
|
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
|
|
|
|
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
|
|
|
|
это освобождает ds и экономит на размере кода).
|
|
|
|
|
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
|
|
|
|
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
|
|
|
|
сообщением об отсутствии LBA.
|
|
|
|
|
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
|
|
|
|
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
|
|
|
|
предполагает уже существующие данные корректными.
|
|
|
|
|
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
|
|
|
|
|
и начальный сектор данных. Кладёт их в стек; впоследствии они
|
|
|
|
|
всегда будут лежать в стеке и адресоваться через bp.
|
|
|
|
|
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
|
|
|
|
|
секторов - минимум из размера корневой папки, указанного в BPB, и 16
|
|
|
|
|
(размер кэша для корневой папки - 2000h байт = 16 секторов).
|
|
|
|
|
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
|
|
|
|
|
он оказывается папкой, или если файл имеет нулевую длину -
|
|
|
|
|
переходит на код обработки ошибок с сообщением о
|
|
|
|
|
ненайденном загрузчике.
|
|
|
|
|
Замечание: на этом этапе загрузки искать можно только в корневой
|
|
|
|
|
папке и только имена, заданные в формате файловой системе FAT
|
|
|
|
|
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
|
|
|
|
быть заглавными, при необходимости имя и расширение дополняются
|
|
|
|
|
пробелами, разделяющей точки нет, завершающего нуля нет).
|
|
|
|
|
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
|
|
|
|
|
ему управление. При этом в регистрах dx:ax оказывается абсолютный
|
|
|
|
|
номер первого сектора kordldr.f1x, а в cx - число считанных секторов
|
|
|
|
|
(равное размеру кластера).
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Вспомогательные процедуры бутсектора.
|
|
|
|
|
Код обработки ошибок (err):
|
|
|
|
|
1. Выводит строку с сообщением об ошибке.
|
|
|
|
|
2. Выводит строку "Press any key...".
|
|
|
|
|
3. Ждёт нажатия any key.
|
|
|
|
|
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
|
|
|
|
5. Для подстраховки зацикливается.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура чтения секторов (read_sectors и read_sectors2):
|
|
|
|
|
на входе должно быть установлено:
|
2010-10-01 11:21:55 +02:00
|
|
|
|
ss:bp = 0:7C00
|
2013-05-28 21:09:31 +02:00
|
|
|
|
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
|
|
|
|
dx:ax = стартовый сектор (относительно начала логического диска
|
|
|
|
|
для read_sectors, относительно начала данных для read_sectors2)
|
|
|
|
|
cx = число секторов (должно быть больше нуля)
|
|
|
|
|
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
|
|
|
|
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
|
|
|
|
в номер относительно начала логического диска, прибавляя номер сектора
|
|
|
|
|
начала данных, хранящийся в стеке как [bp-8].
|
|
|
|
|
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
|
|
|
|
устройстве, прибавляя значение соответствующего поля из BPB.
|
|
|
|
|
2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации
|
|
|
|
|
CHS-версия: все читаемые секторы были на одной дорожке.
|
|
|
|
|
LBA-версия: число читаемых секторов не превосходило 7Fh (требование
|
|
|
|
|
спецификации EDD BIOS).
|
|
|
|
|
CHS-версия:
|
|
|
|
|
3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как
|
|
|
|
|
единица плюс остаток от деления абсолютного номера на число секторов
|
|
|
|
|
на дорожке; дорожка рассчитывается как остаток от деления частного,
|
|
|
|
|
полученного на предыдущем шаге, на число дорожек, а цилиндр - как
|
|
|
|
|
частное от этого же деления. Если число секторов для чтения больше,
|
|
|
|
|
чем число секторов до конца дорожки, уменьшает число секторов для
|
|
|
|
|
чтения.
|
|
|
|
|
4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов,
|
|
|
|
|
dh=головка, (младшие 6 бит cl)=сектор,
|
|
|
|
|
(старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер).
|
|
|
|
|
5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска
|
|
|
|
|
и повторяет попытку чтения, всего делается не более трёх попыток
|
|
|
|
|
(несколько попыток нужно в случае дискеты для гарантии того, что
|
|
|
|
|
мотор раскрутился). Если все три раза происходит ошибка чтения,
|
|
|
|
|
переходит на код обработки ошибок с сообщением "Read error".
|
|
|
|
|
6. В соответствии с числом прочитанных на текущей итерации секторов
|
|
|
|
|
корректирует текущий сектор, число оставшихся секторов и указатель на
|
|
|
|
|
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
|
|
|
|
работу, иначе возвращается на шаг 3.
|
|
|
|
|
LBA-версия:
|
|
|
|
|
3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
|
|
|
|
итерации) до 7Fh.
|
|
|
|
|
4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
|
|
|
|
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
|
|
|
|
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
|
|
|
|
клали).
|
|
|
|
|
5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки
|
|
|
|
|
ошибок с сообщением "Read error". Очищает стек от пакета,
|
|
|
|
|
сформированного на предыдущем шаге.
|
|
|
|
|
6. В соответствии с числом прочитанных на текущей итерации секторов
|
|
|
|
|
корректирует текущий сектор, число оставшихся секторов и указатель на
|
|
|
|
|
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
|
|
|
|
работу, иначе возвращается на шаг 3.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура поиска элемента по имени в уже прочитанных данных папки
|
2010-10-01 11:21:55 +02:00
|
|
|
|
(scan_for_filename):
|
2013-05-28 21:09:31 +02:00
|
|
|
|
на входе должно быть установлено:
|
|
|
|
|
ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
|
|
|
|
|
3 на расширение, все буквы заглавные, если имя/расширение
|
|
|
|
|
короче, оно дополняется до максимума пробелами)
|
|
|
|
|
es = сегмент данных папки
|
|
|
|
|
cx = число элементов в прочитанных данных
|
|
|
|
|
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
|
|
|
|
|
(ZF=1, если либо найден запрошенный элемент, либо достигнут
|
|
|
|
|
конец папки); CF определяет, удалось ли найти элемент с искомым именем
|
|
|
|
|
(CF=1, если не удалось); если удалось, то es:di указывает на него.
|
|
|
|
|
scan_for_filename считает, что данные папки размещаются начиная с es:0.
|
|
|
|
|
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
|
|
|
|
|
проверяет имена.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
|
|
|
|
|
на входе должно быть установлено:
|
2010-10-01 11:21:55 +02:00
|
|
|
|
ss:bp = 0:7C00
|
2013-05-28 21:09:31 +02:00
|
|
|
|
ds:si = указатель на имя файла в формате FAT (см. выше)
|
|
|
|
|
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
|
|
|
|
CF сброшен и es:di указывает на элемент папки
|
|
|
|
|
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
|
|
|
|
|
сканирует элементы; если по результатам сканирования обнаруживает,
|
|
|
|
|
что нужно читать папку дальше, то считывает не более 0x10000 = 64K
|
|
|
|
|
байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
|
|
|
|
|
не вылезти за пределы используемой памяти, во-вторых, сканирование
|
|
|
|
|
предполагает, что все обрабатываемые элементы располагаются в одном
|
|
|
|
|
сегменте) и продолжает цикл.
|
|
|
|
|
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
|
|
|
|
|
кончились элементы в папке (судя по числу элементов, указанному в BPB);
|
|
|
|
|
очередной элемент папки сигнализирует о конце (первый байт нулевой).
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура вывода на экран ASCIIZ-строки (out_string):
|
|
|
|
|
на входе: ds:si -> строка
|
|
|
|
|
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Работа вспомогательного загрузчика kordldr.f1x:
|
|
|
|
|
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
|
|
|
|
В зависимости от этого устанавливает смещения используемых процедур
|
|
|
|
|
бутсектора. Критерий проверки: scan_for_filename должна начинаться
|
|
|
|
|
с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
|
|
|
|
|
с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
|
|
|
|
|
именно такую форму).
|
|
|
|
|
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
|
|
|
|
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
|
|
|
|
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
|
|
|
|
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
|
|
|
|
Замечание: этот размер не может превосходить 0A0000h байт и
|
|
|
|
|
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
|
|
|
|
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
|
|
|
|
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
|
|
|
|
|
спецификации от Microsoft (версия 1.03 спецификации датирована,
|
|
|
|
|
к слову, 06 декабря 2000 года), разрядность FAT определяется
|
|
|
|
|
исключительно числом кластеров: максимальное число кластеров на
|
|
|
|
|
FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
|
|
|
|
|
может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
|
|
|
|
|
а число 0xFF7 не может быть корректным номером кластера.
|
|
|
|
|
Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
|
|
|
|
|
по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
|
|
|
|
|
явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
|
|
|
|
|
FAT12-том, в результате получается, что последний кластер
|
|
|
|
|
(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
|
|
|
|
|
[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
|
|
|
|
|
[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
|
|
|
|
|
лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
|
|
|
|
|
в соответствии со спецификацией. Linux при определении FAT12/FAT16
|
|
|
|
|
честно следует спецификации.
|
|
|
|
|
Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
|
|
|
|
|
Microsoft если и будет исправлять ошибки, то согласно собственному
|
|
|
|
|
описанию.
|
|
|
|
|
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
|
|
|
|
|
Если размер, указанный в BPB, превосходит 12 секторов,
|
|
|
|
|
это означает, что заявленный размер слишком большой (это не считается
|
|
|
|
|
ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
|
|
|
|
|
заведомо влезает в такой объём данных).
|
|
|
|
|
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
|
|
|
|
|
FAT не загружен (они будут подгружаться позднее, когда понадобятся
|
|
|
|
|
и только те, которые понадобятся).
|
|
|
|
|
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
|
|
|
|
kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
|
|
|
|
|
значения регистров на входе в kordldr.f1x.
|
|
|
|
|
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
|
|
|
|
|
найден, или оказался папкой, или оказался слишком большим, то переходит
|
|
|
|
|
на код обработки ошибок из бутсектора с сообщением
|
2010-10-01 11:21:55 +02:00
|
|
|
|
"Fatal error: cannot load the secondary loader".
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
|
|
|
|
и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
|
|
|
|
|
по-прежнему нет.
|
|
|
|
|
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
|
|
|
|
|
Это нужно, чтобы последующие обращения к коду бутсектора в случае
|
|
|
|
|
ошибок чтения не выводил соответствующее сообщение с последующей
|
|
|
|
|
перезагрузкой, а рапортовал об ошибке чтения, которую мог бы
|
|
|
|
|
как-нибудь обработать вторичный загрузчик.
|
|
|
|
|
8. Если загрузочный диск имеет идентификатор меньше 0x80,
|
|
|
|
|
то устанавливает al='f' ("floppy"), ah=идентификатор диска,
|
|
|
|
|
иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
|
|
|
|
|
Устанавливает bx='12', если тип файловой системы - FAT12, и
|
|
|
|
|
bx='16' в случае FAT16. Устанавливает si=смещение функции обратного
|
|
|
|
|
вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
|
|
|
|
|
9. Передаёт управление по адресу 1000:0000.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Функция обратного вызова для вторичного загрузчика:
|
|
|
|
|
предоставляет возможность чтения файла.
|
|
|
|
|
Вход и выход описаны в спецификации на загрузчик.
|
|
|
|
|
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
|
|
|
|
ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
|
|
|
|
|
кодом должна указывать на 0:7C00, а -8 берётся от того, что
|
|
|
|
|
инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
|
|
|
|
|
и они должны сохраняться в неизменности.
|
|
|
|
|
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
|
|
|
|
|
и вызывает нужную вспомогательную процедуру.
|
|
|
|
|
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Вспомогательные процедуры kordldr.f1x.
|
|
|
|
|
Процедура получения следующего кластера в FAT (get_next_cluster):
|
|
|
|
|
1. Вспоминает разрядность FAT, вычисленную ранее.
|
|
|
|
|
Для FAT12:
|
|
|
|
|
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
|
|
|
|
|
вся таблица FAT.
|
|
|
|
|
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
|
|
|
|
|
слова, задающего следующий кластер. Загружает слово по этому адресу.
|
|
|
|
|
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
|
|
|
|
|
располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
|
|
|
|
|
на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
|
|
|
|
|
надо.
|
|
|
|
|
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
|
|
|
|
|
номера нормальных кластеров меньше, и флаг CF устанавливается;
|
|
|
|
|
специальные значения EOF и BadClus сбрасывают флаг CF.
|
|
|
|
|
Для FAT16:
|
|
|
|
|
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
|
|
|
|
|
в таблице FAT.
|
|
|
|
|
3. Если сектор ещё не загружен, то загружает его.
|
|
|
|
|
4. Вычисляет смещение данных для конкретного кластера относительно начала
|
|
|
|
|
сектора.
|
|
|
|
|
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
|
|
|
|
|
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
|
|
|
|
|
CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура загрузки файла (load_file):
|
|
|
|
|
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
|
|
|
|
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
|
|
|
|
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
|
|
|
|
(больше 8 символов в имени, больше 3 символов в расширении или
|
|
|
|
|
больше одной точки), возвращается с ошибкой.
|
|
|
|
|
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
|
|
|
|
|
папки используется процедура из бутсектора. Для остальных папок:
|
|
|
|
|
a) Проверяет, есть ли такая папка в кэше некорневых папок.
|
|
|
|
|
(Идентификация папок осуществляется по номеру начального кластера.)
|
|
|
|
|
Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
|
|
|
|
|
выкидывает папку, к которой дольше всего не было обращений. (Для
|
|
|
|
|
каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
|
|
|
|
|
определяющая его номер при сортировке по давности последнего обращения.
|
|
|
|
|
При обращении к какому-то элементу его метка становится нулевой,
|
|
|
|
|
а те метки, которые меньше старого значения, увеличиваются на единицу.)
|
|
|
|
|
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
|
|
|
|
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
|
|
|
|
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
|
|
|
|
процедуры с ошибкой.
|
|
|
|
|
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
|
|
|
|
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
|
|
|
|
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
|
|
|
|
и просматривает в нём все элементы. Работает, пока не случится одно из
|
|
|
|
|
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
|
|
|
|
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
|
|
|
|
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
|
|
|
|
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
|
|
|
|
запрошенном имени должен быть файлом, все промежуточные - папками.
|
|
|
|
|
Если текущий компонент имени - промежуточный, продвигает текущую
|
|
|
|
|
рассматриваемую папку и возвращается к пункту 2.
|
|
|
|
|
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
|
|
|
|
при вызове буфер последовательными вызовами функции бутсектора;
|
|
|
|
|
при этом если несколько кластеров файла расположены на диске
|
|
|
|
|
последовательно, то их чтение объединяется в одну операцию.
|
|
|
|
|
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
|
|
|
|
лимит числа секторов для чтения.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура продолжения загрузки файла (continue_load_file): встроена
|
|
|
|
|
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
|
|
|
|
сохранённые из load_file) и продолжает шаг 5.
|