forked from KolibriOS/kolibrios
334 lines
30 KiB
Plaintext
334 lines
30 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.
|
|||
|
;*****************************************************************************
|
|||
|
|
|||
|
Читай между строк - там никогда не бывает опечаток.
|
|||
|
|
|||
|
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
|
|||
|
|
|||
|
=====================================================================
|
|||
|
|
|||
|
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
|||
|
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
|||
|
Требования для работы:
|
|||
|
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
|||
|
должны быть читабельны. (Если дело происходит на носителе с разбиением на
|
|||
|
разделы и загрузочный код в MBR достаточно умный, то читабельности резервной
|
|||
|
копии бутсектора (сектор номер 6 на томе) достаточно вместо читабельности
|
|||
|
самого бутсектора).
|
|||
|
2) Минимальный процессор - 80386.
|
|||
|
3) В системе должно быть как минимум 584K свободной базовой памяти.
|
|||
|
|
|||
|
=====================================================================
|
|||
|
|
|||
|
Документация в тему (ссылки проверялись на валидность 15.05.2008):
|
|||
|
официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
|
|||
|
в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf
|
|||
|
русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip
|
|||
|
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
|||
|
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
|||
|
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
|||
|
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
|||
|
|
|||
|
=====================================================================
|
|||
|
|
|||
|
Схема используемой памяти:
|
|||
|
...-7C00 стек
|
|||
|
7C00-7E00 код бутсектора
|
|||
|
7E00-8200 вспомогательный файл загрузчика (kordldr.f32)
|
|||
|
8400-8C00 информация о кэше для таблицы FAT: 100h входов по 8
|
|||
|
байт: 4 байта (две ссылки - вперёд и назад) для
|
|||
|
организации L2-списка всех прочитанных секторов в
|
|||
|
порядке возрастания последнего времени использования
|
|||
|
+ 4 байта для номера сектора; при переполнении кэша
|
|||
|
выкидывается элемент из головы списка, то есть тот,
|
|||
|
к которому дольше всех не было обращений
|
|||
|
60000-80000 кэш для таблицы FAT (100h секторов)
|
|||
|
80000-90000 текущий кластер текущей рассматриваемой папки
|
|||
|
90000-... кэш для содержимого папок (каждой папке отводится
|
|||
|
2000h байт = 100h входов, одновременно в кэше
|
|||
|
может находиться не более 8 папок;
|
|||
|
точный размер определяется размером доступной
|
|||
|
физической памяти - как правило, непосредственно
|
|||
|
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
|||
|
|
|||
|
=====================================================================
|
|||
|
|
|||
|
Основной процесс загрузки.
|
|||
|
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
|||
|
dl содержит идентификатор диска, с которого идёт загрузка
|
|||
|
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
|||
|
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
|||
|
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
|||
|
это освобождает ds и экономит на размере кода). Сохраняет в стеке
|
|||
|
идентификатор загрузочного диска для последующего обращения
|
|||
|
через byte [bp-2].
|
|||
|
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
|||
|
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
|||
|
сообщением об отсутствии LBA.
|
|||
|
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
|||
|
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
|||
|
предполагает уже существующие данные корректными.
|
|||
|
3. Вычисляет начало данных FAT-тома, сохраняет его в стек для последующего
|
|||
|
обращения через dword [bp-10]. В процессе вычисления узнаёт начало
|
|||
|
первой FAT, сохраняет и его в стек для последующего обращения через
|
|||
|
dword [bp-6].
|
|||
|
4. (Заканчивая тему параметров в стеке) Помещает в стек dword-значение -1
|
|||
|
для последующего обращения через dword [bp-14] - инициализация
|
|||
|
переменной, содержащей текущий сектор, находящийся в кэше FAT
|
|||
|
(-1 не является валидным значением для номера сектора FAT).
|
|||
|
5. Ищет в корневой папке элемент kordldr.f32. Если не находит - переходит на
|
|||
|
код обработки ошибок с сообщением о ненайденном загрузчике.
|
|||
|
Замечание: на этом этапе загрузки искать можно только в корневой
|
|||
|
папке и только имена, заданные в формате файловой системе FAT
|
|||
|
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
|||
|
быть заглавными, при необходимости имя и расширение дополняются
|
|||
|
пробелами, разделяющей точки нет, завершающего нуля нет).
|
|||
|
6. Загружает первый кластер файла kordldr.f32 по адресу 0:7E00 и передаёт
|
|||
|
ему управление. При этом в регистре eax оказывается абсолютный
|
|||
|
номер первого сектора kordldr.f32, а в cx - число считанных секторов
|
|||
|
(равное размеру кластера).
|
|||
|
|
|||
|
Вспомогательные процедуры бутсектора.
|
|||
|
Код обработки ошибок (err):
|
|||
|
1. Выводит строку с сообщением об ошибке.
|
|||
|
2. Выводит строку "Press any key...".
|
|||
|
3. Ждёт нажатия any key.
|
|||
|
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
|||
|
5. Для подстраховки зацикливается.
|
|||
|
|
|||
|
Процедура чтения кластера (read_cluster):
|
|||
|
на входе должно быть установлено:
|
|||
|
ss:bp = 0:7C00
|
|||
|
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
|||
|
eax = номер кластера
|
|||
|
на выходе: ecx = число прочитанных секторов (размер кластера),
|
|||
|
es:bx указывает на конец буфера, в который были прочитаны данные,
|
|||
|
eax и старшие слова других 32-битных регистров разрушаются
|
|||
|
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
|
|||
|
и переходит к следующей процедуре.
|
|||
|
|
|||
|
Процедура чтения секторов (read_sectors32 и read_sectors2):
|
|||
|
на входе должно быть установлено:
|
|||
|
ss:bp = 0:7C00
|
|||
|
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
|||
|
eax = стартовый сектор (относительно начала логического диска
|
|||
|
для read_sectors32, относительно начала данных
|
|||
|
для read_sectors2)
|
|||
|
cx = число секторов (должно быть больше нуля)
|
|||
|
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
|||
|
старшие слова 32-битных регистров могут разрушиться
|
|||
|
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
|||
|
в номер относительно начала логического диска, прибавляя номер сектора
|
|||
|
начала данных, хранящийся в стеке как [bp-10].
|
|||
|
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
|||
|
устройстве, прибавляя значение соответствующего поля из BPB.
|
|||
|
2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации
|
|||
|
CHS-версия: все читаемые секторы были на одной дорожке.
|
|||
|
LBA-версия: число читаемых секторов не превосходило 7Fh (требование
|
|||
|
спецификации EDD BIOS).
|
|||
|
CHS-версия:
|
|||
|
3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как
|
|||
|
единица плюс остаток от деления абсолютного номера на число секторов
|
|||
|
на дорожке; дорожка рассчитывается как остаток от деления частного,
|
|||
|
полученного на предыдущем шаге, на число дорожек, а цилиндр - как
|
|||
|
частное от этого же деления. Если число секторов для чтения больше,
|
|||
|
чем число секторов до конца дорожки, уменьшает число секторов для
|
|||
|
чтения.
|
|||
|
4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов,
|
|||
|
dh=головка, (младшие 6 бит cl)=сектор,
|
|||
|
(старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер).
|
|||
|
5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска
|
|||
|
и повторяет попытку чтения, всего делается не более трёх попыток
|
|||
|
(несколько попыток нужно в случае дискеты для гарантии того, что
|
|||
|
мотор раскрутился). Если все три раза происходит ошибка чтения,
|
|||
|
переходит на код обработки ошибок с сообщением "Read error".
|
|||
|
6. В соответствии с числом прочитанных на текущей итерации секторов
|
|||
|
корректирует текущий сектор, число оставшихся секторов и указатель на
|
|||
|
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
|||
|
работу, иначе возвращается на шаг 3.
|
|||
|
LBA-версия:
|
|||
|
3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
|||
|
итерации) до 7Fh.
|
|||
|
4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
|||
|
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
|||
|
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
|||
|
клали).
|
|||
|
5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки
|
|||
|
ошибок с сообщением "Read error". Очищает стек от пакета,
|
|||
|
сформированного на предыдущем шаге.
|
|||
|
6. В соответствии с числом прочитанных на текущей итерации секторов
|
|||
|
корректирует текущий сектор, число оставшихся секторов и указатель на
|
|||
|
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
|||
|
работу, иначе возвращается на шаг 3.
|
|||
|
|
|||
|
Процедура поиска элемента в папке (lookup_in_dir):
|
|||
|
на входе должно быть установлено:
|
|||
|
ss:bp = 0:7C00
|
|||
|
ds:si = указатель на имя файла в формате FAT (см. выше)
|
|||
|
eax = начальный кластер папки
|
|||
|
bx = 0
|
|||
|
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
|||
|
CF сброшен и es:di указывает на элемент папки
|
|||
|
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
|
|||
|
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
|
|||
|
для продвижения по цепочке кластеров - описанную далее процедуру
|
|||
|
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
|
|||
|
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
|
|||
|
если чтение прервётся раньше) не перекрываются последующими чтениями
|
|||
|
(это будет использовано позднее, в системе кэширования из kordldr.f32).
|
|||
|
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
|
|||
|
кончились элементы в папке (первый байт очередного элемента нулевой);
|
|||
|
кончились данные папки в соответствии с цепочкой кластеров из FAT.
|
|||
|
|
|||
|
Процедура вывода на экран ASCIIZ-строки (out_string):
|
|||
|
на входе: ds:si -> строка
|
|||
|
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
|||
|
|
|||
|
=====================================================================
|
|||
|
|
|||
|
Работа вспомогательного загрузчика kordldr.f32:
|
|||
|
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
|||
|
В зависимости от этого устанавливает смещения используемых процедур
|
|||
|
бутсектора. Критерий проверки: в CHS-версии по адресу err находится
|
|||
|
байт 0xE8 (машинная команда call), в LBA-версии по тому же адресу
|
|||
|
находится байт 0x14, а адрес процедуры err другой.
|
|||
|
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
|||
|
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
|||
|
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
|||
|
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
|||
|
Замечание: этот размер не может превосходить 0A0000h байт и
|
|||
|
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
|||
|
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
|||
|
3. Инициализирует кэширование папок. Бутсектор уже загрузил какую-то часть
|
|||
|
данных корневой папки; копирует загруженные данные в кэш и запоминает,
|
|||
|
что в кэше есть корневая папка.
|
|||
|
4. Инициализирует кэширование FAT. Бутсектор имеет дело с FAT в том и только
|
|||
|
том случае, когда ему приходится загружать данные корневой папки,
|
|||
|
не поместившиеся в один кластер. В этом случае в памяти присутствует
|
|||
|
один сектор FAT (если было несколько обращений - последний из
|
|||
|
использованных).
|
|||
|
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
|||
|
kordldr.f32, и загрузчик подгружает вторую свою часть, используя
|
|||
|
значения регистров на входе в kordldr.f32.
|
|||
|
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
|
|||
|
найден, или оказался папкой, или оказался слишком большим, то переходит
|
|||
|
на код обработки ошибок из бутсектора с сообщением
|
|||
|
"Fatal error: cannot load the secondary loader".
|
|||
|
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
|||
|
и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
|
|||
|
по-прежнему нет.
|
|||
|
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
|
|||
|
Это нужно, чтобы последующие обращения к коду бутсектора в случае
|
|||
|
ошибок чтения не выводил соответствующее сообщение с последующей
|
|||
|
перезагрузкой, а рапортовал об ошибке чтения, которую могло бы
|
|||
|
как-нибудь обработать ядро.
|
|||
|
8. Если загрузочный диск имеет идентификатор меньше 0x80,
|
|||
|
то устанавливает al='f' ("floppy"), ah=идентификатор диска,
|
|||
|
иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
|
|||
|
(Говорите, дискеток с FAT32 не бывает? В чём-то Вы правы... но
|
|||
|
уверены ли Вы, что нет загрузочных устройств, подобных дискетам,
|
|||
|
но большего размера, и для которых BIOS-идентификатор меньше 0x80?)
|
|||
|
Устанавливает bx='32' (тип файловой системы - FAT32).
|
|||
|
Устанавливает si=смещение функции обратного вызова. Поскольку в этот
|
|||
|
момент ds=0, то ds:si образуют полный адрес.
|
|||
|
9. Передаёт управление по адресу 1000:0000.
|
|||
|
|
|||
|
Функция обратного вызова для вторичного загрузчика:
|
|||
|
предоставляет возможность чтения файла.
|
|||
|
Вход и выход описаны в спецификации на загрузчик.
|
|||
|
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
|||
|
ss:sp = 0:(7C00-10), bp=7C00: пара ss:bp при работе с остальным
|
|||
|
кодом должна указывать на 0:7C00, а -10 берётся от того, что
|
|||
|
инициализирующий код бутсектора уже поместил в стек 10 байт параметров,
|
|||
|
и они должны сохраняться в неизменности. (Значение [ebp-14],
|
|||
|
"текущий сектор, находящийся в кэше FAT", не используется после
|
|||
|
инициализации кэширования в kordldr.f32.)
|
|||
|
2. Разбирает переданные параметры и вызывает нужную из вспомогательных
|
|||
|
процедур (загрузки файла либо продолжения загрузки файла).
|
|||
|
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
|||
|
|
|||
|
Вспомогательные процедуры kordldr.f32.
|
|||
|
Процедура получения следующего кластера в FAT (get_next_cluster):
|
|||
|
1. Вычисляет номер сектора в FAT, в котором находится запрошенный элемент.
|
|||
|
(В секторе 0x200 байт, каждый вход занимает 4 байта.)
|
|||
|
2. Проверяет, есть ли сектор в кэше. Если есть, пропускает шаги 3 и 4.
|
|||
|
3. Если нет, то в кэш нужно вставить новый элемент. Если кэш ещё не заполнен,
|
|||
|
выделяет очередной элемент в конце кэша. Если заполнен, удаляет
|
|||
|
самый старый элемент (тот, к которому дольше всего не было обращений);
|
|||
|
для того, чтобы отслеживать порядок элементов по времени последнего
|
|||
|
обращения, все (выделенные) элементы кэша связаны в двусвязный список,
|
|||
|
в котором первым элементом является самый старый, а ссылки вперёд
|
|||
|
указывают на следующий по времени последнего обращения.
|
|||
|
4. Читает соответствующий сектор FAT с диска.
|
|||
|
5. Корректирует список: текущий обрабатываемый элемент удаляется с той позиции,
|
|||
|
где он находится, и добавляется в конец. (В случае со свежедобавленными
|
|||
|
в кэш элементами удаления не делается, поскольку их в списке ещё нет.)
|
|||
|
6. Считывает нужный вход в FAT, сбрасывая старшие 4 бита.
|
|||
|
7. Сравнивает прочитанное значение с пределом: если оно строго меньше
|
|||
|
0x0FFFFFF7, то оно задаёт номер следующего кластера в цепочке;
|
|||
|
в противном случае цепочка закончилась.
|
|||
|
|
|||
|
Процедура загрузки файла (load_file):
|
|||
|
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
|||
|
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
|||
|
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
|||
|
(больше 8 символов в имени, больше 3 символов в расширении или
|
|||
|
больше одной точки), возвращается с ошибкой.
|
|||
|
3. Ищет элемент с таким именем в текущей рассматриваемой папке.
|
|||
|
а) Проверяет, есть ли такая папка в кэше папок. (Идентификация папок
|
|||
|
осуществляется по номеру начального кластера.) Если такой папки ещё
|
|||
|
нет, добавляет её в кэш; если тот переполняется, выкидывает папку,
|
|||
|
к которой дольше всего не было обращений. (Для каждого элемента кэша
|
|||
|
хранится метка от 0 до (размер кэша)-1, определяющая его номер при
|
|||
|
сортировке по давности последнего обращения. При обращении к какому-то
|
|||
|
элементу его метка становится нулевой, а те метки, которые меньше
|
|||
|
старого значения, увеличиваются на единицу.)
|
|||
|
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
|||
|
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
|||
|
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
|||
|
процедуры с ошибкой.
|
|||
|
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
|||
|
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
|||
|
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
|||
|
и просматривает в нём все элементы. Работает, пока не случится одно из
|
|||
|
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
|||
|
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
|||
|
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
|||
|
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
|||
|
запрошенном имени должен быть файлом, все промежуточные - папками.
|
|||
|
Если текущий компонент имени - промежуточный, продвигает текущую
|
|||
|
рассматриваемую папку и возвращается к пункту 2.
|
|||
|
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
|||
|
при вызове буфер последовательными вызовами функции бутсектора;
|
|||
|
при этом если несколько кластеров файла расположены на диске
|
|||
|
последовательно, то их чтение объединяется в одну операцию.
|
|||
|
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
|||
|
лимит числа секторов для чтения.
|
|||
|
|
|||
|
Процедура продолжения загрузки файла (continue_load_file): встроена
|
|||
|
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
|||
|
сохранённые из load_file) и продолжает шаг 5.
|