32b4fcb9ab
git-svn-id: svn://kolibrios.org@3539 a494cfbc-eb01-0410-851d-a64ba20cac60
419 lines
38 KiB
Plaintext
419 lines
38 KiB
Plaintext
; 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.
|
||
;*****************************************************************************
|
||
|
||
Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.?
|
||
|
||
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
||
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
||
либо ISO-9660, либо UDF.)
|
||
|
||
=====================================================================
|
||
|
||
Требования для работы:
|
||
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
||
2) Минимальный процессор - 80386.
|
||
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
||
|
||
=====================================================================
|
||
|
||
Документация в тему (ссылки проверялись на валидность 14.09.2008):
|
||
стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
|
||
стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf
|
||
официальная спецификация расширения 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
|
||
|
||
=====================================================================
|
||
|
||
Схема используемой памяти:
|
||
1000-1800 временный буфер для чтения одиночных секторов
|
||
...-7C00 стек
|
||
7C00-8400 код бутсектора
|
||
8400-8A00 информация о кэше для папок: массив входов следующего
|
||
формата:
|
||
dw следующий элемент в L2-списке закэшированных папок,
|
||
упорядоченном по времени использования
|
||
(голова списка - самый старый);
|
||
dw предыдущий элемент в том же списке;
|
||
dd первый сектор папки;
|
||
dw размер папки в байтах;
|
||
dw сегмент кэша
|
||
60000-... содержимое Path Table, если она используется
|
||
+ кэш для папок;
|
||
точный размер определяется размером доступной
|
||
физической памяти - как правило, непосредственно
|
||
перед A0000 размещается EBDA, Extended BIOS Data Area
|
||
|
||
=====================================================================
|
||
|
||
Основной процесс загрузки.
|
||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||
dl содержит идентификатор диска, с которого идёт загрузка
|
||
1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip
|
||
равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает
|
||
дальний прыжок на самого себя с целью получить cs=0 (в некоторых
|
||
местах используется адресация переменных загрузчика через cs, поскольку
|
||
и ds, и es могут быть заняты под другие сегменты).
|
||
2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом)
|
||
и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления
|
||
и разрешённые прерывания. Сохраняет идентификатор загрузочного диска
|
||
в специальную переменную.
|
||
3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять
|
||
LBA-функции.
|
||
4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту
|
||
ISO9660 со смещения 10h начинается цепочка описателей тома,
|
||
завершающаяся специальным описателем (Volume Descriptor Set
|
||
Terminator). Код по очереди считывает все сектора, пока не наткнётся
|
||
либо на искомый описатель, либо на терминатор. Во втором случае
|
||
выдаётся соответствующее сообщение, и загрузка прекращается.
|
||
Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD
|
||
располагается в последней сессии. И спецификация ElTorito загрузочного
|
||
CD оперирует также с последней сессией. Однако на практике оказывается,
|
||
что: во-первых, реальные BIOSы не понимают мультисессионных CD и
|
||
всегда используют первую сессию; во-вторых, BIOSовский int 13h просто
|
||
не позволяет получить информацию о последней сессии. В связи с этим
|
||
загрузчик также использует первую сессию. (В-третьих, в одной из BIOS
|
||
обнаружилась заготовка, которая в случае запроса сектора 10h, в котором
|
||
во всех нормальных случаях и располагается PVD, перенаправляет его
|
||
на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с
|
||
последней сессии, то благодаря заготовке загрузчик без всяких
|
||
модификаций также читал бы последнюю сессию.)
|
||
5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во
|
||
внутренние переменные: размер логического блока (согласно спецификации,
|
||
должен быть степенью двойки от 512 до размера логического сектора,
|
||
равного 2048 для CD и DVD); положение на диске корневой папки;
|
||
вычисляет число блоков в секторе (из предыдущего примечания следует,
|
||
что оно всегда целое и само является степенью двойки).
|
||
6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет
|
||
размер пространства, которое может использовать загрузчик (от
|
||
адреса 6000:0000 до конца доступной памяти).
|
||
7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит
|
||
базовую информацию обо всех папках на диске. Если таблица слишком
|
||
велика (больше 62K или больше половины доступной памяти), то она
|
||
игнорируется. Если таблица путей недоступна, то запрос типа
|
||
dir1/dir2/dir3/file приведёт к последовательному разбору корневой
|
||
папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать
|
||
саму таблицу путей (где записано положение папки dir1/dir2/dir3)
|
||
и папку dir3. Если таблица загружена, то соответственно уменьшается
|
||
объём оставшейся доступной памяти и увеличивается указатель на
|
||
свободную область.
|
||
8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7
|
||
доступная память отводится под этот кэш).
|
||
9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке
|
||
печатает соответствующее сообщение и прекращает загрузку с CD.
|
||
10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует
|
||
тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is'
|
||
идентифицирует файловую систему ISO-9660; ds:si указывает на
|
||
callback-функцию, которую может вызывать вторичный загрузчик.
|
||
11. Передаёт управление вторичному загрузчику, совершая дальний прыжок
|
||
на адрес, куда kord/loader был загружен.
|
||
|
||
Функция обратного вызова для вторичного загрузчика (callback):
|
||
предоставляет возможность чтения файла.
|
||
Вход и выход описаны в спецификации на загрузчик.
|
||
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
||
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
||
запросах на продолжение загрузки файла).
|
||
|
||
Вспомогательные процедуры.
|
||
Код обработки ошибок (err):
|
||
1. Выводит строку с сообщением об ошибке.
|
||
2. Выводит строку "Press any key...".
|
||
3. Ждёт нажатия any key.
|
||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||
5. Для подстраховки зацикливается.
|
||
|
||
Процедура чтения секторов (read_sectors):
|
||
на входе должно быть установлено:
|
||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||
eax = стартовый сектор
|
||
cx = число секторов
|
||
на выходе:
|
||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||
если произошла ошибка чтения, флаг CF установлен
|
||
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
||
число читаемых секторов не превосходило 7Fh (требование спецификации
|
||
EDD BIOS).
|
||
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||
итерации) до 7Fh.
|
||
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||
клали).
|
||
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
||
устанавливает CF=1 и выходит из процедуры.
|
||
Очищает стек от пакета, сформированного на предыдущем шаге.
|
||
5. В соответствии с числом прочитанных на текущей итерации секторов
|
||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||
работу, иначе возвращается на шаг 2.
|
||
|
||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||
на входе: ds:si -> строка
|
||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||
|
||
Процедура загрузки файла (load_file):
|
||
на входе:
|
||
ds:di = указатель на информационную структуру, описанную в спецификации
|
||
на загрузчик, а также в комментариях к коду
|
||
на выходе:
|
||
bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть,
|
||
2=файл не найден, 3=ошибка чтения
|
||
dx:ax = размер файла, 0xFFFFFFFF, если файл не найден
|
||
1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице,
|
||
иначе переходит сразу к шагу 4, установив eax = начальный блок
|
||
корневой папки.
|
||
2. Устанавливает es:di на начало таблицы путей. Ограничение на размер
|
||
гарантирует, что вся таблица помещается в сегменте 6000h.
|
||
Инициализирует dx (в котором будет хранится номер текущего входа в
|
||
таблице, считая с 1), cx (размер оставшегося участка таблицы),
|
||
bx (номер входа, соответствующего родительской папке для текущего
|
||
рассматриваемого участка пути).
|
||
3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы
|
||
таблицы путей упорядочены (подробно о порядке написано в спецификации),
|
||
так что если родительский элемент для очередного входа больше нужного,
|
||
то нужного входа в таблице нет совсем, и в этом случае происходит
|
||
выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент,
|
||
соответствующий очередной папке в запрошенном пути, то на рассмотрение
|
||
выносится следующая компонента пути. Если эта компонента последняя,
|
||
то осталось найти файл в папке, и код переходит к пункту 4,
|
||
установив eax = начальный блок этой папки. Если же нет, то эта
|
||
компонента должна задавать имя папки, и код возвращается к пункту 3,
|
||
скорректировав указатель на имя ds:si и номер родительского входа bx.
|
||
4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax
|
||
и указатель на имя файла относительно этой папки в ds:si. Если
|
||
папку искали по таблице путей, то имя файла уже не содержит подпапок;
|
||
если же нет, то подпапки вполне возможны.
|
||
5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый
|
||
из которых задаётся отдельным входом в папке. Информация обо всех
|
||
таких кусках при просмотре папки запоминается в области, начинающейся
|
||
с адреса 0000:2000. Переменная cur_desc_end содержит указатель на
|
||
конец этой области, он же указатель, куда будет помещена информация
|
||
при обнаружении следующего входа. (Папки, согласно спецификации,
|
||
должны задаваться одним куском.)
|
||
6. Код сначала ищет запрошенную папку в кэше папок.
|
||
7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка,
|
||
отсортированного по давности последнего обращения и код переходит к
|
||
п.15. (Следующим действием станет добавление папки в конец списка.)
|
||
8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать
|
||
с диска. Сначала загружается первый сектор (физический сектор,
|
||
содержащий первый логический блок). При ошибке ввода/вывода
|
||
происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF.
|
||
Первый элемент папки содержит информацию о самой этой папке, конкретно
|
||
загрузчик интересуется её размером.
|
||
9. Если размер папки слишком большой (больше или равен 64K либо больше половины
|
||
общего размера кэша), то кэшироваться она не будет. В этом случае код
|
||
считывает папку посекторно во временный буфер (0000:1000) и посекторно
|
||
сканирует на наличие запрошенного имени, пока не найдёт такого имени
|
||
или пока не кончатся данные. (Цикл начинается со сканирования,
|
||
поскольку первая часть данных уже прочитана.) В конце код переходит
|
||
к п.17.
|
||
10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно
|
||
обеспечить достаточное количество свободного места. Для этого может
|
||
понадобиться выкинуть какое-то количество старых данных (цикл
|
||
parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря,
|
||
свободное пространство окажется разорванным на несколько фрагментов.
|
||
Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает
|
||
все следующие за ней данные назад по памяти и соответственно
|
||
корректирует информацию о местонахождении данных в информации о кэше.
|
||
При этом новое пространство всегда добавляется в конец доступной
|
||
памяти. Цикл выкидывания продолжается, пока не освободится место,
|
||
достаточное для хранения папки. Из-за ограничений на размер кэшируемых
|
||
папок в конце концов место найдётся.
|
||
11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы
|
||
организуются в единый список свободных элементов; если он непуст,
|
||
то очередной элемент берётся из этого списка; если же пуст, то
|
||
берётся совсем новый элемент из области памяти, предназначенной для
|
||
элементов кэша.
|
||
12. В новом элементе заполняются поля начального блока, сегмента с данными,
|
||
размера в байтах.
|
||
13. Уже прочитанные данные первого физического сектора пересылаются на
|
||
законное место в кэше.
|
||
14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся
|
||
данные с диска. При ошибке чтения, как и раньше, происходит выход из
|
||
процедуры с bx=3, ax=dx=0xFFFF.
|
||
15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов
|
||
кэша.
|
||
16. Загрузчик ищет запрошенное имя в загруженных данных папки.
|
||
(Из-за ограничений на размер кэшируемой папки все данные располагаются
|
||
в одном сегменте.)
|
||
17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено
|
||
никаких кусков файла, то cur_desc_end такой же, каким был вначале.
|
||
В этом случае процедура рапортует о ненайденном файле и выходит.
|
||
18. (filefound) Пропускает текущую компоненту имени. Если она была не последней
|
||
(то есть подпапкой, в которой нужно производить дальнейший поиск),
|
||
то код проверяет, что найденный вход - действительно подпапка,
|
||
устанавливает новый стартовый блок и возвращается к п.4.
|
||
Если же последней, то код проверяет, что найденный вход - регулярный
|
||
файл и начинает загрузку файла.
|
||
19. Нормализует указатель, по которому требуется прочитать файл. Под
|
||
нормализацией понимается преобразование типа
|
||
1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса,
|
||
но гарантирует отсутствие переполнений: в приведённом примере попытка
|
||
переслать 400h байт по rep movsb приведёт к тому, что последние 8
|
||
байт запишутся не в нужное место, а на 64K раньше. Далее нормализация
|
||
будет производиться после каждой пересылки. В cur_limit помещает
|
||
предельный размер для чтения в байтах.
|
||
20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты
|
||
(пункты 21-27).
|
||
21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое
|
||
нужно пропустить с начала фрагмента.
|
||
22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего
|
||
шага, либо напрямую из callback-процедуры при запросе на продолжение
|
||
чтения. Для этого и нужна вышеупомянутая переменная [cur_start] -
|
||
при продолжении чтения, прервавшегося из-за конца буфера посередине
|
||
фрагмента, там будет записано соответствующее значение.
|
||
23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента
|
||
и максимальной длины остатка. Если второе строго меньше, то
|
||
запоминает, что файл слишком большой и прочитан только частично.
|
||
Определяет новое значение числа прочитанных байт во фрагменте
|
||
для возможных будущих вызовов [cur_start].
|
||
24. Переводит пропускаемое число байт в число логических блоков и байт
|
||
в первом блоке, последнее число записывает в переменную [first_byte],
|
||
откуда её позднее достанет read_many_bytes.with_first.
|
||
25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код
|
||
определяет начальный блок фрагмента и вызывает вспомогательную функцию
|
||
чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения)
|
||
и выходит из цикла к п.28.
|
||
26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала
|
||
код пропускает нужное количество непрерывных частей, а потом
|
||
в цикле загружает непрерывные части с помощью той же функции,
|
||
в промежутках между частями увеличивая номер начального блока.
|
||
Пока не кончится фрагмент или пока не наберётся запрошенное число байт.
|
||
При ошибке чтения делает то же самое, что и в предыдущем случае.
|
||
27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер
|
||
ещё не достигнут, переходит к следующему фрагменту и п.20. В противном
|
||
случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли
|
||
переполнение в п.23.
|
||
28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех
|
||
фрагментов.
|
||
|
||
Процедура проверки, является ли текущая компонента имени файла последней
|
||
(is_last_component):
|
||
на входе: ds:si = указатель на имя
|
||
на выходе: флаг CF установлен, если есть последующие компоненты
|
||
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
||
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
||
и выходит.
|
||
|
||
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
||
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
||
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
||
таблицы путей для test_filename1, папки для test_filename2
|
||
на выходе: CF установлен, если имена не совпадают
|
||
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
||
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
||
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
||
возможно только в ситуации типа имени "filename.ext" и элемента
|
||
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
||
именами в папке отсортированы по убыванию версий);
|
||
несовпадение символов - означает, что имена не совпадают;
|
||
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
||
файла, и в зависимости от этого принимать решение о совпадении.
|
||
|
||
Процедура приведения символа в верхний регистр (toupper):
|
||
на входе: ASCII-символ
|
||
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
||
нему неприменимо)
|
||
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
||
остальные символы не трогает.
|
||
|
||
Процедура поиска файла в данных папки (scan_for_filename_in_sector):
|
||
на входе:
|
||
ds:si = указатель на имя файла
|
||
es:bx = указатель на начало данных папки
|
||
es:dx = указатель на конец данных папки
|
||
на выходе:
|
||
CF сброшен, если найден финальный фрагмент файла
|
||
(и дальше сканировать папку не нужно)
|
||
в область для информации о фрагментах файла записывается найденное
|
||
В цикле просматривает все входы папки, пропуская те, у которых установлен
|
||
бит Associated (это специальные входы, дополняющие основные). Если
|
||
имя очередного входа совпадает с именем файла, то запоминает новый
|
||
фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent),
|
||
то код выходит с CF=0. Если достигнут конец данных, то код выходит
|
||
с CF=1. Если очередной вход нулевой (первый байт настоящего входа
|
||
содержит длину и не может быть нулём), то процедура переходит к
|
||
рассмотрению следующего логического блока. При этом потенциально
|
||
возможно переполнение при добавлении размера блока; поскольку такой
|
||
сценарий означает, что процедура вызвана для кэшированной папки
|
||
с размером почти 64K и началом данных bx=0 (это свойство вызывающего
|
||
кода), а размер блока - степень двойки, то после переполнения всегда
|
||
bx=0, так что это можно обнаружить по взведённому ZF после сложения;
|
||
в этом случае также происходит выход (а после переполнения CF=1).
|
||
|
||
Процедура перевода логического блока в номер сектора:
|
||
на входе: eax = логический блок
|
||
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
||
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
||
блоков в секторе, хранящееся во внутренней переменной).
|
||
|
||
Процедура загрузки физического сектора, содержащего указанный логический блок
|
||
(load_phys_sector_for_lb_force):
|
||
на входе: eax = логический блок;
|
||
si - индикатор, задающий, следует ли читать данные в случае,
|
||
если логический блок начинается с начала физического:
|
||
si = 0 - не нужно, si ненулевой - нужно
|
||
на выходе:
|
||
физический сектор загружен по адресу 0000:1000
|
||
si указывает на данные логического блока
|
||
CF установлен при ошибке чтения
|
||
Преобразует предыдущей процедурой номер логического блока в номер физического
|
||
сектора и номер логического блока внутри сектора; если последняя
|
||
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
||
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
||
и читает сектор.
|
||
|
||
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
||
(read_many_bytes и read_many_bytes.with_first):
|
||
на входе:
|
||
eax = логический блок
|
||
esi = число байт для чтения
|
||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||
cur_limit = размер буфера (не меньше esi)
|
||
на выходе:
|
||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||
если произошла ошибка чтения, флаг CF установлен
|
||
cur_limit соответствующим образом уменьшен
|
||
Отличие двух процедур: вторая дополнительно принимает во внимание переменную
|
||
[first_byte], начиная чтение первого блока со смещения [first_byte];
|
||
соответственно, первая читает блок с начала, обнуляя [first_byte]
|
||
при входе.
|
||
1. Отдельно считывает первый физический сектор во временную область 0000:1000,
|
||
если первый логический блок начинается не с начала сектора. При
|
||
ошибке чтения выходит из процедуры.
|
||
2. Пересылает нужную часть данных (возможно, 0 байт), прочитанных в п.1,
|
||
в буфер. Нормализует указатель на буфер.
|
||
3. Если все необходимые данные уже прочитаны, выходит из процедуры.
|
||
4. Дальнейшие данные находятся в нескольких физических секторах, при этом,
|
||
возможно, последний сектор считывать нужно не целиком.
|
||
5. Если в буфере есть место для считывания всех секторов, то сразу читаются
|
||
все сектора, после чего указатель на буфер нужным образом уменьшается.
|
||
6. Если же нет, то считываются все сектора, кроме последнего, после чего
|
||
последний сектор считывается отдельно во временную область, и уже
|
||
оттуда нужная часть данных копируется в буфер.
|