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.
|
|
|
|
|
;*****************************************************************************
|
|
|
|
|
|
|
|
|
|
Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.?
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
|
|
|
|
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
|
|
|
|
либо ISO-9660, либо UDF.)
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Требования для работы:
|
|
|
|
|
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
|
|
|
|
2) Минимальный процессор - 80386.
|
|
|
|
|
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Документация в тему (ссылки проверялись на валидность 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
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
|
|
|
|
=====================================================================
|
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Схема используемой памяти:
|
|
|
|
|
1000-1800 временный буфер для чтения одиночных секторов
|
|
|
|
|
...-7C00 стек
|
|
|
|
|
7C00-8400 код бутсектора
|
|
|
|
|
8400-8A00 информация о кэше для папок: массив входов следующего
|
|
|
|
|
формата:
|
|
|
|
|
dw следующий элемент в L2-списке закэшированных папок,
|
|
|
|
|
упорядоченном по времени использования
|
|
|
|
|
(голова списка - самый старый);
|
|
|
|
|
dw предыдущий элемент в том же списке;
|
|
|
|
|
dd первый сектор папки;
|
|
|
|
|
dw размер папки в байтах;
|
|
|
|
|
dw сегмент кэша
|
|
|
|
|
60000-... содержимое Path Table, если она используется
|
|
|
|
|
+ кэш для папок;
|
|
|
|
|
точный размер определяется размером доступной
|
|
|
|
|
физической памяти - как правило, непосредственно
|
|
|
|
|
перед 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. При передаче управления загрузочному коду в случае 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 был загружен.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Функция обратного вызова для вторичного загрузчика (callback):
|
|
|
|
|
предоставляет возможность чтения файла.
|
|
|
|
|
Вход и выход описаны в спецификации на загрузчик.
|
|
|
|
|
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
|
|
|
|
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
|
|
|
|
запросах на продолжение загрузки файла).
|
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):
|
|
|
|
|
на входе должно быть установлено:
|
|
|
|
|
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
|
|
|
|
eax = стартовый сектор
|
|
|
|
|
cx = число секторов
|
|
|
|
|
на выходе:
|
|
|
|
|
es:bx указывает на конец буфера, в который были прочитаны данные
|
|
|
|
|
если произошла ошибка чтения, флаг CF установлен
|
|
|
|
|
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
|
|
|
|
число читаемых секторов не превосходило 7Fh (требование спецификации
|
2010-10-01 11:21:55 +02:00
|
|
|
|
EDD BIOS).
|
2013-05-28 21:09:31 +02:00
|
|
|
|
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
|
|
|
|
итерации) до 7Fh.
|
|
|
|
|
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
|
|
|
|
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
|
|
|
|
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
|
|
|
|
клали).
|
|
|
|
|
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
|
|
|
|
устанавливает CF=1 и выходит из процедуры.
|
|
|
|
|
Очищает стек от пакета, сформированного на предыдущем шаге.
|
|
|
|
|
5. В соответствии с числом прочитанных на текущей итерации секторов
|
|
|
|
|
корректирует текущий сектор, число оставшихся секторов и указатель на
|
|
|
|
|
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
|
|
|
|
работу, иначе возвращается на шаг 2.
|
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
|
|
|
|
Процедура загрузки файла (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) Подсчитывает общую длину файла, суммируя длины всех
|
|
|
|
|
фрагментов.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура проверки, является ли текущая компонента имени файла последней
|
2010-10-01 11:21:55 +02:00
|
|
|
|
(is_last_component):
|
2013-05-28 21:09:31 +02:00
|
|
|
|
на входе: ds:si = указатель на имя
|
|
|
|
|
на выходе: флаг CF установлен, если есть последующие компоненты
|
|
|
|
|
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
|
|
|
|
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
|
|
|
|
и выходит.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
|
|
|
|
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
|
|
|
|
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
|
|
|
|
таблицы путей для test_filename1, папки для test_filename2
|
|
|
|
|
на выходе: CF установлен, если имена не совпадают
|
|
|
|
|
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
|
|
|
|
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
|
|
|
|
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
|
|
|
|
возможно только в ситуации типа имени "filename.ext" и элемента
|
|
|
|
|
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
|
|
|
|
именами в папке отсортированы по убыванию версий);
|
|
|
|
|
несовпадение символов - означает, что имена не совпадают;
|
|
|
|
|
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
|
|
|
|
файла, и в зависимости от этого принимать решение о совпадении.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура приведения символа в верхний регистр (toupper):
|
|
|
|
|
на входе: ASCII-символ
|
|
|
|
|
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
|
|
|
|
нему неприменимо)
|
|
|
|
|
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
|
|
|
|
остальные символы не трогает.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура поиска файла в данных папки (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).
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура перевода логического блока в номер сектора:
|
|
|
|
|
на входе: eax = логический блок
|
|
|
|
|
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
|
|
|
|
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
|
|
|
|
блоков в секторе, хранящееся во внутренней переменной).
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедура загрузки физического сектора, содержащего указанный логический блок
|
2010-10-01 11:21:55 +02:00
|
|
|
|
(load_phys_sector_for_lb_force):
|
2013-05-28 21:09:31 +02:00
|
|
|
|
на входе: eax = логический блок;
|
|
|
|
|
si - индикатор, задающий, следует ли читать данные в случае,
|
|
|
|
|
если логический блок начинается с начала физического:
|
|
|
|
|
si = 0 - не нужно, si ненулевой - нужно
|
|
|
|
|
на выходе:
|
|
|
|
|
физический сектор загружен по адресу 0000:1000
|
|
|
|
|
si указывает на данные логического блока
|
|
|
|
|
CF установлен при ошибке чтения
|
|
|
|
|
Преобразует предыдущей процедурой номер логического блока в номер физического
|
|
|
|
|
сектора и номер логического блока внутри сектора; если последняя
|
|
|
|
|
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
|
|
|
|
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
|
|
|
|
и читает сектор.
|
2010-10-01 11:21:55 +02:00
|
|
|
|
|
2013-05-28 21:09:31 +02:00
|
|
|
|
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
|
|
|
|
(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. Если же нет, то считываются все сектора, кроме последнего, после чего
|
|
|
|
|
последний сектор считывается отдельно во временную область, и уже
|
|
|
|
|
оттуда нужная часть данных копируется в буфер.
|