Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2465 | Serge | 1 | ; Copyright (c) 2008-2009, diamond |
2 | ; All rights reserved. |
||
3 | ; |
||
4 | ; Redistribution and use in source and binary forms, with or without |
||
5 | ; modification, are permitted provided that the following conditions are met: |
||
6 | ; * Redistributions of source code must retain the above copyright |
||
7 | ; notice, this list of conditions and the following disclaimer. |
||
8 | ; * Redistributions in binary form must reproduce the above copyright |
||
9 | ; notice, this list of conditions and the following disclaimer in the |
||
10 | ; documentation and/or other materials provided with the distribution. |
||
11 | ; * Neither the name of the |
||
12 | ; names of its contributors may be used to endorse or promote products |
||
13 | ; derived from this software without specific prior written permission. |
||
14 | ; |
||
15 | ; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka |
||
16 | ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
17 | ; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
18 | ; DISCLAIMED. IN NO EVENT SHALL |
||
19 | ; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
20 | ; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
21 | ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
22 | ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
23 | ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
24 | ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
25 | ;***************************************************************************** |
||
26 | |||
27 | Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.? |
||
28 | |||
29 | Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660. |
||
30 | (ISO-9660 и её расширения - стандарт для CD; DVD может использовать |
||
31 | либо ISO-9660, либо UDF.) |
||
32 | |||
33 | ===================================================================== |
||
34 | |||
35 | Требования для работы: |
||
36 | 1) Сам бутсектор и все используемые файлы должны быть читабельны. |
||
37 | 2) Минимальный процессор - 80386. |
||
38 | 3) В системе должно быть как минимум 452K свободной базовой памяти. |
||
39 | |||
40 | ===================================================================== |
||
41 | |||
42 | Документация в тему (ссылки проверялись на валидность 14.09.2008): |
||
43 | стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf |
||
44 | стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf |
||
45 | официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf |
||
46 | то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf |
||
47 | описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html |
||
48 | официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf |
||
49 | |||
50 | ===================================================================== |
||
51 | |||
52 | Схема используемой памяти: |
||
53 | 1000-1800 временный буфер для чтения одиночных секторов |
||
54 | ...-7C00 стек |
||
55 | 7C00-8400 код бутсектора |
||
56 | 8400-8A00 информация о кэше для папок: массив входов следующего |
||
57 | формата: |
||
58 | dw следующий элемент в L2-списке закэшированных папок, |
||
59 | упорядоченном по времени использования |
||
60 | (голова списка - самый старый); |
||
61 | dw предыдущий элемент в том же списке; |
||
62 | dd первый сектор папки; |
||
63 | dw размер папки в байтах; |
||
64 | dw сегмент кэша |
||
65 | 60000-... содержимое Path Table, если она используется |
||
66 | + кэш для папок; |
||
67 | точный размер определяется размером доступной |
||
68 | физической памяти - как правило, непосредственно |
||
69 | перед A0000 размещается EBDA, Extended BIOS Data Area |
||
70 | |||
71 | ===================================================================== |
||
72 | |||
73 | Основной процесс загрузки. |
||
74 | Точка входа (start): получает управление от BIOS при загрузке, при этом |
||
75 | dl содержит идентификатор диска, с которого идёт загрузка |
||
76 | 1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip |
||
77 | равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает |
||
78 | дальний прыжок на самого себя с целью получить cs=0 (в некоторых |
||
79 | местах используется адресация переменных загрузчика через cs, поскольку |
||
80 | и ds, и es могут быть заняты под другие сегменты). |
||
81 | 2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом) |
||
82 | и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления |
||
83 | и разрешённые прерывания. Сохраняет идентификатор загрузочного диска |
||
84 | в специальную переменную. |
||
85 | 3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять |
||
86 | LBA-функции. |
||
87 | 4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту |
||
88 | ISO9660 со смещения 10h начинается цепочка описателей тома, |
||
89 | завершающаяся специальным описателем (Volume Descriptor Set |
||
90 | Terminator). Код по очереди считывает все сектора, пока не наткнётся |
||
91 | либо на искомый описатель, либо на терминатор. Во втором случае |
||
92 | выдаётся соответствующее сообщение, и загрузка прекращается. |
||
93 | Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD |
||
94 | располагается в последней сессии. И спецификация ElTorito загрузочного |
||
95 | CD оперирует также с последней сессией. Однако на практике оказывается, |
||
96 | что: во-первых, реальные BIOSы не понимают мультисессионных CD и |
||
97 | всегда используют первую сессию; во-вторых, BIOSовский int 13h просто |
||
98 | не позволяет получить информацию о последней сессии. В связи с этим |
||
99 | загрузчик также использует первую сессию. (В-третьих, в одной из BIOS |
||
100 | обнаружилась заготовка, которая в случае запроса сектора 10h, в котором |
||
101 | во всех нормальных случаях и располагается PVD, перенаправляет его |
||
102 | на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с |
||
103 | последней сессии, то благодаря заготовке загрузчик без всяких |
||
104 | модификаций также читал бы последнюю сессию.) |
||
105 | 5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во |
||
106 | внутренние переменные: размер логического блока (согласно спецификации, |
||
107 | должен быть степенью двойки от 512 до размера логического сектора, |
||
108 | равного 2048 для CD и DVD); положение на диске корневой папки; |
||
109 | вычисляет число блоков в секторе (из предыдущего примечания следует, |
||
110 | что оно всегда целое и само является степенью двойки). |
||
111 | 6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет |
||
112 | размер пространства, которое может использовать загрузчик (от |
||
113 | адреса 6000:0000 до конца доступной памяти). |
||
114 | 7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит |
||
115 | базовую информацию обо всех папках на диске. Если таблица слишком |
||
116 | велика (больше 62K или больше половины доступной памяти), то она |
||
117 | игнорируется. Если таблица путей недоступна, то запрос типа |
||
118 | dir1/dir2/dir3/file приведёт к последовательному разбору корневой |
||
119 | папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать |
||
120 | саму таблицу путей (где записано положение папки dir1/dir2/dir3) |
||
121 | и папку dir3. Если таблица загружена, то соответственно уменьшается |
||
122 | объём оставшейся доступной памяти и увеличивается указатель на |
||
123 | свободную область. |
||
124 | 8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7 |
||
125 | доступная память отводится под этот кэш). |
||
126 | 9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке |
||
127 | печатает соответствующее сообщение и прекращает загрузку с CD. |
||
128 | 10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует |
||
129 | тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is' |
||
130 | идентифицирует файловую систему ISO-9660; ds:si указывает на |
||
131 | callback-функцию, которую может вызывать вторичный загрузчик. |
||
132 | 11. Передаёт управление вторичному загрузчику, совершая дальний прыжок |
||
133 | на адрес, куда kord/loader был загружен. |
||
134 | |||
135 | Функция обратного вызова для вторичного загрузчика (callback): |
||
136 | предоставляет возможность чтения файла. |
||
137 | Вход и выход описаны в спецификации на загрузчик. |
||
138 | Перенаправляет запрос соответствующей локальной процедуре (load_file при |
||
139 | первом запросе на загрузку файла, loadloop.loadnew при последующих |
||
140 | запросах на продолжение загрузки файла). |
||
141 | |||
142 | Вспомогательные процедуры. |
||
143 | Код обработки ошибок (err): |
||
144 | 1. Выводит строку с сообщением об ошибке. |
||
145 | 2. Выводит строку "Press any key...". |
||
146 | 3. Ждёт нажатия any key. |
||
147 | 4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё. |
||
148 | 5. Для подстраховки зацикливается. |
||
149 | |||
150 | Процедура чтения секторов (read_sectors): |
||
151 | на входе должно быть установлено: |
||
152 | es:bx = указатель на начало буфера, куда будут прочитаны данные |
||
153 | eax = стартовый сектор |
||
154 | cx = число секторов |
||
155 | на выходе: |
||
156 | es:bx указывает на конец буфера, в который были прочитаны данные |
||
157 | если произошла ошибка чтения, флаг CF установлен |
||
158 | 1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации |
||
159 | число читаемых секторов не превосходило 7Fh (требование спецификации |
||
160 | EDD BIOS). |
||
161 | 2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей |
||
162 | итерации) до 7Fh. |
||
163 | 3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами |
||
164 | push, причём в обратном порядке: стек - структура LIFO, и данные в |
||
165 | стеке хранятся в обратном порядке по отношению к тому, как их туда |
||
166 | клали). |
||
167 | 4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек, |
||
168 | устанавливает CF=1 и выходит из процедуры. |
||
169 | Очищает стек от пакета, сформированного на предыдущем шаге. |
||
170 | 5. В соответствии с числом прочитанных на текущей итерации секторов |
||
171 | корректирует текущий сектор, число оставшихся секторов и указатель на |
||
172 | буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает |
||
173 | работу, иначе возвращается на шаг 2. |
||
174 | |||
175 | Процедура вывода на экран ASCIIZ-строки (out_string): |
||
176 | на входе: ds:si -> строка |
||
177 | В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh. |
||
178 | |||
179 | Процедура загрузки файла (load_file): |
||
180 | на входе: |
||
181 | ds:di = указатель на информационную структуру, описанную в спецификации |
||
182 | на загрузчик, а также в комментариях к коду |
||
183 | на выходе: |
||
184 | bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть, |
||
185 | 2=файл не найден, 3=ошибка чтения |
||
186 | dx:ax = размер файла, 0xFFFFFFFF, если файл не найден |
||
187 | 1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице, |
||
188 | иначе переходит сразу к шагу 4, установив eax = начальный блок |
||
189 | корневой папки. |
||
190 | 2. Устанавливает es:di на начало таблицы путей. Ограничение на размер |
||
191 | гарантирует, что вся таблица помещается в сегменте 6000h. |
||
192 | Инициализирует dx (в котором будет хранится номер текущего входа в |
||
193 | таблице, считая с 1), cx (размер оставшегося участка таблицы), |
||
194 | bx (номер входа, соответствующего родительской папке для текущего |
||
195 | рассматриваемого участка пути). |
||
196 | 3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы |
||
197 | таблицы путей упорядочены (подробно о порядке написано в спецификации), |
||
198 | так что если родительский элемент для очередного входа больше нужного, |
||
199 | то нужного входа в таблице нет совсем, и в этом случае происходит |
||
200 | выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент, |
||
201 | соответствующий очередной папке в запрошенном пути, то на рассмотрение |
||
202 | выносится следующая компонента пути. Если эта компонента последняя, |
||
203 | то осталось найти файл в папке, и код переходит к пункту 4, |
||
204 | установив eax = начальный блок этой папки. Если же нет, то эта |
||
205 | компонента должна задавать имя папки, и код возвращается к пункту 3, |
||
206 | скорректировав указатель на имя ds:si и номер родительского входа bx. |
||
207 | 4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax |
||
208 | и указатель на имя файла относительно этой папки в ds:si. Если |
||
209 | папку искали по таблице путей, то имя файла уже не содержит подпапок; |
||
210 | если же нет, то подпапки вполне возможны. |
||
211 | 5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый |
||
212 | из которых задаётся отдельным входом в папке. Информация обо всех |
||
213 | таких кусках при просмотре папки запоминается в области, начинающейся |
||
214 | с адреса 0000:2000. Переменная cur_desc_end содержит указатель на |
||
215 | конец этой области, он же указатель, куда будет помещена информация |
||
216 | при обнаружении следующего входа. (Папки, согласно спецификации, |
||
217 | должны задаваться одним куском.) |
||
218 | 6. Код сначала ищет запрошенную папку в кэше папок. |
||
219 | 7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка, |
||
220 | отсортированного по давности последнего обращения и код переходит к |
||
221 | п.15. (Следующим действием станет добавление папки в конец списка.) |
||
222 | 8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать |
||
223 | с диска. Сначала загружается первый сектор (физический сектор, |
||
224 | содержащий первый логический блок). При ошибке ввода/вывода |
||
225 | происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF. |
||
226 | Первый элемент папки содержит информацию о самой этой папке, конкретно |
||
227 | загрузчик интересуется её размером. |
||
228 | 9. Если размер папки слишком большой (больше или равен 64K либо больше половины |
||
229 | общего размера кэша), то кэшироваться она не будет. В этом случае код |
||
230 | считывает папку посекторно во временный буфер (0000:1000) и посекторно |
||
231 | сканирует на наличие запрошенного имени, пока не найдёт такого имени |
||
232 | или пока не кончатся данные. (Цикл начинается со сканирования, |
||
233 | поскольку первая часть данных уже прочитана.) В конце код переходит |
||
234 | к п.17. |
||
235 | 10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно |
||
236 | обеспечить достаточное количество свободного места. Для этого может |
||
237 | понадобиться выкинуть какое-то количество старых данных (цикл |
||
238 | parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря, |
||
239 | свободное пространство окажется разорванным на несколько фрагментов. |
||
240 | Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает |
||
241 | все следующие за ней данные назад по памяти и соответственно |
||
242 | корректирует информацию о местонахождении данных в информации о кэше. |
||
243 | При этом новое пространство всегда добавляется в конец доступной |
||
244 | памяти. Цикл выкидывания продолжается, пока не освободится место, |
||
245 | достаточное для хранения папки. Из-за ограничений на размер кэшируемых |
||
246 | папок в конце концов место найдётся. |
||
247 | 11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы |
||
248 | организуются в единый список свободных элементов; если он непуст, |
||
249 | то очередной элемент берётся из этого списка; если же пуст, то |
||
250 | берётся совсем новый элемент из области памяти, предназначенной для |
||
251 | элементов кэша. |
||
252 | 12. В новом элементе заполняются поля начального блока, сегмента с данными, |
||
253 | размера в байтах. |
||
254 | 13. Уже прочитанные данные первого физического сектора пересылаются на |
||
255 | законное место в кэше. |
||
256 | 14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся |
||
257 | данные с диска. При ошибке чтения, как и раньше, происходит выход из |
||
258 | процедуры с bx=3, ax=dx=0xFFFF. |
||
259 | 15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов |
||
260 | кэша. |
||
261 | 16. Загрузчик ищет запрошенное имя в загруженных данных папки. |
||
262 | (Из-за ограничений на размер кэшируемой папки все данные располагаются |
||
263 | в одном сегменте.) |
||
264 | 17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено |
||
265 | никаких кусков файла, то cur_desc_end такой же, каким был вначале. |
||
266 | В этом случае процедура рапортует о ненайденном файле и выходит. |
||
267 | 18. (filefound) Пропускает текущую компоненту имени. Если она была не последней |
||
268 | (то есть подпапкой, в которой нужно производить дальнейший поиск), |
||
269 | то код проверяет, что найденный вход - действительно подпапка, |
||
270 | устанавливает новый стартовый блок и возвращается к п.4. |
||
271 | Если же последней, то код проверяет, что найденный вход - регулярный |
||
272 | файл и начинает загрузку файла. |
||
273 | 19. Нормализует указатель, по которому требуется прочитать файл. Под |
||
274 | нормализацией понимается преобразование типа |
||
275 | 1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса, |
||
276 | но гарантирует отсутствие переполнений: в приведённом примере попытка |
||
277 | переслать 400h байт по rep movsb приведёт к тому, что последние 8 |
||
278 | байт запишутся не в нужное место, а на 64K раньше. Далее нормализация |
||
279 | будет производиться после каждой пересылки. В cur_limit помещает |
||
280 | предельный размер для чтения в байтах. |
||
281 | 20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты |
||
282 | (пункты 21-27). |
||
283 | 21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое |
||
284 | нужно пропустить с начала фрагмента. |
||
285 | 22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего |
||
286 | шага, либо напрямую из callback-процедуры при запросе на продолжение |
||
287 | чтения. Для этого и нужна вышеупомянутая переменная [cur_start] - |
||
288 | при продолжении чтения, прервавшегося из-за конца буфера посередине |
||
289 | фрагмента, там будет записано соответствующее значение. |
||
290 | 23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента |
||
291 | и максимальной длины остатка. Если второе строго меньше, то |
||
292 | запоминает, что файл слишком большой и прочитан только частично. |
||
293 | Определяет новое значение числа прочитанных байт во фрагменте |
||
294 | для возможных будущих вызовов [cur_start]. |
||
295 | 24. Переводит пропускаемое число байт в число логических блоков и байт |
||
296 | в первом блоке, последнее число записывает в переменную [first_byte], |
||
297 | откуда её позднее достанет read_many_bytes.with_first. |
||
298 | 25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код |
||
299 | определяет начальный блок фрагмента и вызывает вспомогательную функцию |
||
300 | чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения) |
||
301 | и выходит из цикла к п.28. |
||
302 | 26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала |
||
303 | код пропускает нужное количество непрерывных частей, а потом |
||
304 | в цикле загружает непрерывные части с помощью той же функции, |
||
305 | в промежутках между частями увеличивая номер начального блока. |
||
306 | Пока не кончится фрагмент или пока не наберётся запрошенное число байт. |
||
307 | При ошибке чтения делает то же самое, что и в предыдущем случае. |
||
308 | 27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер |
||
309 | ещё не достигнут, переходит к следующему фрагменту и п.20. В противном |
||
310 | случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли |
||
311 | переполнение в п.23. |
||
312 | 28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех |
||
313 | фрагментов. |
||
314 | |||
315 | Процедура проверки, является ли текущая компонента имени файла последней |
||
316 | (is_last_component): |
||
317 | на входе: ds:si = указатель на имя |
||
318 | на выходе: флаг CF установлен, если есть последующие компоненты |
||
319 | В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый, |
||
320 | то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF |
||
321 | и выходит. |
||
322 | |||
323 | Процедуры проверки на совпадение текущей компоненты имени файла с именем |
||
324 | текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки): |
||
325 | на входе: ds:si = указатель на имя, es:di = указатель на элемент |
||
326 | таблицы путей для test_filename1, папки для test_filename2 |
||
327 | на выходе: CF установлен, если имена не совпадают |
||
328 | В цикле проверяет совпадение приведённых к верхнему регистру очередных символов |
||
329 | имён файла и элемента. Условия выхода из цикла: закончилось имя файла |
||
330 | в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение |
||
331 | возможно только в ситуации типа имени "filename.ext" и элемента |
||
332 | "filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми |
||
333 | именами в папке отсортированы по убыванию версий); |
||
334 | несовпадение символов - означает, что имена не совпадают; |
||
335 | закончилось имя элемента - нужно проверить, закончилось ли при этом имя |
||
336 | файла, и в зависимости от этого принимать решение о совпадении. |
||
337 | |||
338 | Процедура приведения символа в верхний регистр (toupper): |
||
339 | на входе: ASCII-символ |
||
340 | на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к |
||
341 | нему неприменимо) |
||
342 | Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A', |
||
343 | остальные символы не трогает. |
||
344 | |||
345 | Процедура поиска файла в данных папки (scan_for_filename_in_sector): |
||
346 | на входе: |
||
347 | ds:si = указатель на имя файла |
||
348 | es:bx = указатель на начало данных папки |
||
349 | es:dx = указатель на конец данных папки |
||
350 | на выходе: |
||
351 | CF сброшен, если найден финальный фрагмент файла |
||
352 | (и дальше сканировать папку не нужно) |
||
353 | в область для информации о фрагментах файла записывается найденное |
||
354 | В цикле просматривает все входы папки, пропуская те, у которых установлен |
||
355 | бит Associated (это специальные входы, дополняющие основные). Если |
||
356 | имя очередного входа совпадает с именем файла, то запоминает новый |
||
357 | фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent), |
||
358 | то код выходит с CF=0. Если достигнут конец данных, то код выходит |
||
359 | с CF=1. Если очередной вход нулевой (первый байт настоящего входа |
||
360 | содержит длину и не может быть нулём), то процедура переходит к |
||
361 | рассмотрению следующего логического блока. При этом потенциально |
||
362 | возможно переполнение при добавлении размера блока; поскольку такой |
||
363 | сценарий означает, что процедура вызвана для кэшированной папки |
||
364 | с размером почти 64K и началом данных bx=0 (это свойство вызывающего |
||
365 | кода), а размер блока - степень двойки, то после переполнения всегда |
||
366 | bx=0, так что это можно обнаружить по взведённому ZF после сложения; |
||
367 | в этом случае также происходит выход (а после переполнения CF=1). |
||
368 | |||
369 | Процедура перевода логического блока в номер сектора: |
||
370 | на входе: eax = логический блок |
||
371 | на выходе: eax = физический сектор, dx = номер логического блока в секторе |
||
372 | Осуществляет обычное деление 32-битного числа на 32-битное (число логических |
||
373 | блоков в секторе, хранящееся во внутренней переменной). |
||
374 | |||
375 | Процедура загрузки физического сектора, содержащего указанный логический блок |
||
376 | (load_phys_sector_for_lb_force): |
||
377 | на входе: eax = логический блок; |
||
378 | si - индикатор, задающий, следует ли читать данные в случае, |
||
379 | если логический блок начинается с начала физического: |
||
380 | si = 0 - не нужно, si ненулевой - нужно |
||
381 | на выходе: |
||
382 | физический сектор загружен по адресу 0000:1000 |
||
383 | si указывает на данные логического блока |
||
384 | CF установлен при ошибке чтения |
||
385 | Преобразует предыдущей процедурой номер логического блока в номер физического |
||
386 | сектора и номер логического блока внутри сектора; если последняя |
||
387 | величина нулевая и никаких действий в этом случае не запрошено (si=0), |
||
388 | то ничего и не делает; иначе устанавливает si в соответствии с ней |
||
389 | и читает сектор. |
||
390 | |||
391 | Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков |
||
392 | (read_many_bytes и read_many_bytes.with_first): |
||
393 | на входе: |
||
394 | eax = логический блок |
||
395 | esi = число байт для чтения |
||
396 | es:bx = указатель на начало буфера, куда будут прочитаны данные |
||
397 | cur_limit = размер буфера (не меньше esi) |
||
398 | на выходе: |
||
399 | es:bx указывает на конец буфера, в который были прочитаны данные |
||
400 | если произошла ошибка чтения, флаг CF установлен |
||
401 | cur_limit соответствующим образом уменьшен |
||
402 | Отличие двух процедур: вторая дополнительно принимает во внимание переменную |
||
403 | [first_byte], начиная чтение первого блока со смещения [first_byte]; |
||
404 | соответственно, первая читает блок с начала, обнуляя [first_byte] |
||
405 | при входе. |
||
406 | 1. Отдельно считывает первый физический сектор во временную область 0000:1000, |
||
407 | если первый логический блок начинается не с начала сектора. При |
||
408 | ошибке чтения выходит из процедуры. |
||
409 | 2. Пересылает нужную часть данных (возможно, 0 байт), прочитанных в п.1, |
||
410 | в буфер. Нормализует указатель на буфер. |
||
411 | 3. Если все необходимые данные уже прочитаны, выходит из процедуры. |
||
412 | 4. Дальнейшие данные находятся в нескольких физических секторах, при этом, |
||
413 | возможно, последний сектор считывать нужно не целиком. |
||
414 | 5. Если в буфере есть место для считывания всех секторов, то сразу читаются |
||
415 | все сектора, после чего указатель на буфер нужным образом уменьшается. |
||
416 | 6. Если же нет, то считываются все сектора, кроме последнего, после чего |
||
417 | последний сектор считывается отдельно во временную область, и уже |
||
418 | оттуда нужная часть данных копируется в буфер. |