Subversion Repositories Kolibri OS

Rev

Rev 2465 | Go to most recent revision | Details | Compare with Previous | 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  nor 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  ''AS IS'' AND ANY
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  BE LIABLE FOR ANY
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
 
3555 Serge 29
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
30
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
31
либо ISO-9660, либо UDF.)
2465 Serge 32
 
33
=====================================================================
34
 
3555 Serge 35
Требования для работы:
36
1) Сам бутсектор и все используемые файлы должны быть читабельны.
37
2) Минимальный процессор - 80386.
38
3) В системе должно быть как минимум 452K свободной базовой памяти.
2465 Serge 39
 
40
=====================================================================
41
 
3555 Serge 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
2465 Serge 49
 
50
=====================================================================
51
 
3555 Serge 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
2465 Serge 70
 
71
=====================================================================
72
 
3555 Serge 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 был загружен.
2465 Serge 134
 
3555 Serge 135
Функция обратного вызова для вторичного загрузчика (callback):
136
	предоставляет возможность чтения файла.
137
Вход и выход описаны в спецификации на загрузчик.
138
Перенаправляет запрос соответствующей локальной процедуре (load_file при
139
	первом запросе на загрузку файла, loadloop.loadnew при последующих
140
	запросах на продолжение загрузки файла).
2465 Serge 141
 
3555 Serge 142
Вспомогательные процедуры.
143
Код обработки ошибок (err):
144
1. Выводит строку с сообщением об ошибке.
145
2. Выводит строку "Press any key...".
146
3. Ждёт нажатия any key.
147
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
148
5. Для подстраховки зацикливается.
2465 Serge 149
 
3555 Serge 150
Процедура чтения секторов (read_sectors):
151
на входе должно быть установлено:
152
	es:bx = указатель на начало буфера, куда будут прочитаны данные
153
	eax = стартовый сектор
154
	cx = число секторов
155
на выходе:
156
	es:bx указывает на конец буфера, в который были прочитаны данные
157
	если произошла ошибка чтения, флаг CF установлен
158
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
159
	число читаемых секторов не превосходило 7Fh (требование	спецификации
2465 Serge 160
	EDD BIOS).
3555 Serge 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.
2465 Serge 174
 
3555 Serge 175
Процедура вывода на экран ASCIIZ-строки (out_string):
176
на входе: ds:si -> строка
177
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
2465 Serge 178
 
3555 Serge 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
	фрагментов.
2465 Serge 314
 
3555 Serge 315
Процедура проверки, является ли текущая компонента имени файла последней
2465 Serge 316
	(is_last_component):
3555 Serge 317
на входе: ds:si = указатель на имя
318
на выходе: флаг CF установлен, если есть последующие компоненты
319
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
320
	то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
321
	и выходит.
2465 Serge 322
 
3555 Serge 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
	файла, и в зависимости от этого принимать решение о совпадении.
2465 Serge 337
 
3555 Serge 338
Процедура приведения символа в верхний регистр (toupper):
339
на входе: ASCII-символ
340
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
341
	нему неприменимо)
342
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
343
	остальные символы не трогает.
2465 Serge 344
 
3555 Serge 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).
2465 Serge 368
 
3555 Serge 369
Процедура перевода логического блока в номер сектора:
370
на входе: eax = логический блок
371
на выходе: eax = физический сектор, dx = номер логического блока в секторе
372
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
373
	блоков в секторе, хранящееся во внутренней переменной).
2465 Serge 374
 
3555 Serge 375
Процедура загрузки физического сектора, содержащего указанный логический блок
2465 Serge 376
	(load_phys_sector_for_lb_force):
3555 Serge 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
	и читает сектор.
2465 Serge 390
 
3555 Serge 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
	оттуда нужная часть данных копируется в буфер.