forked from KolibriOS/kolibrios
recode all kernel sources to UTF-8; binary still uses single-byte encoding and isn't changed at all
git-svn-id: svn://kolibrios.org@3539 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
0a61fc62ad
commit
32b4fcb9ab
@ -9,21 +9,21 @@ $Revision$
|
||||
|
||||
|
||||
;**********************************************************
|
||||
; Непосредственная работа с устройством СD (ATAPI)
|
||||
; Непосредственная работа с устройством СD (ATAPI)
|
||||
;**********************************************************
|
||||
; Автор части исходного текста Кулаков Владимир Геннадьевич
|
||||
; Адаптация, доработка и разработка Mario79,<Lrz>
|
||||
; Автор части исходного текста Кулаков Владимир Геннадьевич
|
||||
; Адаптация, доработка и разработка Mario79,<Lrz>
|
||||
|
||||
; Максимальное количество повторений операции чтения
|
||||
; Максимальное количество повторений операции чтения
|
||||
MaxRetr equ 10
|
||||
; Предельное время ожидания готовности к приему команды
|
||||
; (в тиках)
|
||||
; Предельное время ожидания готовности к приему команды
|
||||
; (в тиках)
|
||||
BSYWaitTime equ 1000 ;2
|
||||
NoTickWaitTime equ 0xfffff
|
||||
CDBlockSize equ 2048
|
||||
;********************************************
|
||||
;* ЧТЕНИЕ СЕКТОРА С ПОВТОРАМИ *
|
||||
;* Многократное повторение чтения при сбоях *
|
||||
;* ЧТЕНИЕ СЕКТОРА С ПОВТОРАМИ *
|
||||
;* Многократное повторение чтения при сбоях *
|
||||
;********************************************
|
||||
ReadCDWRetr:
|
||||
;-----------------------------------------------------------
|
||||
@ -85,34 +85,34 @@ ReadCDWRetr:
|
||||
ReadCDWRetr_1:
|
||||
pushad
|
||||
|
||||
; Цикл, пока команда не выполнена успешно или не
|
||||
; исчерпано количество попыток
|
||||
; Цикл, пока команда не выполнена успешно или не
|
||||
; исчерпано количество попыток
|
||||
mov ECX, MaxRetr
|
||||
@@NextRetr:
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
;*************************************************
|
||||
;* ПОЛНОЕ ЧТЕНИЕ СЕКТОРА КОМПАКТ-ДИСКА *
|
||||
;* Считываются данные пользователя, информация *
|
||||
;* субканала и контрольная информация *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* CDSectorAddress - адрес считываемого сектора. *
|
||||
;* Данные считывается в массив CDDataBuf. *
|
||||
;* ПОЛНОЕ ЧТЕНИЕ СЕКТОРА КОМПАКТ-ДИСКА *
|
||||
;* Считываются данные пользователя, информация *
|
||||
;* субканала и контрольная информация *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* CDSectorAddress - адрес считываемого сектора. *
|
||||
;* Данные считывается в массив CDDataBuf. *
|
||||
;*************************************************
|
||||
;ReadCD:
|
||||
push ecx
|
||||
; pusha
|
||||
; Задать размер сектора
|
||||
; Задать размер сектора
|
||||
; mov [CDBlockSize],2048 ;2352
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Сформировать пакетную команду для считывания
|
||||
; сектора данных
|
||||
; Задать код команды Read CD
|
||||
; Сформировать пакетную команду для считывания
|
||||
; сектора данных
|
||||
; Задать код команды Read CD
|
||||
mov [PacketCommand], byte 0x28;0xBE
|
||||
; Задать адрес сектора
|
||||
; Задать адрес сектора
|
||||
mov AX, word [CDSectorAddress+2]
|
||||
xchg AL, AH
|
||||
mov word [PacketCommand+2], AX
|
||||
@ -121,11 +121,11 @@ ReadCDWRetr_1:
|
||||
mov word [PacketCommand+4], AX
|
||||
; mov eax,[CDSectorAddress]
|
||||
; mov [PacketCommand+2],eax
|
||||
; Задать количество считываемых секторов
|
||||
; Задать количество считываемых секторов
|
||||
mov [PacketCommand+8], byte 1
|
||||
; Задать считывание данных в полном объеме
|
||||
; Задать считывание данных в полном объеме
|
||||
; mov [PacketCommand+9],byte 0xF8
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketDatCommand
|
||||
pop ecx
|
||||
; ret
|
||||
@ -147,7 +147,7 @@ ReadCDWRetr_1:
|
||||
jz @@NextRetr
|
||||
jmp .wait
|
||||
@@:
|
||||
; Задержка на 2,5 секунды
|
||||
; Задержка на 2,5 секунды
|
||||
; mov EAX,[timer_ticks]
|
||||
; add EAX,50 ;250
|
||||
;@@Wait:
|
||||
@ -161,66 +161,66 @@ ReadCDWRetr_1:
|
||||
ret
|
||||
|
||||
|
||||
; Универсальные процедуры, обеспечивающие выполнение
|
||||
; пакетных команд в режиме PIO
|
||||
; Универсальные процедуры, обеспечивающие выполнение
|
||||
; пакетных команд в режиме PIO
|
||||
|
||||
; Максимально допустимое время ожидания реакции
|
||||
; устройства на пакетную команду (в тиках)
|
||||
; Максимально допустимое время ожидания реакции
|
||||
; устройства на пакетную команду (в тиках)
|
||||
|
||||
MaxCDWaitTime equ 1000 ;200 ;10 секунд
|
||||
MaxCDWaitTime equ 1000 ;200 ;10 секунд
|
||||
uglobal
|
||||
; Область памяти для формирования пакетной команды
|
||||
; Область памяти для формирования пакетной команды
|
||||
PacketCommand:
|
||||
rb 12 ;DB 12 DUP (?)
|
||||
; Область памяти для приема данных от дисковода
|
||||
; Область памяти для приема данных от дисковода
|
||||
;CDDataBuf DB 4096 DUP (0)
|
||||
; Размер принимаемого блока данных в байтах
|
||||
; Размер принимаемого блока данных в байтах
|
||||
;CDBlockSize DW ?
|
||||
; Адрес считываемого сектора данных
|
||||
; Адрес считываемого сектора данных
|
||||
CDSectorAddress:
|
||||
DD ?
|
||||
; Время начала очередной операции с диском
|
||||
; Время начала очередной операции с диском
|
||||
TickCounter_1 DD 0
|
||||
; Время начала ожидания готовности устройства
|
||||
; Время начала ожидания готовности устройства
|
||||
WURStartTime DD 0
|
||||
; указатель буфера для считывания
|
||||
; указатель буфера для считывания
|
||||
CDDataBuf_pointer dd 0
|
||||
endg
|
||||
;****************************************************
|
||||
;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, *
|
||||
;* ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧУ ОДНОГО СЕКТОРА ДАННЫХ *
|
||||
;* РАЗМЕРОМ 2048 БАЙТ ОТ УСТРОЙСТВА К ХОСТУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* PacketCommand - 12-байтный командный пакет; *
|
||||
;* CDBlockSize - размер принимаемого блока данных. *
|
||||
;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, *
|
||||
;* ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧУ ОДНОГО СЕКТОРА ДАННЫХ *
|
||||
;* РАЗМЕРОМ 2048 БАЙТ ОТ УСТРОЙСТВА К ХОСТУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* PacketCommand - 12-байтный командный пакет; *
|
||||
;* CDBlockSize - размер принимаемого блока данных. *
|
||||
; return eax DevErrorCode
|
||||
;****************************************************
|
||||
SendPacketDatCommand:
|
||||
xor eax, eax
|
||||
; mov byte [DevErrorCode],al
|
||||
; Задать режим CHS
|
||||
; Задать режим CHS
|
||||
mov byte [ATAAddressMode], al
|
||||
; Послать ATA-команду передачи пакетной команды
|
||||
; Послать ATA-команду передачи пакетной команды
|
||||
mov byte [ATAFeatures], al
|
||||
mov byte [ATASectorCount], al
|
||||
mov byte [ATASectorNumber], al
|
||||
; Загрузить размер передаваемого блока
|
||||
; Загрузить размер передаваемого блока
|
||||
mov [ATAHead], al
|
||||
; mov AX,[CDBlockSize]
|
||||
mov [ATACylinder], CDBlockSize
|
||||
mov [ATACommand], 0A0h
|
||||
call SendCommandToHDD_1
|
||||
test eax, eax
|
||||
; cmp [DevErrorCode],0 ;проверить код ошибки
|
||||
jnz @@End_8 ;закончить, сохранив код ошибки
|
||||
; cmp [DevErrorCode],0 ;проверить код ошибки
|
||||
jnz @@End_8 ;закончить, сохранив код ошибки
|
||||
|
||||
; Ожидание готовности дисковода к приему
|
||||
; пакетной команды
|
||||
; Ожидание готовности дисковода к приему
|
||||
; пакетной команды
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;порт 1х7h
|
||||
add DX, 7 ;порт 1х7h
|
||||
mov ecx, NoTickWaitTime
|
||||
@@WaitDevice0:
|
||||
cmp [timer_ticks_enable], 0
|
||||
@ -231,21 +231,21 @@ SendPacketDatCommand:
|
||||
jmp .test
|
||||
@@:
|
||||
call change_task
|
||||
; Проверить время выполнения команды
|
||||
; Проверить время выполнения команды
|
||||
mov EAX, [timer_ticks]
|
||||
sub EAX, [TickCounter_1]
|
||||
cmp EAX, BSYWaitTime
|
||||
ja @@Err1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
ja @@Err1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
.test:
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitDevice0
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Err6
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
jz @@WaitDevice0
|
||||
; Послать пакетную команду
|
||||
; Послать пакетную команду
|
||||
cli
|
||||
mov DX, [ATABasePortAddr]
|
||||
mov AX, [PacketCommand]
|
||||
@ -261,9 +261,9 @@ SendPacketDatCommand:
|
||||
mov AX, [PacketCommand+10]
|
||||
out DX, AX
|
||||
sti
|
||||
; Ожидание готовности данных
|
||||
; Ожидание готовности данных
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;порт 1х7h
|
||||
add DX, 7 ;порт 1х7h
|
||||
mov ecx, NoTickWaitTime
|
||||
@@WaitDevice1:
|
||||
cmp [timer_ticks_enable], 0
|
||||
@ -274,40 +274,40 @@ SendPacketDatCommand:
|
||||
jmp .test_1
|
||||
@@:
|
||||
call change_task
|
||||
; Проверить время выполнения команды
|
||||
; Проверить время выполнения команды
|
||||
mov EAX, [timer_ticks]
|
||||
sub EAX, [TickCounter_1]
|
||||
cmp EAX, MaxCDWaitTime
|
||||
ja @@Err1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
ja @@Err1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
.test_1:
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitDevice1
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Err6_temp
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
jz @@WaitDevice1
|
||||
; Принять блок данных от контроллера
|
||||
; Принять блок данных от контроллера
|
||||
mov EDI, [CDDataBuf_pointer];0x7000 ;CDDataBuf
|
||||
; Загрузить адрес регистра данных контроллера
|
||||
mov DX, [ATABasePortAddr];порт 1x0h
|
||||
; Загрузить в счетчик размер блока в байтах
|
||||
; Загрузить адрес регистра данных контроллера
|
||||
mov DX, [ATABasePortAddr];порт 1x0h
|
||||
; Загрузить в счетчик размер блока в байтах
|
||||
xor ecx, ecx
|
||||
mov CX, CDBlockSize
|
||||
; Вычислить размер блока в 16-разрядных словах
|
||||
shr CX, 1;разделить размер блока на 2
|
||||
; Принять блок данных
|
||||
; Вычислить размер блока в 16-разрядных словах
|
||||
shr CX, 1;разделить размер блока на 2
|
||||
; Принять блок данных
|
||||
cli
|
||||
cld
|
||||
rep insw
|
||||
sti
|
||||
; Успешное завершение приема данных
|
||||
; Успешное завершение приема данных
|
||||
@@End_8:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Err1_1:
|
||||
xor eax, eax
|
||||
inc eax
|
||||
@ -329,21 +329,21 @@ SendPacketDatCommand:
|
||||
|
||||
|
||||
;***********************************************
|
||||
;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, *
|
||||
;* НЕ ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧИ ДАННЫХ *
|
||||
;* Входные параметры передаются через *
|
||||
;* глобальные перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* PacketCommand - 12-байтный командный пакет. *
|
||||
;* ПОСЛАТЬ УСТРОЙСТВУ ATAPI ПАКЕТНУЮ КОМАНДУ, *
|
||||
;* НЕ ПРЕДУСМАТРИВАЮЩУЮ ПЕРЕДАЧИ ДАННЫХ *
|
||||
;* Входные параметры передаются через *
|
||||
;* глобальные перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале; *
|
||||
;* PacketCommand - 12-байтный командный пакет. *
|
||||
;***********************************************
|
||||
SendPacketNoDatCommand:
|
||||
pushad
|
||||
xor eax, eax
|
||||
; mov byte [DevErrorCode],al
|
||||
; Задать режим CHS
|
||||
; Задать режим CHS
|
||||
mov byte [ATAAddressMode], al
|
||||
; Послать ATA-команду передачи пакетной команды
|
||||
; Послать ATA-команду передачи пакетной команды
|
||||
mov byte [ATAFeatures], al
|
||||
mov byte [ATASectorCount], al
|
||||
mov byte [ATASectorNumber], al
|
||||
@ -351,29 +351,29 @@ SendPacketNoDatCommand:
|
||||
mov byte [ATAHead], al
|
||||
mov [ATACommand], 0A0h
|
||||
call SendCommandToHDD_1
|
||||
; cmp [DevErrorCode],0 ;проверить код ошибки
|
||||
; cmp [DevErrorCode],0 ;проверить код ошибки
|
||||
test eax, eax
|
||||
jnz @@End_9 ;закончить, сохранив код ошибки
|
||||
; Ожидание готовности дисковода к приему
|
||||
; пакетной команды
|
||||
jnz @@End_9 ;закончить, сохранив код ошибки
|
||||
; Ожидание готовности дисковода к приему
|
||||
; пакетной команды
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;порт 1х7h
|
||||
add DX, 7 ;порт 1х7h
|
||||
@@WaitDevice0_1:
|
||||
call change_task
|
||||
; Проверить время ожидания
|
||||
; Проверить время ожидания
|
||||
mov EAX, [timer_ticks]
|
||||
sub EAX, [TickCounter_1]
|
||||
cmp EAX, BSYWaitTime
|
||||
ja @@Err1_3 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
ja @@Err1_3 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitDevice0_1
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Err6_1
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
jz @@WaitDevice0_1
|
||||
; Послать пакетную команду
|
||||
; Послать пакетную команду
|
||||
; cli
|
||||
mov DX, [ATABasePortAddr]
|
||||
mov AX, word [PacketCommand]
|
||||
@ -391,29 +391,29 @@ SendPacketNoDatCommand:
|
||||
; sti
|
||||
cmp [ignore_CD_eject_wait], 1
|
||||
je @@clear_DEC
|
||||
; Ожидание подтверждения приема команды
|
||||
; Ожидание подтверждения приема команды
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;порт 1х7h
|
||||
add DX, 7 ;порт 1х7h
|
||||
@@WaitDevice1_1:
|
||||
call change_task
|
||||
; Проверить время выполнения команды
|
||||
; Проверить время выполнения команды
|
||||
mov EAX, [timer_ticks]
|
||||
sub EAX, [TickCounter_1]
|
||||
cmp EAX, MaxCDWaitTime
|
||||
ja @@Err1_3 ;ошибка тайм-аута
|
||||
; Ожидать освобождения устройства
|
||||
ja @@Err1_3 ;ошибка тайм-аута
|
||||
; Ожидать освобождения устройства
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitDevice1_1
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Err6_1
|
||||
test AL, 40h ;состояние сигнала DRDY
|
||||
test AL, 40h ;состояние сигнала DRDY
|
||||
jz @@WaitDevice1_1
|
||||
@@clear_DEC:
|
||||
and [DevErrorCode], 0
|
||||
popad
|
||||
ret
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Err1_3:
|
||||
xor eax, eax
|
||||
inc eax
|
||||
@ -426,53 +426,53 @@ SendPacketNoDatCommand:
|
||||
ret
|
||||
|
||||
;****************************************************
|
||||
;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1); *
|
||||
;* ATAFeatures - "особенности"; *
|
||||
;* ATASectorCount - количество секторов; *
|
||||
;* ATASectorNumber - номер начального сектора; *
|
||||
;* ATACylinder - номер начального цилиндра; *
|
||||
;* ATAHead - номер начальной головки; *
|
||||
;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); *
|
||||
;* ATACommand - код команды. *
|
||||
;* После успешного выполнения функции: *
|
||||
;* в ATABasePortAddr - базовый адрес HDD; *
|
||||
;* в DevErrorCode - ноль. *
|
||||
;* При возникновении ошибки в DevErrorCode будет *
|
||||
;* возвращен код ошибки в eax *
|
||||
;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1); *
|
||||
;* ATAFeatures - "особенности"; *
|
||||
;* ATASectorCount - количество секторов; *
|
||||
;* ATASectorNumber - номер начального сектора; *
|
||||
;* ATACylinder - номер начального цилиндра; *
|
||||
;* ATAHead - номер начальной головки; *
|
||||
;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); *
|
||||
;* ATACommand - код команды. *
|
||||
;* После успешного выполнения функции: *
|
||||
;* в ATABasePortAddr - базовый адрес HDD; *
|
||||
;* в DevErrorCode - ноль. *
|
||||
;* При возникновении ошибки в DevErrorCode будет *
|
||||
;* возвращен код ошибки в eax *
|
||||
;****************************************************
|
||||
SendCommandToHDD_1:
|
||||
; pushad
|
||||
; mov [DevErrorCode],0 not need
|
||||
; Проверить значение кода режима
|
||||
; Проверить значение кода режима
|
||||
cmp [ATAAddressMode], 1
|
||||
ja @@Err2_4
|
||||
; Проверить корректность номера канала
|
||||
; Проверить корректность номера канала
|
||||
mov BX, [ChannelNumber]
|
||||
cmp BX, 1
|
||||
jb @@Err3_4
|
||||
cmp BX, 2
|
||||
ja @@Err3_4
|
||||
; Установить базовый адрес
|
||||
; Установить базовый адрес
|
||||
dec BX
|
||||
shl BX, 1
|
||||
movzx ebx, bx
|
||||
mov AX, [ebx+StandardATABases]
|
||||
mov [ATABasePortAddr], AX
|
||||
; Ожидание готовности HDD к приему команды
|
||||
; Выбрать нужный диск
|
||||
; Ожидание готовности HDD к приему команды
|
||||
; Выбрать нужный диск
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 6 ;адрес регистра головок
|
||||
add DX, 6 ;адрес регистра головок
|
||||
mov AL, [DiskNumber]
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
ja @@Err4_4
|
||||
shl AL, 4
|
||||
or AL, 10100000b
|
||||
out DX, AL
|
||||
; Ожидать, пока диск не будет готов
|
||||
; Ожидать, пока диск не будет готов
|
||||
inc DX
|
||||
mov eax, [timer_ticks]
|
||||
mov [TickCounter_1], eax
|
||||
@ -486,43 +486,43 @@ SendCommandToHDD_1:
|
||||
jmp .test
|
||||
@@:
|
||||
call change_task
|
||||
; Проверить время ожидания
|
||||
; Проверить время ожидания
|
||||
mov eax, [timer_ticks]
|
||||
sub eax, [TickCounter_1]
|
||||
cmp eax, BSYWaitTime;300 ;ожидать 3 сек.
|
||||
ja @@Err1_4 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояния
|
||||
cmp eax, BSYWaitTime;300 ;ожидать 3 сек.
|
||||
ja @@Err1_4 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояния
|
||||
.test:
|
||||
in AL, DX
|
||||
; Проверить состояние сигнала BSY
|
||||
; Проверить состояние сигнала BSY
|
||||
test AL, 80h
|
||||
jnz @@WaitHDReady_2
|
||||
; Проверить состояние сигнала DRQ
|
||||
; Проверить состояние сигнала DRQ
|
||||
test AL, 08h
|
||||
jnz @@WaitHDReady_2
|
||||
|
||||
; Загрузить команду в регистры контроллера
|
||||
; Загрузить команду в регистры контроллера
|
||||
cli
|
||||
mov DX, [ATABasePortAddr]
|
||||
inc DX ;регистр "особенностей"
|
||||
inc DX ;регистр "особенностей"
|
||||
mov AL, [ATAFeatures]
|
||||
out DX, AL
|
||||
inc DX ;счетчик секторов
|
||||
inc DX ;счетчик секторов
|
||||
mov AL, [ATASectorCount]
|
||||
out DX, AL
|
||||
inc DX ;регистр номера сектора
|
||||
inc DX ;регистр номера сектора
|
||||
mov AL, [ATASectorNumber]
|
||||
out DX, AL
|
||||
inc DX ;номер цилиндра (младший байт)
|
||||
inc DX ;номер цилиндра (младший байт)
|
||||
mov AX, [ATACylinder]
|
||||
out DX, AL
|
||||
inc DX ;номер цилиндра (старший байт)
|
||||
inc DX ;номер цилиндра (старший байт)
|
||||
mov AL, AH
|
||||
out DX, AL
|
||||
inc DX ;номер головки/номер диска
|
||||
inc DX ;номер головки/номер диска
|
||||
mov AL, [DiskNumber]
|
||||
shl AL, 4
|
||||
cmp [ATAHead], 0Fh;проверить номер головки
|
||||
cmp [ATAHead], 0Fh;проверить номер головки
|
||||
ja @@Err5_4
|
||||
or AL, [ATAHead]
|
||||
or AL, 10100000b
|
||||
@ -530,17 +530,17 @@ SendCommandToHDD_1:
|
||||
shl AH, 6
|
||||
or AL, AH
|
||||
out DX, AL
|
||||
; Послать команду
|
||||
; Послать команду
|
||||
mov AL, [ATACommand]
|
||||
inc DX ;регистр команд
|
||||
inc DX ;регистр команд
|
||||
out DX, AL
|
||||
sti
|
||||
; Сбросить признак ошибки
|
||||
; Сбросить признак ошибки
|
||||
; mov [DevErrorCode],0
|
||||
@@End_10:
|
||||
xor eax, eax
|
||||
ret
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Err1_4:
|
||||
xor eax, eax
|
||||
inc eax
|
||||
@ -561,31 +561,31 @@ SendCommandToHDD_1:
|
||||
@@Err5_4:
|
||||
mov eax, 5
|
||||
; mov [DevErrorCode],5
|
||||
; Завершение работы программы
|
||||
; Завершение работы программы
|
||||
ret
|
||||
; sti
|
||||
; popad
|
||||
|
||||
;*************************************************
|
||||
;* ОЖИДАНИЕ ГОТОВНОСТИ УСТРОЙСТВА К РАБОТЕ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* ОЖИДАНИЕ ГОТОВНОСТИ УСТРОЙСТВА К РАБОТЕ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
WaitUnitReady:
|
||||
pusha
|
||||
; Запомнить время начала операции
|
||||
; Запомнить время начала операции
|
||||
mov EAX, [timer_ticks]
|
||||
mov [WURStartTime], EAX
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Сформировать команду TEST UNIT READY
|
||||
; Сформировать команду TEST UNIT READY
|
||||
mov [PacketCommand], word 00h
|
||||
; ЦИКЛ ОЖИДАНИЯ ГОТОВНОСТИ УСТРОЙСТВА
|
||||
; ЦИКЛ ОЖИДАНИЯ ГОТОВНОСТИ УСТРОЙСТВА
|
||||
mov ecx, NoTickWaitTime
|
||||
@@SendCommand:
|
||||
; Подать команду проверки готовности
|
||||
; Подать команду проверки готовности
|
||||
call SendPacketNoDatCommand
|
||||
cmp [timer_ticks_enable], 0
|
||||
jne @f
|
||||
@ -597,37 +597,37 @@ WaitUnitReady:
|
||||
jmp @@SendCommand
|
||||
@@:
|
||||
call change_task
|
||||
; Проверить код ошибки
|
||||
; Проверить код ошибки
|
||||
cmp [DevErrorCode], 0
|
||||
je @@End_11
|
||||
; Проверить время ожидания готовности
|
||||
; Проверить время ожидания готовности
|
||||
mov EAX, [timer_ticks]
|
||||
sub EAX, [WURStartTime]
|
||||
cmp EAX, MaxCDWaitTime
|
||||
jb @@SendCommand
|
||||
.Error:
|
||||
; Ошибка тайм-аута
|
||||
; Ошибка тайм-аута
|
||||
mov [DevErrorCode], 1
|
||||
@@End_11:
|
||||
popa
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* ЗАПРЕТИТЬ СМЕНУ ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* ЗАПРЕТИТЬ СМЕНУ ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
prevent_medium_removal:
|
||||
pusha
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Задать код команды
|
||||
; Задать код команды
|
||||
mov [PacketCommand], byte 0x1E
|
||||
; Задать код запрета
|
||||
; Задать код запрета
|
||||
mov [PacketCommand+4], byte 11b
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketNoDatCommand
|
||||
mov eax, ATAPI_IDE0_lock
|
||||
add eax, [cdpos]
|
||||
@ -637,21 +637,21 @@ prevent_medium_removal:
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* РАЗРЕШИТЬ СМЕНУ ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* РАЗРЕШИТЬ СМЕНУ ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
allow_medium_removal:
|
||||
pusha
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Задать код команды
|
||||
; Задать код команды
|
||||
mov [PacketCommand], byte 0x1E
|
||||
; Задать код запрета
|
||||
; Задать код запрета
|
||||
mov [PacketCommand+4], byte 00b
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketNoDatCommand
|
||||
mov eax, ATAPI_IDE0_lock
|
||||
add eax, [cdpos]
|
||||
@ -661,54 +661,54 @@ allow_medium_removal:
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* ЗАГРУЗИТЬ НОСИТЕЛЬ В ДИСКОВОД *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* ЗАГРУЗИТЬ НОСИТЕЛЬ В ДИСКОВОД *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
LoadMedium:
|
||||
pusha
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Сформировать команду START/STOP UNIT
|
||||
; Задать код команды
|
||||
; Сформировать команду START/STOP UNIT
|
||||
; Задать код команды
|
||||
mov [PacketCommand], word 1Bh
|
||||
; Задать операцию загрузки носителя
|
||||
; Задать операцию загрузки носителя
|
||||
mov [PacketCommand+4], word 00000011b
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketNoDatCommand
|
||||
popa
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* ИЗВЛЕЧЬ НОСИТЕЛЬ ИЗ ДИСКОВОДА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* ИЗВЛЕЧЬ НОСИТЕЛЬ ИЗ ДИСКОВОДА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
EjectMedium:
|
||||
pusha
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Сформировать команду START/STOP UNIT
|
||||
; Задать код команды
|
||||
; Сформировать команду START/STOP UNIT
|
||||
; Задать код команды
|
||||
mov [PacketCommand], word 1Bh
|
||||
; Задать операцию извлечения носителя
|
||||
; Задать операцию извлечения носителя
|
||||
mov [PacketCommand+4], word 00000010b
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketNoDatCommand
|
||||
popa
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* Проверить событие нажатия кнопки извлечения *
|
||||
;* диска *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* Проверить событие нажатия кнопки извлечения *
|
||||
;* диска *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
proc check_ATAPI_device_event_has_work?
|
||||
mov eax, [timer_ticks]
|
||||
@ -867,78 +867,78 @@ ATAPI_IDE3_lock db 0
|
||||
ignore_CD_eject_wait db 0
|
||||
endg
|
||||
;*************************************************
|
||||
;* Получить сообщение о событии или состоянии *
|
||||
;* устройства *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* Получить сообщение о событии или состоянии *
|
||||
;* устройства *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
GetEvent_StatusNotification:
|
||||
pusha
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Задать код команды
|
||||
; Задать код команды
|
||||
mov [PacketCommand], byte 4Ah
|
||||
mov [PacketCommand+1], byte 00000001b
|
||||
; Задать запрос класса сообщений
|
||||
; Задать запрос класса сообщений
|
||||
mov [PacketCommand+4], byte 00010000b
|
||||
; Размер выделенной области
|
||||
; Размер выделенной области
|
||||
mov [PacketCommand+7], byte 8h
|
||||
mov [PacketCommand+8], byte 0h
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketDatCommand
|
||||
popa
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
; прочитать информацию из TOC
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
; прочитать информацию из TOC
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
Read_TOC:
|
||||
pusha
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
call clear_packet_buffer
|
||||
; Сформировать пакетную команду для считывания
|
||||
; сектора данных
|
||||
; Сформировать пакетную команду для считывания
|
||||
; сектора данных
|
||||
mov [PacketCommand], byte 0x43
|
||||
; Задать формат
|
||||
; Задать формат
|
||||
mov [PacketCommand+2], byte 1
|
||||
; Размер выделенной области
|
||||
; Размер выделенной области
|
||||
mov [PacketCommand+7], byte 0xFF
|
||||
mov [PacketCommand+8], byte 0h
|
||||
; Подать команду
|
||||
; Подать команду
|
||||
call SendPacketDatCommand
|
||||
popa
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* ОПРЕДЕЛИТЬ ОБЩЕЕ КОЛИЧЕСТВО СЕКТОРОВ НА ДИСКЕ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* ОПРЕДЕЛИТЬ ОБЩЕЕ КОЛИЧЕСТВО СЕКТОРОВ НА ДИСКЕ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;*************************************************
|
||||
;ReadCapacity:
|
||||
; pusha
|
||||
;; Очистить буфер пакетной команды
|
||||
;; Очистить буфер пакетной команды
|
||||
; call clear_packet_buffer
|
||||
;; Задать размер буфера в байтах
|
||||
;; Задать размер буфера в байтах
|
||||
; mov [CDBlockSize],8
|
||||
;; Сформировать команду READ CAPACITY
|
||||
;; Сформировать команду READ CAPACITY
|
||||
; mov [PacketCommand],word 25h
|
||||
;; Подать команду
|
||||
;; Подать команду
|
||||
; call SendPacketDatCommand
|
||||
; popa
|
||||
; ret
|
||||
|
||||
clear_packet_buffer:
|
||||
; Очистить буфер пакетной команды
|
||||
; Очистить буфер пакетной команды
|
||||
and [PacketCommand], dword 0
|
||||
and [PacketCommand+4], dword 0
|
||||
and [PacketCommand+8], dword 0
|
||||
|
@ -397,7 +397,7 @@ saved_esi_pos = 16+12 ; size of local variables + size of registers before esi
|
||||
mov dword [esi+8], 1 ; same as in hd
|
||||
mov eax, [esi]
|
||||
mov edx, [esi+4] ; edx:eax = sector to write
|
||||
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
|
||||
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
|
||||
cmp ecx, 1
|
||||
jz .nonext
|
||||
cmp dword [esi+12+8], 2
|
||||
|
@ -37,9 +37,9 @@ save_image:
|
||||
call check_label
|
||||
cmp [FDC_Status], 0
|
||||
jne unnecessary_save_image
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 1; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 1; Сектор
|
||||
mov esi, RAMDISK
|
||||
call SeekTrack
|
||||
save_image_1:
|
||||
|
@ -9,12 +9,12 @@ $Revision$
|
||||
|
||||
|
||||
;**********************************************************
|
||||
; Непосредственная работа с контроллером гибкого диска
|
||||
; Непосредственная работа с контроллером гибкого диска
|
||||
;**********************************************************
|
||||
; Автор исходного текста Кулаков Владимир Геннадьевич.
|
||||
; Адаптация и доработка Mario79
|
||||
; Автор исходного текста Кулаков Владимир Геннадьевич.
|
||||
; Адаптация и доработка Mario79
|
||||
|
||||
;give_back_application_data: ; переслать приложению
|
||||
;give_back_application_data: ; переслать приложению
|
||||
; mov edi,[TASK_BASE]
|
||||
; mov edi,[edi+TASKDATA.mem_start]
|
||||
; add edi,ecx
|
||||
@ -26,7 +26,7 @@ give_back_application_data_1:
|
||||
rep movsd
|
||||
ret
|
||||
|
||||
;take_data_from_application: ; взять из приложени
|
||||
;take_data_from_application: ; взять из приложени
|
||||
; mov esi,[TASK_BASE]
|
||||
; mov esi,[esi+TASKDATA.mem_start]
|
||||
; add esi,ecx
|
||||
@ -38,37 +38,37 @@ take_data_from_application_1:
|
||||
rep movsd
|
||||
ret
|
||||
|
||||
; Коды завершения операции с контроллером (FDC_Status)
|
||||
FDC_Normal equ 0 ;нормальное завершение
|
||||
FDC_TimeOut equ 1 ;ошибка тайм-аута
|
||||
FDC_DiskNotFound equ 2 ;в дисководе нет диска
|
||||
FDC_TrackNotFound equ 3 ;дорожка не найдена
|
||||
FDC_SectorNotFound equ 4 ;сектор не найден
|
||||
; Коды завершения операции с контроллером (FDC_Status)
|
||||
FDC_Normal equ 0 ;нормальное завершение
|
||||
FDC_TimeOut equ 1 ;ошибка тайм-аута
|
||||
FDC_DiskNotFound equ 2 ;в дисководе нет диска
|
||||
FDC_TrackNotFound equ 3 ;дорожка не найдена
|
||||
FDC_SectorNotFound equ 4 ;сектор не найден
|
||||
|
||||
; Максимальные значения координат сектора (заданные
|
||||
; значения соответствуют параметрам стандартного
|
||||
; трехдюймового гибкого диска объемом 1,44 Мб)
|
||||
; Максимальные значения координат сектора (заданные
|
||||
; значения соответствуют параметрам стандартного
|
||||
; трехдюймового гибкого диска объемом 1,44 Мб)
|
||||
MAX_Track equ 79
|
||||
MAX_Head equ 1
|
||||
MAX_Sector equ 18
|
||||
|
||||
uglobal
|
||||
; Счетчик тиков таймера
|
||||
; Счетчик тиков таймера
|
||||
TickCounter dd ?
|
||||
; Код завершения операции с контроллером НГМД
|
||||
; Код завершения операции с контроллером НГМД
|
||||
FDC_Status DB ?
|
||||
; Флаг прерывания от НГМД
|
||||
; Флаг прерывания от НГМД
|
||||
FDD_IntFlag DB ?
|
||||
; Момент начала последней операции с НГМД
|
||||
; Момент начала последней операции с НГМД
|
||||
FDD_Time DD ?
|
||||
; Номер дисковода
|
||||
; Номер дисковода
|
||||
FDD_Type db 0
|
||||
; Координаты сектора
|
||||
; Координаты сектора
|
||||
FDD_Track DB ?
|
||||
FDD_Head DB ?
|
||||
FDD_Sector DB ?
|
||||
|
||||
; Блок результата операции
|
||||
; Блок результата операции
|
||||
FDC_ST0 DB ?
|
||||
FDC_ST1 DB ?
|
||||
FDC_ST2 DB ?
|
||||
@ -76,18 +76,18 @@ FDC_C DB ?
|
||||
FDC_H DB ?
|
||||
FDC_R DB ?
|
||||
FDC_N DB ?
|
||||
; Счетчик повторения операции чтени
|
||||
; Счетчик повторения операции чтени
|
||||
ReadRepCounter DB ?
|
||||
; Счетчик повторения операции рекалибровки
|
||||
; Счетчик повторения операции рекалибровки
|
||||
RecalRepCounter DB ?
|
||||
endg
|
||||
; Область памяти для хранения прочитанного сектора
|
||||
; Область памяти для хранения прочитанного сектора
|
||||
;FDD_DataBuffer: times 512 db 0 ;DB 512 DUP (?)
|
||||
fdd_motor_status db 0
|
||||
timer_fdd_motor dd 0
|
||||
|
||||
;*************************************
|
||||
;* ИНИЦИАЛИЗАЦИЯ РЕЖИМА ПДП ДЛЯ НГМД *
|
||||
;* ИНИЦИАЛИЗАЦИЯ РЕЖИМА ПДП ДЛЯ НГМД *
|
||||
;*************************************
|
||||
Init_FDC_DMA:
|
||||
pushad
|
||||
@ -117,29 +117,29 @@ Init_FDC_DMA:
|
||||
ret
|
||||
|
||||
;***********************************
|
||||
;* ЗАПИСАТЬ БАЙТ В ПОРТ ДАННЫХ FDC *
|
||||
;* Параметры: *
|
||||
;* AL - выводимый байт. *
|
||||
;* ЗАПИСАТЬ БАЙТ В ПОРТ ДАННЫХ FDC *
|
||||
;* Параметры: *
|
||||
;* AL - выводимый байт. *
|
||||
;***********************************
|
||||
FDCDataOutput:
|
||||
; pusha
|
||||
push eax ecx edx
|
||||
mov AH, AL ;запомнить байт в AH
|
||||
; Сбросить переменную состояния контроллера
|
||||
mov AH, AL ;запомнить байт в AH
|
||||
; Сбросить переменную состояния контроллера
|
||||
mov [FDC_Status], FDC_Normal
|
||||
; Проверить готовность контроллера к приему данных
|
||||
mov DX, 3F4h ;(порт состояния FDC)
|
||||
mov ecx, 0x10000 ;установить счетчик тайм-аута
|
||||
; Проверить готовность контроллера к приему данных
|
||||
mov DX, 3F4h ;(порт состояния FDC)
|
||||
mov ecx, 0x10000 ;установить счетчик тайм-аута
|
||||
@@TestRS:
|
||||
in AL, DX ;прочитать регистр RS
|
||||
and AL, 0C0h ;выделить разряды 6 и 7
|
||||
cmp AL, 80h ;проверить разряды 6 и 7
|
||||
in AL, DX ;прочитать регистр RS
|
||||
and AL, 0C0h ;выделить разряды 6 и 7
|
||||
cmp AL, 80h ;проверить разряды 6 и 7
|
||||
je @@OutByteToFDC
|
||||
loop @@TestRS
|
||||
; Ошибка тайм-аута
|
||||
; Ошибка тайм-аута
|
||||
mov [FDC_Status], FDC_TimeOut
|
||||
jmp @@End_5
|
||||
; Вывести байт в порт данных
|
||||
; Вывести байт в порт данных
|
||||
@@OutByteToFDC:
|
||||
inc DX
|
||||
mov AL, AH
|
||||
@ -150,29 +150,29 @@ FDCDataOutput:
|
||||
ret
|
||||
|
||||
;******************************************
|
||||
;* ПРОЧИТАТЬ БАЙТ ИЗ ПОРТА ДАННЫХ FDC *
|
||||
;* Процедура не имеет входных параметров. *
|
||||
;* Выходные данные: *
|
||||
;* AL - считанный байт. *
|
||||
;* ПРОЧИТАТЬ БАЙТ ИЗ ПОРТА ДАННЫХ FDC *
|
||||
;* Процедура не имеет входных параметров. *
|
||||
;* Выходные данные: *
|
||||
;* AL - считанный байт. *
|
||||
;******************************************
|
||||
FDCDataInput:
|
||||
push ECX
|
||||
push DX
|
||||
; Сбросить переменную состояния контроллера
|
||||
; Сбросить переменную состояния контроллера
|
||||
mov [FDC_Status], FDC_Normal
|
||||
; Проверить готовность контроллера к передаче данных
|
||||
mov DX, 3F4h ;(порт состояния FDC)
|
||||
xor CX, CX ;установить счетчик тайм-аута
|
||||
; Проверить готовность контроллера к передаче данных
|
||||
mov DX, 3F4h ;(порт состояния FDC)
|
||||
xor CX, CX ;установить счетчик тайм-аута
|
||||
@@TestRS_1:
|
||||
in AL, DX ;прочитать регистр RS
|
||||
and AL, 0C0h ;выдлить разряды 6 и 7
|
||||
cmp AL, 0C0h ;проверить разряды 6 и 7
|
||||
in AL, DX ;прочитать регистр RS
|
||||
and AL, 0C0h ;выдлить разряды 6 и 7
|
||||
cmp AL, 0C0h ;проверить разряды 6 и 7
|
||||
je @@GetByteFromFDC
|
||||
loop @@TestRS_1
|
||||
; Ошибка тайм-аута
|
||||
; Ошибка тайм-аута
|
||||
mov [FDC_Status], FDC_TimeOut
|
||||
jmp @@End_6
|
||||
; Ввести байт из порта данных
|
||||
; Ввести байт из порта данных
|
||||
@@GetByteFromFDC:
|
||||
inc DX
|
||||
in AL, DX
|
||||
@ -182,45 +182,45 @@ FDCDataInput:
|
||||
ret
|
||||
|
||||
;*********************************************
|
||||
;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
|
||||
;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
|
||||
;*********************************************
|
||||
FDCInterrupt:
|
||||
; Установить флаг прерывани
|
||||
; Установить флаг прерывани
|
||||
mov [FDD_IntFlag], 1
|
||||
ret
|
||||
|
||||
|
||||
;******************************************
|
||||
;* УСТАНОВИТЬ НОВЫЙ ОБРАБОТЧИК ПРЕРЫВАНИЙ *
|
||||
;* НГМД *
|
||||
;* УСТАНОВИТЬ НОВЫЙ ОБРАБОТЧИК ПРЕРЫВАНИЙ *
|
||||
;* НГМД *
|
||||
;******************************************
|
||||
SetUserInterrupts:
|
||||
mov [fdc_irq_func], FDCInterrupt
|
||||
ret
|
||||
|
||||
;*******************************************
|
||||
;* ОЖИДАНИЕ ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
|
||||
;* ОЖИДАНИЕ ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
|
||||
;*******************************************
|
||||
WaitFDCInterrupt:
|
||||
pusha
|
||||
; Сбросить байт состояния операции
|
||||
; Сбросить байт состояния операции
|
||||
mov [FDC_Status], FDC_Normal
|
||||
; Сбросить флаг прерывани
|
||||
; Сбросить флаг прерывани
|
||||
mov [FDD_IntFlag], 0
|
||||
; Обнулить счетчик тиков
|
||||
; Обнулить счетчик тиков
|
||||
mov eax, [timer_ticks]
|
||||
mov [TickCounter], eax
|
||||
; Ожидать установки флага прерывания НГМД
|
||||
; Ожидать установки флага прерывания НГМД
|
||||
@@TestRS_2:
|
||||
cmp [FDD_IntFlag], 0
|
||||
jnz @@End_7 ;прерывание произошло
|
||||
jnz @@End_7 ;прерывание произошло
|
||||
call change_task
|
||||
mov eax, [timer_ticks]
|
||||
sub eax, [TickCounter]
|
||||
cmp eax, 50 ;25 ;5 ;ожидать 5 тиков
|
||||
cmp eax, 50 ;25 ;5 ;ожидать 5 тиков
|
||||
jb @@TestRS_2
|
||||
; jl @@TestRS_2
|
||||
; Ошибка тайм-аута
|
||||
; Ошибка тайм-аута
|
||||
mov [FDC_Status], FDC_TimeOut
|
||||
; mov [flp_status],0
|
||||
@@End_7:
|
||||
@ -228,7 +228,7 @@ WaitFDCInterrupt:
|
||||
ret
|
||||
|
||||
;*********************************
|
||||
;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" *
|
||||
;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" *
|
||||
;*********************************
|
||||
FDDMotorON:
|
||||
pusha
|
||||
@ -237,11 +237,11 @@ FDDMotorON:
|
||||
mov al, [flp_number]
|
||||
cmp [fdd_motor_status], al
|
||||
je fdd_motor_on
|
||||
; Произвести сброс контроллера НГМД
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
; Произвести сброс контроллера НГМД
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
mov AL, 0
|
||||
out DX, AL
|
||||
; Выбрать и включить мотор дисковода
|
||||
; Выбрать и включить мотор дисковода
|
||||
cmp [flp_number], 1
|
||||
jne FDDMotorON_B
|
||||
; call FDDMotorOFF_B
|
||||
@ -252,10 +252,10 @@ FDDMotorON_B:
|
||||
mov AL, 2Dh ; Floppy B
|
||||
FDDMotorON_1:
|
||||
out DX, AL
|
||||
; Обнулить счетчик тиков
|
||||
; Обнулить счетчик тиков
|
||||
mov eax, [timer_ticks]
|
||||
mov [TickCounter], eax
|
||||
; Ожидать 0,5 с
|
||||
; Ожидать 0,5 с
|
||||
@@dT:
|
||||
call change_task
|
||||
mov eax, [timer_ticks]
|
||||
@ -274,7 +274,7 @@ fdd_motor_on:
|
||||
ret
|
||||
|
||||
;*****************************************
|
||||
;* СОХРАНЕНИЕ УКАЗАТЕЛЯ ВРЕМЕНИ *
|
||||
;* СОХРАНЕНИЕ УКАЗАТЕЛЯ ВРЕМЕНИ *
|
||||
;*****************************************
|
||||
save_timer_fdd_motor:
|
||||
mov eax, [timer_ticks]
|
||||
@ -282,7 +282,7 @@ save_timer_fdd_motor:
|
||||
ret
|
||||
|
||||
;*****************************************
|
||||
;* ПРОВЕРКА ЗАДЕРЖКИ ВЫКЛЮЧЕНИЯ МОТОРА *
|
||||
;* ПРОВЕРКА ЗАДЕРЖКИ ВЫКЛЮЧЕНИЯ МОТОРА *
|
||||
;*****************************************
|
||||
proc check_fdd_motor_status_has_work?
|
||||
cmp [flp_status], 0
|
||||
@ -318,7 +318,7 @@ end_check_fdd_motor_status:
|
||||
ret
|
||||
|
||||
;**********************************
|
||||
;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА *
|
||||
;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА *
|
||||
;**********************************
|
||||
FDDMotorOFF:
|
||||
push AX
|
||||
@ -332,35 +332,35 @@ FDDMotorOFF_1:
|
||||
FDDMotorOFF_2:
|
||||
pop DX
|
||||
pop AX
|
||||
; сброс флагов кеширования в связи с устареванием информации
|
||||
; сброс флагов кеширования в связи с устареванием информации
|
||||
mov [root_read], 0
|
||||
mov [flp_fat], 0
|
||||
ret
|
||||
|
||||
FDDMotorOFF_A:
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
mov AL, 0Ch ; Floppy A
|
||||
out DX, AL
|
||||
ret
|
||||
|
||||
FDDMotorOFF_B:
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
mov DX, 3F2h;порт управления двигателями
|
||||
mov AL, 5h ; Floppy B
|
||||
out DX, AL
|
||||
ret
|
||||
|
||||
;*******************************
|
||||
;* РЕКАЛИБРОВКА ДИСКОВОДА "A:" *
|
||||
;* РЕКАЛИБРОВКА ДИСКОВОДА "A:" *
|
||||
;*******************************
|
||||
RecalibrateFDD:
|
||||
pusha
|
||||
call save_timer_fdd_motor
|
||||
; Подать команду "Рекалибровка"
|
||||
; Подать команду "Рекалибровка"
|
||||
mov AL, 07h
|
||||
call FDCDataOutput
|
||||
mov AL, 00h
|
||||
call FDCDataOutput
|
||||
; Ожидать завершения операции
|
||||
; Ожидать завершения операции
|
||||
call WaitFDCInterrupt
|
||||
; cmp [FDC_Status],0
|
||||
; je no_fdc_status_error
|
||||
@ -371,54 +371,54 @@ RecalibrateFDD:
|
||||
ret
|
||||
|
||||
;*****************************************************
|
||||
;* ПОИСК ДОРОЖКИ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* ПОИСК ДОРОЖКИ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;*****************************************************
|
||||
SeekTrack:
|
||||
pusha
|
||||
call save_timer_fdd_motor
|
||||
; Подать команду "Поиск"
|
||||
; Подать команду "Поиск"
|
||||
mov AL, 0Fh
|
||||
call FDCDataOutput
|
||||
; Передать байт номера головки/накопител
|
||||
; Передать байт номера головки/накопител
|
||||
mov AL, [FDD_Head]
|
||||
shl AL, 2
|
||||
call FDCDataOutput
|
||||
; Передать байт номера дорожки
|
||||
; Передать байт номера дорожки
|
||||
mov AL, [FDD_Track]
|
||||
call FDCDataOutput
|
||||
; Ожидать завершения операции
|
||||
; Ожидать завершения операции
|
||||
call WaitFDCInterrupt
|
||||
cmp [FDC_Status], FDC_Normal
|
||||
jne @@Exit
|
||||
; Сохранить результат поиска
|
||||
; Сохранить результат поиска
|
||||
mov AL, 08h
|
||||
call FDCDataOutput
|
||||
call FDCDataInput
|
||||
mov [FDC_ST0], AL
|
||||
call FDCDataInput
|
||||
mov [FDC_C], AL
|
||||
; Проверить результат поиска
|
||||
; Поиск завершен?
|
||||
; Проверить результат поиска
|
||||
; Поиск завершен?
|
||||
test [FDC_ST0], 100000b
|
||||
je @@Err
|
||||
; Заданный трек найден?
|
||||
; Заданный трек найден?
|
||||
mov AL, [FDC_C]
|
||||
cmp AL, [FDD_Track]
|
||||
jne @@Err
|
||||
; Номер головки совпадает с заданным?
|
||||
; Номер головки совпадает с заданным?
|
||||
mov AL, [FDC_ST0]
|
||||
and AL, 100b
|
||||
shr AL, 2
|
||||
cmp AL, [FDD_Head]
|
||||
jne @@Err
|
||||
; Операция завершена успешно
|
||||
; Операция завершена успешно
|
||||
mov [FDC_Status], FDC_Normal
|
||||
jmp @@Exit
|
||||
@@Err: ; Трек не найден
|
||||
@@Err: ; Трек не найден
|
||||
mov [FDC_Status], FDC_TrackNotFound
|
||||
; mov [flp_status],0
|
||||
@@Exit:
|
||||
@ -427,27 +427,27 @@ SeekTrack:
|
||||
ret
|
||||
|
||||
;*******************************************************
|
||||
;* ЧТЕНИЕ СЕКТОРА ДАННЫХ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции чтения *
|
||||
;* содержимое сектора будет занесено в FDD_DataBuffer. *
|
||||
;* ЧТЕНИЕ СЕКТОРА ДАННЫХ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции чтения *
|
||||
;* содержимое сектора будет занесено в FDD_DataBuffer. *
|
||||
;*******************************************************
|
||||
ReadSector:
|
||||
pushad
|
||||
call save_timer_fdd_motor
|
||||
; Установить скорость передачи 500 Кбайт/с
|
||||
; Установить скорость передачи 500 Кбайт/с
|
||||
mov AX, 0
|
||||
mov DX, 03F7h
|
||||
out DX, AL
|
||||
; Инициализировать канал прямого доступа к памяти
|
||||
; Инициализировать канал прямого доступа к памяти
|
||||
mov [dmamode], 0x46
|
||||
call Init_FDC_DMA
|
||||
; Подать команду "Чтение данных"
|
||||
mov AL, 0E6h ;чтение в мультитрековом режиме
|
||||
; Подать команду "Чтение данных"
|
||||
mov AL, 0E6h ;чтение в мультитрековом режиме
|
||||
call FDCDataOutput
|
||||
mov AL, [FDD_Head]
|
||||
shl AL, 2
|
||||
@ -458,19 +458,19 @@ ReadSector:
|
||||
call FDCDataOutput
|
||||
mov AL, [FDD_Sector]
|
||||
call FDCDataOutput
|
||||
mov AL, 2 ;код размера сектора (512 байт)
|
||||
mov AL, 2 ;код размера сектора (512 байт)
|
||||
call FDCDataOutput
|
||||
mov AL, 18 ;+1; 3Fh ;число секторов на дорожке
|
||||
mov AL, 18 ;+1; 3Fh ;число секторов на дорожке
|
||||
call FDCDataOutput
|
||||
mov AL, 1Bh ;значение GPL
|
||||
mov AL, 1Bh ;значение GPL
|
||||
call FDCDataOutput
|
||||
mov AL, 0FFh;значение DTL
|
||||
mov AL, 0FFh;значение DTL
|
||||
call FDCDataOutput
|
||||
; Ожидаем прерывание по завершении операции
|
||||
; Ожидаем прерывание по завершении операции
|
||||
call WaitFDCInterrupt
|
||||
cmp [FDC_Status], FDC_Normal
|
||||
jne @@Exit_1
|
||||
; Считываем статус завершения операции
|
||||
; Считываем статус завершения операции
|
||||
call GetStatusInfo
|
||||
test [FDC_ST0], 11011000b
|
||||
jnz @@Err_1
|
||||
@ -485,21 +485,21 @@ ReadSector:
|
||||
ret
|
||||
|
||||
;*******************************************************
|
||||
;* ЧТЕНИЕ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции чтения *
|
||||
;* содержимое сектора будет занесено в FDD_DataBuffer. *
|
||||
;* ЧТЕНИЕ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции чтения *
|
||||
;* содержимое сектора будет занесено в FDD_DataBuffer. *
|
||||
;*******************************************************
|
||||
ReadSectWithRetr:
|
||||
pusha
|
||||
; Обнулить счетчик повторения операции рекалибровки
|
||||
; Обнулить счетчик повторения операции рекалибровки
|
||||
mov [RecalRepCounter], 0
|
||||
@@TryAgain:
|
||||
; Обнулить счетчик повторения операции чтени
|
||||
; Обнулить счетчик повторения операции чтени
|
||||
mov [ReadRepCounter], 0
|
||||
@@ReadSector_1:
|
||||
call ReadSector
|
||||
@ -507,11 +507,11 @@ ReadSectWithRetr:
|
||||
je @@Exit_2
|
||||
cmp [FDC_Status], 1
|
||||
je @@Err_3
|
||||
; Троекратное повторение чтени
|
||||
; Троекратное повторение чтени
|
||||
inc [ReadRepCounter]
|
||||
cmp [ReadRepCounter], 3
|
||||
jb @@ReadSector_1
|
||||
; Троекратное повторение рекалибровки
|
||||
; Троекратное повторение рекалибровки
|
||||
call RecalibrateFDD
|
||||
call SeekTrack
|
||||
inc [RecalRepCounter]
|
||||
@ -527,27 +527,27 @@ ReadSectWithRetr:
|
||||
ret
|
||||
|
||||
;*******************************************************
|
||||
;* ЗАПИСЬ СЕКТОРА ДАННЫХ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции записи *
|
||||
;* содержимое FDD_DataBuffer будет занесено в сектор. *
|
||||
;* ЗАПИСЬ СЕКТОРА ДАННЫХ *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции записи *
|
||||
;* содержимое FDD_DataBuffer будет занесено в сектор. *
|
||||
;*******************************************************
|
||||
WriteSector:
|
||||
pushad
|
||||
call save_timer_fdd_motor
|
||||
; Установить скорость передачи 500 Кбайт/с
|
||||
; Установить скорость передачи 500 Кбайт/с
|
||||
mov AX, 0
|
||||
mov DX, 03F7h
|
||||
out DX, AL
|
||||
; Инициализировать канал прямого доступа к памяти
|
||||
; Инициализировать канал прямого доступа к памяти
|
||||
mov [dmamode], 0x4A
|
||||
call Init_FDC_DMA
|
||||
; Подать команду "Запись данных"
|
||||
mov AL, 0xC5 ;0x45 ;запись в мультитрековом режиме
|
||||
; Подать команду "Запись данных"
|
||||
mov AL, 0xC5 ;0x45 ;запись в мультитрековом режиме
|
||||
call FDCDataOutput
|
||||
mov AL, [FDD_Head]
|
||||
shl AL, 2
|
||||
@ -558,19 +558,19 @@ WriteSector:
|
||||
call FDCDataOutput
|
||||
mov AL, [FDD_Sector]
|
||||
call FDCDataOutput
|
||||
mov AL, 2 ;код размера сектора (512 байт)
|
||||
mov AL, 2 ;код размера сектора (512 байт)
|
||||
call FDCDataOutput
|
||||
mov AL, 18; 3Fh ;число секторов на дорожке
|
||||
mov AL, 18; 3Fh ;число секторов на дорожке
|
||||
call FDCDataOutput
|
||||
mov AL, 1Bh ;значение GPL
|
||||
mov AL, 1Bh ;значение GPL
|
||||
call FDCDataOutput
|
||||
mov AL, 0FFh;значение DTL
|
||||
mov AL, 0FFh;значение DTL
|
||||
call FDCDataOutput
|
||||
; Ожидаем прерывание по завершении операции
|
||||
; Ожидаем прерывание по завершении операции
|
||||
call WaitFDCInterrupt
|
||||
cmp [FDC_Status], FDC_Normal
|
||||
jne @@Exit_3
|
||||
; Считываем статус завершения операции
|
||||
; Считываем статус завершения операции
|
||||
call GetStatusInfo
|
||||
test [FDC_ST0], 11000000b ;11011000b
|
||||
jnz @@Err_2
|
||||
@ -584,21 +584,21 @@ WriteSector:
|
||||
ret
|
||||
|
||||
;*******************************************************
|
||||
;* ЗАПИСЬ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции записи *
|
||||
;* содержимое FDD_DataBuffer будет занесено в сектор. *
|
||||
;* ЗАПИСЬ СЕКТОРА (С ПОВТОРЕНИЕМ ОПЕРАЦИИ ПРИ СБОЕ) *
|
||||
;* Параметры передаются через глобальные переменные: *
|
||||
;* FDD_Track - номер дорожки (0-79); *
|
||||
;* FDD_Head - номер головки (0-1); *
|
||||
;* FDD_Sector - номер сектора (1-18). *
|
||||
;* Результат операции заносится в FDC_Status. *
|
||||
;* В случае успешного выполнения операции записи *
|
||||
;* содержимое FDD_DataBuffer будет занесено в сектор. *
|
||||
;*******************************************************
|
||||
WriteSectWithRetr:
|
||||
pusha
|
||||
; Обнулить счетчик повторения операции рекалибровки
|
||||
; Обнулить счетчик повторения операции рекалибровки
|
||||
mov [RecalRepCounter], 0
|
||||
@@TryAgain_1:
|
||||
; Обнулить счетчик повторения операции чтени
|
||||
; Обнулить счетчик повторения операции чтени
|
||||
mov [ReadRepCounter], 0
|
||||
@@WriteSector_1:
|
||||
call WriteSector
|
||||
@ -606,11 +606,11 @@ WriteSectWithRetr:
|
||||
je @@Exit_4
|
||||
cmp [FDC_Status], 1
|
||||
je @@Err_4
|
||||
; Троекратное повторение чтени
|
||||
; Троекратное повторение чтени
|
||||
inc [ReadRepCounter]
|
||||
cmp [ReadRepCounter], 3
|
||||
jb @@WriteSector_1
|
||||
; Троекратное повторение рекалибровки
|
||||
; Троекратное повторение рекалибровки
|
||||
call RecalibrateFDD
|
||||
call SeekTrack
|
||||
inc [RecalRepCounter]
|
||||
@ -625,7 +625,7 @@ WriteSectWithRetr:
|
||||
ret
|
||||
|
||||
;*********************************************
|
||||
;* ПОЛУЧИТЬ ИНФОРМАЦИЮ О РЕЗУЛЬТАТЕ ОПЕРАЦИИ *
|
||||
;* ПОЛУЧИТЬ ИНФОРМАЦИЮ О РЕЗУЛЬТАТЕ ОПЕРАЦИИ *
|
||||
;*********************************************
|
||||
GetStatusInfo:
|
||||
push AX
|
||||
|
@ -109,28 +109,28 @@ hd_read_pio:
|
||||
xor eax, eax
|
||||
mov edx, [hdbase]
|
||||
inc edx
|
||||
out dx, al; ATAFeatures ॣ¨áâà "®á®¡¥®á⥩"
|
||||
out dx, al; ATAFeatures регистр "особенностей"
|
||||
inc edx
|
||||
inc eax
|
||||
out dx, al; ATASectorCount áçñâ稪 ᥪâ®à®¢
|
||||
out dx, al; ATASectorCount счётчик секторов
|
||||
inc edx
|
||||
mov eax, [esp+4]
|
||||
out dx, al; ATASectorNumber ॣ¨áâà ®¬¥à ᥪâ®à
|
||||
out dx, al; ATASectorNumber регистр номера сектора
|
||||
shr eax, 8
|
||||
inc edx
|
||||
out dx, al; ATACylinder ®¬¥à 樫¨¤à (¬« ¤è¨© ¡ ©â)
|
||||
out dx, al; ATACylinder номер цилиндра (младший байт)
|
||||
shr eax, 8
|
||||
inc edx
|
||||
out dx, al; ®¬¥à 樫¨¤à (áâ à訩 ¡ ©â)
|
||||
out dx, al; номер цилиндра (старший байт)
|
||||
shr eax, 8
|
||||
inc edx
|
||||
and al, 1+2+4+8
|
||||
add al, byte [hdid]
|
||||
add al, 128+64+32
|
||||
out dx, al; ®¬¥à £®«®¢ª¨/®¬¥à ¤¨áª
|
||||
out dx, al; номер головки/номер диска
|
||||
inc edx
|
||||
mov al, 20h
|
||||
out dx, al; ATACommand ॣ¨áâà ª®¬ ¤
|
||||
out dx, al; ATACommand регистр команд
|
||||
sti
|
||||
|
||||
call wait_for_sector_buffer
|
||||
|
@ -52,7 +52,7 @@ write_cache_more:
|
||||
cmp [dma_hdd], 1
|
||||
jnz .nodma
|
||||
@@:
|
||||
; Ž¡ê¥¤¨ï¥¬ § ¯¨áì 楯®çª¨ ¯®á«¥¤®¢ ⥫ìëå ᥪâ®à®¢ ¢ ®¤® ®¡à 饨¥ ª ¤¨áªã
|
||||
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
|
||||
cmp ecx, 1
|
||||
jz .nonext
|
||||
cmp dword [esi+8+4], 2
|
||||
|
@ -346,10 +346,10 @@ uni2ansi_str:
|
||||
mov al, '_'
|
||||
jmp .doit
|
||||
.yo1:
|
||||
mov al, 'ð'
|
||||
mov al, 0xF0 ; 'Ё'
|
||||
jmp .doit
|
||||
.yo2:
|
||||
mov al, 'ñ'
|
||||
mov al, 0xF1 ; 'ё'
|
||||
jmp .doit
|
||||
.rus1:
|
||||
; 0x410-0x43F -> 0x80-0xAF
|
||||
@ -389,9 +389,9 @@ ansi2uni_char:
|
||||
; 0xF0 -> 0x401
|
||||
; 0xF1 -> 0x451
|
||||
@@:
|
||||
cmp al, 'ð'
|
||||
cmp al, 0xF0 ; 'Ё'
|
||||
jz .yo1
|
||||
cmp al, 'ñ'
|
||||
cmp al, 0xF1 ; 'ё'
|
||||
jz .yo2
|
||||
.unk:
|
||||
mov al, '_' ; ah=0
|
||||
@ -411,16 +411,16 @@ char_toupper:
|
||||
jb .ret
|
||||
cmp al, 'z'
|
||||
jbe .az
|
||||
cmp al, 'ñ'
|
||||
cmp al, 0xF1 ; 'ё'
|
||||
jz .yo1
|
||||
cmp al, ' '
|
||||
cmp al, 0xA0 ; 'а'
|
||||
jb .ret
|
||||
cmp al, 'à'
|
||||
cmp al, 0xE0 ; 'р'
|
||||
jb .rus1
|
||||
cmp al, 'ï'
|
||||
cmp al, 0xEF ; 'я'
|
||||
ja .ret
|
||||
; 0xE0-0xEF -> 0x90-0x9F
|
||||
sub al, 'à'-'<27>'
|
||||
sub al, 0xE0-0x90
|
||||
.ret:
|
||||
ret
|
||||
.rus1:
|
||||
|
@ -428,7 +428,7 @@ sayerr:
|
||||
.nopci:
|
||||
; \end{Mario79}
|
||||
|
||||
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
|
||||
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
|
||||
out 0x60, al
|
||||
xor cx, cx
|
||||
wait_loop: ; variant 2
|
||||
@ -847,21 +847,21 @@ end if
|
||||
xor dx, dx
|
||||
div bx
|
||||
if lang eq ru
|
||||
; Ї®¤®¦¤ЁвҐ 5 ᥪг¤, 4/3/2 ᥪг¤л, 1 ᥪг¤г
|
||||
; подождите 5 секунд, 4/3/2 секунды, 1 секунду
|
||||
cmp al, 5
|
||||
mov cl, ' '
|
||||
jae @f
|
||||
cmp al, 1
|
||||
mov cl, 'г'
|
||||
mov cl, 0xE3 ; 'у' in cp866
|
||||
jz @f
|
||||
mov cl, 'л'
|
||||
mov cl, 0xEB ; 'ы' in cp866
|
||||
@@:
|
||||
mov [time_str+9], cl
|
||||
else if lang eq et
|
||||
cmp al, 1
|
||||
ja @f
|
||||
mov [time_str+9], ' '
|
||||
mov [time_str+10], ' '
|
||||
mov byte [time_str+9], ' '
|
||||
mov byte [time_str+10], ' '
|
||||
@@:
|
||||
else if lang eq sp
|
||||
; esperar 5/4/3/2 segundos, 1 segundo
|
||||
|
@ -89,11 +89,11 @@ save_quest db "Remember current settings? [y/n]: ",0
|
||||
loader_block_error db "Bootloader data invalid, I cannot continue. Stopped.",0
|
||||
end if
|
||||
|
||||
_st db 186,' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿',13,10,0
|
||||
_r1 db 186,' ³ 320x200 EGA/CGA 256 colors ³ ³',13,10,0
|
||||
_r2 db 186,' ³ 640x480 VGA 16 colors ³ ³',13,10,0
|
||||
_rs db 186,' ³ ????x????@?? SVGA VESA ³ ³',13,10,0
|
||||
_bt db 186,' ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ',13,10,0
|
||||
_st:latin1 '║ ┌───────────────────────────────┬─┐',13,10,0
|
||||
_r1:latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0
|
||||
_r2:latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0
|
||||
_rs:latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0
|
||||
_bt:latin1 '║ └───────────────────────────────┴─┘',13,10,0
|
||||
|
||||
remark1 db "Default values were selected to match most of configurations, but not all.",0
|
||||
remark2 db "If the system does not boot, try to disable the item [b].",0
|
||||
|
@ -15,87 +15,87 @@ $Revision$
|
||||
|
||||
|
||||
d80x25_bottom:
|
||||
db 186,' KolibriOS pohineb MenuetOS ja kaasas IGASUGUSE GARANTI'
|
||||
db 'ITA ',186
|
||||
db 186,' Naha faili COPYING detailid '
|
||||
db ' ',186
|
||||
latin1 '║ KolibriOS pohineb MenuetOS ja kaasas IGASUGUSE GARANTI'
|
||||
latin1 'ITA ║'
|
||||
latin1 '║ Naha faili COPYING detailid '
|
||||
latin1 ' ║'
|
||||
line_full_bottom
|
||||
d80x25_bottom_num = 3
|
||||
|
||||
msg_apm db " APM x.x ", 0
|
||||
novesa db "Ekraan: EGA/CGA",13,10,0
|
||||
s_vesa db "Vesa versioon: "
|
||||
msg_apm: latin1 " APM x.x ", 0
|
||||
novesa: latin1 "Ekraan: EGA/CGA",13,10,0
|
||||
s_vesa: latin1 "Vesa versioon: "
|
||||
.ver db "?.?",13,10,0
|
||||
|
||||
gr_mode db "Vali videomode: ",13,10,0
|
||||
gr_mode: latin1 "Vali videomode: ",13,10,0
|
||||
|
||||
ask_bd db "Lisa kettad nahtavaks BIOS reziim V86? [1-jah, 2-no]: ",0
|
||||
ask_bd: latin1 "Lisa kettad nahtavaks BIOS reziim V86? [1-jah, 2-no]: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
bdev db "Paigalda mäluketas [1-diskett; 2-kolibri.img]: ",0
|
||||
bdev: latin1 "Paigalda mäluketas [1-diskett; 2-kolibri.img]: ",0
|
||||
else
|
||||
bdev db "Paigalda mäluketas [1-diskett; 2-C:\kolibri.img (FAT32);"
|
||||
db 13,10,186," "
|
||||
db "3-kasuta eellaaditud mäluketast kerneli restardist;"
|
||||
db 13,10,186," "
|
||||
db "4-loo tühi pilt]: ",0
|
||||
bdev: latin1 "Paigalda mäluketas [1-diskett; 2-C:\kolibri.img (FAT32);"
|
||||
latin1 13,10,"║ "
|
||||
latin1 "3-kasuta eellaaditud mäluketast kerneli restardist;"
|
||||
latin1 13,10,"║ "
|
||||
latin1 "4-loo tühi pilt]: ",0
|
||||
end if
|
||||
|
||||
prnotfnd db "Fataalne - Videoreziimi ei leitud.",0
|
||||
prnotfnd: latin1 "Fataalne - Videoreziimi ei leitud.",0
|
||||
|
||||
not386 db "Fataalne - CPU 386+ on vajalik.",0
|
||||
fatalsel db "Fataalne - Graafilist reziimi riistvara ei toeta.",0
|
||||
pres_key db "Vajutage suvalist klahvi, et valida uus videomode.",0
|
||||
badsect db 13,10,186," Fataalne - Vigane sektor. Asenda diskett.",0
|
||||
memmovefailed db 13,10,186," Fataalne - Int 0x15 liigutamine ebaõnnestus.",0
|
||||
okt db " ... OK"
|
||||
linef db 13,10,0
|
||||
diskload db "Loen disketti: 00 %",8,8,8,8,0
|
||||
pros db "00"
|
||||
backspace2 db 8,8,0
|
||||
not386: latin1 "Fataalne - CPU 386+ on vajalik.",0
|
||||
fatalsel: latin1 "Fataalne - Graafilist reziimi riistvara ei toeta.",0
|
||||
pres_key: latin1 "Vajutage suvalist klahvi, et valida uus videomode.",0
|
||||
badsect: latin1 13,10,"║ Fataalne - Vigane sektor. Asenda diskett.",0
|
||||
memmovefailed:latin1 13,10,"║ Fataalne - Int 0x15 liigutamine ebaõnnestus.",0
|
||||
okt: latin1 " ... OK"
|
||||
linef: latin1 13,10,0
|
||||
diskload: latin1 "Loen disketti: 00 %",8,8,8,8,0
|
||||
pros: latin1 "00"
|
||||
backspace2:latin1 8,8,0
|
||||
boot_dev db 0 ; 0=floppy, 1=hd
|
||||
start_msg db "Vajuta [abcd] seadete muutmiseks, vajuta [Enter] laadimise jätkamiseks",13,10,0
|
||||
time_msg db " või oota "
|
||||
time_str db " 5 sekundit"
|
||||
db " automaatseks jätkamiseks",13,10,0
|
||||
current_cfg_msg db "Praegused seaded:",13,10,0
|
||||
curvideo_msg db " [a] Videoreziim: ",0
|
||||
start_msg:latin1 "Vajuta [abcd] seadete muutmiseks, vajuta [Enter] laadimise jätkamiseks",13,10,0
|
||||
time_msg: latin1 " või oota "
|
||||
time_str: latin1 " 5 sekundit"
|
||||
latin1 " automaatseks jätkamiseks",13,10,0
|
||||
current_cfg_msg:latin1 "Praegused seaded:",13,10,0
|
||||
curvideo_msg:latin1 " [a] Videoreziim: ",0
|
||||
|
||||
mode0 db "320x200, EGA/CGA 256 värvi",0
|
||||
mode9 db "640x480, VGA 16 värvi",0
|
||||
mode0: latin1 "320x200, EGA/CGA 256 värvi",0
|
||||
mode9: latin1 "640x480, VGA 16 värvi",0
|
||||
|
||||
usebd_msg db " [b] Lisa kettad nahtavaks BIOS:",0
|
||||
on_msg db " sees",13,10,0
|
||||
off_msg db " väljas",13,10,0
|
||||
usebd_msg:latin1 " [b] Lisa kettad nahtavaks BIOS:",0
|
||||
on_msg: latin1 " sees",13,10,0
|
||||
off_msg: latin1 " väljas",13,10,0
|
||||
|
||||
preboot_device_msg db " [c] Disketi kujutis: ",0
|
||||
preboot_device_msg:latin1 " [c] Disketi kujutis: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,0
|
||||
pdm1 db "reaalne diskett",13,10,0
|
||||
pdm2 db "kolibri.img",13,10,0
|
||||
pdm1: latin1 "reaalne diskett",13,10,0
|
||||
pdm2: latin1 "kolibri.img",13,10,0
|
||||
else
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,pdm3
|
||||
pdm1 db "reaalne diskett",13,10,0
|
||||
pdm2 db "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3 db "kasuta juba laaditud kujutist",13,10,0
|
||||
pdm4 db "loo tühi pilt",13,10,0
|
||||
pdm1: latin1 "reaalne diskett",13,10,0
|
||||
pdm2: latin1 "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3: latin1 "kasuta juba laaditud kujutist",13,10,0
|
||||
pdm4: latin1 "loo tühi pilt",13,10,0
|
||||
end if
|
||||
|
||||
loading_msg db "Laadin KolibriOS...",0
|
||||
loading_msg:latin1 "Laadin KolibriOS...",0
|
||||
|
||||
if ~ defined extended_primary_loader
|
||||
save_quest db "Jäta meelde praegused seaded? [y/n]: ",0
|
||||
loader_block_error db "Alglaaduri andmed vigased, ei saa jätkata. Peatatud.",0
|
||||
save_quest:latin1 "Jäta meelde praegused seaded? [y/n]: ",0
|
||||
loader_block_error:latin1 "Alglaaduri andmed vigased, ei saa jätkata. Peatatud.",0
|
||||
end if
|
||||
|
||||
_st db 186,' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿',13,10,0
|
||||
_r1 db 186,' ³ 320x200 EGA/CGA 256 colors ³ ³',13,10,0
|
||||
_r2 db 186,' ³ 640x480 VGA 16 colors ³ ³',13,10,0
|
||||
_rs db 186,' ³ ????x????@?? SVGA VESA ³ ³',13,10,0
|
||||
_bt db 186,' ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ',13,10,0
|
||||
_st:latin1 '║ ┌───────────────────────────────┬─┐',13,10,0
|
||||
_r1:latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0
|
||||
_r2:latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0
|
||||
_rs:latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0
|
||||
_bt:latin1 '║ └───────────────────────────────┴─┘',13,10,0
|
||||
|
||||
remark1 db "Vaikimisi maaratud vaartused on valitud mugavuse enamikes, kuid mitte koik.",0
|
||||
remark2 db "Kui susteem ei kaivitu, proovige lulitada kirje [b].",0
|
||||
remark1:latin1 "Vaikimisi maaratud vaartused on valitud mugavuse enamikes, kuid mitte koik.",0
|
||||
remark2:latin1 "Kui susteem ei kaivitu, proovige lulitada kirje [b].",0
|
||||
remarks dw remark1, remark2
|
||||
num_remarks = 2
|
||||
|
@ -89,11 +89,11 @@ save_quest db "Aktuelle Einstellungen speichern? [y/n]: ",0
|
||||
loader_block_error db "Bootloader Daten ungueltig, Kann nicht fortfahren. Angehalten.",0
|
||||
end if
|
||||
|
||||
_st db 186,' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿',13,10,0
|
||||
_r1 db 186,' ³ 320x200 EGA/CGA 256 colors ³ ³',13,10,0
|
||||
_r2 db 186,' ³ 640x480 VGA 16 colors ³ ³',13,10,0
|
||||
_rs db 186,' ³ ????x????@?? SVGA VESA ³ ³',13,10,0
|
||||
_bt db 186,' ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ',13,10,0
|
||||
_st:latin1 '║ ┌───────────────────────────────┬─┐',13,10,0
|
||||
_r1:latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0
|
||||
_r2:latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0
|
||||
_rs:latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0
|
||||
_bt:latin1 '║ └───────────────────────────────┴─┘',13,10,0
|
||||
|
||||
remark1 db "Die Standardwerte sind fur die meisten gewahlt, aber nicht fur jedermann.",0
|
||||
remark2 db "Wenn das System nicht bootet, versuchen, das Element [b] deaktivieren.",0
|
||||
|
@ -15,87 +15,83 @@ $Revision$
|
||||
|
||||
|
||||
d80x25_bottom:
|
||||
db 186,' KolibriOS ®á®¢ MenuetOS ¨ <20>… <20><>…„Ž‘’€‚‹Ÿ…’ <20>ˆ'
|
||||
db 'Š€Šˆ• ƒ€<C692>A<EFBFBD>’ˆ‰. ',186
|
||||
db 186,' <20>®¤à®¡¥¥ ᬮâà¨â¥ ¢ ä ©«¥ COPYING.TXT '
|
||||
db ' ',186
|
||||
cp866 '║ KolibriOS основана на MenuetOS и НЕ ПРЕДОСТАВЛЯЕТ НИКАКИХ ГАРAНТИЙ. ║'
|
||||
cp866 '║ Подробнее смотрите в файле COPYING.TXT ║'
|
||||
line_full_bottom
|
||||
d80x25_bottom_num = 3
|
||||
|
||||
msg_apm db " APM x.x ", 0
|
||||
novesa db "‚¨¤¥®ª àâ : EGA/CGA",13,10,0
|
||||
s_vesa db "‚¥àá¨ï VESA: "
|
||||
.ver db "?.?",13,10,0
|
||||
msg_apm: cp866 " APM x.x ", 0
|
||||
novesa: cp866 "Видеокарта: EGA/CGA",13,10,0
|
||||
s_vesa: cp866 "Версия VESA: "
|
||||
.ver db "?.?",13,10,0
|
||||
|
||||
gr_mode db "‚ë¡¥à¨â¥ ¢¨¤¥®à¥¦¨¬: ",13,10,0
|
||||
gr_mode: cp866 "Выберите видеорежим: ",13,10,0
|
||||
|
||||
ask_bd db "„®¡ ¢¨âì ¤¨áª¨, ¢¨¤¨¬ë¥ ç¥à¥§ BIOS ¢ ०¨¬¥ V86? [1-¤ , 2-¥â]: ",0
|
||||
ask_bd: cp866 "Добавить диски, видимые через BIOS в режиме V86? [1-да, 2-нет]: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
bdev db "‡ £à㧨âì ®¡à § ¨§ [1-¤¨áª¥â ; 2-kolibri.img ¨§ ¯ ¯ª¨ § £à㧪¨]: ",0
|
||||
bdev: cp866 "Загрузить образ из [1-дискета; 2-kolibri.img из папки загрузки]: ",0
|
||||
else
|
||||
bdev db "‡ £à㧨âì ®¡à § ¨§ [1-¤¨áª¥â ; 2-C:\kolibri.img (FAT32);"
|
||||
db 13,10,186," "
|
||||
db "3-¨á¯®«ì§®¢ âì 㦥 § £àã¦¥ë© ®¡à §;"
|
||||
db 13,10,186," "
|
||||
db "4-ᮧ¤ âì ç¨áâë© ®¡à §]: ",0
|
||||
bdev: cp866 "Загрузить образ из [1-дискета; 2-C:\kolibri.img (FAT32);",13,10
|
||||
cp866 "║ 3-использовать уже загруженный образ;",13,10
|
||||
cp866 "║ 4-создать чистый образ]: ",0
|
||||
end if
|
||||
|
||||
prnotfnd db "Žè¨¡ª - ‚¨¤¥®à¥¦¨¬ ¥ ©¤¥.",0
|
||||
prnotfnd: cp866 "Ошибка - Видеорежим не найден.",0
|
||||
|
||||
not386 db "Žè¨¡ª - ’ॡã¥âáï ¯à®æ¥áá®à 386+.",0
|
||||
fatalsel db "Žè¨¡ª - ‚ë¡à ë© ¢¨¤¥®à¥¦¨¬ ¥ ¯®¤¤¥à¦¨¢ ¥âáï.",0
|
||||
pres_key db "<EFBFBD> ¦¨¬¨â¥ «î¡ãî ª« ¢¨èã, ¤«ï ¯¥à¥å®¤ ¢ ¢ë¡®à ०¨¬®¢.",0
|
||||
badsect db 13,10,186," Žè¨¡ª - „¨áª¥â ¯®¢à¥¦¤¥ . <20>®¯à®¡ã©â¥ ¤àã£ãî.",0
|
||||
memmovefailed db 13,10,186," Žè¨¡ª - Int 0x15 move failed.",0
|
||||
okt db " ... OK"
|
||||
linef db 13,10,0
|
||||
diskload db "‡ £à㧪 ¤¨áª¥âë: 00 %",8,8,8,8,0
|
||||
pros db "00"
|
||||
backspace2 db 8,8,0
|
||||
boot_dev db 0
|
||||
start_msg db "<EFBFBD> ¦¬¨â¥ [abcd] ¤«ï ¨§¬¥¥¨ï áâ஥ª, [Enter] ¤«ï ¯à®¤®«¦¥¨ï § £à㧪¨",13,10,0
|
||||
time_msg db " ¨«¨ ¯®¤®¦¤¨â¥ "
|
||||
time_str db " 5 ᥪ㤠"
|
||||
db " ¤® ¢â®¬ â¨ç¥áª®£® ¯à®¤®«¦¥¨ï",13,10,0
|
||||
current_cfg_msg db "’¥ªã騥 áâனª¨:",13,10,0
|
||||
curvideo_msg db " [a] ‚¨¤¥®à¥¦¨¬: ",0
|
||||
not386: cp866 "Ошибка - Требуется процессор 386+.",0
|
||||
fatalsel: cp866 "Ошибка - Выбранный видеорежим не поддерживается.",0
|
||||
pres_key: cp866 "Нажимите любую клавишу, для перехода в выбор режимов.",0
|
||||
badsect: cp866 13,10,"║ Ошибка - Дискета повреждена. Попробуйте другую.",0
|
||||
memmovefailed:cp866 13,10,"║ Ошибка - Int 0x15 move failed.",0
|
||||
okt: cp866 " ... OK"
|
||||
linef: cp866 13,10,0
|
||||
diskload: cp866 "Загрузка дискеты: 00 %",8,8,8,8,0
|
||||
pros: cp866 "00"
|
||||
backspace2:cp866 8,8,0
|
||||
boot_dev db 0
|
||||
start_msg:cp866 "Нажмите [abcd] для изменения настроек, [Enter] для продолжения загрузки",13,10,0
|
||||
time_msg: cp866 " или подождите "
|
||||
time_str: cp866 " 5 секунд "
|
||||
cp866 " до автоматического продолжения",13,10,0
|
||||
current_cfg_msg:cp866 "Текущие настройки:",13,10,0
|
||||
curvideo_msg:cp866 " [a] Видеорежим: ",0
|
||||
|
||||
mode0 db "320x200, EGA/CGA 256 梥⮢",13,10,0
|
||||
mode9 db "640x480, VGA 16 梥⮢",13,10,0
|
||||
mode0: cp866 "320x200, EGA/CGA 256 цветов",13,10,0
|
||||
mode9: cp866 "640x480, VGA 16 цветов",13,10,0
|
||||
|
||||
usebd_msg db " [b] „®¡ ¢¨âì ¤¨áª¨, ¢¨¤¨¬ë¥ ç¥à¥§ BIOS:",0
|
||||
on_msg db " ¢ª«",13,10,0
|
||||
off_msg db " ¢ëª«",13,10,0
|
||||
usebd_msg:cp866 " [b] Добавить диски, видимые через BIOS:",0
|
||||
on_msg: cp866 " вкл",13,10,0
|
||||
off_msg: cp866 " выкл",13,10,0
|
||||
|
||||
preboot_device_msg db " [c] Ž¡à § ¤¨áª¥âë: ",0
|
||||
preboot_device_msg:cp866 " [c] Образ дискеты: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,0
|
||||
pdm1 db " áâ®ïé ï ¤¨áª¥â ",13,10,0
|
||||
pdm2 db "kolibri.img ¨§ ¯ ¯ª¨ § £à㧪¨",13,10,0
|
||||
pdm1: cp866 "настоящая дискета",13,10,0
|
||||
pdm2: cp866 "kolibri.img из папки загрузки",13,10,0
|
||||
else
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4
|
||||
pdm1 db " áâ®ïé ï ¤¨áª¥â ",13,10,0
|
||||
pdm2 db "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3 db "¨á¯®«ì§®¢ âì 㦥 § £àã¦¥ë© ®¡à §",13,10,0
|
||||
pdm4 db "ᮧ¤ âì ç¨áâë© ®¡à §",13,10,0
|
||||
pdm1: cp866 "настоящая дискета",13,10,0
|
||||
pdm2: cp866 "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3: cp866 "использовать уже загруженный образ",13,10,0
|
||||
pdm4: cp866 "создать чистый образ",13,10,0
|
||||
end if
|
||||
|
||||
loading_msg db "ˆ¤ñâ § £à㧪 KolibriOS...",0
|
||||
loading_msg:cp866 "Идёт загрузка KolibriOS...",0
|
||||
|
||||
if ~ defined extended_primary_loader ; saving not supported in this case
|
||||
save_quest db "‡ ¯®¬¨âì ⥪ã騥 áâனª¨? [y/n]: ",0
|
||||
loader_block_error db "Žè¨¡ª ¢ ¤ ëå ç «ì®£® § £àã§ç¨ª , ¯à®¤®«¦¥¨¥ ¥¢®§¬®¦®.",0
|
||||
save_quest:cp866 "Запомнить текущие настройки? [y/n]: ",0
|
||||
loader_block_error:cp866 "Ошибка в данных начального загрузчика, продолжение невозможно.",0
|
||||
end if
|
||||
|
||||
_st db 186,' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ',13,10,0
|
||||
_r1 db 186,' ³ 320x200 EGA/CGA 256 梥⮢ ³ ³ ',13,10,0
|
||||
_r2 db 186,' ³ 640x480 VGA 16 梥⮢ ³ ³ ',13,10,0
|
||||
_rs db 186,' ³ ????x????@?? SVGA VESA ³ ³ ',13,10,0
|
||||
_bt db 186,' ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ',13,10,0
|
||||
_st:cp866 '║ ┌───────────────────────────────┬─┐ ',13,10,0
|
||||
_r1:cp866 '║ │ 320x200 EGA/CGA 256 цветов │ │ ',13,10,0
|
||||
_r2:cp866 '║ │ 640x480 VGA 16 цветов │ │ ',13,10,0
|
||||
_rs:cp866 '║ │ ????x????@?? SVGA VESA │ │ ',13,10,0
|
||||
_bt:cp866 '║ └───────────────────────────────┴─┘ ',13,10,0
|
||||
|
||||
remark1 db "‡ ç¥¨ï ¯® 㬮«ç ¨î ¢ë¡à ë ¤«ï 㤮¡á⢠¡®«ìè¨á⢠, ® ¥ ¢á¥å.",0
|
||||
remark2 db "…᫨ ã ‚ á ¥ £à㧨âáï á¨á⥬ , ¯®¯à®¡ã©â¥ ®âª«îç¨âì ¯ãªâ [b].",0
|
||||
remark1:cp866 "Значения по умолчанию выбраны для удобства большинства, но не всех.",0
|
||||
remark2:cp866 "Если у Вас не грузится система, попробуйте отключить пункт [b].",0
|
||||
remarks dw remark1, remark2
|
||||
num_remarks = 2
|
||||
|
@ -11,93 +11,93 @@
|
||||
;
|
||||
;======================================================================
|
||||
|
||||
; Para modificar ‚ste archivo es necesario abrirlo con codificaci¢n CP850
|
||||
; Para modificar éste archivo es necesario abrirlo con codificación CP850
|
||||
|
||||
$Revision: 2455 $
|
||||
|
||||
|
||||
d80x25_bottom:
|
||||
db 186,' KolibriOS est basado en MenuetOS y viene ABSOLUTAMENTE '
|
||||
db 'SIN GARANT¡A ',186
|
||||
db 186,' Lee el archivo COPYING por m s detalles '
|
||||
db ' ',186
|
||||
cp850 '║ KolibriOS está basado en MenuetOS y viene ABSOLUTAMENTE '
|
||||
cp850 'SIN GARANTíA ║'
|
||||
cp850 '║ Lee el archivo COPYING por más detalles '
|
||||
cp850 ' ║'
|
||||
line_full_bottom
|
||||
d80x25_bottom_num = 3
|
||||
|
||||
msg_apm db " APM x.x ", 0
|
||||
novesa db "Monitor: EGA/CGA",13,10,0
|
||||
s_vesa db "Versi¢n de VESA: "
|
||||
msg_apm: cp850 " APM x.x ", 0
|
||||
novesa: cp850 "Monitor: EGA/CGA",13,10,0
|
||||
s_vesa: cp850 "Versión de VESA: "
|
||||
.ver db "?.?",13,10,0
|
||||
|
||||
gr_mode db "Selecciona un modo de video: ",13,10,0
|
||||
gr_mode: cp850 "Selecciona un modo de video: ",13,10,0
|
||||
|
||||
ask_bd db "¨Agregar discos visibles por el BIOS emulados en modo V86? [1-si, 2-no]: ",0
|
||||
ask_bd: cp850 "¿Agregar discos visibles por el BIOS emulados en modo V86? [1-si, 2-no]: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
bdev db "Cargar unidad ram desde [1-disquete; 2-kolibri.img]: ",0
|
||||
bdev: cp850 "Cargar unidad ram desde [1-disquete; 2-kolibri.img]: ",0
|
||||
else
|
||||
bdev db "Cargar unidad ram desde [1-disquete; 2-C:\kolibri.img (FAT32);"
|
||||
db 13,10,186," "
|
||||
db "3-usar imagen precargada en el reinicio del n£cleo;"
|
||||
db 13,10,186," "
|
||||
db "4-crear imagen vac¡a]: ",0
|
||||
bdev: cp850 "Cargar unidad ram desde [1-disquete; 2-C:\kolibri.img (FAT32);"
|
||||
cp850 13,10,"║ "
|
||||
cp850 "3-usar imagen precargada en el reinicio del núcleo;"
|
||||
cp850 13,10,"║ "
|
||||
cp850 "4-crear imagen vacía]: ",0
|
||||
end if
|
||||
|
||||
prnotfnd db "Fatal - Modo de video no encontrado.",0
|
||||
prnotfnd: cp850 "Fatal - Modo de video no encontrado.",0
|
||||
|
||||
not386 db "Fatal - CPU 386+ requerido.",0
|
||||
fatalsel db "Fatal - Modo de gr ficos no soportado por hardware.",0
|
||||
pres_key db "Presiona una tecla para seleccionar otro modo de video.",0
|
||||
badsect db 13,10,186," Fatal - Sector mal. Reemplaze el disquete.",0
|
||||
memmovefailed db 13,10,186," Fatal - Int 0x15 move failed.",0
|
||||
okt db " ... BIEN"
|
||||
linef db 13,10,0
|
||||
diskload db "Cargando disquete: 00 %",8,8,8,8,0
|
||||
pros db "00"
|
||||
backspace2 db 8,8,0
|
||||
not386: cp850 "Fatal - CPU 386+ requerido.",0
|
||||
fatalsel: cp850 "Fatal - Modo de gráficos no soportado por hardware.",0
|
||||
pres_key: cp850 "Presiona una tecla para seleccionar otro modo de video.",0
|
||||
badsect: cp850 13,10,"║ Fatal - Sector mal. Reemplaze el disquete.",0
|
||||
memmovefailed:cp850 13,10,"║ Fatal - Int 0x15 move failed.",0
|
||||
okt: cp850 " ... BIEN"
|
||||
linef: cp850 13,10,0
|
||||
diskload: cp850 "Cargando disquete: 00 %",8,8,8,8,0
|
||||
pros: cp850 "00"
|
||||
backspace2:cp850 8,8,0
|
||||
boot_dev db 0 ; 0=floppy, 1=hd
|
||||
start_msg db "Presiona [abcd] para cambiar la configuraci¢n, [Enter] para continuar",13,10,0
|
||||
time_msg db " o espera "
|
||||
time_str db " 5 segundos"
|
||||
db " para que inicie autom ticamente",13,10,0
|
||||
current_cfg_msg db "Configuraci¢n actual:",13,10,0
|
||||
curvideo_msg db " [a] Modo de video: ",0
|
||||
start_msg:cp850 "Presiona [abcd] para cambiar la configuración, [Enter] para continuar",13,10,0
|
||||
time_msg: cp850 " o espera "
|
||||
time_str: cp850 " 5 segundos"
|
||||
cp850 " para que inicie automáticamente",13,10,0
|
||||
current_cfg_msg:cp850 "Configuración actual:",13,10,0
|
||||
curvideo_msg:cp850 " [a] Modo de video: ",0
|
||||
|
||||
mode0 db "320x200, EGA/CGA 256 colores",13,10,0
|
||||
mode9 db "640x480, VGA 16 colores",13,10,0
|
||||
mode0: cp850 "320x200, EGA/CGA 256 colores",13,10,0
|
||||
mode9: cp850 "640x480, VGA 16 colores",13,10,0
|
||||
|
||||
usebd_msg db " [b] Agregar discos visibles por el BIOS:",0
|
||||
on_msg db " activado",13,10,0
|
||||
off_msg db " desactivado",13,10,0
|
||||
usebd_msg:cp850 " [b] Agregar discos visibles por el BIOS:",0
|
||||
on_msg: cp850 " activado",13,10,0
|
||||
off_msg: cp850 " desactivado",13,10,0
|
||||
|
||||
preboot_device_msg db " [c] Imagen de disquete: ",0
|
||||
preboot_device_msg:cp850 " [c] Imagen de disquete: ",0
|
||||
|
||||
if defined extended_primary_loader
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,0
|
||||
pdm1 db "disquete real",13,10,0
|
||||
pdm2 db "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm1: cp850 "disquete real",13,10,0
|
||||
pdm2: cp850 "C:\kolibri.img (FAT32)",13,10,0
|
||||
else
|
||||
preboot_device_msgs dw 0,pdm1,pdm2,pdm3
|
||||
pdm1 db "disquete real",13,10,0
|
||||
pdm2 db "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3 db "usar imagen ya cargada",13,10,0
|
||||
pdm4 db "crear imagen vac¡a",13,10,0
|
||||
pdm1: cp850 "disquete real",13,10,0
|
||||
pdm2: cp850 "C:\kolibri.img (FAT32)",13,10,0
|
||||
pdm3: cp850 "usar imagen ya cargada",13,10,0
|
||||
pdm4: cp850 "crear imagen vacía",13,10,0
|
||||
end if
|
||||
|
||||
loading_msg db "Cargando KolibriOS...",0
|
||||
loading_msg:cp850 "Cargando KolibriOS...",0
|
||||
|
||||
if ~ defined extended_primary_loader
|
||||
save_quest db "¨Recordar configuraci¢n actual? [s/n]: ",0
|
||||
loader_block_error db "Bootloader inv lido, no puedo continuar. Detenido.",0
|
||||
save_quest:cp850 "¿Recordar configuración actual? [s/n]: ",0
|
||||
loader_block_error:cp850 "Bootloader inválido, no puedo continuar. Detenido.",0
|
||||
end if
|
||||
|
||||
_st db 186,' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿',13,10,0
|
||||
_r1 db 186,' ³ 320x200 EGA/CGA 256 colores ³ ³',13,10,0
|
||||
_r2 db 186,' ³ 640x480 VGA 16 colores ³ ³',13,10,0
|
||||
_rs db 186,' ³ ????x????@?? SVGA VESA ³ ³',13,10,0
|
||||
_bt db 186,' ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ',13,10,0
|
||||
_st:cp850 '║ ┌───────────────────────────────┬─┐',13,10,0
|
||||
_r1:cp850 '║ │ 320x200 EGA/CGA 256 colores │ │',13,10,0
|
||||
_r2:cp850 '║ │ 640x480 VGA 16 colores │ │',13,10,0
|
||||
_rs:cp850 '║ │ ????x????@?? SVGA VESA │ │',13,10,0
|
||||
_bt:cp850 '║ └───────────────────────────────┴─┘',13,10,0
|
||||
|
||||
remark1 db "Los valores por defecto puede que no funcionen en algunas configuraciones.",0
|
||||
remark2 db "Si el sistema no inicia, prueba deshabilitar la opci¢n [b].",0
|
||||
remark1:cp850 "Los valores por defecto puede que no funcionen en algunas configuraciones.",0
|
||||
remark2:cp850 "Si el sistema no inicia, prueba deshabilitar la opción [b].",0
|
||||
remarks dw remark1, remark2
|
||||
num_remarks = 2
|
||||
|
@ -78,7 +78,7 @@ virtual at $A000
|
||||
mi VBE_ModeInfo
|
||||
modes_table:
|
||||
end virtual
|
||||
cursor_pos dw 0 ;âðåìåííîå õðàíåíèå êóðñîðà.
|
||||
cursor_pos dw 0 ;временное хранение курсора.
|
||||
home_cursor dw 0 ;current shows rows a table
|
||||
end_cursor dw 0 ;end of position current shows rows a table
|
||||
scroll_start dw 0 ;start position of scroll bar
|
||||
@ -189,7 +189,7 @@ calc_vmodes_table:
|
||||
lfs si, [es:vi.VideoModePtr]
|
||||
|
||||
mov bx, modes_table
|
||||
;save no vesa mode of work 320x200, EGA/CGA 256 梥⮢ and 640x480, VGA 16 梥⮢
|
||||
;save no vesa mode of work 320x200, EGA/CGA 256 梥⮢ and 640x480, VGA 16 梥⮢
|
||||
mov word [es:bx], 640
|
||||
mov word [es:bx+2], 480
|
||||
mov word [es:bx+6], 0x13
|
||||
|
@ -9,7 +9,7 @@ $Revision$
|
||||
|
||||
|
||||
; Full ASCII code font
|
||||
; only õ and ä added
|
||||
; only õ and ä added
|
||||
; Kaitz
|
||||
ET_FNT:
|
||||
fontfile file "ETFONT.FNT"
|
||||
|
@ -11,9 +11,9 @@ $Revision$
|
||||
; Generated by RUFNT.EXE
|
||||
; By BadBugsKiller (C)
|
||||
; Modifyed by BadBugsKiller 12.01.2004 17:45
|
||||
; Шрифт уменьшен в размере и теперь состоит из 2-ух частей,
|
||||
; содержащих только символы русского алфавита.
|
||||
; символы в кодировке ASCII (ДОС'овская), кодовая страница 866.
|
||||
; Шрифт уменьшен в размере и теперь состоит из 2-ух частей,
|
||||
; содержащих только символы русского алфавита.
|
||||
; символы в кодировке ASCII (ДОС'овская), кодовая страница 866.
|
||||
RU_FNT1:
|
||||
db 0x00, 0x00, 0x1E, 0x36, 0x66, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00
|
||||
db 0x00, 0x00, 0xFE, 0x62, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00
|
||||
|
@ -24,368 +24,368 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Нет повести печальнее на свете,
|
||||
Чем повесть о заклинившем Reset'е...
|
||||
Нет повести печальнее на свете,
|
||||
Чем повесть о заклинившем Reset'е...
|
||||
|
||||
Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает
|
||||
Windows, для носителей с размером сектора 512 байт.
|
||||
Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает
|
||||
Windows, для носителей с размером сектора 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Требования для работы:
|
||||
1) Все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
4) Пути к используемым файлам не должны содержать символических ссылок NTFS
|
||||
(жёсткие ссылки допускаются).
|
||||
5) Используемые файлы не должны быть сжатыми или разреженными файлами
|
||||
(актуально для NTFS, для FAT выполнено автоматически).
|
||||
Требования для работы:
|
||||
1) Все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
4) Пути к используемым файлам не должны содержать символических ссылок NTFS
|
||||
(жёсткие ссылки допускаются).
|
||||
5) Используемые файлы не должны быть сжатыми или разреженными файлами
|
||||
(актуально для NTFS, для FAT выполнено автоматически).
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки проверялись на валидность 08.08.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
|
||||
спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys
|
||||
и file://C:/ntldr либо file://C:/bootmgr
|
||||
неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543
|
||||
официальная спецификация расширения 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
|
||||
официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx
|
||||
официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx
|
||||
формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx
|
||||
Документация в тему (ссылки проверялись на валидность 08.08.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
|
||||
спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys
|
||||
и file://C:/ntldr либо file://C:/bootmgr
|
||||
неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543
|
||||
официальная спецификация расширения 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
|
||||
официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx
|
||||
официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx
|
||||
формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx
|
||||
|
||||
=====================================================================
|
||||
|
||||
Схема используемой памяти:
|
||||
600-2000 код загрузчика (и данные)
|
||||
2000-3000 стек
|
||||
3000-3200 сектор MBR
|
||||
3200-3400 бутсектор логического диска
|
||||
3400-3C00 информация о кэше для таблиц FAT16/FAT32:
|
||||
для FAT16 - массив на 0x100 байт, каждый байт равен
|
||||
0 или 1 в зависимости от того, загружен ли
|
||||
соответствующий сектор таблицы FAT16;
|
||||
для FAT32 - 100h входов по 8 байт: 4 байта
|
||||
(две ссылки - вперёд и назад) для организации L2-списка
|
||||
всех прочитанных секторов в порядке возрастания
|
||||
последнего времени использования + 4 байта для номера
|
||||
сектора; при переполнении кэша выкидывается элемент из
|
||||
головы списка, то есть тот, к которому дольше всех
|
||||
не было обращений
|
||||
3400-3440 информация о кэше для файловых записей NTFS в
|
||||
таком же формате, как и кэш для FAT32, но на 8 входов
|
||||
3480-34C0 заголовки для кэшей записей индекса NTFS
|
||||
3500-3D00 информация о кэшах записей индекса NTFS: с каждой
|
||||
файловой записью связан свой кэш для
|
||||
соответствующего индекса
|
||||
4000-8000 место для информации об атрибутах для NTFS
|
||||
60000-80000 таблица FAT12 / место под таблицу FAT16 /
|
||||
кэш для таблицы FAT32 / кэш для структур NTFS
|
||||
80000-90000 текущий рассматриваемый кластер
|
||||
90000-92000 FAT: кэш для корневой папки
|
||||
92000-... FAT: кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
Схема используемой памяти:
|
||||
600-2000 код загрузчика (и данные)
|
||||
2000-3000 стек
|
||||
3000-3200 сектор MBR
|
||||
3200-3400 бутсектор логического диска
|
||||
3400-3C00 информация о кэше для таблиц FAT16/FAT32:
|
||||
для FAT16 - массив на 0x100 байт, каждый байт равен
|
||||
0 или 1 в зависимости от того, загружен ли
|
||||
соответствующий сектор таблицы FAT16;
|
||||
для FAT32 - 100h входов по 8 байт: 4 байта
|
||||
(две ссылки - вперёд и назад) для организации L2-списка
|
||||
всех прочитанных секторов в порядке возрастания
|
||||
последнего времени использования + 4 байта для номера
|
||||
сектора; при переполнении кэша выкидывается элемент из
|
||||
головы списка, то есть тот, к которому дольше всех
|
||||
не было обращений
|
||||
3400-3440 информация о кэше для файловых записей NTFS в
|
||||
таком же формате, как и кэш для FAT32, но на 8 входов
|
||||
3480-34C0 заголовки для кэшей записей индекса NTFS
|
||||
3500-3D00 информация о кэшах записей индекса NTFS: с каждой
|
||||
файловой записью связан свой кэш для
|
||||
соответствующего индекса
|
||||
4000-8000 место для информации об атрибутах для NTFS
|
||||
60000-80000 таблица FAT12 / место под таблицу FAT16 /
|
||||
кэш для таблицы FAT32 / кэш для структур NTFS
|
||||
80000-90000 текущий рассматриваемый кластер
|
||||
90000-92000 FAT: кэш для корневой папки
|
||||
92000-... FAT: кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется
|
||||
размещением команды install=c:\kordldr.win в первой строке config.sys;
|
||||
при этом основной загрузчик системы загружает kordldr.win как обычный
|
||||
com-файл, в какой-то сегмент по смещению 100h и передаёт управление
|
||||
в начало кода (xxxx:0100).
|
||||
0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется
|
||||
добавлением строки наподобие c:\kordldr.win="KordOS" в секцию
|
||||
[operating systems] файла boot.ini; если загружаемый файл имеет размер
|
||||
не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS'
|
||||
(в случае kordldr.win так и есть), то основной загрузчик каждой из
|
||||
этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт
|
||||
управление на адрес 0D00:0256.
|
||||
0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями
|
||||
с базой данных основного загрузчика через bcdedit и подробно описана в
|
||||
инструкции к kordldr.win; основной загрузчик загружает целиком
|
||||
kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода.
|
||||
1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная
|
||||
им программа окажется в свою очередь загрузчиком, и в этом случае
|
||||
kordldr.win оказывается в условиях, когда основной загрузчик уже
|
||||
установил какое-то окружение, в частности, перехватил некоторые
|
||||
прерывания. Поэтому перед остальными действиями загрузчик должен
|
||||
восстановить систему в начальное состояние. (При загрузке под
|
||||
NT-линейкой такой проблемы не возникает, поскольку там основной
|
||||
загрузчик ничего в системе не трогает.) Поэтому перед собственно
|
||||
инициализацией KordOS при работе из-под DOS/9x производятся
|
||||
дополнительные действия. Первым делом kordldr проверяет, какой из
|
||||
случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт
|
||||
управление не на начало кода): определяет значение ip (команда call
|
||||
помещает в стек адрес следующей после call инструкции, команда pop si
|
||||
выталкивает его в регистр si), и если оно равно 100h, то kordldr
|
||||
загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения
|
||||
у пользователя (поскольку в этой схеме kordldr загружается всегда,
|
||||
он должен оставить возможность продолжить загрузку DOS/9x). Если
|
||||
пользователь хочет продолжить обычную загрузку, kordldr завершается.
|
||||
Иначе используется тот факт, что при выдаче прерывания перезагрузки
|
||||
int 19h система предварительно снимает все свои перехваты BIOSовских
|
||||
прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что
|
||||
kordldr устанавливает свой обработчик трассировочного прерывания,
|
||||
устанавливает флаг трассировки и передаёт управление DOSовскому
|
||||
обработчику. Обработчик трассировочного прерывания ничего не делает
|
||||
до тех пор, пока следующей инструкцией не оказывается int 19h, а
|
||||
в этот момент отбирает управление и продолжает загрузку KordOS.
|
||||
При этом BIOSовские обработчики восстановлены за исключением,
|
||||
быть может, прерывания таймера int 8, которое, возможно, восстановлено
|
||||
до команды jmp far на оригинальный обработчик. В последнем случае его
|
||||
нужно восстановить явно.
|
||||
2. Загрузчик перемещает свой код на адрес 0000:0600.
|
||||
3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0,
|
||||
настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы
|
||||
все данные можно было адресовать через [bp+N] с однобайтовым N
|
||||
(в дальнейшем они так и будут адресоваться для освобождения ds и
|
||||
экономии на размере кода). Разрешает прерывания на случай, если
|
||||
они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся
|
||||
с весёлой рожицы (символ с ASCII-кодом 2).
|
||||
4. Определяет характеристики жёсткого диска, указанного в качестве
|
||||
загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h),
|
||||
если LBA не поддерживается, то определяет геометрию - число дорожек
|
||||
и число секторов на дорожке (функция 8 прерывания 13h), эти параметры
|
||||
нужны функции чтения с диска.
|
||||
5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска.
|
||||
Цель цикла - для каждого логического диска попытаться загрузиться с
|
||||
него (действия по загрузке с конкретного логического диска начинаются
|
||||
с метки not_extended), при ошибке загрузки управление передаётся
|
||||
назад этому циклу (метка next_partition), и поиск подходящего раздела
|
||||
продолжается. На выходе заполняется одна переменная partition_start,
|
||||
имеющая смысл начала текущего рассматриваемого логического диска,
|
||||
но по ходу дела из-за приколов таблиц разделов используются ещё четыре
|
||||
переменных. cur_partition_ofs - фактически счётчик цикла, формально
|
||||
указатель на текущий вход в текущей загрузочной записи. Сама
|
||||
загрузочная запись считывается в память начиная с адреса 3000h.
|
||||
Три оставшихся нужны для правильной работы с расширенными разделами.
|
||||
В каждой загрузочной записи помещается не более 4 записей о разделах.
|
||||
Поэтому главной загрузочной записи, размещающейся в первом физическом
|
||||
секторе диска, может не хватить, и обычно создаётся так называемый
|
||||
расширенный раздел с расширенными загрузочными записями, формат
|
||||
которых почти идентичен главной. Расширенный раздел может быть только
|
||||
один, но в нём может быть много логических дисков и расширенных
|
||||
загрузочных записей. Расширенные загрузочные записи организованы
|
||||
в односвязный список, в каждой такой записи первый вход указывает
|
||||
на соответствующий логический диск, а второй - на следующую расширенную
|
||||
загрузочную запись.
|
||||
При этом в главной загрузочной записи все адреса разделов являются
|
||||
абсолютными номерами секторов. В расширенных же записях адреса разделов
|
||||
относительны, причём с разными базами: адрес логического диска
|
||||
указывается относительно расширенной записи, а адрес следующей
|
||||
расширенной записи указывается относительно начала расширенного
|
||||
раздела. Такой разнобой выглядит несколько странно, но имеет место
|
||||
быть. Три оставшихся переменных содержат: extended_part_start -
|
||||
начало расширенного раздела; extended_parent - текущая рассматриваемая
|
||||
расширенная загрузочная запись; extended_part_cur - следующая
|
||||
загрузочная запись для рассмотрения.
|
||||
Цикл выглядит так: просматриваются все разделы, указанные в текущей
|
||||
(главной или расширенной) загрузочной записи; для нормальных разделов
|
||||
(они же логические диски) происходит переход на not_extended, где
|
||||
устанавливается partition_start и начинается собственно загрузка
|
||||
(последующие шаги); при встрече с разделом, тип которого указывает
|
||||
на расширенность (5 или 0xF), код запоминает начало этого раздела
|
||||
(в главной загрузочной записи такой тип означает расширенный раздел,
|
||||
в расширенной - только указатель на следующую расширенную запись,
|
||||
в обоих случаях он может встретиться только один раз в данной записи);
|
||||
когда код доходит до конца списка, все нормальные разделы, описываемые
|
||||
в этой записи, уже просмотрены, так что код с чистой совестью переходит
|
||||
к следующей расширенной записи. Если он её не встретил, значит, уже
|
||||
все логические разделы были подвергнуты попыткам загрузиться, и все
|
||||
безрезультатно, так что выводится ругательство и работа останавливается
|
||||
Основной процесс загрузки.
|
||||
0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется
|
||||
размещением команды install=c:\kordldr.win в первой строке config.sys;
|
||||
при этом основной загрузчик системы загружает kordldr.win как обычный
|
||||
com-файл, в какой-то сегмент по смещению 100h и передаёт управление
|
||||
в начало кода (xxxx:0100).
|
||||
0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется
|
||||
добавлением строки наподобие c:\kordldr.win="KordOS" в секцию
|
||||
[operating systems] файла boot.ini; если загружаемый файл имеет размер
|
||||
не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS'
|
||||
(в случае kordldr.win так и есть), то основной загрузчик каждой из
|
||||
этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт
|
||||
управление на адрес 0D00:0256.
|
||||
0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями
|
||||
с базой данных основного загрузчика через bcdedit и подробно описана в
|
||||
инструкции к kordldr.win; основной загрузчик загружает целиком
|
||||
kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода.
|
||||
1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная
|
||||
им программа окажется в свою очередь загрузчиком, и в этом случае
|
||||
kordldr.win оказывается в условиях, когда основной загрузчик уже
|
||||
установил какое-то окружение, в частности, перехватил некоторые
|
||||
прерывания. Поэтому перед остальными действиями загрузчик должен
|
||||
восстановить систему в начальное состояние. (При загрузке под
|
||||
NT-линейкой такой проблемы не возникает, поскольку там основной
|
||||
загрузчик ничего в системе не трогает.) Поэтому перед собственно
|
||||
инициализацией KordOS при работе из-под DOS/9x производятся
|
||||
дополнительные действия. Первым делом kordldr проверяет, какой из
|
||||
случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт
|
||||
управление не на начало кода): определяет значение ip (команда call
|
||||
помещает в стек адрес следующей после call инструкции, команда pop si
|
||||
выталкивает его в регистр si), и если оно равно 100h, то kordldr
|
||||
загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения
|
||||
у пользователя (поскольку в этой схеме kordldr загружается всегда,
|
||||
он должен оставить возможность продолжить загрузку DOS/9x). Если
|
||||
пользователь хочет продолжить обычную загрузку, kordldr завершается.
|
||||
Иначе используется тот факт, что при выдаче прерывания перезагрузки
|
||||
int 19h система предварительно снимает все свои перехваты BIOSовских
|
||||
прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что
|
||||
kordldr устанавливает свой обработчик трассировочного прерывания,
|
||||
устанавливает флаг трассировки и передаёт управление DOSовскому
|
||||
обработчику. Обработчик трассировочного прерывания ничего не делает
|
||||
до тех пор, пока следующей инструкцией не оказывается int 19h, а
|
||||
в этот момент отбирает управление и продолжает загрузку KordOS.
|
||||
При этом BIOSовские обработчики восстановлены за исключением,
|
||||
быть может, прерывания таймера int 8, которое, возможно, восстановлено
|
||||
до команды jmp far на оригинальный обработчик. В последнем случае его
|
||||
нужно восстановить явно.
|
||||
2. Загрузчик перемещает свой код на адрес 0000:0600.
|
||||
3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0,
|
||||
настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы
|
||||
все данные можно было адресовать через [bp+N] с однобайтовым N
|
||||
(в дальнейшем они так и будут адресоваться для освобождения ds и
|
||||
экономии на размере кода). Разрешает прерывания на случай, если
|
||||
они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся
|
||||
с весёлой рожицы (символ с ASCII-кодом 2).
|
||||
4. Определяет характеристики жёсткого диска, указанного в качестве
|
||||
загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h),
|
||||
если LBA не поддерживается, то определяет геометрию - число дорожек
|
||||
и число секторов на дорожке (функция 8 прерывания 13h), эти параметры
|
||||
нужны функции чтения с диска.
|
||||
5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска.
|
||||
Цель цикла - для каждого логического диска попытаться загрузиться с
|
||||
него (действия по загрузке с конкретного логического диска начинаются
|
||||
с метки not_extended), при ошибке загрузки управление передаётся
|
||||
назад этому циклу (метка next_partition), и поиск подходящего раздела
|
||||
продолжается. На выходе заполняется одна переменная partition_start,
|
||||
имеющая смысл начала текущего рассматриваемого логического диска,
|
||||
но по ходу дела из-за приколов таблиц разделов используются ещё четыре
|
||||
переменных. cur_partition_ofs - фактически счётчик цикла, формально
|
||||
указатель на текущий вход в текущей загрузочной записи. Сама
|
||||
загрузочная запись считывается в память начиная с адреса 3000h.
|
||||
Три оставшихся нужны для правильной работы с расширенными разделами.
|
||||
В каждой загрузочной записи помещается не более 4 записей о разделах.
|
||||
Поэтому главной загрузочной записи, размещающейся в первом физическом
|
||||
секторе диска, может не хватить, и обычно создаётся так называемый
|
||||
расширенный раздел с расширенными загрузочными записями, формат
|
||||
которых почти идентичен главной. Расширенный раздел может быть только
|
||||
один, но в нём может быть много логических дисков и расширенных
|
||||
загрузочных записей. Расширенные загрузочные записи организованы
|
||||
в односвязный список, в каждой такой записи первый вход указывает
|
||||
на соответствующий логический диск, а второй - на следующую расширенную
|
||||
загрузочную запись.
|
||||
При этом в главной загрузочной записи все адреса разделов являются
|
||||
абсолютными номерами секторов. В расширенных же записях адреса разделов
|
||||
относительны, причём с разными базами: адрес логического диска
|
||||
указывается относительно расширенной записи, а адрес следующей
|
||||
расширенной записи указывается относительно начала расширенного
|
||||
раздела. Такой разнобой выглядит несколько странно, но имеет место
|
||||
быть. Три оставшихся переменных содержат: extended_part_start -
|
||||
начало расширенного раздела; extended_parent - текущая рассматриваемая
|
||||
расширенная загрузочная запись; extended_part_cur - следующая
|
||||
загрузочная запись для рассмотрения.
|
||||
Цикл выглядит так: просматриваются все разделы, указанные в текущей
|
||||
(главной или расширенной) загрузочной записи; для нормальных разделов
|
||||
(они же логические диски) происходит переход на not_extended, где
|
||||
устанавливается partition_start и начинается собственно загрузка
|
||||
(последующие шаги); при встрече с разделом, тип которого указывает
|
||||
на расширенность (5 или 0xF), код запоминает начало этого раздела
|
||||
(в главной загрузочной записи такой тип означает расширенный раздел,
|
||||
в расширенной - только указатель на следующую расширенную запись,
|
||||
в обоих случаях он может встретиться только один раз в данной записи);
|
||||
когда код доходит до конца списка, все нормальные разделы, описываемые
|
||||
в этой записи, уже просмотрены, так что код с чистой совестью переходит
|
||||
к следующей расширенной записи. Если он её не встретил, значит, уже
|
||||
все логические разделы были подвергнуты попыткам загрузиться, и все
|
||||
безрезультатно, так что выводится ругательство и работа останавливается
|
||||
(jmp $).
|
||||
Может возникнуть вопрос, зачем нужна такая сложная схема и почему
|
||||
нельзя узнать нужный логический диск заранее или хотя бы ограничиться
|
||||
первым попавшимся логическим диском, не крутя цикл. Так вот, вариант
|
||||
с предварительным определением нужного раздела в данном случае не
|
||||
используется, поскольку повлёк бы за собой нетривиальные лишние
|
||||
действия по установке (в текущем виде установку можно провести вручную,
|
||||
и она сводится к указанию системному загрузчику на существование
|
||||
kordldr); кстати, в альтернативной версии загрузки после
|
||||
Windows-загрузчика, когда установка осуществляется не вручную, а
|
||||
специальной программой под Windows, используется модифицированная
|
||||
версия, в которой как раз начальный физический сектор нужного раздела
|
||||
прописывается установщиком. Сам kordldr не может установить, с какого
|
||||
раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан
|
||||
быть файлом на диске C:\). Вариант с первым попавшимся логическим
|
||||
диском был реализован в первой версии загрузчика, но по ходу дела
|
||||
обнаружилось, что таки нужно крутить цикл: во-вторых, может быть
|
||||
приятным, что сама система может стоять вовсе не на системном C:\, а и
|
||||
на других дисках; во-первых, диск C: может и не быть первым логическим
|
||||
разделом - Vista любит создавать скрытый первичный раздел перед
|
||||
системным, и тогда диск C: становится вторым логическим.
|
||||
6. Извещает пользователя о том, что происходит попытка загрузки с очередного
|
||||
логического диска.
|
||||
7. Читает первый сектор логического диска и определяет файловую систему.
|
||||
И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе
|
||||
и должно совпадать с характеристикой физического носителя, то есть
|
||||
200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число
|
||||
секторов в кластере и должно быть степенью двойки.
|
||||
Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со
|
||||
смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано
|
||||
быть ненулевым).
|
||||
Критерий FAT: загрузчик вычисляет число кластеров, определяет
|
||||
предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению
|
||||
+38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29).
|
||||
После определения типа файловой системы извещает пользователя об
|
||||
определённом типе. Если файловая система не распознана, выдаёт
|
||||
соответствующее сообщение и переходит к следующему логическому диску.
|
||||
8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '12'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT12-обработчик; считывает в память всю
|
||||
таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке
|
||||
чтения пытается использовать другие копии FAT.
|
||||
8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '16'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (массив байт с возможными значениями 0 и 1,
|
||||
означающими, был ли уже загружен соответствующий сектор - всего в
|
||||
таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не
|
||||
загружен, все байты нулевые.
|
||||
8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '32'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (формат информации описан выше, в распределении
|
||||
используемой загрузчиком памяти) - ни один сектор ещё не загружен.
|
||||
8г. Общее для FAT-томов: определяет значения служебных переменных
|
||||
root_start (первый сектор корневого каталога в FAT12/16, игнорируется
|
||||
при обработке FAT32-томов), data_start (начало данных с поправкой,
|
||||
вводимой для того, чтобы кластер N начинался с сектора
|
||||
N*sectors_per_cluster+data_start), root_clus (первый кластер корневого
|
||||
каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию
|
||||
загрузки файла на FAT-обработчик.
|
||||
8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу 'nt'; определяет значение служебной переменной frs_size
|
||||
(размер в байтах файловой записи, File Record Segment), для полной
|
||||
корректности проверяет, что это значение (равное 0x400 байт на всех
|
||||
реальных NTFS-томах - единственный способ изменить его заключается
|
||||
в пересоздании всех системных структур вручную) не превосходит 0x1000
|
||||
и кратно размеру сектора 0x200 байт; инициализирует кэш файловых
|
||||
записей - ничего ещё не загружено; считывает первый кластер $MFT
|
||||
и загружает информацию о расположении на диске всей таблицы $MFT
|
||||
(атрибут 0x80, $Data); устанавливает указатель на функцию загрузки
|
||||
файла на NTFS-обработчик.
|
||||
9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного
|
||||
загрузчика. При обнаружении ошибки переходит на обработчик ошибок с
|
||||
соответствующим сообщением.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск),
|
||||
ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h),
|
||||
может быть изменён путём модификации константы в исходнике или
|
||||
специальным установщиком), bx=идентификатор файловой системы (берётся
|
||||
из стека, куда ранее был засунут на шаге 8), ds:si=указатель на
|
||||
callback-функцию.
|
||||
11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000.
|
||||
Может возникнуть вопрос, зачем нужна такая сложная схема и почему
|
||||
нельзя узнать нужный логический диск заранее или хотя бы ограничиться
|
||||
первым попавшимся логическим диском, не крутя цикл. Так вот, вариант
|
||||
с предварительным определением нужного раздела в данном случае не
|
||||
используется, поскольку повлёк бы за собой нетривиальные лишние
|
||||
действия по установке (в текущем виде установку можно провести вручную,
|
||||
и она сводится к указанию системному загрузчику на существование
|
||||
kordldr); кстати, в альтернативной версии загрузки после
|
||||
Windows-загрузчика, когда установка осуществляется не вручную, а
|
||||
специальной программой под Windows, используется модифицированная
|
||||
версия, в которой как раз начальный физический сектор нужного раздела
|
||||
прописывается установщиком. Сам kordldr не может установить, с какого
|
||||
раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан
|
||||
быть файлом на диске C:\). Вариант с первым попавшимся логическим
|
||||
диском был реализован в первой версии загрузчика, но по ходу дела
|
||||
обнаружилось, что таки нужно крутить цикл: во-вторых, может быть
|
||||
приятным, что сама система может стоять вовсе не на системном C:\, а и
|
||||
на других дисках; во-первых, диск C: может и не быть первым логическим
|
||||
разделом - Vista любит создавать скрытый первичный раздел перед
|
||||
системным, и тогда диск C: становится вторым логическим.
|
||||
6. Извещает пользователя о том, что происходит попытка загрузки с очередного
|
||||
логического диска.
|
||||
7. Читает первый сектор логического диска и определяет файловую систему.
|
||||
И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе
|
||||
и должно совпадать с характеристикой физического носителя, то есть
|
||||
200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число
|
||||
секторов в кластере и должно быть степенью двойки.
|
||||
Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со
|
||||
смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано
|
||||
быть ненулевым).
|
||||
Критерий FAT: загрузчик вычисляет число кластеров, определяет
|
||||
предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению
|
||||
+38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29).
|
||||
После определения типа файловой системы извещает пользователя об
|
||||
определённом типе. Если файловая система не распознана, выдаёт
|
||||
соответствующее сообщение и переходит к следующему логическому диску.
|
||||
8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '12'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT12-обработчик; считывает в память всю
|
||||
таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке
|
||||
чтения пытается использовать другие копии FAT.
|
||||
8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '16'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (массив байт с возможными значениями 0 и 1,
|
||||
означающими, был ли уже загружен соответствующий сектор - всего в
|
||||
таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не
|
||||
загружен, все байты нулевые.
|
||||
8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '32'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (формат информации описан выше, в распределении
|
||||
используемой загрузчиком памяти) - ни один сектор ещё не загружен.
|
||||
8г. Общее для FAT-томов: определяет значения служебных переменных
|
||||
root_start (первый сектор корневого каталога в FAT12/16, игнорируется
|
||||
при обработке FAT32-томов), data_start (начало данных с поправкой,
|
||||
вводимой для того, чтобы кластер N начинался с сектора
|
||||
N*sectors_per_cluster+data_start), root_clus (первый кластер корневого
|
||||
каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию
|
||||
загрузки файла на FAT-обработчик.
|
||||
8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу 'nt'; определяет значение служебной переменной frs_size
|
||||
(размер в байтах файловой записи, File Record Segment), для полной
|
||||
корректности проверяет, что это значение (равное 0x400 байт на всех
|
||||
реальных NTFS-томах - единственный способ изменить его заключается
|
||||
в пересоздании всех системных структур вручную) не превосходит 0x1000
|
||||
и кратно размеру сектора 0x200 байт; инициализирует кэш файловых
|
||||
записей - ничего ещё не загружено; считывает первый кластер $MFT
|
||||
и загружает информацию о расположении на диске всей таблицы $MFT
|
||||
(атрибут 0x80, $Data); устанавливает указатель на функцию загрузки
|
||||
файла на NTFS-обработчик.
|
||||
9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного
|
||||
загрузчика. При обнаружении ошибки переходит на обработчик ошибок с
|
||||
соответствующим сообщением.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск),
|
||||
ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h),
|
||||
может быть изменён путём модификации константы в исходнике или
|
||||
специальным установщиком), bx=идентификатор файловой системы (берётся
|
||||
из стека, куда ранее был засунут на шаге 8), ds:si=указатель на
|
||||
callback-функцию.
|
||||
11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Чтение файла:
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:dat.
|
||||
2. Разбирает переданные параметры и вызывает процедуру загрузки файла.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Чтение файла:
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:dat.
|
||||
2. Разбирает переданные параметры и вызывает процедуру загрузки файла.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
|
||||
Вспомогательные процедуры.
|
||||
Процедура чтения секторов (read):
|
||||
на входе должно быть установлено:
|
||||
Вспомогательные процедуры.
|
||||
Процедура чтения секторов (read):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:dat
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор (относительно начала логического диска)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
||||
устройстве, прибавляя номер первого сектора логического диска,
|
||||
найденный при переборе дисков.
|
||||
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.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор (относительно начала логического диска)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
||||
устройстве, прибавляя номер первого сектора логического диска,
|
||||
найденный при переборе дисков.
|
||||
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.
|
||||
|
||||
Процедура обработки ошибок (find_error_si и find_error_sp):
|
||||
на входе: указатель на сообщение об ошибке в si либо на верхушке стека
|
||||
0. Если вызывается find_error_si, она помещает переданный указатель в стек.
|
||||
1. Если ошибка произошла в процессе работы callback-функции, то
|
||||
(метка error_in_callback) обработчик просто возвращает управление
|
||||
вызвавшему коду, рапортуя о ненайденном файле.
|
||||
2. Если же ошибка произошла до передачи управления вторичному загрузчику,
|
||||
обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>"
|
||||
и (восстановив стек) переходит к следующему логическому диску.
|
||||
Процедура обработки ошибок (find_error_si и find_error_sp):
|
||||
на входе: указатель на сообщение об ошибке в si либо на верхушке стека
|
||||
0. Если вызывается find_error_si, она помещает переданный указатель в стек.
|
||||
1. Если ошибка произошла в процессе работы callback-функции, то
|
||||
(метка error_in_callback) обработчик просто возвращает управление
|
||||
вызвавшему коду, рапортуя о ненайденном файле.
|
||||
2. Если же ошибка произошла до передачи управления вторичному загрузчику,
|
||||
обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>"
|
||||
и (восстановив стек) переходит к следующему логическому диску.
|
||||
|
||||
Процедура чтения файла/атрибута по известному размещению на диске
|
||||
Процедура чтения файла/атрибута по известному размещению на диске
|
||||
(read_file_chunk):
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на информацию о размещении
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
ecx = лимит числа секторов для чтения, старшее слово должно быть 0
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Определяет, является ли атрибут резидентным (возможно только в NTFS
|
||||
и означает, что данные файла/атрибута уже были целиком прочитаны при
|
||||
обработке информации о файле) или нерезидентным (означает, что данные
|
||||
хранятся где-то на диске, и имеется информация о том, где именно).
|
||||
2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует
|
||||
данные по месту назначения (с учётом указанного лимита).
|
||||
3. Для нерезидентных атрибутов информация состоит из пар <размер очередного
|
||||
фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура
|
||||
читает фрагменты, пока файл не закончится или пока не будет достигнут
|
||||
указанный лимит.
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на информацию о размещении
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
ecx = лимит числа секторов для чтения, старшее слово должно быть 0
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Определяет, является ли атрибут резидентным (возможно только в NTFS
|
||||
и означает, что данные файла/атрибута уже были целиком прочитаны при
|
||||
обработке информации о файле) или нерезидентным (означает, что данные
|
||||
хранятся где-то на диске, и имеется информация о том, где именно).
|
||||
2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует
|
||||
данные по месту назначения (с учётом указанного лимита).
|
||||
3. Для нерезидентных атрибутов информация состоит из пар <размер очередного
|
||||
фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура
|
||||
читает фрагменты, пока файл не закончится или пока не будет достигнут
|
||||
указанный лимит.
|
||||
|
||||
Процедура просмотра кэша (cache_lookup):
|
||||
на входе должно быть установлено:
|
||||
eax = искомое значение
|
||||
ss:si = указатель на структуру-заголовок кэша
|
||||
на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение
|
||||
было только что добавлено, и сброшен, если оно уже было в кэше.
|
||||
1. Просматривает кэш в поисках указанного значения. Если значение найдено
|
||||
(при этом флаг CF оказывается сброшенным), переходит к шагу 4.
|
||||
2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в
|
||||
голове двусвязного списка), иначе добавляет к кэшу ещё один вход.
|
||||
3. Устанавливает в полученном входе указанное значение. Устанавливает флаг
|
||||
CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5.
|
||||
4. Удаляет вход из списка.
|
||||
5. Добавляет сектор в конец списка (самый новый вход).
|
||||
Процедура просмотра кэша (cache_lookup):
|
||||
на входе должно быть установлено:
|
||||
eax = искомое значение
|
||||
ss:si = указатель на структуру-заголовок кэша
|
||||
на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение
|
||||
было только что добавлено, и сброшен, если оно уже было в кэше.
|
||||
1. Просматривает кэш в поисках указанного значения. Если значение найдено
|
||||
(при этом флаг CF оказывается сброшенным), переходит к шагу 4.
|
||||
2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в
|
||||
голове двусвязного списка), иначе добавляет к кэшу ещё один вход.
|
||||
3. Устанавливает в полученном входе указанное значение. Устанавливает флаг
|
||||
CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5.
|
||||
4. Удаляет вход из списка.
|
||||
5. Добавляет сектор в конец списка (самый новый вход).
|
||||
|
@ -26,393 +26,393 @@
|
||||
|
||||
Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.?
|
||||
|
||||
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
||||
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
||||
либо ISO-9660, либо UDF.)
|
||||
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
||||
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
||||
либо ISO-9660, либо UDF.)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Требования для работы:
|
||||
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки проверялись на валидность 14.09.2008):
|
||||
стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
|
||||
стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf
|
||||
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
||||
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
||||
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
||||
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
||||
Документация в тему (ссылки проверялись на валидность 14.09.2008):
|
||||
стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
|
||||
стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf
|
||||
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
||||
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
||||
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
||||
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
||||
|
||||
=====================================================================
|
||||
|
||||
Схема используемой памяти:
|
||||
1000-1800 временный буфер для чтения одиночных секторов
|
||||
...-7C00 стек
|
||||
7C00-8400 код бутсектора
|
||||
8400-8A00 информация о кэше для папок: массив входов следующего
|
||||
формата:
|
||||
dw следующий элемент в L2-списке закэшированных папок,
|
||||
упорядоченном по времени использования
|
||||
(голова списка - самый старый);
|
||||
dw предыдущий элемент в том же списке;
|
||||
dd первый сектор папки;
|
||||
dw размер папки в байтах;
|
||||
dw сегмент кэша
|
||||
60000-... содержимое Path Table, если она используется
|
||||
+ кэш для папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area
|
||||
Схема используемой памяти:
|
||||
1000-1800 временный буфер для чтения одиночных секторов
|
||||
...-7C00 стек
|
||||
7C00-8400 код бутсектора
|
||||
8400-8A00 информация о кэше для папок: массив входов следующего
|
||||
формата:
|
||||
dw следующий элемент в L2-списке закэшированных папок,
|
||||
упорядоченном по времени использования
|
||||
(голова списка - самый старый);
|
||||
dw предыдущий элемент в том же списке;
|
||||
dd первый сектор папки;
|
||||
dw размер папки в байтах;
|
||||
dw сегмент кэша
|
||||
60000-... содержимое Path Table, если она используется
|
||||
+ кэш для папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip
|
||||
равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает
|
||||
дальний прыжок на самого себя с целью получить cs=0 (в некоторых
|
||||
местах используется адресация переменных загрузчика через cs, поскольку
|
||||
и ds, и es могут быть заняты под другие сегменты).
|
||||
2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом)
|
||||
и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления
|
||||
и разрешённые прерывания. Сохраняет идентификатор загрузочного диска
|
||||
в специальную переменную.
|
||||
3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять
|
||||
LBA-функции.
|
||||
4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту
|
||||
ISO9660 со смещения 10h начинается цепочка описателей тома,
|
||||
завершающаяся специальным описателем (Volume Descriptor Set
|
||||
Terminator). Код по очереди считывает все сектора, пока не наткнётся
|
||||
либо на искомый описатель, либо на терминатор. Во втором случае
|
||||
выдаётся соответствующее сообщение, и загрузка прекращается.
|
||||
Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD
|
||||
располагается в последней сессии. И спецификация ElTorito загрузочного
|
||||
CD оперирует также с последней сессией. Однако на практике оказывается,
|
||||
что: во-первых, реальные BIOSы не понимают мультисессионных CD и
|
||||
всегда используют первую сессию; во-вторых, BIOSовский int 13h просто
|
||||
не позволяет получить информацию о последней сессии. В связи с этим
|
||||
загрузчик также использует первую сессию. (В-третьих, в одной из BIOS
|
||||
обнаружилась заготовка, которая в случае запроса сектора 10h, в котором
|
||||
во всех нормальных случаях и располагается PVD, перенаправляет его
|
||||
на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с
|
||||
последней сессии, то благодаря заготовке загрузчик без всяких
|
||||
модификаций также читал бы последнюю сессию.)
|
||||
5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во
|
||||
внутренние переменные: размер логического блока (согласно спецификации,
|
||||
должен быть степенью двойки от 512 до размера логического сектора,
|
||||
равного 2048 для CD и DVD); положение на диске корневой папки;
|
||||
вычисляет число блоков в секторе (из предыдущего примечания следует,
|
||||
что оно всегда целое и само является степенью двойки).
|
||||
6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет
|
||||
размер пространства, которое может использовать загрузчик (от
|
||||
адреса 6000:0000 до конца доступной памяти).
|
||||
7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит
|
||||
базовую информацию обо всех папках на диске. Если таблица слишком
|
||||
велика (больше 62K или больше половины доступной памяти), то она
|
||||
игнорируется. Если таблица путей недоступна, то запрос типа
|
||||
dir1/dir2/dir3/file приведёт к последовательному разбору корневой
|
||||
папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать
|
||||
саму таблицу путей (где записано положение папки dir1/dir2/dir3)
|
||||
и папку dir3. Если таблица загружена, то соответственно уменьшается
|
||||
объём оставшейся доступной памяти и увеличивается указатель на
|
||||
свободную область.
|
||||
8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7
|
||||
доступная память отводится под этот кэш).
|
||||
9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке
|
||||
печатает соответствующее сообщение и прекращает загрузку с CD.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует
|
||||
тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is'
|
||||
идентифицирует файловую систему ISO-9660; ds:si указывает на
|
||||
callback-функцию, которую может вызывать вторичный загрузчик.
|
||||
11. Передаёт управление вторичному загрузчику, совершая дальний прыжок
|
||||
на адрес, куда kord/loader был загружен.
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip
|
||||
равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает
|
||||
дальний прыжок на самого себя с целью получить cs=0 (в некоторых
|
||||
местах используется адресация переменных загрузчика через cs, поскольку
|
||||
и ds, и es могут быть заняты под другие сегменты).
|
||||
2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом)
|
||||
и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления
|
||||
и разрешённые прерывания. Сохраняет идентификатор загрузочного диска
|
||||
в специальную переменную.
|
||||
3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять
|
||||
LBA-функции.
|
||||
4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту
|
||||
ISO9660 со смещения 10h начинается цепочка описателей тома,
|
||||
завершающаяся специальным описателем (Volume Descriptor Set
|
||||
Terminator). Код по очереди считывает все сектора, пока не наткнётся
|
||||
либо на искомый описатель, либо на терминатор. Во втором случае
|
||||
выдаётся соответствующее сообщение, и загрузка прекращается.
|
||||
Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD
|
||||
располагается в последней сессии. И спецификация ElTorito загрузочного
|
||||
CD оперирует также с последней сессией. Однако на практике оказывается,
|
||||
что: во-первых, реальные BIOSы не понимают мультисессионных CD и
|
||||
всегда используют первую сессию; во-вторых, BIOSовский int 13h просто
|
||||
не позволяет получить информацию о последней сессии. В связи с этим
|
||||
загрузчик также использует первую сессию. (В-третьих, в одной из BIOS
|
||||
обнаружилась заготовка, которая в случае запроса сектора 10h, в котором
|
||||
во всех нормальных случаях и располагается PVD, перенаправляет его
|
||||
на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с
|
||||
последней сессии, то благодаря заготовке загрузчик без всяких
|
||||
модификаций также читал бы последнюю сессию.)
|
||||
5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во
|
||||
внутренние переменные: размер логического блока (согласно спецификации,
|
||||
должен быть степенью двойки от 512 до размера логического сектора,
|
||||
равного 2048 для CD и DVD); положение на диске корневой папки;
|
||||
вычисляет число блоков в секторе (из предыдущего примечания следует,
|
||||
что оно всегда целое и само является степенью двойки).
|
||||
6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет
|
||||
размер пространства, которое может использовать загрузчик (от
|
||||
адреса 6000:0000 до конца доступной памяти).
|
||||
7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит
|
||||
базовую информацию обо всех папках на диске. Если таблица слишком
|
||||
велика (больше 62K или больше половины доступной памяти), то она
|
||||
игнорируется. Если таблица путей недоступна, то запрос типа
|
||||
dir1/dir2/dir3/file приведёт к последовательному разбору корневой
|
||||
папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать
|
||||
саму таблицу путей (где записано положение папки dir1/dir2/dir3)
|
||||
и папку dir3. Если таблица загружена, то соответственно уменьшается
|
||||
объём оставшейся доступной памяти и увеличивается указатель на
|
||||
свободную область.
|
||||
8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7
|
||||
доступная память отводится под этот кэш).
|
||||
9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке
|
||||
печатает соответствующее сообщение и прекращает загрузку с CD.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует
|
||||
тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is'
|
||||
идентифицирует файловую систему ISO-9660; ds:si указывает на
|
||||
callback-функцию, которую может вызывать вторичный загрузчик.
|
||||
11. Передаёт управление вторичному загрузчику, совершая дальний прыжок
|
||||
на адрес, куда kord/loader был загружен.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика (callback):
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
||||
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
||||
запросах на продолжение загрузки файла).
|
||||
Функция обратного вызова для вторичного загрузчика (callback):
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
||||
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
||||
запросах на продолжение загрузки файла).
|
||||
|
||||
Вспомогательные процедуры.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения секторов (read_sectors):
|
||||
на входе должно быть установлено:
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор
|
||||
cx = число секторов
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
||||
число читаемых секторов не превосходило 7Fh (требование спецификации
|
||||
Процедура чтения секторов (read_sectors):
|
||||
на входе должно быть установлено:
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор
|
||||
cx = число секторов
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
||||
число читаемых секторов не превосходило 7Fh (требование спецификации
|
||||
EDD BIOS).
|
||||
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||||
итерации) до 7Fh.
|
||||
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||||
клали).
|
||||
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
||||
устанавливает CF=1 и выходит из процедуры.
|
||||
Очищает стек от пакета, сформированного на предыдущем шаге.
|
||||
5. В соответствии с числом прочитанных на текущей итерации секторов
|
||||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||||
работу, иначе возвращается на шаг 2.
|
||||
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||||
итерации) до 7Fh.
|
||||
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||||
клали).
|
||||
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
||||
устанавливает CF=1 и выходит из процедуры.
|
||||
Очищает стек от пакета, сформированного на предыдущем шаге.
|
||||
5. В соответствии с числом прочитанных на текущей итерации секторов
|
||||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||||
работу, иначе возвращается на шаг 2.
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
|
||||
Процедура загрузки файла (load_file):
|
||||
на входе:
|
||||
ds:di = указатель на информационную структуру, описанную в спецификации
|
||||
на загрузчик, а также в комментариях к коду
|
||||
на выходе:
|
||||
bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть,
|
||||
2=файл не найден, 3=ошибка чтения
|
||||
dx:ax = размер файла, 0xFFFFFFFF, если файл не найден
|
||||
1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице,
|
||||
иначе переходит сразу к шагу 4, установив eax = начальный блок
|
||||
корневой папки.
|
||||
2. Устанавливает es:di на начало таблицы путей. Ограничение на размер
|
||||
гарантирует, что вся таблица помещается в сегменте 6000h.
|
||||
Инициализирует dx (в котором будет хранится номер текущего входа в
|
||||
таблице, считая с 1), cx (размер оставшегося участка таблицы),
|
||||
bx (номер входа, соответствующего родительской папке для текущего
|
||||
рассматриваемого участка пути).
|
||||
3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы
|
||||
таблицы путей упорядочены (подробно о порядке написано в спецификации),
|
||||
так что если родительский элемент для очередного входа больше нужного,
|
||||
то нужного входа в таблице нет совсем, и в этом случае происходит
|
||||
выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент,
|
||||
соответствующий очередной папке в запрошенном пути, то на рассмотрение
|
||||
выносится следующая компонента пути. Если эта компонента последняя,
|
||||
то осталось найти файл в папке, и код переходит к пункту 4,
|
||||
установив eax = начальный блок этой папки. Если же нет, то эта
|
||||
компонента должна задавать имя папки, и код возвращается к пункту 3,
|
||||
скорректировав указатель на имя ds:si и номер родительского входа bx.
|
||||
4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax
|
||||
и указатель на имя файла относительно этой папки в ds:si. Если
|
||||
папку искали по таблице путей, то имя файла уже не содержит подпапок;
|
||||
если же нет, то подпапки вполне возможны.
|
||||
5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый
|
||||
из которых задаётся отдельным входом в папке. Информация обо всех
|
||||
таких кусках при просмотре папки запоминается в области, начинающейся
|
||||
с адреса 0000:2000. Переменная cur_desc_end содержит указатель на
|
||||
конец этой области, он же указатель, куда будет помещена информация
|
||||
при обнаружении следующего входа. (Папки, согласно спецификации,
|
||||
должны задаваться одним куском.)
|
||||
6. Код сначала ищет запрошенную папку в кэше папок.
|
||||
7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка,
|
||||
отсортированного по давности последнего обращения и код переходит к
|
||||
п.15. (Следующим действием станет добавление папки в конец списка.)
|
||||
8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать
|
||||
с диска. Сначала загружается первый сектор (физический сектор,
|
||||
содержащий первый логический блок). При ошибке ввода/вывода
|
||||
происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF.
|
||||
Первый элемент папки содержит информацию о самой этой папке, конкретно
|
||||
загрузчик интересуется её размером.
|
||||
9. Если размер папки слишком большой (больше или равен 64K либо больше половины
|
||||
общего размера кэша), то кэшироваться она не будет. В этом случае код
|
||||
считывает папку посекторно во временный буфер (0000:1000) и посекторно
|
||||
сканирует на наличие запрошенного имени, пока не найдёт такого имени
|
||||
или пока не кончатся данные. (Цикл начинается со сканирования,
|
||||
поскольку первая часть данных уже прочитана.) В конце код переходит
|
||||
к п.17.
|
||||
10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно
|
||||
обеспечить достаточное количество свободного места. Для этого может
|
||||
понадобиться выкинуть какое-то количество старых данных (цикл
|
||||
parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря,
|
||||
свободное пространство окажется разорванным на несколько фрагментов.
|
||||
Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает
|
||||
все следующие за ней данные назад по памяти и соответственно
|
||||
корректирует информацию о местонахождении данных в информации о кэше.
|
||||
При этом новое пространство всегда добавляется в конец доступной
|
||||
памяти. Цикл выкидывания продолжается, пока не освободится место,
|
||||
достаточное для хранения папки. Из-за ограничений на размер кэшируемых
|
||||
папок в конце концов место найдётся.
|
||||
11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы
|
||||
организуются в единый список свободных элементов; если он непуст,
|
||||
то очередной элемент берётся из этого списка; если же пуст, то
|
||||
берётся совсем новый элемент из области памяти, предназначенной для
|
||||
элементов кэша.
|
||||
12. В новом элементе заполняются поля начального блока, сегмента с данными,
|
||||
размера в байтах.
|
||||
13. Уже прочитанные данные первого физического сектора пересылаются на
|
||||
законное место в кэше.
|
||||
14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся
|
||||
данные с диска. При ошибке чтения, как и раньше, происходит выход из
|
||||
процедуры с bx=3, ax=dx=0xFFFF.
|
||||
15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов
|
||||
кэша.
|
||||
16. Загрузчик ищет запрошенное имя в загруженных данных папки.
|
||||
(Из-за ограничений на размер кэшируемой папки все данные располагаются
|
||||
в одном сегменте.)
|
||||
17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено
|
||||
никаких кусков файла, то cur_desc_end такой же, каким был вначале.
|
||||
В этом случае процедура рапортует о ненайденном файле и выходит.
|
||||
18. (filefound) Пропускает текущую компоненту имени. Если она была не последней
|
||||
(то есть подпапкой, в которой нужно производить дальнейший поиск),
|
||||
то код проверяет, что найденный вход - действительно подпапка,
|
||||
устанавливает новый стартовый блок и возвращается к п.4.
|
||||
Если же последней, то код проверяет, что найденный вход - регулярный
|
||||
файл и начинает загрузку файла.
|
||||
19. Нормализует указатель, по которому требуется прочитать файл. Под
|
||||
нормализацией понимается преобразование типа
|
||||
1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса,
|
||||
но гарантирует отсутствие переполнений: в приведённом примере попытка
|
||||
переслать 400h байт по rep movsb приведёт к тому, что последние 8
|
||||
байт запишутся не в нужное место, а на 64K раньше. Далее нормализация
|
||||
будет производиться после каждой пересылки. В cur_limit помещает
|
||||
предельный размер для чтения в байтах.
|
||||
20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты
|
||||
(пункты 21-27).
|
||||
21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое
|
||||
нужно пропустить с начала фрагмента.
|
||||
22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего
|
||||
шага, либо напрямую из callback-процедуры при запросе на продолжение
|
||||
чтения. Для этого и нужна вышеупомянутая переменная [cur_start] -
|
||||
при продолжении чтения, прервавшегося из-за конца буфера посередине
|
||||
фрагмента, там будет записано соответствующее значение.
|
||||
23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента
|
||||
и максимальной длины остатка. Если второе строго меньше, то
|
||||
запоминает, что файл слишком большой и прочитан только частично.
|
||||
Определяет новое значение числа прочитанных байт во фрагменте
|
||||
для возможных будущих вызовов [cur_start].
|
||||
24. Переводит пропускаемое число байт в число логических блоков и байт
|
||||
в первом блоке, последнее число записывает в переменную [first_byte],
|
||||
откуда её позднее достанет read_many_bytes.with_first.
|
||||
25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код
|
||||
определяет начальный блок фрагмента и вызывает вспомогательную функцию
|
||||
чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения)
|
||||
и выходит из цикла к п.28.
|
||||
26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала
|
||||
код пропускает нужное количество непрерывных частей, а потом
|
||||
в цикле загружает непрерывные части с помощью той же функции,
|
||||
в промежутках между частями увеличивая номер начального блока.
|
||||
Пока не кончится фрагмент или пока не наберётся запрошенное число байт.
|
||||
При ошибке чтения делает то же самое, что и в предыдущем случае.
|
||||
27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер
|
||||
ещё не достигнут, переходит к следующему фрагменту и п.20. В противном
|
||||
случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли
|
||||
переполнение в п.23.
|
||||
28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех
|
||||
фрагментов.
|
||||
Процедура загрузки файла (load_file):
|
||||
на входе:
|
||||
ds:di = указатель на информационную структуру, описанную в спецификации
|
||||
на загрузчик, а также в комментариях к коду
|
||||
на выходе:
|
||||
bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть,
|
||||
2=файл не найден, 3=ошибка чтения
|
||||
dx:ax = размер файла, 0xFFFFFFFF, если файл не найден
|
||||
1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице,
|
||||
иначе переходит сразу к шагу 4, установив eax = начальный блок
|
||||
корневой папки.
|
||||
2. Устанавливает es:di на начало таблицы путей. Ограничение на размер
|
||||
гарантирует, что вся таблица помещается в сегменте 6000h.
|
||||
Инициализирует dx (в котором будет хранится номер текущего входа в
|
||||
таблице, считая с 1), cx (размер оставшегося участка таблицы),
|
||||
bx (номер входа, соответствующего родительской папке для текущего
|
||||
рассматриваемого участка пути).
|
||||
3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы
|
||||
таблицы путей упорядочены (подробно о порядке написано в спецификации),
|
||||
так что если родительский элемент для очередного входа больше нужного,
|
||||
то нужного входа в таблице нет совсем, и в этом случае происходит
|
||||
выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент,
|
||||
соответствующий очередной папке в запрошенном пути, то на рассмотрение
|
||||
выносится следующая компонента пути. Если эта компонента последняя,
|
||||
то осталось найти файл в папке, и код переходит к пункту 4,
|
||||
установив eax = начальный блок этой папки. Если же нет, то эта
|
||||
компонента должна задавать имя папки, и код возвращается к пункту 3,
|
||||
скорректировав указатель на имя ds:si и номер родительского входа bx.
|
||||
4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax
|
||||
и указатель на имя файла относительно этой папки в ds:si. Если
|
||||
папку искали по таблице путей, то имя файла уже не содержит подпапок;
|
||||
если же нет, то подпапки вполне возможны.
|
||||
5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый
|
||||
из которых задаётся отдельным входом в папке. Информация обо всех
|
||||
таких кусках при просмотре папки запоминается в области, начинающейся
|
||||
с адреса 0000:2000. Переменная cur_desc_end содержит указатель на
|
||||
конец этой области, он же указатель, куда будет помещена информация
|
||||
при обнаружении следующего входа. (Папки, согласно спецификации,
|
||||
должны задаваться одним куском.)
|
||||
6. Код сначала ищет запрошенную папку в кэше папок.
|
||||
7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка,
|
||||
отсортированного по давности последнего обращения и код переходит к
|
||||
п.15. (Следующим действием станет добавление папки в конец списка.)
|
||||
8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать
|
||||
с диска. Сначала загружается первый сектор (физический сектор,
|
||||
содержащий первый логический блок). При ошибке ввода/вывода
|
||||
происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF.
|
||||
Первый элемент папки содержит информацию о самой этой папке, конкретно
|
||||
загрузчик интересуется её размером.
|
||||
9. Если размер папки слишком большой (больше или равен 64K либо больше половины
|
||||
общего размера кэша), то кэшироваться она не будет. В этом случае код
|
||||
считывает папку посекторно во временный буфер (0000:1000) и посекторно
|
||||
сканирует на наличие запрошенного имени, пока не найдёт такого имени
|
||||
или пока не кончатся данные. (Цикл начинается со сканирования,
|
||||
поскольку первая часть данных уже прочитана.) В конце код переходит
|
||||
к п.17.
|
||||
10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно
|
||||
обеспечить достаточное количество свободного места. Для этого может
|
||||
понадобиться выкинуть какое-то количество старых данных (цикл
|
||||
parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря,
|
||||
свободное пространство окажется разорванным на несколько фрагментов.
|
||||
Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает
|
||||
все следующие за ней данные назад по памяти и соответственно
|
||||
корректирует информацию о местонахождении данных в информации о кэше.
|
||||
При этом новое пространство всегда добавляется в конец доступной
|
||||
памяти. Цикл выкидывания продолжается, пока не освободится место,
|
||||
достаточное для хранения папки. Из-за ограничений на размер кэшируемых
|
||||
папок в конце концов место найдётся.
|
||||
11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы
|
||||
организуются в единый список свободных элементов; если он непуст,
|
||||
то очередной элемент берётся из этого списка; если же пуст, то
|
||||
берётся совсем новый элемент из области памяти, предназначенной для
|
||||
элементов кэша.
|
||||
12. В новом элементе заполняются поля начального блока, сегмента с данными,
|
||||
размера в байтах.
|
||||
13. Уже прочитанные данные первого физического сектора пересылаются на
|
||||
законное место в кэше.
|
||||
14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся
|
||||
данные с диска. При ошибке чтения, как и раньше, происходит выход из
|
||||
процедуры с bx=3, ax=dx=0xFFFF.
|
||||
15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов
|
||||
кэша.
|
||||
16. Загрузчик ищет запрошенное имя в загруженных данных папки.
|
||||
(Из-за ограничений на размер кэшируемой папки все данные располагаются
|
||||
в одном сегменте.)
|
||||
17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено
|
||||
никаких кусков файла, то cur_desc_end такой же, каким был вначале.
|
||||
В этом случае процедура рапортует о ненайденном файле и выходит.
|
||||
18. (filefound) Пропускает текущую компоненту имени. Если она была не последней
|
||||
(то есть подпапкой, в которой нужно производить дальнейший поиск),
|
||||
то код проверяет, что найденный вход - действительно подпапка,
|
||||
устанавливает новый стартовый блок и возвращается к п.4.
|
||||
Если же последней, то код проверяет, что найденный вход - регулярный
|
||||
файл и начинает загрузку файла.
|
||||
19. Нормализует указатель, по которому требуется прочитать файл. Под
|
||||
нормализацией понимается преобразование типа
|
||||
1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса,
|
||||
но гарантирует отсутствие переполнений: в приведённом примере попытка
|
||||
переслать 400h байт по rep movsb приведёт к тому, что последние 8
|
||||
байт запишутся не в нужное место, а на 64K раньше. Далее нормализация
|
||||
будет производиться после каждой пересылки. В cur_limit помещает
|
||||
предельный размер для чтения в байтах.
|
||||
20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты
|
||||
(пункты 21-27).
|
||||
21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое
|
||||
нужно пропустить с начала фрагмента.
|
||||
22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего
|
||||
шага, либо напрямую из callback-процедуры при запросе на продолжение
|
||||
чтения. Для этого и нужна вышеупомянутая переменная [cur_start] -
|
||||
при продолжении чтения, прервавшегося из-за конца буфера посередине
|
||||
фрагмента, там будет записано соответствующее значение.
|
||||
23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента
|
||||
и максимальной длины остатка. Если второе строго меньше, то
|
||||
запоминает, что файл слишком большой и прочитан только частично.
|
||||
Определяет новое значение числа прочитанных байт во фрагменте
|
||||
для возможных будущих вызовов [cur_start].
|
||||
24. Переводит пропускаемое число байт в число логических блоков и байт
|
||||
в первом блоке, последнее число записывает в переменную [first_byte],
|
||||
откуда её позднее достанет read_many_bytes.with_first.
|
||||
25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код
|
||||
определяет начальный блок фрагмента и вызывает вспомогательную функцию
|
||||
чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения)
|
||||
и выходит из цикла к п.28.
|
||||
26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала
|
||||
код пропускает нужное количество непрерывных частей, а потом
|
||||
в цикле загружает непрерывные части с помощью той же функции,
|
||||
в промежутках между частями увеличивая номер начального блока.
|
||||
Пока не кончится фрагмент или пока не наберётся запрошенное число байт.
|
||||
При ошибке чтения делает то же самое, что и в предыдущем случае.
|
||||
27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер
|
||||
ещё не достигнут, переходит к следующему фрагменту и п.20. В противном
|
||||
случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли
|
||||
переполнение в п.23.
|
||||
28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех
|
||||
фрагментов.
|
||||
|
||||
Процедура проверки, является ли текущая компонента имени файла последней
|
||||
Процедура проверки, является ли текущая компонента имени файла последней
|
||||
(is_last_component):
|
||||
на входе: ds:si = указатель на имя
|
||||
на выходе: флаг CF установлен, если есть последующие компоненты
|
||||
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
||||
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
||||
и выходит.
|
||||
на входе: ds:si = указатель на имя
|
||||
на выходе: флаг CF установлен, если есть последующие компоненты
|
||||
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
||||
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
||||
и выходит.
|
||||
|
||||
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
||||
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
||||
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
||||
таблицы путей для test_filename1, папки для test_filename2
|
||||
на выходе: CF установлен, если имена не совпадают
|
||||
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
||||
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
||||
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
||||
возможно только в ситуации типа имени "filename.ext" и элемента
|
||||
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
||||
именами в папке отсортированы по убыванию версий);
|
||||
несовпадение символов - означает, что имена не совпадают;
|
||||
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
||||
файла, и в зависимости от этого принимать решение о совпадении.
|
||||
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
||||
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
||||
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
||||
таблицы путей для test_filename1, папки для test_filename2
|
||||
на выходе: CF установлен, если имена не совпадают
|
||||
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
||||
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
||||
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
||||
возможно только в ситуации типа имени "filename.ext" и элемента
|
||||
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
||||
именами в папке отсортированы по убыванию версий);
|
||||
несовпадение символов - означает, что имена не совпадают;
|
||||
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
||||
файла, и в зависимости от этого принимать решение о совпадении.
|
||||
|
||||
Процедура приведения символа в верхний регистр (toupper):
|
||||
на входе: ASCII-символ
|
||||
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
||||
нему неприменимо)
|
||||
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
||||
остальные символы не трогает.
|
||||
Процедура приведения символа в верхний регистр (toupper):
|
||||
на входе: ASCII-символ
|
||||
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
||||
нему неприменимо)
|
||||
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
||||
остальные символы не трогает.
|
||||
|
||||
Процедура поиска файла в данных папки (scan_for_filename_in_sector):
|
||||
на входе:
|
||||
ds:si = указатель на имя файла
|
||||
es:bx = указатель на начало данных папки
|
||||
es:dx = указатель на конец данных папки
|
||||
на выходе:
|
||||
CF сброшен, если найден финальный фрагмент файла
|
||||
(и дальше сканировать папку не нужно)
|
||||
в область для информации о фрагментах файла записывается найденное
|
||||
В цикле просматривает все входы папки, пропуская те, у которых установлен
|
||||
бит Associated (это специальные входы, дополняющие основные). Если
|
||||
имя очередного входа совпадает с именем файла, то запоминает новый
|
||||
фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent),
|
||||
то код выходит с CF=0. Если достигнут конец данных, то код выходит
|
||||
с CF=1. Если очередной вход нулевой (первый байт настоящего входа
|
||||
содержит длину и не может быть нулём), то процедура переходит к
|
||||
рассмотрению следующего логического блока. При этом потенциально
|
||||
возможно переполнение при добавлении размера блока; поскольку такой
|
||||
сценарий означает, что процедура вызвана для кэшированной папки
|
||||
с размером почти 64K и началом данных bx=0 (это свойство вызывающего
|
||||
кода), а размер блока - степень двойки, то после переполнения всегда
|
||||
bx=0, так что это можно обнаружить по взведённому ZF после сложения;
|
||||
в этом случае также происходит выход (а после переполнения CF=1).
|
||||
Процедура поиска файла в данных папки (scan_for_filename_in_sector):
|
||||
на входе:
|
||||
ds:si = указатель на имя файла
|
||||
es:bx = указатель на начало данных папки
|
||||
es:dx = указатель на конец данных папки
|
||||
на выходе:
|
||||
CF сброшен, если найден финальный фрагмент файла
|
||||
(и дальше сканировать папку не нужно)
|
||||
в область для информации о фрагментах файла записывается найденное
|
||||
В цикле просматривает все входы папки, пропуская те, у которых установлен
|
||||
бит Associated (это специальные входы, дополняющие основные). Если
|
||||
имя очередного входа совпадает с именем файла, то запоминает новый
|
||||
фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent),
|
||||
то код выходит с CF=0. Если достигнут конец данных, то код выходит
|
||||
с CF=1. Если очередной вход нулевой (первый байт настоящего входа
|
||||
содержит длину и не может быть нулём), то процедура переходит к
|
||||
рассмотрению следующего логического блока. При этом потенциально
|
||||
возможно переполнение при добавлении размера блока; поскольку такой
|
||||
сценарий означает, что процедура вызвана для кэшированной папки
|
||||
с размером почти 64K и началом данных bx=0 (это свойство вызывающего
|
||||
кода), а размер блока - степень двойки, то после переполнения всегда
|
||||
bx=0, так что это можно обнаружить по взведённому ZF после сложения;
|
||||
в этом случае также происходит выход (а после переполнения CF=1).
|
||||
|
||||
Процедура перевода логического блока в номер сектора:
|
||||
на входе: eax = логический блок
|
||||
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
||||
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
||||
блоков в секторе, хранящееся во внутренней переменной).
|
||||
Процедура перевода логического блока в номер сектора:
|
||||
на входе: eax = логический блок
|
||||
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
||||
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
||||
блоков в секторе, хранящееся во внутренней переменной).
|
||||
|
||||
Процедура загрузки физического сектора, содержащего указанный логический блок
|
||||
Процедура загрузки физического сектора, содержащего указанный логический блок
|
||||
(load_phys_sector_for_lb_force):
|
||||
на входе: eax = логический блок;
|
||||
si - индикатор, задающий, следует ли читать данные в случае,
|
||||
если логический блок начинается с начала физического:
|
||||
si = 0 - не нужно, si ненулевой - нужно
|
||||
на выходе:
|
||||
физический сектор загружен по адресу 0000:1000
|
||||
si указывает на данные логического блока
|
||||
CF установлен при ошибке чтения
|
||||
Преобразует предыдущей процедурой номер логического блока в номер физического
|
||||
сектора и номер логического блока внутри сектора; если последняя
|
||||
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
||||
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
||||
и читает сектор.
|
||||
на входе: eax = логический блок;
|
||||
si - индикатор, задающий, следует ли читать данные в случае,
|
||||
если логический блок начинается с начала физического:
|
||||
si = 0 - не нужно, si ненулевой - нужно
|
||||
на выходе:
|
||||
физический сектор загружен по адресу 0000:1000
|
||||
si указывает на данные логического блока
|
||||
CF установлен при ошибке чтения
|
||||
Преобразует предыдущей процедурой номер логического блока в номер физического
|
||||
сектора и номер логического блока внутри сектора; если последняя
|
||||
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
||||
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
||||
и читает сектор.
|
||||
|
||||
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
||||
(read_many_bytes и read_many_bytes.with_first):
|
||||
на входе:
|
||||
eax = логический блок
|
||||
esi = число байт для чтения
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
cur_limit = размер буфера (не меньше esi)
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
cur_limit соответствующим образом уменьшен
|
||||
Отличие двух процедур: вторая дополнительно принимает во внимание переменную
|
||||
[first_byte], начиная чтение первого блока со смещения [first_byte];
|
||||
соответственно, первая читает блок с начала, обнуляя [first_byte]
|
||||
при входе.
|
||||
1. Отдельно считывает первый физический сектор во временную область 0000:1000,
|
||||
если первый логический блок начинается не с начала сектора. При
|
||||
ошибке чтения выходит из процедуры.
|
||||
2. Пересылает нужную часть данных (возможно, 0 байт), прочитанных в п.1,
|
||||
в буфер. Нормализует указатель на буфер.
|
||||
3. Если все необходимые данные уже прочитаны, выходит из процедуры.
|
||||
4. Дальнейшие данные находятся в нескольких физических секторах, при этом,
|
||||
возможно, последний сектор считывать нужно не целиком.
|
||||
5. Если в буфере есть место для считывания всех секторов, то сразу читаются
|
||||
все сектора, после чего указатель на буфер нужным образом уменьшается.
|
||||
6. Если же нет, то считываются все сектора, кроме последнего, после чего
|
||||
последний сектор считывается отдельно во временную область, и уже
|
||||
оттуда нужная часть данных копируется в буфер.
|
||||
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
||||
(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. Если же нет, то считываются все сектора, кроме последнего, после чего
|
||||
последний сектор считывается отдельно во временную область, и уже
|
||||
оттуда нужная часть данных копируется в буфер.
|
||||
|
@ -24,337 +24,337 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Встречаются вирус и FAT.
|
||||
- Привет, ты кто?
|
||||
- Я? Вирус.
|
||||
- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
|
||||
Встречаются вирус и FAT.
|
||||
- Привет, ты кто?
|
||||
- Я? Вирус.
|
||||
- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
|
||||
|
||||
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны.
|
||||
2) Минимальный процессор - 80186.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны.
|
||||
2) Минимальный процессор - 80186.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки валидны на момент написания этого файла, 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
|
||||
Документация в тему (ссылки валидны на момент написания этого файла, 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
|
||||
|
||||
=====================================================================
|
||||
|
||||
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
|
||||
занимает 12 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x17EE = 6126 байт. Вся таблица помещается в памяти.
|
||||
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
|
||||
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
|
||||
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
|
||||
на практике нужна только небольшая её часть. Поэтому место в памяти
|
||||
резервируется, но данные считываются только в момент, когда к ним
|
||||
действительно идёт обращение.
|
||||
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
|
||||
занимает 12 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x17EE = 6126 байт. Вся таблица помещается в памяти.
|
||||
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
|
||||
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
|
||||
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
|
||||
на практике нужна только небольшая её часть. Поэтому место в памяти
|
||||
резервируется, но данные считываются только в момент, когда к ним
|
||||
действительно идёт обращение.
|
||||
|
||||
Схема используемой памяти:
|
||||
...-7C00 стек
|
||||
7C00-7E00 код бутсектора
|
||||
7E00-8200 вспомогательный файл загрузчика (kordldr.f1x)
|
||||
8200-8300 список загруженных секторов таблицы FAT16
|
||||
(1 = соответствующий сектор загружен)
|
||||
60000-80000 загруженная таблица FAT12 / место для таблицы FAT16
|
||||
80000-90000 текущий кластер текущей рассматриваемой папки
|
||||
90000-92000 кэш для корневой папки
|
||||
92000-... кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
Схема используемой памяти:
|
||||
...-7C00 стек
|
||||
7C00-7E00 код бутсектора
|
||||
7E00-8200 вспомогательный файл загрузчика (kordldr.f1x)
|
||||
8200-8300 список загруженных секторов таблицы FAT16
|
||||
(1 = соответствующий сектор загружен)
|
||||
60000-80000 загруженная таблица FAT12 / место для таблицы FAT16
|
||||
80000-90000 текущий кластер текущей рассматриваемой папки
|
||||
90000-92000 кэш для корневой папки
|
||||
92000-... кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
||||
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
||||
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
||||
это освобождает ds и экономит на размере кода).
|
||||
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
||||
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
||||
сообщением об отсутствии LBA.
|
||||
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
||||
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
||||
предполагает уже существующие данные корректными.
|
||||
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
|
||||
и начальный сектор данных. Кладёт их в стек; впоследствии они
|
||||
всегда будут лежать в стеке и адресоваться через bp.
|
||||
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
|
||||
секторов - минимум из размера корневой папки, указанного в BPB, и 16
|
||||
(размер кэша для корневой папки - 2000h байт = 16 секторов).
|
||||
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
|
||||
он оказывается папкой, или если файл имеет нулевую длину -
|
||||
переходит на код обработки ошибок с сообщением о
|
||||
ненайденном загрузчике.
|
||||
Замечание: на этом этапе загрузки искать можно только в корневой
|
||||
папке и только имена, заданные в формате файловой системе FAT
|
||||
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
||||
быть заглавными, при необходимости имя и расширение дополняются
|
||||
пробелами, разделяющей точки нет, завершающего нуля нет).
|
||||
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
|
||||
ему управление. При этом в регистрах dx:ax оказывается абсолютный
|
||||
номер первого сектора kordldr.f1x, а в cx - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
||||
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
||||
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
||||
это освобождает ds и экономит на размере кода).
|
||||
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
||||
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
||||
сообщением об отсутствии LBA.
|
||||
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
||||
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
||||
предполагает уже существующие данные корректными.
|
||||
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
|
||||
и начальный сектор данных. Кладёт их в стек; впоследствии они
|
||||
всегда будут лежать в стеке и адресоваться через bp.
|
||||
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
|
||||
секторов - минимум из размера корневой папки, указанного в BPB, и 16
|
||||
(размер кэша для корневой папки - 2000h байт = 16 секторов).
|
||||
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
|
||||
он оказывается папкой, или если файл имеет нулевую длину -
|
||||
переходит на код обработки ошибок с сообщением о
|
||||
ненайденном загрузчике.
|
||||
Замечание: на этом этапе загрузки искать можно только в корневой
|
||||
папке и только имена, заданные в формате файловой системе FAT
|
||||
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
||||
быть заглавными, при необходимости имя и расширение дополняются
|
||||
пробелами, разделяющей точки нет, завершающего нуля нет).
|
||||
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
|
||||
ему управление. При этом в регистрах dx:ax оказывается абсолютный
|
||||
номер первого сектора kordldr.f1x, а в cx - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения секторов (read_sectors и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения секторов (read_sectors и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
dx:ax = стартовый сектор (относительно начала логического диска
|
||||
для read_sectors, относительно начала данных для read_sectors2)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
||||
в номер относительно начала логического диска, прибавляя номер сектора
|
||||
начала данных, хранящийся в стеке как [bp-8].
|
||||
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.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
dx:ax = стартовый сектор (относительно начала логического диска
|
||||
для read_sectors, относительно начала данных для read_sectors2)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
||||
в номер относительно начала логического диска, прибавляя номер сектора
|
||||
начала данных, хранящийся в стеке как [bp-8].
|
||||
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.
|
||||
|
||||
Процедура поиска элемента по имени в уже прочитанных данных папки
|
||||
Процедура поиска элемента по имени в уже прочитанных данных папки
|
||||
(scan_for_filename):
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
|
||||
3 на расширение, все буквы заглавные, если имя/расширение
|
||||
короче, оно дополняется до максимума пробелами)
|
||||
es = сегмент данных папки
|
||||
cx = число элементов в прочитанных данных
|
||||
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
|
||||
(ZF=1, если либо найден запрошенный элемент, либо достигнут
|
||||
конец папки); CF определяет, удалось ли найти элемент с искомым именем
|
||||
(CF=1, если не удалось); если удалось, то es:di указывает на него.
|
||||
scan_for_filename считает, что данные папки размещаются начиная с es:0.
|
||||
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
|
||||
проверяет имена.
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
|
||||
3 на расширение, все буквы заглавные, если имя/расширение
|
||||
короче, оно дополняется до максимума пробелами)
|
||||
es = сегмент данных папки
|
||||
cx = число элементов в прочитанных данных
|
||||
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
|
||||
(ZF=1, если либо найден запрошенный элемент, либо достигнут
|
||||
конец папки); CF определяет, удалось ли найти элемент с искомым именем
|
||||
(CF=1, если не удалось); если удалось, то es:di указывает на него.
|
||||
scan_for_filename считает, что данные папки размещаются начиная с es:0.
|
||||
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
|
||||
проверяет имена.
|
||||
|
||||
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
|
||||
на входе должно быть установлено:
|
||||
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
|
||||
сканирует элементы; если по результатам сканирования обнаруживает,
|
||||
что нужно читать папку дальше, то считывает не более 0x10000 = 64K
|
||||
байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
|
||||
не вылезти за пределы используемой памяти, во-вторых, сканирование
|
||||
предполагает, что все обрабатываемые элементы располагаются в одном
|
||||
сегменте) и продолжает цикл.
|
||||
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
|
||||
кончились элементы в папке (судя по числу элементов, указанному в BPB);
|
||||
очередной элемент папки сигнализирует о конце (первый байт нулевой).
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
|
||||
сканирует элементы; если по результатам сканирования обнаруживает,
|
||||
что нужно читать папку дальше, то считывает не более 0x10000 = 64K
|
||||
байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
|
||||
не вылезти за пределы используемой памяти, во-вторых, сканирование
|
||||
предполагает, что все обрабатываемые элементы располагаются в одном
|
||||
сегменте) и продолжает цикл.
|
||||
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
|
||||
кончились элементы в папке (судя по числу элементов, указанному в BPB);
|
||||
очередной элемент папки сигнализирует о конце (первый байт нулевой).
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Работа вспомогательного загрузчика kordldr.f1x:
|
||||
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
||||
В зависимости от этого устанавливает смещения используемых процедур
|
||||
бутсектора. Критерий проверки: scan_for_filename должна начинаться
|
||||
с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
|
||||
с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
|
||||
именно такую форму).
|
||||
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
||||
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
||||
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
||||
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
||||
Замечание: этот размер не может превосходить 0A0000h байт и
|
||||
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
||||
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
||||
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
|
||||
спецификации от Microsoft (версия 1.03 спецификации датирована,
|
||||
к слову, 06 декабря 2000 года), разрядность FAT определяется
|
||||
исключительно числом кластеров: максимальное число кластеров на
|
||||
FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
|
||||
может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
|
||||
а число 0xFF7 не может быть корректным номером кластера.
|
||||
Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
|
||||
по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
|
||||
явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
|
||||
FAT12-том, в результате получается, что последний кластер
|
||||
(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
|
||||
[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
|
||||
[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
|
||||
лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
|
||||
в соответствии со спецификацией. Linux при определении FAT12/FAT16
|
||||
честно следует спецификации.
|
||||
Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
|
||||
Microsoft если и будет исправлять ошибки, то согласно собственному
|
||||
описанию.
|
||||
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
|
||||
Если размер, указанный в BPB, превосходит 12 секторов,
|
||||
это означает, что заявленный размер слишком большой (это не считается
|
||||
ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
|
||||
заведомо влезает в такой объём данных).
|
||||
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
|
||||
FAT не загружен (они будут подгружаться позднее, когда понадобятся
|
||||
и только те, которые понадобятся).
|
||||
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
||||
kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
|
||||
значения регистров на входе в kordldr.f1x.
|
||||
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
|
||||
найден, или оказался папкой, или оказался слишком большим, то переходит
|
||||
на код обработки ошибок из бутсектора с сообщением
|
||||
Работа вспомогательного загрузчика kordldr.f1x:
|
||||
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
||||
В зависимости от этого устанавливает смещения используемых процедур
|
||||
бутсектора. Критерий проверки: scan_for_filename должна начинаться
|
||||
с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
|
||||
с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
|
||||
именно такую форму).
|
||||
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
||||
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
||||
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
||||
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
||||
Замечание: этот размер не может превосходить 0A0000h байт и
|
||||
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
||||
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
||||
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
|
||||
спецификации от Microsoft (версия 1.03 спецификации датирована,
|
||||
к слову, 06 декабря 2000 года), разрядность FAT определяется
|
||||
исключительно числом кластеров: максимальное число кластеров на
|
||||
FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
|
||||
может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
|
||||
а число 0xFF7 не может быть корректным номером кластера.
|
||||
Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
|
||||
по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
|
||||
явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
|
||||
FAT12-том, в результате получается, что последний кластер
|
||||
(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
|
||||
[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
|
||||
[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
|
||||
лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
|
||||
в соответствии со спецификацией. Linux при определении FAT12/FAT16
|
||||
честно следует спецификации.
|
||||
Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
|
||||
Microsoft если и будет исправлять ошибки, то согласно собственному
|
||||
описанию.
|
||||
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
|
||||
Если размер, указанный в BPB, превосходит 12 секторов,
|
||||
это означает, что заявленный размер слишком большой (это не считается
|
||||
ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
|
||||
заведомо влезает в такой объём данных).
|
||||
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
|
||||
FAT не загружен (они будут подгружаться позднее, когда понадобятся
|
||||
и только те, которые понадобятся).
|
||||
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
||||
kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
|
||||
значения регистров на входе в kordldr.f1x.
|
||||
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 (номер диска).
|
||||
Устанавливает bx='12', если тип файловой системы - FAT12, и
|
||||
bx='16' в случае FAT16. Устанавливает si=смещение функции обратного
|
||||
вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
|
||||
9. Передаёт управление по адресу 1000:0000.
|
||||
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
||||
и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
|
||||
по-прежнему нет.
|
||||
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
|
||||
Это нужно, чтобы последующие обращения к коду бутсектора в случае
|
||||
ошибок чтения не выводил соответствующее сообщение с последующей
|
||||
перезагрузкой, а рапортовал об ошибке чтения, которую мог бы
|
||||
как-нибудь обработать вторичный загрузчик.
|
||||
8. Если загрузочный диск имеет идентификатор меньше 0x80,
|
||||
то устанавливает al='f' ("floppy"), ah=идентификатор диска,
|
||||
иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
|
||||
Устанавливает bx='12', если тип файловой системы - FAT12, и
|
||||
bx='16' в случае FAT16. Устанавливает si=смещение функции обратного
|
||||
вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
|
||||
9. Передаёт управление по адресу 1000:0000.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:7C00, а -8 берётся от того, что
|
||||
инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
|
||||
и они должны сохраняться в неизменности.
|
||||
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
|
||||
и вызывает нужную вспомогательную процедуру.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:7C00, а -8 берётся от того, что
|
||||
инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
|
||||
и они должны сохраняться в неизменности.
|
||||
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
|
||||
и вызывает нужную вспомогательную процедуру.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
|
||||
Вспомогательные процедуры kordldr.f1x.
|
||||
Процедура получения следующего кластера в FAT (get_next_cluster):
|
||||
1. Вспоминает разрядность FAT, вычисленную ранее.
|
||||
Для FAT12:
|
||||
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
|
||||
вся таблица FAT.
|
||||
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
|
||||
слова, задающего следующий кластер. Загружает слово по этому адресу.
|
||||
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
|
||||
располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
|
||||
на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
|
||||
надо.
|
||||
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
|
||||
номера нормальных кластеров меньше, и флаг CF устанавливается;
|
||||
специальные значения EOF и BadClus сбрасывают флаг CF.
|
||||
Для FAT16:
|
||||
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
|
||||
в таблице FAT.
|
||||
3. Если сектор ещё не загружен, то загружает его.
|
||||
4. Вычисляет смещение данных для конкретного кластера относительно начала
|
||||
сектора.
|
||||
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
|
||||
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
|
||||
CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
|
||||
Вспомогательные процедуры kordldr.f1x.
|
||||
Процедура получения следующего кластера в FAT (get_next_cluster):
|
||||
1. Вспоминает разрядность FAT, вычисленную ранее.
|
||||
Для FAT12:
|
||||
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
|
||||
вся таблица FAT.
|
||||
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
|
||||
слова, задающего следующий кластер. Загружает слово по этому адресу.
|
||||
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
|
||||
располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
|
||||
на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
|
||||
надо.
|
||||
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
|
||||
номера нормальных кластеров меньше, и флаг CF устанавливается;
|
||||
специальные значения EOF и BadClus сбрасывают флаг CF.
|
||||
Для FAT16:
|
||||
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
|
||||
в таблице FAT.
|
||||
3. Если сектор ещё не загружен, то загружает его.
|
||||
4. Вычисляет смещение данных для конкретного кластера относительно начала
|
||||
сектора.
|
||||
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
|
||||
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
|
||||
CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
|
||||
|
||||
Процедура загрузки файла (load_file):
|
||||
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
||||
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
||||
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
||||
(больше 8 символов в имени, больше 3 символов в расширении или
|
||||
больше одной точки), возвращается с ошибкой.
|
||||
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
|
||||
папки используется процедура из бутсектора. Для остальных папок:
|
||||
a) Проверяет, есть ли такая папка в кэше некорневых папок.
|
||||
(Идентификация папок осуществляется по номеру начального кластера.)
|
||||
Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
|
||||
выкидывает папку, к которой дольше всего не было обращений. (Для
|
||||
каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
|
||||
определяющая его номер при сортировке по давности последнего обращения.
|
||||
При обращении к какому-то элементу его метка становится нулевой,
|
||||
а те метки, которые меньше старого значения, увеличиваются на единицу.)
|
||||
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
||||
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
||||
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
||||
процедуры с ошибкой.
|
||||
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
||||
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
||||
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
||||
и просматривает в нём все элементы. Работает, пока не случится одно из
|
||||
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
||||
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
||||
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
||||
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
||||
запрошенном имени должен быть файлом, все промежуточные - папками.
|
||||
Если текущий компонент имени - промежуточный, продвигает текущую
|
||||
рассматриваемую папку и возвращается к пункту 2.
|
||||
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
Процедура загрузки файла (load_file):
|
||||
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
||||
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
||||
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
||||
(больше 8 символов в имени, больше 3 символов в расширении или
|
||||
больше одной точки), возвращается с ошибкой.
|
||||
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
|
||||
папки используется процедура из бутсектора. Для остальных папок:
|
||||
a) Проверяет, есть ли такая папка в кэше некорневых папок.
|
||||
(Идентификация папок осуществляется по номеру начального кластера.)
|
||||
Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
|
||||
выкидывает папку, к которой дольше всего не было обращений. (Для
|
||||
каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
|
||||
определяющая его номер при сортировке по давности последнего обращения.
|
||||
При обращении к какому-то элементу его метка становится нулевой,
|
||||
а те метки, которые меньше старого значения, увеличиваются на единицу.)
|
||||
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
||||
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
||||
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
||||
процедуры с ошибкой.
|
||||
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
||||
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
||||
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
||||
и просматривает в нём все элементы. Работает, пока не случится одно из
|
||||
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
||||
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
||||
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
||||
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
||||
запрошенном имени должен быть файлом, все промежуточные - папками.
|
||||
Если текущий компонент имени - промежуточный, продвигает текущую
|
||||
рассматриваемую папку и возвращается к пункту 2.
|
||||
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
|
@ -24,310 +24,310 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Читай между строк - там никогда не бывает опечаток.
|
||||
Читай между строк - там никогда не бывает опечаток.
|
||||
|
||||
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны. (Если дело происходит на носителе с разбиением на
|
||||
разделы и загрузочный код в MBR достаточно умный, то читабельности резервной
|
||||
копии бутсектора (сектор номер 6 на томе) достаточно вместо читабельности
|
||||
самого бутсектора).
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 584K свободной базовой памяти.
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель 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
|
||||
Документация в тему (ссылки проверялись на валидность 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)
|
||||
Схема используемой памяти:
|
||||
...-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, сохраняет и его в стек для последующего обращения через
|
||||
Основной процесс загрузки.
|
||||
Точка входа (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 - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
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. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения кластера (read_cluster):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения кластера (read_cluster):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = номер кластера
|
||||
на выходе: ecx = число прочитанных секторов (размер кластера),
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
eax и старшие слова других 32-битных регистров разрушаются
|
||||
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
|
||||
и переходит к следующей процедуре.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = номер кластера
|
||||
на выходе: ecx = число прочитанных секторов (размер кластера),
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
eax и старшие слова других 32-битных регистров разрушаются
|
||||
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
|
||||
и переходит к следующей процедуре.
|
||||
|
||||
Процедура чтения секторов (read_sectors32 и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения секторов (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.
|
||||
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):
|
||||
на входе должно быть установлено:
|
||||
Процедура поиска элемента в папке (lookup_in_dir):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
eax = начальный кластер папки
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
eax = начальный кластер папки
|
||||
bx = 0
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
|
||||
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
|
||||
для продвижения по цепочке кластеров - описанную далее процедуру
|
||||
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
|
||||
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
|
||||
если чтение прервётся раньше) не перекрываются последующими чтениями
|
||||
(это будет использовано позднее, в системе кэширования из kordldr.f32).
|
||||
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
|
||||
кончились элементы в папке (первый байт очередного элемента нулевой);
|
||||
кончились данные папки в соответствии с цепочкой кластеров из FAT.
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
|
||||
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
|
||||
для продвижения по цепочке кластеров - описанную далее процедуру
|
||||
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
|
||||
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
|
||||
если чтение прервётся раньше) не перекрываются последующими чтениями
|
||||
(это будет использовано позднее, в системе кэширования из kordldr.f32).
|
||||
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
|
||||
кончились элементы в папке (первый байт очередного элемента нулевой);
|
||||
кончились данные папки в соответствии с цепочкой кластеров из FAT.
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран 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. Если файл не
|
||||
найден, или оказался папкой, или оказался слишком большим, то переходит
|
||||
на код обработки ошибок из бутсектора с сообщением
|
||||
Работа вспомогательного загрузчика 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.
|
||||
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
||||
и в формате 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. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
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, то оно задаёт номер следующего кластера в цепочке;
|
||||
в противном случае цепочка закончилась.
|
||||
Вспомогательные процедуры 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 и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
Процедура загрузки файла (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.
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
|
@ -5,25 +5,25 @@
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
‡ £à㧮çë© á¥ªâ®à ¤«ï Ž‘ Š®«¨¡à¨ (FAT12, ¤¨áª¥â )
|
||||
Загрузочный сектор для ОС Колибри (FAT12, дискета)
|
||||
|
||||
- Ž¯¨á ¨¥
|
||||
<EFBFBD>®§¢®«ï¥â § £à㦠âì KERNEL.MNT á ¤¨áª¥â/®¡à §®¢
|
||||
®¡êñ¬®¬ 1.44M, 1.68M, 1.72M ¨ 2.88M
|
||||
„«ï ¢ë¡®à ®¡êñ¬ ¤¨áª , ¤«ï ª®â®à®£® ¤® ᮡà âì
|
||||
§ £à㧮çë© á¥ªâ®à, ¥®¡å®¤¨¬® ¢ ä ©«¥ boot_fat12.asm
|
||||
à ᪮¬¬¥â¨à®¢ âì áâப㠢¨¤ :
|
||||
- Описание
|
||||
Позволяет загружать KERNEL.MNT с дискет/образов
|
||||
объёмом 1.44M, 1.68M, 1.72M и 2.88M
|
||||
Для выбора объёма диска, для которого надо собрать
|
||||
загрузочный сектор, необходимо в файле boot_fat12.asm
|
||||
раскомментировать строку вида:
|
||||
include 'floppy????.inc'
|
||||
¤«ï ¥®¡å®¤¨¬®£® ®¡êñ¬ ¤¨áª . „®áâã¯ë¥ ¢ ਠâë:
|
||||
для необходимого объёма диска. Доступные варианты:
|
||||
floppy1440.inc,
|
||||
floppy1680.inc,
|
||||
floppy1743.inc ¨ floppy2880.inc
|
||||
floppy1743.inc и floppy2880.inc
|
||||
|
||||
- ‘¡®àª
|
||||
- Сборка
|
||||
fasm boot_fat12.asm
|
||||
|
||||
- „«ï § ¯¨á¨ § £à㧮箣® ᥪâ®à ¤¨áª/®¡à § ¯®¤ Linux
|
||||
¬®¦® ¢®á¯®«ì§®¢ âìáï á«¥¤ãî饩 ª®¬ ¤®©:
|
||||
- Для записи загрузочного сектора на диск/образ под Linux
|
||||
можно воспользоваться следующей командой:
|
||||
dd if=boot_fat12.bin of=288.img bs=512 count=1 conv=notrunc
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
@ -330,7 +330,7 @@ new_app_base equ 0;
|
||||
twdw equ 0x2000 ;(CURRENT_TASK-window_data)
|
||||
|
||||
std_application_base_address equ new_app_base
|
||||
RING0_STACK_SIZE equ (0x2000 - 512) ;512 áàéò äëÿ êîíòåêñòà FPU
|
||||
RING0_STACK_SIZE equ (0x2000 - 512) ;512 байт для контекста FPU
|
||||
|
||||
REG_SS equ (RING0_STACK_SIZE-4)
|
||||
REG_APP_ESP equ (RING0_STACK_SIZE-8)
|
||||
|
@ -1,11 +1,11 @@
|
||||
; <EFBFBD>ste archivo debe ser editado con codificaci¢n CP866
|
||||
; Éste archivo debe ser editado con codificación CP866
|
||||
|
||||
ugui_mouse_speed db 'velocidad del rat¢n',0
|
||||
ugui_mouse_delay db 'demora del rat¢n',0
|
||||
ugui_mouse_speed:cp850 'velocidad del ratón',0
|
||||
ugui_mouse_delay:cp850 'demora del ratón',0
|
||||
|
||||
udev db 'disp',0
|
||||
unet db 'red',0
|
||||
unet_active db 'activa',0
|
||||
unet_addr db 'direc',0
|
||||
unet_mask db 'm sc',0
|
||||
unet_gate db 'puer',0
|
||||
udev:cp850 'disp',0
|
||||
unet:cp850 'red',0
|
||||
unet_active:cp850 'activa',0
|
||||
unet_addr:cp850 'direc',0
|
||||
unet_mask:cp850 'másc',0
|
||||
unet_gate:cp850 'puer',0
|
||||
|
@ -164,7 +164,7 @@ endp
|
||||
proc strtoint_dec stdcall,strs
|
||||
pushad
|
||||
xor edx, edx
|
||||
; ッョィ皙 ェョュ譬
|
||||
; поиск конца
|
||||
mov esi, [strs]
|
||||
@@:
|
||||
lodsb
|
||||
@ -180,7 +180,7 @@ proc strtoint_dec stdcall,strs
|
||||
dec ebx
|
||||
or ebx, ebx
|
||||
jz @f
|
||||
imul ecx, ecx, 10; ッョ瑜、ョェ
|
||||
imul ecx, ecx, 10; порядок
|
||||
jmp @b
|
||||
@@:
|
||||
|
||||
|
@ -631,21 +631,21 @@ proc page_fault_handler
|
||||
mov eax, [pf_err_code]
|
||||
|
||||
cmp ebx, OS_BASE ;ebx == .err_addr
|
||||
jb .user_space ;страница в памяти приложения ;
|
||||
jb .user_space ;страница в памяти приложения ;
|
||||
|
||||
cmp ebx, page_tabs
|
||||
jb .kernel_space ;страница в памяти ядра
|
||||
jb .kernel_space ;страница в памяти ядра
|
||||
|
||||
cmp ebx, kernel_tabs
|
||||
jb .alloc;.app_tabs ;таблицы страниц приложения ;
|
||||
;просто создадим одну
|
||||
if 0 ;пока это просто лишнее
|
||||
jb .alloc;.app_tabs ;таблицы страниц приложения ;
|
||||
;просто создадим одну
|
||||
if 0 ;пока это просто лишнее
|
||||
cmp ebx, LFB_BASE
|
||||
jb .core_tabs ;таблицы страниц ядра
|
||||
;Ошибка
|
||||
jb .core_tabs ;таблицы страниц ядра
|
||||
;Ошибка
|
||||
.lfb:
|
||||
;область LFB
|
||||
;Ошибка
|
||||
;область LFB
|
||||
;Ошибка
|
||||
jmp .fail
|
||||
end if
|
||||
.core_tabs:
|
||||
@ -661,21 +661,21 @@ end if
|
||||
|
||||
.user_space:
|
||||
test eax, PG_MAP
|
||||
jnz .err_access ;Страница присутствует
|
||||
;Ошибка доступа ?
|
||||
jnz .err_access ;Страница присутствует
|
||||
;Ошибка доступа ?
|
||||
|
||||
shr ebx, 12
|
||||
mov ecx, ebx
|
||||
shr ecx, 10
|
||||
mov edx, [master_tab+ecx*4]
|
||||
test edx, PG_MAP
|
||||
jz .fail ;таблица страниц не создана
|
||||
;неверный адрес в программе
|
||||
jz .fail ;таблица страниц не создана
|
||||
;неверный адрес в программе
|
||||
|
||||
mov eax, [page_tabs+ebx*4]
|
||||
test eax, 2
|
||||
jz .fail ;адрес не зарезервирован для ;
|
||||
;использования. Ошибка
|
||||
jz .fail ;адрес не зарезервирован для ;
|
||||
;использования. Ошибка
|
||||
.alloc:
|
||||
call alloc_page
|
||||
test eax, eax
|
||||
@ -731,16 +731,16 @@ end if
|
||||
|
||||
.kernel_space:
|
||||
test eax, PG_MAP
|
||||
jz .fail ;страница не присутствует
|
||||
jz .fail ;страница не присутствует
|
||||
|
||||
test eax, 12 ;U/S (+below)
|
||||
jnz .fail ;приложение обратилось к памяти
|
||||
;ядра
|
||||
jnz .fail ;приложение обратилось к памяти
|
||||
;ядра
|
||||
;test eax, 8
|
||||
;jnz .fail ;установлен зарезервированный бит
|
||||
;в таблицах страниц. добавлено в P4/Xeon
|
||||
;jnz .fail ;установлен зарезервированный бит
|
||||
;в таблицах страниц. добавлено в P4/Xeon
|
||||
|
||||
;попытка записи в защищённую страницу ядра
|
||||
;попытка записи в защищённую страницу ядра
|
||||
|
||||
cmp ebx, tss._io_map_0
|
||||
jb .fail
|
||||
|
@ -99,8 +99,8 @@ updatecputimes:
|
||||
loop .newupdate
|
||||
ret
|
||||
|
||||
;TODO: Надо бы убрать использование do_change_task из V86...
|
||||
; и после этого перенести обработку TASKDATA.counter_add/sum в do_change_task
|
||||
;TODO: Надо бы убрать использование do_change_task из V86...
|
||||
; и после этого перенести обработку TASKDATA.counter_add/sum в do_change_task
|
||||
|
||||
align 4
|
||||
do_change_task:
|
||||
|
@ -1,4 +1,4 @@
|
||||
; <EFBFBD>ste archivo debe ser editado con codificaci¢n CP866
|
||||
; Éste archivo debe ser editado con codificación CP866
|
||||
|
||||
msg_sel_ker db "n£cleo", 0
|
||||
msg_sel_app db "aplicaci¢n", 0
|
||||
msg_sel_ker: cp850 "núcleo", 0
|
||||
msg_sel_app: cp850 "aplicación", 0
|
||||
|
@ -61,7 +61,7 @@ iglobal
|
||||
idtreg: ; data for LIDT instruction (!!! must be immediately below sys_int data)
|
||||
dw 2*($-sys_int-4)-1
|
||||
dd idts ;0x8000B100
|
||||
dw 0 ;просто выравнивание
|
||||
dw 0 ;просто выравнивание
|
||||
|
||||
msg_fault_sel dd msg_exc_8,msg_exc_u,msg_exc_a,msg_exc_b
|
||||
dd msg_exc_c,msg_exc_d,msg_exc_e
|
||||
@ -109,19 +109,19 @@ uglobal
|
||||
pf_err_code dd ?
|
||||
endg
|
||||
|
||||
page_fault_exc: ; дуракоусточивость: селекторы испорчены...
|
||||
pop [ss:pf_err_code]; действительно до следующего #PF
|
||||
page_fault_exc: ; дуракоусточивость: селекторы испорчены...
|
||||
pop [ss:pf_err_code]; действительно до следующего #PF
|
||||
save_ring3_context
|
||||
mov bl, 14
|
||||
|
||||
exc_c: ; исключения (все, кроме 7-го - #NM)
|
||||
; Фрэйм стека при исключении/прерывании из 3-го кольца + pushad (т.е., именно здесь)
|
||||
exc_c: ; исключения (все, кроме 7-го - #NM)
|
||||
; Фрэйм стека при исключении/прерывании из 3-го кольца + pushad (т.е., именно здесь)
|
||||
reg_ss equ esp+0x30
|
||||
reg_esp3 equ esp+0x2C
|
||||
reg_eflags equ esp+0x28
|
||||
reg_cs3 equ esp+0x24
|
||||
reg_eip equ esp+0x20
|
||||
; это фрэйм от pushad
|
||||
; это фрэйм от pushad
|
||||
reg_eax equ esp+0x1C
|
||||
reg_ecx equ esp+0x18
|
||||
reg_edx equ esp+0x14
|
||||
@ -131,10 +131,10 @@ exc_c: ;
|
||||
reg_esi equ esp+0x04
|
||||
reg_edi equ esp+0x00
|
||||
|
||||
mov ax, app_data ;исключение
|
||||
mov ds, ax ;загрузим правильные значения
|
||||
mov es, ax ;в регистры
|
||||
cld ; и приводим DF к стандарту
|
||||
mov ax, app_data ;исключение
|
||||
mov ds, ax ;загрузим правильные значения
|
||||
mov es, ax ;в регистры
|
||||
cld ; и приводим DF к стандарту
|
||||
movzx ebx, bl
|
||||
; redirect to V86 manager? (EFLAGS & 0x20000) != 0?
|
||||
test byte[reg_eflags+2], 2
|
||||
@ -290,12 +290,12 @@ unlock_application_table:
|
||||
|
||||
ret
|
||||
|
||||
; * eax = 64 - номер функции
|
||||
; * ebx = 1 - единственная подфункция
|
||||
; * ecx = новый размер памяти
|
||||
;Возвращаемое значение:
|
||||
; * eax = 0 - успешно
|
||||
; * eax = 1 - недостаточно памяти
|
||||
; * eax = 64 - номер функции
|
||||
; * ebx = 1 - единственная подфункция
|
||||
; * ecx = новый размер памяти
|
||||
;Возвращаемое значение:
|
||||
; * eax = 0 - успешно
|
||||
; * eax = 1 - недостаточно памяти
|
||||
|
||||
align 4
|
||||
sys_resize_app_memory:
|
||||
@ -685,17 +685,6 @@ terminate: ; terminate application
|
||||
ret
|
||||
restore .slot
|
||||
|
||||
;iglobal
|
||||
;if lang eq ru
|
||||
; boot_sched_1 db '‘®§¤ ЁҐ GDT TSS гЄ § ⥫п',0
|
||||
; boot_sched_2 db '‘®§¤ ЁҐ IDT в Ў«Ёжл',0
|
||||
;else
|
||||
; boot_sched_1 db 'Building gdt tss pointer',0
|
||||
; boot_sched_2 db 'Building IDT table',0
|
||||
;end if
|
||||
;endg
|
||||
|
||||
|
||||
;build_scheduler:
|
||||
; mov esi, boot_sched_1
|
||||
; call boot_log
|
||||
|
@ -29,7 +29,7 @@ cross_order:
|
||||
|
||||
align 32
|
||||
sysenter_entry:
|
||||
; Настраиваем стек
|
||||
; Настраиваем стек
|
||||
mov esp, [ss:tss._esp0]
|
||||
sti
|
||||
push ebp ; save app esp + 4
|
||||
@ -47,7 +47,7 @@ sysenter_entry:
|
||||
call unprotect_from_terminate
|
||||
popad
|
||||
;------------------
|
||||
xchg ecx, [ss:esp] ; в вершин стека - app ecx, ecx - app esp + 4
|
||||
xchg ecx, [ss:esp] ; в вершин стека - app ecx, ecx - app esp + 4
|
||||
sub ecx, 4
|
||||
xchg edx, [ecx] ; edx - return point, & save original edx
|
||||
push edx
|
||||
|
@ -178,7 +178,7 @@ v86_set_page:
|
||||
; esi=handle
|
||||
; out: eax=V86 address, para-aligned (0x10 multiple)
|
||||
; destroys: nothing
|
||||
; Ґ¤®ЇЁб !!!
|
||||
; недописана!!!
|
||||
;v86_alloc:
|
||||
; push ebx ecx edx edi
|
||||
; lea ebx, [esi+V86_machine.mutex]
|
||||
@ -377,8 +377,8 @@ v86_exc_c:
|
||||
jnz .nogp
|
||||
; Otherwise we can safely access byte at CS:IP
|
||||
; (because it is #GP, not #PF handler)
|
||||
; …б«Ё Ўл ¬л ¬®Ј«Ё бе«®Ї®в вм ЁбЄ«о票Ґ в®«мЄ® Ё§-§ звҐЁп Ў ©в®ў Є®¤ ,
|
||||
; ¬л Ўл ҐЈ® 㦥 бе«®Ї®в «Ё Ё нв® Ўл«® Ўл Ґ #GP
|
||||
; Если бы мы могли схлопотать исключение только из-за чтения байтов кода,
|
||||
; мы бы его уже схлопотали и это было бы не #GP
|
||||
movzx esi, word [esp+v86_regs.cs]
|
||||
shl esi, 4
|
||||
add esi, [esp+v86_regs.eip]
|
||||
|
@ -13,9 +13,9 @@ preboot_lfb db 0
|
||||
preboot_bootlog db 0
|
||||
boot_drive db 0
|
||||
bx_from_load:
|
||||
dw 'r1' ; структура для хранения параметров- откуда гашрузились, берется ниже из bx ; {SPraid}[13.03.2007]
|
||||
; a,b,c,d - винчестеры, r - рам диск
|
||||
; # диска... символ, а не байт. '1', а не 1
|
||||
dw 'r1' ; структура для хранения параметров- откуда гашрузились, берется ниже из bx ; {SPraid}[13.03.2007]
|
||||
; a,b,c,d - винчестеры, r - рам диск
|
||||
; # диска... символ, а не байт. '1', а не 1
|
||||
|
||||
align 4
|
||||
old_ints_h:
|
||||
|
@ -49,43 +49,43 @@ keymap_alt:
|
||||
|
||||
|
||||
if lang eq ru
|
||||
boot_initirq db 'ˆ¨æ¨ «¨§ æ¨ï IRQ',0
|
||||
boot_picinit db 'ˆ¨æ¨ «¨§ æ¨ï PIC',0
|
||||
boot_v86machine db 'ˆ¨æ¨ «¨§ æ¨ï á¨á⥬ë V86 ¬ è¨ë',0
|
||||
boot_inittimer db 'ˆ¨æ¨ «¨§ æ¨ï á¨á⥬®£® â ©¬¥à (IRQ0)',0
|
||||
boot_initapic db '<27>®¯ë⪠¨¨æ¨ «¨§ 樨 APIC',0
|
||||
boot_enableirq db '‚ª«îç¨âì ¯à¥àë¢ ¨ï 2, 6, 13, 14, 15',0
|
||||
boot_enablint_ide db '<27> §à¥è¥¨¥ ¯à¥àë¢ ¨© ¢ ª®â஫«¥à¥ IDE',0
|
||||
boot_detectfloppy db '<27>®¨áª floppy ¤¨áª®¢®¤®¢',0
|
||||
boot_detecthdcd db '<27>®¨áª ¦¥áâª¨å ¤¨áª®¢ ¨ ATAPI ¯à¨¢®¤®¢',0
|
||||
boot_getcache db '<27>®«ã票¥ ¯ ¬ï⨠¤«ï ªíè ',0
|
||||
boot_detectpart db '<27>®¨áª à §¤¥«®¢ ¤¨áª®¢ëå ãáâனá⢠å',0
|
||||
boot_init_sys db 'ˆ¨æ¨ «¨§ æ¨ï á¨á⥬®£® ª â «®£ /sys',0
|
||||
boot_loadlibs db '‡ £à㧪 ¡¨¡«¨®â¥ª (.obj)',0
|
||||
boot_memdetect db 'Š®«¨ç¥á⢮ ®¯¥à ⨢®© ¯ ¬ïâ¨',' ',' Œ¡',0
|
||||
boot_tss db '“áâ ®¢ª TSSs',0
|
||||
boot_cpuid db '—⥨¥ CPUIDs',0
|
||||
; boot_devices db '<27>®¨áª ãáâனáâ¢',0
|
||||
boot_timer db '“áâ ®¢ª â ©¬¥à ',0
|
||||
boot_irqs db '<27>¥à¥®¯à¥¤¥«¥¨¥ IRQ',0
|
||||
boot_setmouse db '“áâ ®¢ª ¬ëè¨',0
|
||||
boot_windefs db '“áâ ®¢ª áâ஥ª ®ª® ¯® 㬮«ç ¨î',0
|
||||
boot_bgr db '“áâ ®¢ª ä® ',0
|
||||
boot_resirqports db '<27>¥§¥à¢¨à®¢ ¨¥ IRQ ¨ ¯®à⮢',0
|
||||
boot_setrports db '“áâ ®¢ª ¤à¥á®¢ IRQ',0
|
||||
boot_setostask db '‘®§¤ ¨¥ ¯à®æ¥áá ï¤à ',0
|
||||
boot_allirqs db 'Žâªàë⨥ ¢á¥å IRQ',0
|
||||
boot_tsc db '—⥨¥ TSC',0
|
||||
boot_cpufreq db '— áâ®â ¯à®æ¥áá®à ',' ',' Œƒæ',0
|
||||
boot_pal_ega db '“áâ ®¢ª EGA/CGA 320x200 ¯ «¨âàë',0
|
||||
boot_pal_vga db '“áâ ®¢ª VGA 640x480 ¯ «¨âàë',0
|
||||
boot_failed db '‡ £à㧪 ¯¥à¢®£® ¯à¨«®¦¥¨ï ¥ 㤠« áì',0
|
||||
boot_mtrr db '“áâ ®¢ª MTRR',0
|
||||
boot_initirq: cp866 'Инициализация IRQ',0
|
||||
boot_picinit: cp866 'Инициализация PIC',0
|
||||
boot_v86machine: cp866 'Инициализация системы V86 машины',0
|
||||
boot_inittimer: cp866 'Инициализация системного таймера (IRQ0)',0
|
||||
boot_initapic: cp866 'Попытка инициализации APIC',0
|
||||
boot_enableirq: cp866 'Включить прерывания 2, 6, 13, 14, 15',0
|
||||
boot_enablint_ide:cp866 'Разрешение прерываний в контроллере IDE',0
|
||||
boot_detectfloppy:cp866 'Поиск floppy дисководов',0
|
||||
boot_detecthdcd: cp866 'Поиск жестких дисков и ATAPI приводов',0
|
||||
boot_getcache: cp866 'Получение памяти для кэша',0
|
||||
boot_detectpart: cp866 'Поиск разделов на дисковых устройствах',0
|
||||
boot_init_sys: cp866 'Инициализация системного каталога /sys',0
|
||||
boot_loadlibs: cp866 'Загрузка библиотек (.obj)',0
|
||||
boot_memdetect: cp866 'Количество оперативной памяти',' ',' Мб',0
|
||||
boot_tss: cp866 'Установка TSSs',0
|
||||
boot_cpuid: cp866 'Чтение CPUIDs',0
|
||||
; boot_devices: cp866 'Поиск устройств',0
|
||||
boot_timer: cp866 'Установка таймера',0
|
||||
boot_irqs: cp866 'Переопределение IRQ',0
|
||||
boot_setmouse: cp866 'Установка мыши',0
|
||||
boot_windefs: cp866 'Установка настроек окон по умолчанию',0
|
||||
boot_bgr: cp866 'Установка фона',0
|
||||
boot_resirqports: cp866 'Резервирование IRQ и портов',0
|
||||
boot_setrports: cp866 'Установка адресов IRQ',0
|
||||
boot_setostask: cp866 'Создание процесса ядра',0
|
||||
boot_allirqs: cp866 'Открытие всех IRQ',0
|
||||
boot_tsc: cp866 'Чтение TSC',0
|
||||
boot_cpufreq: cp866 'Частота процессора ',' ',' МГц',0
|
||||
boot_pal_ega: cp866 'Установка EGA/CGA 320x200 палитры',0
|
||||
boot_pal_vga: cp866 'Установка VGA 640x480 палитры',0
|
||||
boot_failed: cp866 'Загрузка первого приложения не удалась',0
|
||||
boot_mtrr: cp866 'Установка MTRR',0
|
||||
|
||||
boot_APIC_found db 'APIC ¢ª«îç¥', 0
|
||||
boot_APIC_nfound db 'APIC ¥ ©¤¥', 0
|
||||
boot_APIC_found: cp866 'APIC включен', 0
|
||||
boot_APIC_nfound: cp866 'APIC не найден', 0
|
||||
if preboot_blogesc
|
||||
boot_tasking db '‚ᥠ£®â®¢® ¤«ï § ¯ã᪠, ¦¬¨âॠESC ¤«ï áâ àâ ',0
|
||||
boot_tasking: cp866 'Все готово для запуска, нажмитре ESC для старта',0
|
||||
end if
|
||||
else if lang eq sp
|
||||
include 'data32sp.inc'
|
||||
@ -159,7 +159,7 @@ read_firstapp db '/sys/'
|
||||
firstapp db 'LAUNCHER',0
|
||||
notifyapp db '@notify',0
|
||||
if lang eq ru
|
||||
ud_user_message db 'Žè¨¡ª : ¥¯®¤¤¥à¦¨¢ ¥¬ ï ¨áâàãªæ¨ï ¯à®æ¥áá®à ',0
|
||||
ud_user_message: cp866 'Ошибка: неподдерживаемая инструкция процессора',0
|
||||
else if ~ lang eq sp
|
||||
ud_user_message db 'Error: unsupported processor instruction',0
|
||||
end if
|
||||
|
@ -1,40 +1,40 @@
|
||||
boot_initirq db 'Inicializar IRQ',0
|
||||
boot_picinit db 'Inicializar PIC',0
|
||||
boot_v86machine db 'Inicializar sistema V86',0
|
||||
boot_inittimer db 'Inicializar reloj del sistema (IRQ0)',0
|
||||
boot_initapic db 'Prueba inicializar APIC',0
|
||||
boot_enableirq db 'Habilitar interrupciones 2, 6, 13, 14, 15',0
|
||||
boot_enablint_ide db 'Habiliar interrupciones en controladores IDE',0
|
||||
boot_detectfloppy db 'Buscar unidades de disquete',0
|
||||
boot_detecthdcd db 'Buscar discos duros y unidades ATAPI',0
|
||||
boot_getcache db 'Tomar memoria para cach‚',0
|
||||
boot_detectpart db 'Buscar particiones en discos',0
|
||||
boot_init_sys db 'Inicializar directorio del sistema /sys',0
|
||||
boot_loadlibs db 'Cargando librer¡as (.obj)',0
|
||||
boot_memdetect db 'Determinando cantidad de memoria',0
|
||||
boot_tss db 'Configurando TSSs',0
|
||||
boot_cpuid db 'Leyendo CPUIDs',0
|
||||
; boot_devices db 'Detectando dispositivos',0
|
||||
boot_setmouse db 'Configurando el rat¢n',0
|
||||
boot_windefs db 'Setting window defaults',0
|
||||
boot_bgr db 'Calculating background',0
|
||||
boot_resirqports db 'Reservando IRQs y puertos',0
|
||||
boot_setostask db 'Configurando tarea OS',0
|
||||
boot_allirqs db 'Desenmascarando IRQs',0
|
||||
boot_tsc db 'Leyendo TSC',0
|
||||
boot_cpufreq db 'La frequencia del CPU es ',' ',' MHz',0
|
||||
boot_pal_ega db 'Configurando paleta EGA/CGA 320x200',0
|
||||
boot_pal_vga db 'Configurando paleta VGA 640x480',0
|
||||
boot_failed db 'Fallo al iniciar la primer aplicaci¢n',0
|
||||
boot_mtrr db 'Configurando MTRR',0
|
||||
boot_initirq: cp850 'Inicializar IRQ',0
|
||||
boot_picinit: cp850 'Inicializar PIC',0
|
||||
boot_v86machine: cp850 'Inicializar sistema V86',0
|
||||
boot_inittimer: cp850 'Inicializar reloj del sistema (IRQ0)',0
|
||||
boot_initapic: cp850 'Prueba inicializar APIC',0
|
||||
boot_enableirq: cp850 'Habilitar interrupciones 2, 6, 13, 14, 15',0
|
||||
boot_enablint_ide:cp850 'Habiliar interrupciones en controladores IDE',0
|
||||
boot_detectfloppy:cp850 'Buscar unidades de disquete',0
|
||||
boot_detecthdcd: cp850 'Buscar discos duros y unidades ATAPI',0
|
||||
boot_getcache: cp850 'Tomar memoria para caché',0
|
||||
boot_detectpart: cp850 'Buscar particiones en discos',0
|
||||
boot_init_sys: cp850 'Inicializar directorio del sistema /sys',0
|
||||
boot_loadlibs: cp850 'Cargando librerías (.obj)',0
|
||||
boot_memdetect: cp850 'Determinando cantidad de memoria',0
|
||||
boot_tss: cp850 'Configurando TSSs',0
|
||||
boot_cpuid: cp850 'Leyendo CPUIDs',0
|
||||
; boot_devices: cp850 'Detectando dispositivos',0
|
||||
boot_setmouse: cp850 'Configurando el ratón',0
|
||||
boot_windefs: cp850 'Setting window defaults',0
|
||||
boot_bgr: cp850 'Calculating background',0
|
||||
boot_resirqports: cp850 'Reservando IRQs y puertos',0
|
||||
boot_setostask: cp850 'Configurando tarea OS',0
|
||||
boot_allirqs: cp850 'Desenmascarando IRQs',0
|
||||
boot_tsc: cp850 'Leyendo TSC',0
|
||||
boot_cpufreq: cp850 'La frequencia del CPU es ',' ',' MHz',0
|
||||
boot_pal_ega: cp850 'Configurando paleta EGA/CGA 320x200',0
|
||||
boot_pal_vga: cp850 'Configurando paleta VGA 640x480',0
|
||||
boot_failed: cp850 'Fallo al iniciar la primer aplicación',0
|
||||
boot_mtrr: cp850 'Configurando MTRR',0
|
||||
|
||||
boot_APIC_found db 'APIC habilitado', 0
|
||||
boot_APIC_nfound db 'APIC no encontrado', 0
|
||||
boot_APIC_found: cp850 'APIC habilitado', 0
|
||||
boot_APIC_nfound: cp850 'APIC no encontrado', 0
|
||||
if preboot_blogesc
|
||||
boot_tasking db 'Todo configurado - presiona ESC para iniciar',0
|
||||
boot_tasking: cp850 'Todo configurado - presiona ESC para iniciar',0
|
||||
end if
|
||||
|
||||
msg_version db 'versi¢n incompatible del controlador',13,10,0
|
||||
msg_www db 'por favor, visita www.kolibrios.org',13,10,0
|
||||
msg_version: cp850 'versión incompatible del controlador',13,10,0
|
||||
msg_www: cp850 'por favor, visita www.kolibrios.org',13,10,0
|
||||
|
||||
ud_user_message db 'Error: instrucci¢n no soportada por el procesador',0
|
||||
ud_user_message:cp850 'Error: instrucción no soportada por el procesador',0
|
||||
|
@ -9,9 +9,9 @@ $Revision$
|
||||
|
||||
|
||||
;***************************************************
|
||||
; предварительная очистка области таблицы
|
||||
; поиск и занесение в таблицу приводов FDD
|
||||
; автор Mario79
|
||||
; предварительная очистка области таблицы
|
||||
; поиск и занесение в таблицу приводов FDD
|
||||
; автор Mario79
|
||||
;***************************************************
|
||||
xor eax, eax
|
||||
mov edi, DRIVE_DATA
|
||||
|
@ -9,13 +9,13 @@ $Revision$
|
||||
|
||||
|
||||
;******************************************************
|
||||
; поиск приводов HDD и CD
|
||||
; автор исходного текста Кулаков Владимир Геннадьевич.
|
||||
; адаптация и доработка Mario79
|
||||
; поиск приводов HDD и CD
|
||||
; автор исходного текста Кулаков Владимир Геннадьевич.
|
||||
; адаптация и доработка Mario79
|
||||
;******************************************************
|
||||
|
||||
;****************************************************
|
||||
;* ПОИСК HDD и CD *
|
||||
;* ПОИСК HDD и CD *
|
||||
;****************************************************
|
||||
FindHDD:
|
||||
mov [ChannelNumber], 1
|
||||
@ -71,54 +71,54 @@ FindHDD_3:
|
||||
ret
|
||||
|
||||
|
||||
; Адрес считываемого сектора в режиме LBA
|
||||
; Адрес считываемого сектора в режиме LBA
|
||||
uglobal
|
||||
SectorAddress DD ?
|
||||
endg
|
||||
;*************************************************
|
||||
;* ЧТЕНИЕ ИДЕНТИФИКАТОРА ЖЕСТКОГО ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска на канале (0 или 1). *
|
||||
;* Идентификационный блок данных считывается *
|
||||
;* в массив Sector512. *
|
||||
;* ЧТЕНИЕ ИДЕНТИФИКАТОРА ЖЕСТКОГО ДИСКА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска на канале (0 или 1). *
|
||||
;* Идентификационный блок данных считывается *
|
||||
;* в массив Sector512. *
|
||||
;*************************************************
|
||||
ReadHDD_ID:
|
||||
; Задать режим CHS
|
||||
; Задать режим CHS
|
||||
mov [ATAAddressMode], 0
|
||||
; Послать команду идентификации устройства
|
||||
; Послать команду идентификации устройства
|
||||
mov [ATAFeatures], 0
|
||||
mov [ATAHead], 0
|
||||
mov [ATACommand], 0ECh
|
||||
call SendCommandToHDD
|
||||
cmp [DevErrorCode], 0;проверить код ошибки
|
||||
jne @@End ;закончить, сохранив код ошибки
|
||||
cmp [DevErrorCode], 0;проверить код ошибки
|
||||
jne @@End ;закончить, сохранив код ошибки
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;адрес регистра состояни
|
||||
add DX, 7 ;адрес регистра состояни
|
||||
mov ecx, 0xffff
|
||||
@@WaitCompleet:
|
||||
; Проверить время выполнения команды
|
||||
; Проверить время выполнения команды
|
||||
dec ecx
|
||||
; cmp ecx,0
|
||||
jz @@Error1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
jz @@Error1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitCompleet
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Error6
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
jz @@WaitCompleet
|
||||
; Принять блок данных от контроллера
|
||||
; Принять блок данных от контроллера
|
||||
; mov AX,DS
|
||||
; mov ES,AX
|
||||
mov EDI, Sector512 ;offset Sector512
|
||||
mov DX, [ATABasePortAddr];регистр данных
|
||||
mov CX, 256 ;число считываемых слов
|
||||
rep insw ;принять блок данных
|
||||
mov DX, [ATABasePortAddr];регистр данных
|
||||
mov CX, 256 ;число считываемых слов
|
||||
rep insw ;принять блок данных
|
||||
ret
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Error1:
|
||||
mov [DevErrorCode], 1
|
||||
ret
|
||||
@ -129,120 +129,120 @@ ReadHDD_ID:
|
||||
|
||||
|
||||
iglobal
|
||||
; Стандартные базовые адреса каналов 1 и 2
|
||||
; Стандартные базовые адреса каналов 1 и 2
|
||||
StandardATABases DW 1F0h, 170h
|
||||
endg
|
||||
uglobal
|
||||
; Номер канала
|
||||
; Номер канала
|
||||
ChannelNumber DW ?
|
||||
; Номер диска
|
||||
; Номер диска
|
||||
DiskNumber DB ?
|
||||
; Базовый адрес группы портов контроллера ATA
|
||||
; Базовый адрес группы портов контроллера ATA
|
||||
ATABasePortAddr DW ?
|
||||
; Параметры ATA-команды
|
||||
ATAFeatures DB ? ;особенности
|
||||
ATASectorCount DB ? ;количество обрабатываемых секторов
|
||||
ATASectorNumber DB ? ;номер начального сектора
|
||||
ATACylinder DW ? ;номер начального цилиндра
|
||||
ATAHead DB ? ;номер начальной головки
|
||||
ATAAddressMode DB ? ;режим адресации (0 - CHS, 1 - LBA)
|
||||
ATACommand DB ? ;код команды, подлежащей выполнению
|
||||
; Код ошибки (0 - нет ошибок, 1 - превышен допустимый
|
||||
; интервал ожидания, 2 - неверный код режима адресации,
|
||||
; 3 - неверный номер канала, 4 - неверный номер диска,
|
||||
; 5 - неверный номер головки, 6 - ошибка при выполнении
|
||||
; команды)
|
||||
; Параметры ATA-команды
|
||||
ATAFeatures DB ? ;особенности
|
||||
ATASectorCount DB ? ;количество обрабатываемых секторов
|
||||
ATASectorNumber DB ? ;номер начального сектора
|
||||
ATACylinder DW ? ;номер начального цилиндра
|
||||
ATAHead DB ? ;номер начальной головки
|
||||
ATAAddressMode DB ? ;режим адресации (0 - CHS, 1 - LBA)
|
||||
ATACommand DB ? ;код команды, подлежащей выполнению
|
||||
; Код ошибки (0 - нет ошибок, 1 - превышен допустимый
|
||||
; интервал ожидания, 2 - неверный код режима адресации,
|
||||
; 3 - неверный номер канала, 4 - неверный номер диска,
|
||||
; 5 - неверный номер головки, 6 - ошибка при выполнении
|
||||
; команды)
|
||||
DevErrorCode dd ?
|
||||
endg
|
||||
;****************************************************
|
||||
;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1); *
|
||||
;* ATAFeatures - "особенности"; *
|
||||
;* ATASectorCount - количество секторов; *
|
||||
;* ATASectorNumber - номер начального сектора; *
|
||||
;* ATACylinder - номер начального цилиндра; *
|
||||
;* ATAHead - номер начальной головки; *
|
||||
;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); *
|
||||
;* ATACommand - код команды. *
|
||||
;* После успешного выполнения функции: *
|
||||
;* в ATABasePortAddr - базовый адрес HDD; *
|
||||
;* в DevErrorCode - ноль. *
|
||||
;* При возникновении ошибки в DevErrorCode будет *
|
||||
;* возвращен код ошибки. *
|
||||
;* ПОСЛАТЬ КОМАНДУ ЗАДАННОМУ ДИСКУ *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1); *
|
||||
;* ATAFeatures - "особенности"; *
|
||||
;* ATASectorCount - количество секторов; *
|
||||
;* ATASectorNumber - номер начального сектора; *
|
||||
;* ATACylinder - номер начального цилиндра; *
|
||||
;* ATAHead - номер начальной головки; *
|
||||
;* ATAAddressMode - режим адресации (0-CHS, 1-LBA); *
|
||||
;* ATACommand - код команды. *
|
||||
;* После успешного выполнения функции: *
|
||||
;* в ATABasePortAddr - базовый адрес HDD; *
|
||||
;* в DevErrorCode - ноль. *
|
||||
;* При возникновении ошибки в DevErrorCode будет *
|
||||
;* возвращен код ошибки. *
|
||||
;****************************************************
|
||||
SendCommandToHDD:
|
||||
; Проверить значение кода режима
|
||||
; Проверить значение кода режима
|
||||
cmp [ATAAddressMode], 1
|
||||
ja @@Err2
|
||||
; Проверить корректность номера канала
|
||||
; Проверить корректность номера канала
|
||||
mov BX, [ChannelNumber]
|
||||
cmp BX, 1
|
||||
jb @@Err3
|
||||
cmp BX, 2
|
||||
ja @@Err3
|
||||
; Установить базовый адрес
|
||||
; Установить базовый адрес
|
||||
dec BX
|
||||
shl BX, 1
|
||||
movzx ebx, bx
|
||||
mov AX, [ebx+StandardATABases]
|
||||
mov [ATABasePortAddr], AX
|
||||
; Ожидание готовности HDD к приему команды
|
||||
; Выбрать нужный диск
|
||||
; Ожидание готовности HDD к приему команды
|
||||
; Выбрать нужный диск
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 6 ;адрес регистра головок
|
||||
add DX, 6 ;адрес регистра головок
|
||||
mov AL, [DiskNumber]
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
ja @@Err4
|
||||
shl AL, 4
|
||||
or AL, 10100000b
|
||||
out DX, AL
|
||||
; Ожидать, пока диск не будет готов
|
||||
; Ожидать, пока диск не будет готов
|
||||
inc DX
|
||||
mov ecx, 0xfff
|
||||
; mov eax,[timer_ticks]
|
||||
; mov [TickCounter_1],eax
|
||||
@@WaitHDReady:
|
||||
; Проверить время ожидани
|
||||
; Проверить время ожидани
|
||||
dec ecx
|
||||
; cmp ecx,0
|
||||
jz @@Err1
|
||||
; mov eax,[timer_ticks]
|
||||
; sub eax,[TickCounter_1]
|
||||
; cmp eax,300 ;ожидать 300 тиков
|
||||
; ja @@Err1 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояни
|
||||
; cmp eax,300 ;ожидать 300 тиков
|
||||
; ja @@Err1 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояни
|
||||
in AL, DX
|
||||
; Проверить состояние сигнала BSY
|
||||
; Проверить состояние сигнала BSY
|
||||
test AL, 80h
|
||||
jnz @@WaitHDReady
|
||||
; Проверить состояние сигнала DRQ
|
||||
; Проверить состояние сигнала DRQ
|
||||
test AL, 08h
|
||||
jnz @@WaitHDReady
|
||||
; Загрузить команду в регистры контроллера
|
||||
; Загрузить команду в регистры контроллера
|
||||
cli
|
||||
mov DX, [ATABasePortAddr]
|
||||
inc DX ;регистр "особенностей"
|
||||
inc DX ;регистр "особенностей"
|
||||
mov AL, [ATAFeatures]
|
||||
out DX, AL
|
||||
inc DX ;счетчик секторов
|
||||
inc DX ;счетчик секторов
|
||||
mov AL, [ATASectorCount]
|
||||
out DX, AL
|
||||
inc DX ;регистр номера сектора
|
||||
inc DX ;регистр номера сектора
|
||||
mov AL, [ATASectorNumber]
|
||||
out DX, AL
|
||||
inc DX ;номер цилиндра (младший байт)
|
||||
inc DX ;номер цилиндра (младший байт)
|
||||
mov AX, [ATACylinder]
|
||||
out DX, AL
|
||||
inc DX ;номер цилиндра (старший байт)
|
||||
inc DX ;номер цилиндра (старший байт)
|
||||
mov AL, AH
|
||||
out DX, AL
|
||||
inc DX ;номер головки/номер диска
|
||||
inc DX ;номер головки/номер диска
|
||||
mov AL, [DiskNumber]
|
||||
shl AL, 4
|
||||
cmp [ATAHead], 0Fh;проверить номер головки
|
||||
cmp [ATAHead], 0Fh;проверить номер головки
|
||||
ja @@Err5
|
||||
or AL, [ATAHead]
|
||||
or AL, 10100000b
|
||||
@ -250,15 +250,15 @@ SendCommandToHDD:
|
||||
shl AH, 6
|
||||
or AL, AH
|
||||
out DX, AL
|
||||
; Послать команду
|
||||
; Послать команду
|
||||
mov AL, [ATACommand]
|
||||
inc DX ;регистр команд
|
||||
inc DX ;регистр команд
|
||||
out DX, AL
|
||||
sti
|
||||
; Сбросить признак ошибки
|
||||
; Сбросить признак ошибки
|
||||
mov [DevErrorCode], 0
|
||||
ret
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Err1:
|
||||
mov [DevErrorCode], 1
|
||||
ret
|
||||
@ -273,22 +273,22 @@ SendCommandToHDD:
|
||||
ret
|
||||
@@Err5:
|
||||
mov [DevErrorCode], 5
|
||||
; Завершение работы программы
|
||||
; Завершение работы программы
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* ЧТЕНИЕ ИДЕНТИФИКАТОРА УСТРОЙСТВА ATAPI *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* Идентификационный блок данных считывается *
|
||||
;* в массив Sector512. *
|
||||
;* ЧТЕНИЕ ИДЕНТИФИКАТОРА УСТРОЙСТВА ATAPI *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* перменные: *
|
||||
;* ChannelNumber - номер канала; *
|
||||
;* DiskNumber - номер диска на канале. *
|
||||
;* Идентификационный блок данных считывается *
|
||||
;* в массив Sector512. *
|
||||
;*************************************************
|
||||
ReadCD_ID:
|
||||
; Задать режим CHS
|
||||
; Задать режим CHS
|
||||
mov [ATAAddressMode], 0
|
||||
; Послать команду идентификации устройства
|
||||
; Послать команду идентификации устройства
|
||||
mov [ATAFeatures], 0
|
||||
mov [ATASectorCount], 0
|
||||
mov [ATASectorNumber], 0
|
||||
@ -296,34 +296,34 @@ ReadCD_ID:
|
||||
mov [ATAHead], 0
|
||||
mov [ATACommand], 0A1h
|
||||
call SendCommandToHDD
|
||||
cmp [DevErrorCode], 0;проверить код ошибки
|
||||
jne @@End_1 ;закончить, сохранив код ошибки
|
||||
; Ожидать готовность данных HDD
|
||||
cmp [DevErrorCode], 0;проверить код ошибки
|
||||
jne @@End_1 ;закончить, сохранив код ошибки
|
||||
; Ожидать готовность данных HDD
|
||||
mov DX, [ATABasePortAddr]
|
||||
add DX, 7 ;порт 1х7h
|
||||
add DX, 7 ;порт 1х7h
|
||||
mov ecx, 0xffff
|
||||
@@WaitCompleet_1:
|
||||
; Проверить врем
|
||||
; Проверить врем
|
||||
dec ecx
|
||||
; cmp ecx,0
|
||||
jz @@Error1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
jz @@Error1_1 ;ошибка тайм-аута
|
||||
; Проверить готовность
|
||||
in AL, DX
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
test AL, 80h ;состояние сигнала BSY
|
||||
jnz @@WaitCompleet_1
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
test AL, 1 ;состояние сигнала ERR
|
||||
jnz @@Error6_1
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
test AL, 08h ;состояние сигнала DRQ
|
||||
jz @@WaitCompleet_1
|
||||
; Принять блок данных от контроллера
|
||||
; Принять блок данных от контроллера
|
||||
; mov AX,DS
|
||||
; mov ES,AX
|
||||
mov EDI, Sector512 ;offset Sector512
|
||||
mov DX, [ATABasePortAddr];порт 1x0h
|
||||
mov CX, 256;число считываемых слов
|
||||
mov DX, [ATABasePortAddr];порт 1x0h
|
||||
mov CX, 256;число считываемых слов
|
||||
rep insw
|
||||
ret
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
@@Error1_1:
|
||||
mov [DevErrorCode], 1
|
||||
ret
|
||||
@ -333,52 +333,52 @@ ReadCD_ID:
|
||||
ret
|
||||
|
||||
;*************************************************
|
||||
;* СБРОС УСТРОЙСТВА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1). *
|
||||
;* СБРОС УСТРОЙСТВА *
|
||||
;* Входные параметры передаются через глобальные *
|
||||
;* переменные: *
|
||||
;* ChannelNumber - номер канала (1 или 2); *
|
||||
;* DiskNumber - номер диска (0 или 1). *
|
||||
;*************************************************
|
||||
DeviceReset:
|
||||
; Проверить корректность номера канала
|
||||
; Проверить корректность номера канала
|
||||
mov BX, [ChannelNumber]
|
||||
cmp BX, 1
|
||||
jb @@Err3_2
|
||||
cmp BX, 2
|
||||
ja @@Err3_2
|
||||
; Установить базовый адрес
|
||||
; Установить базовый адрес
|
||||
dec BX
|
||||
shl BX, 1
|
||||
movzx ebx, bx
|
||||
mov DX, [ebx+StandardATABases]
|
||||
mov [ATABasePortAddr], DX
|
||||
; Выбрать нужный диск
|
||||
add DX, 6 ;адрес регистра головок
|
||||
; Выбрать нужный диск
|
||||
add DX, 6 ;адрес регистра головок
|
||||
mov AL, [DiskNumber]
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
cmp AL, 1 ;проверить номера диска
|
||||
ja @@Err4_2
|
||||
shl AL, 4
|
||||
or AL, 10100000b
|
||||
out DX, AL
|
||||
; Послать команду "Сброс"
|
||||
; Послать команду "Сброс"
|
||||
mov AL, 08h
|
||||
inc DX ;регистр команд
|
||||
inc DX ;регистр команд
|
||||
out DX, AL
|
||||
mov ecx, 0x80000
|
||||
@@WaitHDReady_1:
|
||||
; Проверить время ожидани
|
||||
; Проверить время ожидани
|
||||
dec ecx
|
||||
; cmp ecx,0
|
||||
je @@Err1_2 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояни
|
||||
je @@Err1_2 ;ошибка тайм-аута
|
||||
; Прочитать регистр состояни
|
||||
in AL, DX
|
||||
; Проверить состояние сигнала BSY
|
||||
; Проверить состояние сигнала BSY
|
||||
test AL, 80h
|
||||
jnz @@WaitHDReady_1
|
||||
; Сбросить признак ошибки
|
||||
; Сбросить признак ошибки
|
||||
mov [DevErrorCode], 0
|
||||
ret
|
||||
; Обработка ошибок
|
||||
; Обработка ошибок
|
||||
@@Err1_2:
|
||||
mov [DevErrorCode], 1
|
||||
ret
|
||||
@ -387,7 +387,7 @@ DeviceReset:
|
||||
ret
|
||||
@@Err4_2:
|
||||
mov [DevErrorCode], 4
|
||||
; Записать код ошибки
|
||||
; Записать код ошибки
|
||||
ret
|
||||
|
||||
EndFindHDD:
|
||||
|
@ -9,9 +9,9 @@ $Revision$
|
||||
|
||||
|
||||
;****************************************************
|
||||
; поиск логических дисков на обнаруженных HDD
|
||||
; и занесение данных в область таблицы
|
||||
; автор Mario79
|
||||
; поиск логических дисков на обнаруженных HDD
|
||||
; и занесение данных в область таблицы
|
||||
; автор Mario79
|
||||
;****************************************************
|
||||
mov [transfer_adress], DRIVE_DATA+0xa
|
||||
search_partitions_ide0:
|
||||
|
@ -8,46 +8,46 @@
|
||||
; (english text below)
|
||||
|
||||
;------------------------------------------
|
||||
; Интерфейс сохранения параметров
|
||||
; Интерфейс сохранения параметров
|
||||
;------------------------------------------
|
||||
Если при передаче управления ядру загрузчик устанавливает AX='KL',
|
||||
то в DS:SI ядро ожидает дальнего указателя на следующую структуру:
|
||||
db версия структуры, должна быть 1
|
||||
dw флаги:
|
||||
бит 0 установлен = присутствует образ рамдиска в памяти
|
||||
dd дальний указатель на процедуру сохранения параметров
|
||||
может быть 0, если загрузчик не поддерживает
|
||||
Процедура сохранения параметров должна записать первый сектор ядра
|
||||
kernel.mnt назад на то место, откуда она его считала; возврат из
|
||||
процедуры осуществляется по retf.
|
||||
Если при передаче управления ядру загрузчик устанавливает AX='KL',
|
||||
то в DS:SI ядро ожидает дальнего указателя на следующую структуру:
|
||||
db версия структуры, должна быть 1
|
||||
dw флаги:
|
||||
бит 0 установлен = присутствует образ рамдиска в памяти
|
||||
dd дальний указатель на процедуру сохранения параметров
|
||||
может быть 0, если загрузчик не поддерживает
|
||||
Процедура сохранения параметров должна записать первый сектор ядра
|
||||
kernel.mnt назад на то место, откуда она его считала; возврат из
|
||||
процедуры осуществляется по retf.
|
||||
|
||||
;------------------------------------------
|
||||
; Указание загрузчиком системного каталога
|
||||
; Указание загрузчиком системного каталога
|
||||
;------------------------------------------
|
||||
Перед передачей управления ядру могут быть установлены следующие регистры:
|
||||
Перед передачей управления ядру могут быть установлены следующие регистры:
|
||||
CX='HA'
|
||||
DX='RD'
|
||||
Это указывает на то, что регистр BX указывает на системный раздел. Каталог /kolibri/ на
|
||||
этом разделе является системным, к нему можно обращаться как к /sys/
|
||||
Это указывает на то, что регистр BX указывает на системный раздел. Каталог /kolibri/ на
|
||||
этом разделе является системным, к нему можно обращаться как к /sys/
|
||||
|
||||
Возможные значения регистра BL (указывает на устройство):
|
||||
Возможные значения регистра BL (указывает на устройство):
|
||||
'a' - Primary Master
|
||||
'b' - Primary Slave
|
||||
'c' - Secondary Master
|
||||
'd' - Secondary Slave
|
||||
'r' - RAM диск
|
||||
'm' - Приводы CD-ROM
|
||||
'r' - RAM диск
|
||||
'm' - Приводы CD-ROM
|
||||
|
||||
Возможные значения регистра BH (указывает на раздел):
|
||||
для BL='a','b','c','d','r' - указывает на раздел, где расположен системный каталог
|
||||
для BL='m',указывает на номер физического устройства, с которого надо начинать поиск системного каталога.
|
||||
Возможные значения регистра BH (указывает на раздел):
|
||||
для BL='a','b','c','d','r' - указывает на раздел, где расположен системный каталог
|
||||
для BL='m',указывает на номер физического устройства, с которого надо начинать поиск системного каталога.
|
||||
|
||||
примеры значений регистра BX:
|
||||
примеры значений регистра BX:
|
||||
'a1' - /hd0/1/
|
||||
'a2' - /hd0/2/
|
||||
'b1' - /hd1/1/
|
||||
'd4' - /hd3/4/
|
||||
'm0' - поиск по сидюкам каталога kolibri
|
||||
'm0' - поиск по сидюкам каталога kolibri
|
||||
'r1' - /rd/1/
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2923,9 +2923,9 @@ Remarks:
|
||||
* if one want to read 0 blocks, function considers,
|
||||
that he requested 1;
|
||||
* if one requests more than 14 blocks or starting block is
|
||||
not less than 14, function returns eax=5 (not found) è ebx=-1;
|
||||
not less than 14, function returns eax=5 (not found) and ebx=-1;
|
||||
* size of ramdisk root folder is 14 blocks,
|
||||
0x1C00=7168 áàéò; but function returns ebx=0
|
||||
0x1C00=7168 bytes; but function returns ebx=0
|
||||
(except of the case of previous item);
|
||||
* strangely enough, it is possible to read 14th block (which
|
||||
generally contains a garbage - I remind, the indexing begins
|
||||
@ -2994,7 +2994,7 @@ Remarks:
|
||||
data of the concrete partition are required, application must
|
||||
define starting sector of this partition (either directly
|
||||
through MBR, or from the full structure returned by
|
||||
ïîäôóíêöèåé 11 ôóíêöèè 18).
|
||||
subfunction 11 of function 18).
|
||||
* Function does not check error code of hard disk, so request of
|
||||
nonexisting sector reads something (most probably it will be
|
||||
zeroes, but this is defined by device) and this is considered
|
||||
@ -3868,7 +3868,7 @@ Message codes:
|
||||
* in addition dword-image of the register DR6 is given:
|
||||
* bits 0-3: condition of the corresponding breakpoint (set by
|
||||
subfunction 9) is satisfied
|
||||
* áèò 14: exception has occured because of the trace mode
|
||||
* bit 14: exception has occured because of the trace mode
|
||||
(flag TF is set TF)
|
||||
* process is suspended
|
||||
When debugger terminates, all debugged processes are killed.
|
||||
@ -4440,7 +4440,7 @@ Parameters:
|
||||
Format of the information structure:
|
||||
* +0: dword: 7 = subfunction number
|
||||
* +4: dword: flags field:
|
||||
* áèò 0: start process as debugged
|
||||
* bit 0: start process as debugged
|
||||
* other bits are reserved and must be set to 0
|
||||
* +8: dword: 0 or pointer to ASCIIZ-string with parameters
|
||||
* +12 = +0xC: dword: 0 (reserved)
|
||||
|
@ -297,7 +297,7 @@ free_ports:
|
||||
ret
|
||||
|
||||
|
||||
; <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
; ДАННЫЕ ПРОГРАММЫ
|
||||
title db '',0
|
||||
flags dw 0
|
||||
|
||||
|
@ -174,68 +174,68 @@ endp
|
||||
|
||||
align 4
|
||||
MSMouseSearch:
|
||||
; ПОИСК МЫШИ ЧЕРЕЗ COM-ПОРТЫ
|
||||
; ПОИСК МЫШИ ЧЕРЕЗ COM-ПОРТЫ
|
||||
MouseSearch:
|
||||
; Устанавливаем скорость
|
||||
; приема/передачи 1200 бод
|
||||
; Устанавливаем скорость
|
||||
; приема/передачи 1200 бод
|
||||
; in bx COM Port Base Address
|
||||
mov DX, bx
|
||||
add DX, 3
|
||||
in AL, DX
|
||||
or AL, 80h ;установить бит DLAB
|
||||
or AL, 80h ;установить бит DLAB
|
||||
out DX, AL
|
||||
mov DX, bx
|
||||
mov AL, 60h ;1200 бод
|
||||
mov AL, 60h ;1200 бод
|
||||
out DX, AL
|
||||
inc DX
|
||||
mov AL, 0
|
||||
out DX, AL
|
||||
; Установить длину слова 7 бит, 1 стоповый бит,
|
||||
; четность не контролировать
|
||||
; Установить длину слова 7 бит, 1 стоповый бит,
|
||||
; четность не контролировать
|
||||
mov DX, bx
|
||||
add DX, 3
|
||||
mov AL, 00000010b
|
||||
out DX, AL
|
||||
; Запретить все прерывани
|
||||
; Запретить все прерывани
|
||||
mov dx, bx
|
||||
inc dx
|
||||
mov AL, 0
|
||||
out DX, AL
|
||||
; Проверить, что устройство подключено и являетс
|
||||
; мышью типа MSMouse
|
||||
; Отключить питание мыши и прерывани
|
||||
; Проверить, что устройство подключено и являетс
|
||||
; мышью типа MSMouse
|
||||
; Отключить питание мыши и прерывани
|
||||
mov DX, bx
|
||||
add EDX, 4 ;регистр управления модемом
|
||||
mov AL, 0 ;сбросить DTR, RTS и OUT2
|
||||
add EDX, 4 ;регистр управления модемом
|
||||
mov AL, 0 ;сбросить DTR, RTS и OUT2
|
||||
out DX, AL
|
||||
; Ожидать 5 "тиков" (0,2 с)
|
||||
; Ожидать 5 "тиков" (0,2 с)
|
||||
mov ecx, 0xFFFF
|
||||
loop $
|
||||
; Включить питание мыши
|
||||
; Включить питание мыши
|
||||
mov al, 1
|
||||
out dx, al
|
||||
mov ecx, 0xFFFF
|
||||
loop $
|
||||
; Очистить регистр данных
|
||||
; Очистить регистр данных
|
||||
mov dx, bx
|
||||
in AL, DX
|
||||
add edx, 4
|
||||
mov AL, 1011b ;установить DTR и RTS и OUT2
|
||||
mov AL, 1011b ;установить DTR и RTS и OUT2
|
||||
out DX, AL
|
||||
mov ecx, 0x1FFFF
|
||||
; Цикл опроса порта
|
||||
; Цикл опроса порта
|
||||
WaitData:
|
||||
; Ожидать еще 10 "тиков"
|
||||
; Ожидать еще 10 "тиков"
|
||||
dec ecx
|
||||
; cmp ecx,0
|
||||
jz NoMouse
|
||||
; Проверить наличие идентификационного байта
|
||||
; Проверить наличие идентификационного байта
|
||||
mov DX, bx
|
||||
add DX, 5
|
||||
in AL, DX
|
||||
test AL, 1 ;Данные готовы?
|
||||
test AL, 1 ;Данные готовы?
|
||||
jz WaitData
|
||||
; Ввести данные
|
||||
; Ввести данные
|
||||
mov DX, bx
|
||||
in AL, DX
|
||||
NoMouse:
|
||||
@ -257,41 +257,41 @@ irq_handler:
|
||||
; in: esi -> COM_MOUSE_DATA struc, dx = base port (xF8h)
|
||||
add edx, 5 ; xFDh
|
||||
in al, dx
|
||||
test al, 1 ; Данные готовы?
|
||||
test al, 1 ; Данные готовы?
|
||||
jz .Error
|
||||
; Ввести данные
|
||||
; Ввести данные
|
||||
sub edx, 5
|
||||
in al, dx
|
||||
; Сбросить старший незначащий бит
|
||||
; Сбросить старший незначащий бит
|
||||
and al, 01111111b
|
||||
|
||||
; Определить порядковый номер принимаемого байта
|
||||
; Определить порядковый номер принимаемого байта
|
||||
cmp [esi+COM_MOUSE_DATA.MouseByteNumber], 2
|
||||
ja .Error
|
||||
jz .ThirdByte
|
||||
jp .SecondByte
|
||||
; Сохранить первый байт данных
|
||||
; Сохранить первый байт данных
|
||||
.FirstByte:
|
||||
test al, 1000000b ; Первый байт посылки?
|
||||
test al, 1000000b ; Первый байт посылки?
|
||||
jz .Error
|
||||
mov [esi+COM_MOUSE_DATA.FirstByte], al
|
||||
inc [esi+COM_MOUSE_DATA.MouseByteNumber]
|
||||
jmp .EndMouseInterrupt
|
||||
; Сохранить второй байт данных
|
||||
; Сохранить второй байт данных
|
||||
.SecondByte:
|
||||
test al, 1000000b
|
||||
jnz .Error
|
||||
mov [esi+COM_MOUSE_DATA.SecondByte], al
|
||||
inc [esi+COM_MOUSE_DATA.MouseByteNumber]
|
||||
jmp .EndMouseInterrupt
|
||||
; Сохранить третий байт данных
|
||||
; Сохранить третий байт данных
|
||||
.ThirdByte:
|
||||
test al, 1000000b
|
||||
jnz .Error
|
||||
mov [esi+COM_MOUSE_DATA.ThirdByte], al
|
||||
mov [esi+COM_MOUSE_DATA.MouseByteNumber], 0
|
||||
; (Пакет данных от мыши принят полностью).
|
||||
; Записать новое значение состояния кнопок мыши
|
||||
; (Пакет данных от мыши принят полностью).
|
||||
; Записать новое значение состояния кнопок мыши
|
||||
mov al, [esi+COM_MOUSE_DATA.FirstByte]
|
||||
mov ah, al
|
||||
shr al, 3
|
||||
@ -302,7 +302,7 @@ irq_handler:
|
||||
movzx eax, al
|
||||
mov [BTN_DOWN], eax
|
||||
|
||||
; Прибавить перемещение по X к координате X
|
||||
; Прибавить перемещение по X к координате X
|
||||
mov al, [esi+COM_MOUSE_DATA.FirstByte]
|
||||
shl al, 6
|
||||
or al, [esi+COM_MOUSE_DATA.SecondByte]
|
||||
@ -311,7 +311,7 @@ irq_handler:
|
||||
movzx eax, ax
|
||||
mov [MOUSE_X], eax
|
||||
|
||||
; Прибавить перемещение по Y к координате Y
|
||||
; Прибавить перемещение по Y к координате Y
|
||||
mov al, [esi+COM_MOUSE_DATA.FirstByte]
|
||||
and al, 00001100b
|
||||
shl al, 4
|
||||
@ -327,8 +327,8 @@ irq_handler:
|
||||
jmp .EndMouseInterrupt
|
||||
|
||||
.Error:
|
||||
; Произошел сбой в порядке передачи информации от
|
||||
; мыши, обнулить счетчик байтов пакета данных
|
||||
; Произошел сбой в порядке передачи информации от
|
||||
; мыши, обнулить счетчик байтов пакета данных
|
||||
|
||||
mov [esi+COM_MOUSE_DATA.MouseByteNumber], 0
|
||||
.EndMouseInterrupt:
|
||||
@ -340,9 +340,9 @@ irq_handler:
|
||||
align 4
|
||||
|
||||
struc COM_MOUSE_DATA {
|
||||
; Номер принимаемого от мыши байта
|
||||
; Номер принимаемого от мыши байта
|
||||
.MouseByteNumber db ?
|
||||
; Трехбайтовая структура данных, передаваемая мышью
|
||||
; Трехбайтовая структура данных, передаваемая мышью
|
||||
.FirstByte db ?
|
||||
.SecondByte db ?
|
||||
.ThirdByte db ?
|
||||
|
@ -19,7 +19,7 @@ IRQ_REMAP equ 0
|
||||
IRQ_LINE equ 0
|
||||
|
||||
|
||||
;irq 0,1,2,8,12,13 <EFBFBD>ぎ徕惘<EFBFBD>
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010100000b
|
||||
|
@ -17,7 +17,7 @@ include 'imports.inc'
|
||||
|
||||
REMAP_IRQ equ 0
|
||||
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010101000b
|
||||
|
@ -17,7 +17,7 @@ API_VERSION equ 0x01000100
|
||||
|
||||
USE_COM_IRQ equ 0 ;make irq 3 and irq 4 available for PCI devices
|
||||
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010100000b
|
||||
|
@ -21,7 +21,7 @@ IRQ_REMAP equ 0
|
||||
IRQ_LINE equ 0
|
||||
|
||||
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010100000b
|
||||
|
@ -21,7 +21,7 @@ IRQ_REMAP equ 0
|
||||
IRQ_LINE equ 0
|
||||
|
||||
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010100000b
|
||||
|
@ -19,7 +19,7 @@ IRQ_REMAP equ 0
|
||||
IRQ_LINE equ 0
|
||||
|
||||
|
||||
;irq 0,1,2,8,12,13 <EFBFBD>ぎ徕惘<EFBFBD>
|
||||
;irq 0,1,2,8,12,13 недоступны
|
||||
; FEDCBA9876543210
|
||||
VALID_IRQ equ 1100111011111000b
|
||||
ATTCH_IRQ equ 0000111010100000b
|
||||
|
@ -119,9 +119,9 @@ frfl6_1:
|
||||
call read_flp_fat
|
||||
cmp [FDC_Status], 0
|
||||
jne fdc_status_error_3_1
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
call SeekTrack
|
||||
mov dh, 14
|
||||
l.20_1:
|
||||
@ -253,9 +253,9 @@ read_flp_root:
|
||||
jne unnecessary_root_read
|
||||
cmp [root_read], 1
|
||||
je unnecessary_root_read
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov edi, FLOPPY_BUFF
|
||||
call SeekTrack
|
||||
read_flp_root_1:
|
||||
@ -282,9 +282,9 @@ read_flp_fat:
|
||||
jne unnecessary_flp_fat
|
||||
cmp [flp_fat], 1
|
||||
je unnecessary_flp_fat
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov edi, FLOPPY_BUFF
|
||||
call SeekTrack
|
||||
read_flp_fat_1:
|
||||
@ -351,9 +351,9 @@ calculatefatchain_flp:
|
||||
|
||||
check_label:
|
||||
pushad
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 1; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 1; Сектор
|
||||
call SetUserInterrupts
|
||||
call FDDMotorON
|
||||
call RecalibrateFDD
|
||||
@ -392,9 +392,9 @@ save_flp_root:
|
||||
jne unnecessary_root_save
|
||||
cmp [root_read], 0
|
||||
je unnecessary_root_save
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 1; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov esi, FLOPPY_BUFF
|
||||
call SeekTrack
|
||||
save_flp_root_1:
|
||||
@ -421,9 +421,9 @@ save_flp_fat:
|
||||
cmp [flp_fat], 0
|
||||
je unnecessary_flp_fat_save
|
||||
call restorefatchain_flp
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov [FDD_Track], 0; Цилиндр
|
||||
mov [FDD_Head], 0; Сторона
|
||||
mov [FDD_Sector], 2; Сектор
|
||||
mov esi, FLOPPY_BUFF
|
||||
call SeekTrack
|
||||
save_flp_fat_1:
|
||||
|
@ -441,7 +441,7 @@ hd_err_return:
|
||||
fs_noharddisk:
|
||||
; \begin{diamond}[18.03.2006]
|
||||
mov eax, 5 ; file not found
|
||||
; а может быть, возвращать другой код ошибки?
|
||||
; а может быть, возвращать другой код ошибки?
|
||||
mov ebx, [esp+24+24]; do not change ebx in application
|
||||
; \end{diamond}[18.03.2006]
|
||||
|
||||
@ -713,14 +713,14 @@ expand_pathz:
|
||||
;* output eax - number
|
||||
;*******************************************
|
||||
StringToNumber:
|
||||
; ПЕРЕВОД СТРОКОВОГО ЧИСЛА В ЧИСЛОВОЙ ВИД
|
||||
; Вход:
|
||||
; EDI - адрес строки с числом. Конец числа отмечен кодом 0Dh
|
||||
; Выход:
|
||||
; CF - индикатор ошибок:
|
||||
; 0 - ошибок нет;
|
||||
; 1 - ошибка
|
||||
; Если CF=0, то AX - число.
|
||||
; ПЕРЕВОД СТРОКОВОГО ЧИСЛА В ЧИСЛОВОЙ ВИД
|
||||
; Вход:
|
||||
; EDI - адрес строки с числом. Конец числа отмечен кодом 0Dh
|
||||
; Выход:
|
||||
; CF - индикатор ошибок:
|
||||
; 0 - ошибок нет;
|
||||
; 1 - ошибка
|
||||
; Если CF=0, то AX - число.
|
||||
|
||||
push bx
|
||||
push cx
|
||||
|
@ -137,7 +137,7 @@ fs_CdRead:
|
||||
.l1:
|
||||
push ecx edx
|
||||
push 0
|
||||
mov eax, [edi+10] ; реальный размер файловой секции
|
||||
mov eax, [edi+10] ; реальный размер файловой секции
|
||||
sub eax, ebx
|
||||
jb .eof
|
||||
cmp eax, ecx
|
||||
@ -159,7 +159,7 @@ fs_CdRead:
|
||||
jb .incomplete_sector
|
||||
; we may read and memmove complete sector
|
||||
mov [CDDataBuf_pointer], edx
|
||||
call ReadCDWRetr; читаем сектор файла
|
||||
call ReadCDWRetr; читаем сектор файла
|
||||
cmp [DevErrorCode], 0
|
||||
jne .noaccess_3
|
||||
add edx, 2048
|
||||
@ -170,7 +170,7 @@ fs_CdRead:
|
||||
.incomplete_sector:
|
||||
; we must read and memmove incomplete sector
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
call ReadCDWRetr; читаем сектор файла
|
||||
call ReadCDWRetr; читаем сектор файла
|
||||
cmp [DevErrorCode], 0
|
||||
jne .noaccess_3
|
||||
push ecx
|
||||
@ -240,7 +240,7 @@ fs_CdReadFolder:
|
||||
.found_dir:
|
||||
mov eax, [edi+2] ; eax=cluster
|
||||
mov [CDSectorAddress], eax
|
||||
mov eax, [edi+10] ; размер директрории
|
||||
mov eax, [edi+10] ; размер директрории
|
||||
.doit:
|
||||
; init header
|
||||
push eax ecx
|
||||
@ -252,7 +252,7 @@ fs_CdReadFolder:
|
||||
mov byte [edx], 1 ; version
|
||||
mov [cd_mem_location], edx
|
||||
add [cd_mem_location], 32
|
||||
; начинаем переброску БДВК в УСВК
|
||||
; начинаем переброску БДВК в УСВК
|
||||
;.mainloop:
|
||||
mov [cd_counter_block], dword 0
|
||||
dec dword [CDSectorAddress]
|
||||
@ -260,12 +260,12 @@ fs_CdReadFolder:
|
||||
.read_to_buffer:
|
||||
inc dword [CDSectorAddress]
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
call ReadCDWRetr ; читаем сектор директории
|
||||
call ReadCDWRetr ; читаем сектор директории
|
||||
cmp [DevErrorCode], 0
|
||||
jne .noaccess_1
|
||||
call .get_names_from_buffer
|
||||
sub eax, 2048
|
||||
; директория закончилась?
|
||||
; директория закончилась?
|
||||
ja .read_to_buffer
|
||||
mov edi, [cd_counter_block]
|
||||
mov [edx+8], edi
|
||||
@ -310,17 +310,17 @@ fs_CdReadFolder:
|
||||
call uni2ansi_char
|
||||
cld
|
||||
stosb
|
||||
; проверка конца файла
|
||||
; проверка конца файла
|
||||
mov ax, [esi]
|
||||
cmp ax, word 3B00h; сепаратор конца файла ';'
|
||||
cmp ax, word 3B00h; сепаратор конца файла ';'
|
||||
je .cd_get_parameters_of_file_1
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
movzx eax, byte [ebp-33]
|
||||
add eax, ebp
|
||||
sub eax, 34
|
||||
cmp esi, eax
|
||||
je .cd_get_parameters_of_file_1
|
||||
; проверка конца папки
|
||||
; проверка конца папки
|
||||
movzx eax, byte [ebp-1]
|
||||
add eax, ebp
|
||||
cmp esi, eax
|
||||
@ -347,17 +347,17 @@ fs_CdReadFolder:
|
||||
jbe .unicode_parent_directory
|
||||
cld
|
||||
movsw
|
||||
; проверка конца файла
|
||||
; проверка конца файла
|
||||
mov ax, [esi]
|
||||
cmp ax, word 3B00h; сепаратор конца файла ';'
|
||||
cmp ax, word 3B00h; сепаратор конца файла ';'
|
||||
je .cd_get_parameters_of_file_2
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
movzx eax, byte [ebp-33]
|
||||
add eax, ebp
|
||||
sub eax, 34
|
||||
cmp esi, eax
|
||||
je .cd_get_parameters_of_file_2
|
||||
; проверка конца папки
|
||||
; проверка конца папки
|
||||
movzx eax, byte [ebp-1]
|
||||
add eax, ebp
|
||||
cmp esi, eax
|
||||
@ -386,60 +386,60 @@ fs_CdReadFolder:
|
||||
cd_get_parameters_of_file:
|
||||
mov edi, [cd_mem_location]
|
||||
cd_get_parameters_of_file_1:
|
||||
; получаем атрибуты файла
|
||||
; получаем атрибуты файла
|
||||
xor eax, eax
|
||||
; файл не архивировался
|
||||
; файл не архивировался
|
||||
inc eax
|
||||
shl eax, 1
|
||||
; это каталог?
|
||||
; это каталог?
|
||||
test [ebp-8], byte 2
|
||||
jz .file
|
||||
inc eax
|
||||
.file:
|
||||
; метка тома не как в FAT, в этом виде отсутсвует
|
||||
; файл не является системным
|
||||
; метка тома не как в FAT, в этом виде отсутсвует
|
||||
; файл не является системным
|
||||
shl eax, 3
|
||||
; файл является скрытым? (атрибут существование)
|
||||
; файл является скрытым? (атрибут существование)
|
||||
test [ebp-8], byte 1
|
||||
jz .hidden
|
||||
inc eax
|
||||
.hidden:
|
||||
shl eax, 1
|
||||
; файл всегда только для чтения, так как это CD
|
||||
; файл всегда только для чтения, так как это CD
|
||||
inc eax
|
||||
mov [edi], eax
|
||||
; получаем время для файла
|
||||
;час
|
||||
; получаем время для файла
|
||||
;час
|
||||
movzx eax, byte [ebp-12]
|
||||
shl eax, 8
|
||||
;минута
|
||||
;минута
|
||||
mov al, [ebp-11]
|
||||
shl eax, 8
|
||||
;секунда
|
||||
;секунда
|
||||
mov al, [ebp-10]
|
||||
;время создания файла
|
||||
;время создания файла
|
||||
mov [edi+8], eax
|
||||
;время последнего доступа
|
||||
;время последнего доступа
|
||||
mov [edi+16], eax
|
||||
;время последней записи
|
||||
;время последней записи
|
||||
mov [edi+24], eax
|
||||
; получаем дату для файла
|
||||
;год
|
||||
; получаем дату для файла
|
||||
;год
|
||||
movzx eax, byte [ebp-15]
|
||||
add eax, 1900
|
||||
shl eax, 8
|
||||
;месяц
|
||||
;месяц
|
||||
mov al, [ebp-14]
|
||||
shl eax, 8
|
||||
;день
|
||||
;день
|
||||
mov al, [ebp-13]
|
||||
;дата создания файла
|
||||
;дата создания файла
|
||||
mov [edi+12], eax
|
||||
;время последнего доступа
|
||||
;время последнего доступа
|
||||
mov [edi+20], eax
|
||||
;время последней записи
|
||||
;время последней записи
|
||||
mov [edi+28], eax
|
||||
; получаем тип данных имени
|
||||
; получаем тип данных имени
|
||||
xor eax, eax
|
||||
test dword [ebx+4], 1; 0=ANSI, 1=UNICODE
|
||||
jnz .unicode_1
|
||||
@ -449,7 +449,7 @@ cd_get_parameters_of_file_1:
|
||||
inc eax
|
||||
mov [edi+4], eax
|
||||
@@:
|
||||
; получаем размер файла в байтах
|
||||
; получаем размер файла в байтах
|
||||
xor eax, eax
|
||||
mov [edi+32+4], eax
|
||||
mov eax, [ebp-23]
|
||||
@ -503,21 +503,21 @@ cd_find_lfn:
|
||||
; out: CF=1 - file not found
|
||||
; else CF=0 and [cd_current_pointer_of_input] direntry
|
||||
push eax esi
|
||||
; 16 сектор начало набора дескрипторов томов
|
||||
; 16 сектор начало набора дескрипторов томов
|
||||
|
||||
call WaitUnitReady
|
||||
cmp [DevErrorCode], 0
|
||||
jne .access_denied
|
||||
|
||||
call prevent_medium_removal
|
||||
; тестовое чтение
|
||||
; тестовое чтение
|
||||
mov [CDSectorAddress], dword 16
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
call ReadCDWRetr;_1
|
||||
cmp [DevErrorCode], 0
|
||||
jne .access_denied
|
||||
|
||||
; вычисление последней сессии
|
||||
; вычисление последней сессии
|
||||
call WaitUnitReady
|
||||
cmp [DevErrorCode], 0
|
||||
jne .access_denied
|
||||
@ -539,37 +539,37 @@ cd_find_lfn:
|
||||
jne .access_denied
|
||||
|
||||
.start_check:
|
||||
; проверка на вшивость
|
||||
; проверка на вшивость
|
||||
cmp [CDDataBuf+1], dword 'CD00'
|
||||
jne .access_denied
|
||||
cmp [CDDataBuf+5], byte '1'
|
||||
jne .access_denied
|
||||
; сектор является терминатором набор дескрипторов томов?
|
||||
; сектор является терминатором набор дескрипторов томов?
|
||||
cmp [CDDataBuf], byte 0xff
|
||||
je .access_denied
|
||||
; сектор является дополнительным и улучшенным дескриптором тома?
|
||||
; сектор является дополнительным и улучшенным дескриптором тома?
|
||||
cmp [CDDataBuf], byte 0x2
|
||||
jne .start
|
||||
; сектор является дополнительным дескриптором тома?
|
||||
; сектор является дополнительным дескриптором тома?
|
||||
cmp [CDDataBuf+6], byte 0x1
|
||||
jne .start
|
||||
|
||||
; параметры root директрории
|
||||
mov eax, [CDDataBuf+0x9c+2]; начало root директрории
|
||||
; параметры root директрории
|
||||
mov eax, [CDDataBuf+0x9c+2]; начало root директрории
|
||||
mov [CDSectorAddress], eax
|
||||
mov eax, [CDDataBuf+0x9c+10]; размер root директрории
|
||||
mov eax, [CDDataBuf+0x9c+10]; размер root директрории
|
||||
cmp byte [esi], 0
|
||||
jnz @f
|
||||
mov [cd_current_pointer_of_input], CDDataBuf+0x9c
|
||||
jmp .done
|
||||
@@:
|
||||
; начинаем поиск
|
||||
; начинаем поиск
|
||||
.mainloop:
|
||||
dec dword [CDSectorAddress]
|
||||
.read_to_buffer:
|
||||
inc dword [CDSectorAddress]
|
||||
mov [CDDataBuf_pointer], CDDataBuf
|
||||
call ReadCDWRetr ; читаем сектор директории
|
||||
call ReadCDWRetr ; читаем сектор директории
|
||||
cmp [DevErrorCode], 0
|
||||
jne .access_denied
|
||||
push ebp
|
||||
@ -577,27 +577,27 @@ cd_find_lfn:
|
||||
pop ebp
|
||||
jnc .found
|
||||
sub eax, 2048
|
||||
; директория закончилась?
|
||||
; директория закончилась?
|
||||
cmp eax, 0
|
||||
ja .read_to_buffer
|
||||
; нет искомого элемента цепочки
|
||||
; нет искомого элемента цепочки
|
||||
.access_denied:
|
||||
pop esi eax
|
||||
mov [cd_appl_data], 1
|
||||
stc
|
||||
ret
|
||||
; искомый элемент цепочки найден
|
||||
; искомый элемент цепочки найден
|
||||
.found:
|
||||
; конец пути файла
|
||||
; конец пути файла
|
||||
cmp byte [esi-1], 0
|
||||
jz .done
|
||||
.nested:
|
||||
mov eax, [cd_current_pointer_of_input]
|
||||
push dword [eax+2]
|
||||
pop dword [CDSectorAddress] ; начало директории
|
||||
mov eax, [eax+2+8]; размер директории
|
||||
pop dword [CDSectorAddress] ; начало директории
|
||||
mov eax, [eax+2+8]; размер директории
|
||||
jmp .mainloop
|
||||
; указатель файла найден
|
||||
; указатель файла найден
|
||||
.done:
|
||||
test ebp, ebp
|
||||
jz @f
|
||||
@ -629,13 +629,13 @@ cd_get_name:
|
||||
mov ebp, [cd_current_pointer_of_input_2]
|
||||
mov [cd_current_pointer_of_input], ebp
|
||||
mov eax, [ebp]
|
||||
test eax, eax ; входы закончились?
|
||||
test eax, eax ; входы закончились?
|
||||
jz .next_sector
|
||||
cmp ebp, CDDataBuf+2048 ; буфер закончился?
|
||||
cmp ebp, CDDataBuf+2048 ; буфер закончился?
|
||||
jae .next_sector
|
||||
movzx eax, byte [ebp]
|
||||
add [cd_current_pointer_of_input_2], eax; следующий вход каталога
|
||||
add ebp, 33; указатель установлен на начало имени
|
||||
add [cd_current_pointer_of_input_2], eax; следующий вход каталога
|
||||
add ebp, 33; указатель установлен на начало имени
|
||||
pop eax
|
||||
clc
|
||||
ret
|
||||
@ -669,9 +669,9 @@ cd_compare_name:
|
||||
scasw
|
||||
jne .name_not_coincide
|
||||
.coincides:
|
||||
cmp [esi], byte '/'; разделитель пути, конец имени текущего элемента
|
||||
cmp [esi], byte '/'; разделитель пути, конец имени текущего элемента
|
||||
je .done
|
||||
cmp [esi], byte 0; разделитель пути, конец имени текущего элемента
|
||||
cmp [esi], byte 0; разделитель пути, конец имени текущего элемента
|
||||
je .done
|
||||
jmp .loop
|
||||
.name_not_coincide:
|
||||
@ -679,16 +679,16 @@ cd_compare_name:
|
||||
stc
|
||||
ret
|
||||
.done:
|
||||
; проверка конца файла
|
||||
cmp [edi], word 3B00h; сепаратор конца файла ';'
|
||||
; проверка конца файла
|
||||
cmp [edi], word 3B00h; сепаратор конца файла ';'
|
||||
je .done_1
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
; проверка для файлов не заканчивающихся сепаратором
|
||||
movzx eax, byte [ebp-33]
|
||||
add eax, ebp
|
||||
sub eax, 34
|
||||
cmp edi, eax
|
||||
je .done_1
|
||||
; проверка конца папки
|
||||
; проверка конца папки
|
||||
movzx eax, byte [ebp-1]
|
||||
add eax, ebp
|
||||
cmp edi, eax
|
||||
@ -708,14 +708,14 @@ char_todown:
|
||||
jb .ret
|
||||
cmp al, 'Z'
|
||||
jbe .az
|
||||
cmp al, 'Ђ'
|
||||
cmp al, 0x80 ; 'А'
|
||||
jb .ret
|
||||
cmp al, 'ђ'
|
||||
cmp al, 0x90 ; 'Р'
|
||||
jb .rus1
|
||||
cmp al, 'џ'
|
||||
cmp al, 0x9F ; 'Я'
|
||||
ja .ret
|
||||
; 0x90-0x9F -> 0xE0-0xEF
|
||||
add al, 'а'-'ђ'
|
||||
add al, 0xE0-0x90
|
||||
.ret:
|
||||
ret
|
||||
.rus1:
|
||||
@ -744,10 +744,10 @@ uni2ansi_char:
|
||||
mov al, '_'
|
||||
jmp .doit
|
||||
.yo1:
|
||||
mov al, 'р'
|
||||
mov al, 0xF0 ; 'Ё' in cp866
|
||||
jmp .doit
|
||||
.yo2:
|
||||
mov al, 'с'
|
||||
mov al, 0xF1 ; 'ё' in cp866
|
||||
jmp .doit
|
||||
.rus1:
|
||||
; 0x410-0x43F -> 0x80-0xAF
|
||||
|
@ -77,10 +77,10 @@ ext2_data:
|
||||
.inode_size dd ?
|
||||
.count_pointer_in_block dd ? ; block_size / 4
|
||||
.count_pointer_in_block_square dd ? ; (block_size / 4)**2
|
||||
.ext2_save_block dd ? ; ¡«®ª £«®¡ «ìãî 1 ¯à®æ¥¤ãàã
|
||||
.ext2_temp_block dd ? ; ¡«®ª ¤«ï ¬¥«ª¨å ¯à®æ¥¤ãà
|
||||
.ext2_save_inode dd ? ; inode £«®¡ «ìãî ¯à®æ¥¤ãàã
|
||||
.ext2_temp_inode dd ? ; inode ¤«ï ¬¥«ª¨å ¯à®æ¥¤ãà
|
||||
.ext2_save_block dd ? ; блок на глобальную 1 процедуру
|
||||
.ext2_temp_block dd ? ; блок для мелких процедур
|
||||
.ext2_save_inode dd ? ; inode на глобальную процедуру
|
||||
.ext2_temp_inode dd ? ; inode для мелких процедур
|
||||
.sb dd ? ; superblock
|
||||
.groups_count dd ?
|
||||
if $ > fs_dependent_data_end
|
||||
@ -278,7 +278,7 @@ test_primary_partition_3:
|
||||
;pop edx
|
||||
|
||||
test_ext_partition_0:
|
||||
pop eax ; ¯à®áâ® ¢ëª¨¤ë¢ ¥¬ ¨§ á⥪
|
||||
pop eax ; просто выкидываем из стека
|
||||
mov al, [ebx+0x1be+4]; get extended partition type
|
||||
call scan_extended_types
|
||||
jnz test_ext_partition_1
|
||||
@ -368,7 +368,7 @@ hd_and_partition_ok:
|
||||
|
||||
; mov edx, [PARTITION_END]
|
||||
; sub edx, eax
|
||||
; inc edx ; edx = length of partition § 祬 ®® ¬??
|
||||
; inc edx ; edx = length of partition зачем оно нам??
|
||||
|
||||
; mov [hd_setup],1
|
||||
mov ebx, buffer
|
||||
|
@ -21,8 +21,8 @@ align 4
|
||||
event_uid dd 0
|
||||
endg
|
||||
EV_SPACE = 512
|
||||
FreeEvents = event_start-EVENT.fd ; "виртуальный" event, используются только поля:
|
||||
; FreeEvents.fd=event_start и FreeEvents.bk=event_end
|
||||
FreeEvents = event_start-EVENT.fd ; "виртуальный" event, используются только поля:
|
||||
; FreeEvents.fd=event_start и FreeEvents.bk=event_end
|
||||
;-----------------------------------------------------------------------------
|
||||
align 4
|
||||
init_events: ;; used from kernel.asm
|
||||
@ -31,8 +31,8 @@ init_events: ;; used from kernel.asm
|
||||
jz .fail
|
||||
; eax - current event, ebx - previos event below
|
||||
mov ecx, EV_SPACE ; current - in allocated space
|
||||
mov ebx, FreeEvents ; previos - начало списка
|
||||
push ebx ; оно же и конец потом будет
|
||||
mov ebx, FreeEvents ; previos - начало списка
|
||||
push ebx ; оно же и конец потом будет
|
||||
;--------------------------------------
|
||||
align 4
|
||||
@@:
|
||||
@ -41,7 +41,7 @@ align 4
|
||||
mov ebx, eax ; previos <- current
|
||||
add eax, sizeof.EVENT ; new current
|
||||
loop @b
|
||||
pop eax ; вот оно концом и стало
|
||||
pop eax ; вот оно концом и стало
|
||||
mov [ebx+EVENT.fd], eax
|
||||
mov [eax+EVENT.bk], ebx
|
||||
;--------------------------------------
|
||||
@ -49,16 +49,16 @@ align 4
|
||||
.fail:
|
||||
ret
|
||||
;-----------------------------------------------------------------------------
|
||||
EVENT_WATCHED equ 0x10000000 ;бит 28
|
||||
EVENT_SIGNALED equ 0x20000000 ;бит 29
|
||||
MANUAL_RESET equ 0x40000000 ;бит 30
|
||||
MANUAL_DESTROY equ 0x80000000 ;бит 31
|
||||
EVENT_WATCHED equ 0x10000000 ;бит 28
|
||||
EVENT_SIGNALED equ 0x20000000 ;бит 29
|
||||
MANUAL_RESET equ 0x40000000 ;бит 30
|
||||
MANUAL_DESTROY equ 0x80000000 ;бит 31
|
||||
;-----------------------------------------------------------------------------
|
||||
align 4
|
||||
create_event: ;; EXPORT use
|
||||
;info:
|
||||
; Переносим EVENT из списка FreeEvents в список ObjList текущего слота
|
||||
; EVENT.state устанавливаем из ecx, EVENT.code косвенно из esi (если esi<>0)
|
||||
; Переносим EVENT из списка FreeEvents в список ObjList текущего слота
|
||||
; EVENT.state устанавливаем из ecx, EVENT.code косвенно из esi (если esi<>0)
|
||||
;param:
|
||||
; esi - event data
|
||||
; ecx - flags
|
||||
@ -76,9 +76,9 @@ create_event: ;; EXPORT use
|
||||
align 4
|
||||
set_event: ;; INTERNAL use !!! don't use for Call
|
||||
;info:
|
||||
; Берем новый event из FreeEvents, заполняем его поля, как указано в ecx,edx,esi
|
||||
; и устанавливаем в список, указанный в ebx.
|
||||
; Возвращаем сам event (в eax), и его uid (в edx)
|
||||
; Берем новый event из FreeEvents, заполняем его поля, как указано в ecx,edx,esi
|
||||
; и устанавливаем в список, указанный в ebx.
|
||||
; Возвращаем сам event (в eax), и его uid (в edx)
|
||||
;param:
|
||||
; ebx - start-chain "virtual" event for entry new event Right of him
|
||||
; ecx - flags (copied to EVENT.state)
|
||||
@ -115,13 +115,13 @@ align 4
|
||||
align 4
|
||||
RemoveEventTo: ;; INTERNAL use !!! don't use for Call
|
||||
;param:
|
||||
; eax - указатель на event, КОТОРЫЙ вставляем
|
||||
; ebx - указатель на event, ПОСЛЕ которого вставляем
|
||||
; eax - указатель на event, КОТОРЫЙ вставляем
|
||||
; ebx - указатель на event, ПОСЛЕ которого вставляем
|
||||
;scratched: ebx,ecx
|
||||
mov ecx, eax ; ecx=eax=Self, ebx=NewLeft
|
||||
xchg ecx, [ebx+EVENT.fd] ; NewLeft.fd=Self, ecx=NewRight
|
||||
cmp eax, ecx ; стоп, себе думаю...
|
||||
je .break ; - а не дурак ли я?
|
||||
cmp eax, ecx ; стоп, себе думаю...
|
||||
je .break ; - а не дурак ли я?
|
||||
mov [ecx+EVENT.bk], eax ; NewRight.bk=Self
|
||||
xchg ebx, [eax+EVENT.bk] ; Self.bk=NewLeft, ebx=OldLeft
|
||||
xchg ecx, [eax+EVENT.fd] ; Self.fd=NewRight, ecx=OldRight
|
||||
@ -142,22 +142,22 @@ NotDummyTest: ;; INTERNAL use (not returned
|
||||
push edi
|
||||
;--------------------------------------
|
||||
align 4
|
||||
.small: ; криво как-то...
|
||||
.small: ; криво как-то...
|
||||
pop edi
|
||||
pushfd
|
||||
cli
|
||||
call pid_to_slot ; saved all registers (eax - retval)
|
||||
shl eax, 8
|
||||
jz RemoveEventTo.break ; POPF+RET
|
||||
jmp edi ; штатный возврат
|
||||
jmp edi ; штатный возврат
|
||||
;-----------------------------------------------------------------------------
|
||||
align 4
|
||||
raise_event: ;; EXPORT use
|
||||
;info:
|
||||
; Устанавливаем данные EVENT.code
|
||||
; Если там флаг EVENT_SIGNALED уже активен - больше ничего
|
||||
; Иначе: этот флаг взводится, за исключением случая наличия флага EVENT_WATCHED в edx
|
||||
; В этом случае EVENT_SIGNALED взводится лишь при наличие EVENT_WATCHED в самом событии
|
||||
; Устанавливаем данные EVENT.code
|
||||
; Если там флаг EVENT_SIGNALED уже активен - больше ничего
|
||||
; Иначе: этот флаг взводится, за исключением случая наличия флага EVENT_WATCHED в edx
|
||||
; В этом случае EVENT_SIGNALED взводится лишь при наличие EVENT_WATCHED в самом событии
|
||||
;param:
|
||||
; eax - event
|
||||
; ebx - uid (for Dummy testing)
|
||||
@ -205,8 +205,8 @@ clear_event: ;; EXPORT use
|
||||
align 4
|
||||
send_event: ;; EXPORT use
|
||||
;info:
|
||||
; Создает новый EVENT (вытаскивает из списка FreeEvents) в списке EventList
|
||||
; целевого слота (eax=pid), с данными из esi косвенно, и state=EVENT_SIGNALED
|
||||
; Создает новый EVENT (вытаскивает из списка FreeEvents) в списке EventList
|
||||
; целевого слота (eax=pid), с данными из esi косвенно, и state=EVENT_SIGNALED
|
||||
;param:
|
||||
; eax - slots pid, to sending new event
|
||||
; esi - pointer to sending data (in code field of new event)
|
||||
@ -251,24 +251,24 @@ Wait_events:
|
||||
align 4
|
||||
Wait_events_ex:
|
||||
;info:
|
||||
; Ожидание "абстрактного" события через перевод слота в 5-ю позицию.
|
||||
; Абстрактность заключена в том, что факт события определяется функцией APPDATA.wait_test,
|
||||
; которая задается клиентом и может быть фактически любой.
|
||||
; Это позволяет shed-у надежно определить факт события, и не совершать "холостых" переключений,
|
||||
; предназначенных для разборок типа "свой/чужой" внутри задачи.
|
||||
; Ожидание "абстрактного" события через перевод слота в 5-ю позицию.
|
||||
; Абстрактность заключена в том, что факт события определяется функцией APPDATA.wait_test,
|
||||
; которая задается клиентом и может быть фактически любой.
|
||||
; Это позволяет shed-у надежно определить факт события, и не совершать "холостых" переключений,
|
||||
; предназначенных для разборок типа "свой/чужой" внутри задачи.
|
||||
;param:
|
||||
; edx - wait_test, клиентская ф-я тестирования (адрес кода)
|
||||
; ecx - wait_param, дополнительный параметр, возможно необходимый для [wait_test]
|
||||
; edx - wait_test, клиентская ф-я тестирования (адрес кода)
|
||||
; ecx - wait_param, дополнительный параметр, возможно необходимый для [wait_test]
|
||||
; ebx - wait_timeout
|
||||
;retval:
|
||||
; eax - результат вызова [wait_test] (=0 => timeout)
|
||||
; eax - результат вызова [wait_test] (=0 => timeout)
|
||||
;scratched: esi
|
||||
mov esi, [current_slot]
|
||||
mov [esi+APPDATA.wait_param], ecx
|
||||
pushad
|
||||
mov ebx, esi;пока это вопрос, чего куды сувать..........
|
||||
pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет
|
||||
cli ; право рассчитывать на закрытые прерывания, как при вызове из shed
|
||||
mov ebx, esi;пока это вопрос, чего куды сувать..........
|
||||
pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет
|
||||
cli ; право рассчитывать на закрытые прерывания, как при вызове из shed
|
||||
call edx
|
||||
popfd
|
||||
mov [esp+28], eax
|
||||
@ -290,12 +290,12 @@ align 4
|
||||
align 4
|
||||
wait_event: ;; EXPORT use
|
||||
;info:
|
||||
; Ожидание флага EVENT_SIGNALED в совершенно конкретном Event
|
||||
; (устанавливаемого, надо полагать, через raise_event)
|
||||
; При активном флаге MANUAL_RESET - больше ничего
|
||||
; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются,
|
||||
; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота,
|
||||
; а при не активном - уничтожается штатно (destroy_event.internal)
|
||||
; Ожидание флага EVENT_SIGNALED в совершенно конкретном Event
|
||||
; (устанавливаемого, надо полагать, через raise_event)
|
||||
; При активном флаге MANUAL_RESET - больше ничего
|
||||
; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются,
|
||||
; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота,
|
||||
; а при не активном - уничтожается штатно (destroy_event.internal)
|
||||
;param:
|
||||
; eax - event
|
||||
; ebx - uid (for Dummy testing)
|
||||
@ -326,16 +326,16 @@ wait_event_timeout:
|
||||
align 4
|
||||
get_event_ex: ;; f68:14
|
||||
;info:
|
||||
; Ожидание любого события в очереди EventList текущего слота
|
||||
; Данные события code - копируются в память приложения (косвенно по edi)
|
||||
; При активном флаге MANUAL_RESET - больше ничего
|
||||
; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются,
|
||||
; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота,
|
||||
; а при не активном - уничтожается штатно (destroy_event.internal)
|
||||
; Ожидание любого события в очереди EventList текущего слота
|
||||
; Данные события code - копируются в память приложения (косвенно по edi)
|
||||
; При активном флаге MANUAL_RESET - больше ничего
|
||||
; Иначе: флаги EVENT_SIGNALED и EVENT_WATCHED у полученного события сбрасываются,
|
||||
; и, при активном MANUAL_DESTROY - перемещается в список ObjList текущего слота,
|
||||
; а при не активном - уничтожается штатно (destroy_event.internal)
|
||||
;param:
|
||||
; edi - адрес в коде приложения для копирования данных из EVENT.code
|
||||
; edi - адрес в коде приложения для копирования данных из EVENT.code
|
||||
;retval:
|
||||
; eax - собственно EVENT (будем называть это его хэндлом)
|
||||
; eax - собственно EVENT (будем называть это его хэндлом)
|
||||
;scratched: ebx,ecx,edx,esi,edi
|
||||
mov edx, get_event_queue ; wait_test
|
||||
call Wait_events ; timeout ignored
|
||||
@ -361,12 +361,12 @@ wait_finish:
|
||||
align 4
|
||||
destroy_event: ;; EXPORT use
|
||||
;info:
|
||||
; Переносим EVENT в список FreeEvents, чистим поля magic,destroy,pid,id
|
||||
; Переносим EVENT в список FreeEvents, чистим поля magic,destroy,pid,id
|
||||
;param:
|
||||
; eax - event
|
||||
; ebx - uid (for Dummy testing)
|
||||
;retval:
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
;scratched: ebx,ecx
|
||||
call DummyTest ; not returned for fail !!!
|
||||
;--------------------------------------
|
||||
@ -385,17 +385,17 @@ align 4
|
||||
align 4
|
||||
get_event_queue:
|
||||
;info:
|
||||
; клиентская ф-я тестирования для get_event_ex
|
||||
; клиентская ф-я тестирования для get_event_ex
|
||||
;warning:
|
||||
; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot
|
||||
; -may be assumed, that interrupt are disabled
|
||||
; -it is not restriction for scratched registers
|
||||
;param:
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
;retval:
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
add ebx, APP_EV_OFFSET
|
||||
mov eax, [ebx+APPOBJ.bk] ; выбираем с конца, по принципу FIFO
|
||||
mov eax, [ebx+APPOBJ.bk] ; выбираем с конца, по принципу FIFO
|
||||
cmp eax, ebx ; empty ???
|
||||
je get_event_alone.ret0
|
||||
;--------------------------------------
|
||||
@ -406,15 +406,15 @@ align 4
|
||||
align 4
|
||||
get_event_alone:
|
||||
;info:
|
||||
; клиентская ф-я тестирования для wait_event
|
||||
; клиентская ф-я тестирования для wait_event
|
||||
;warning:
|
||||
; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot
|
||||
; -may be assumed, that interrupt are disabled
|
||||
; -it is not restriction for scratched registers
|
||||
;param:
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
;retval:
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
; eax - адрес объекта EVENT (=0 => fail)
|
||||
mov eax, [ebx+APPDATA.wait_param]
|
||||
test byte[eax+EVENT.state+3], EVENT_SIGNALED shr 24
|
||||
jnz .ret
|
||||
@ -458,7 +458,7 @@ align 4
|
||||
;--------------------------------------
|
||||
align 4
|
||||
.result:
|
||||
setae byte[esp+32+4] ;считаем, что исходно: dword[esp+32+4]==72
|
||||
setae byte[esp+32+4] ;считаем, что исходно: dword[esp+32+4]==72
|
||||
;--------------------------------------
|
||||
align 4
|
||||
.retf:
|
||||
@ -470,9 +470,9 @@ align 4
|
||||
;-----------------------------------------------------------------------------
|
||||
align 4
|
||||
sys_getevent: ;; f11
|
||||
mov ebx, [current_slot];пока это вопрос, чего куды сувать..........
|
||||
pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет
|
||||
cli ; право рассчитывать на закрытые прерывания, как при вызове из shed
|
||||
mov ebx, [current_slot];пока это вопрос, чего куды сувать..........
|
||||
pushfd ; это следствие общей концепции: пусть ф-я тестирования имеет
|
||||
cli ; право рассчитывать на закрытые прерывания, как при вызове из shed
|
||||
call get_event_for_app
|
||||
popfd
|
||||
mov [esp+32], eax
|
||||
@ -494,15 +494,15 @@ sys_wait_event_timeout: ;; f23
|
||||
align 4
|
||||
get_event_for_app: ;; used from f10,f11,f23
|
||||
;info:
|
||||
; клиентская ф-я тестирования для приложений (f10,f23)
|
||||
; клиентская ф-я тестирования для приложений (f10,f23)
|
||||
;warning:
|
||||
; -don't use [TASK_BASE],[current_slot],[CURRENT_TASK] - it is not for your slot
|
||||
; -may be assumed, that interrupt are disabled
|
||||
; -it is not restriction for scratched registers
|
||||
;param:
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
; ebx - адрес APPDATA слота тестирования
|
||||
;retval:
|
||||
; eax - номер события (=0 => no events)
|
||||
; eax - номер события (=0 => no events)
|
||||
movzx edi, bh ; bh is assumed as [CURRENT_TASK]
|
||||
shl edi, 5
|
||||
add edi, CURRENT_TASK ; edi is assumed as [TASK_BASE]
|
||||
@ -510,11 +510,11 @@ get_event_for_app: ;; used from f10,f11,f23
|
||||
and ecx, 0x7FFFFFFF
|
||||
;--------------------------------------
|
||||
align 4
|
||||
.loop: ; пока не исчерпаем все биты маски
|
||||
bsr eax, ecx ; находим ненулевой бит маски (31 -> 0)
|
||||
jz .no_events ; исчерпали все биты маски, но ничего не нашли ???
|
||||
btr ecx, eax ; сбрасываем проверяемый бит маски
|
||||
; переходим на обработчик этого (eax) бита
|
||||
.loop: ; пока не исчерпаем все биты маски
|
||||
bsr eax, ecx ; находим ненулевой бит маски (31 -> 0)
|
||||
jz .no_events ; исчерпали все биты маски, но ничего не нашли ???
|
||||
btr ecx, eax ; сбрасываем проверяемый бит маски
|
||||
; переходим на обработчик этого (eax) бита
|
||||
cmp eax, 9
|
||||
jae .loop ; eax=[9..31], ignored (event 10...32)
|
||||
|
||||
|
@ -310,7 +310,7 @@ init_BIOS32:
|
||||
test al, al
|
||||
jnz .PCI_BIOS32_not_found
|
||||
|
||||
; здесь создаются дискрипторы для PCI BIOS
|
||||
; здесь создаются дискрипторы для PCI BIOS
|
||||
|
||||
add ebx, OS_BASE
|
||||
dec ecx
|
||||
@ -334,7 +334,7 @@ init_BIOS32:
|
||||
mov [(pci_bios_entry-OS_BASE)], edx
|
||||
; jmp .end
|
||||
.PCI_BIOS32_not_found:
|
||||
; здесь должна заполнятся pci_emu_dat
|
||||
; здесь должна заполнятся pci_emu_dat
|
||||
.BIOS32_not_found:
|
||||
.end:
|
||||
ret
|
||||
|
@ -84,6 +84,7 @@ debug_direct_print equ 0
|
||||
include "proc32.inc"
|
||||
include "kglobals.inc"
|
||||
include "lang.inc"
|
||||
include "encoding.inc"
|
||||
|
||||
include "const.inc"
|
||||
max_processes equ 255
|
||||
@ -494,9 +495,9 @@ no_mode_0x12:
|
||||
|
||||
; !!!! It`s dirty hack, fix it !!!
|
||||
; Bits of EDX :
|
||||
; Bit 31–16 During the SYSRET instruction, this field is copied into the CS register
|
||||
; Bit 31–16 During the SYSRET instruction, this field is copied into the CS register
|
||||
; and the contents of this field, plus 8, are copied into the SS register.
|
||||
; Bit 15–0 During the SYSCALL instruction, this field is copied into the CS register
|
||||
; Bit 15–0 During the SYSCALL instruction, this field is copied into the CS register
|
||||
; and the contents of this field, plus 8, are copied into the SS register.
|
||||
|
||||
; mov edx, (os_code + 16) * 65536 + os_code
|
||||
@ -3337,8 +3338,8 @@ sys_sheduler:
|
||||
;now counter in ecx
|
||||
;(edx:eax) esi:edi => edx:esi
|
||||
; Fast Call MSR can't be destroy
|
||||
; <EFBFBD>® MSR_AMD_EFER ¬®¦® ¨§¬¥ïâì, â.ª. ¢ í⮬ ॣ¨áâॠ«¨è
|
||||
; ¢ª«îç îâáï/¢ëª«îç îâáï à áè¨à¥ë¥ ¢®§¬®¦®áâ¨
|
||||
; Но MSR_AMD_EFER можно изменять, т.к. в этом регистре лиш
|
||||
; включаются/выключаются расширенные возможности
|
||||
cmp edx, MSR_SYSENTER_CS
|
||||
je @f
|
||||
cmp edx, MSR_SYSENTER_ESP
|
||||
|
@ -1,4 +1,4 @@
|
||||
; <EFBFBD>ste archivo debe ser editado con codificaci¢n CP866
|
||||
; Éste archivo debe ser editado con codificación CP866
|
||||
|
||||
version db 'Kolibri OS versi¢n 0.7.7.0+ ',13,10,13,10,0
|
||||
diff16 "fin del c¢digo del kernel",0,$
|
||||
version: cp850 'Kolibri OS versión 0.7.7.0+ ',13,10,13,10,0
|
||||
diff16 "fin del código del kernel",0,$
|
||||
|
@ -1,52 +1,52 @@
|
||||
При компиляции ядра можно задать - например, в lang.inc, - дополнительный
|
||||
параметр extended_primary_loader=1; он переключает ядро на альтернативный
|
||||
способ загрузки. Загрузка несовместима
|
||||
с основой версией ядра; требуется специальный первичный загрузчик, существующие
|
||||
собраны в папке bootloader/extended_primary_loader.
|
||||
Есть варианты загрузки с FAT12/FAT16/FAT32/ISO,
|
||||
есть вариант загрузчика, встраивающегося в загрузку Windows. Встраивание
|
||||
в GRUB аналогично описанному для основного способа загрузки -
|
||||
последним загрузчиком в цепочке
|
||||
при этом оказывается тот, который установлен в образе дискеты FAT12.
|
||||
При компиляции ядра можно задать - например, в lang.inc, - дополнительный
|
||||
параметр extended_primary_loader=1; он переключает ядро на альтернативный
|
||||
способ загрузки. Загрузка несовместима
|
||||
с основой версией ядра; требуется специальный первичный загрузчик, существующие
|
||||
собраны в папке bootloader/extended_primary_loader.
|
||||
Есть варианты загрузки с FAT12/FAT16/FAT32/ISO,
|
||||
есть вариант загрузчика, встраивающегося в загрузку Windows. Встраивание
|
||||
в GRUB аналогично описанному для основного способа загрузки -
|
||||
последним загрузчиком в цепочке
|
||||
при этом оказывается тот, который установлен в образе дискеты FAT12.
|
||||
|
||||
При загрузке поддерживается опрос параметров из файла config.ini,
|
||||
но не поддерживается сохранение выбранных параметров. Файл config.ini
|
||||
ищется рядом с первичным загрузчиком, как и ядро kernel.mnt; в случае
|
||||
загрузчика с дискеты эти файлы располагаются на самой дискете,
|
||||
в случае других загрузчиков - рядом с первичным загрузчиком вне образа.
|
||||
При загрузке поддерживается опрос параметров из файла config.ini,
|
||||
но не поддерживается сохранение выбранных параметров. Файл config.ini
|
||||
ищется рядом с первичным загрузчиком, как и ядро kernel.mnt; в случае
|
||||
загрузчика с дискеты эти файлы располагаются на самой дискете,
|
||||
в случае других загрузчиков - рядом с первичным загрузчиком вне образа.
|
||||
|
||||
Если config.ini не найден, используются умолчальные значения. Если
|
||||
config.ini найден, то он разбивается на строчки, строчки должны иметь
|
||||
вид <параметр>=<значение>, перед параметром и вокруг знака равенства
|
||||
могут быть пробелы, всё, что идёт в строке после значения, игнорируется.
|
||||
Параметры чувствительны к регистру символов.
|
||||
Строки, не имеющие такого вида, а также строки, в которых параметр неизвестен,
|
||||
а также строки, в которых значение недопустимо, игнорируются.
|
||||
Если config.ini не найден, используются умолчальные значения. Если
|
||||
config.ini найден, то он разбивается на строчки, строчки должны иметь
|
||||
вид <параметр>=<значение>, перед параметром и вокруг знака равенства
|
||||
могут быть пробелы, всё, что идёт в строке после значения, игнорируется.
|
||||
Параметры чувствительны к регистру символов.
|
||||
Строки, не имеющие такого вида, а также строки, в которых параметр неизвестен,
|
||||
а также строки, в которых значение недопустимо, игнорируются.
|
||||
|
||||
Все числа должны быть целыми неотрицательными, записанными в десятичной
|
||||
системе счисления. Булевские значения кодируются следующим образом:
|
||||
0=off=no соответствует выключенному параметру, 1=on=yes - включённому.
|
||||
Все числа должны быть целыми неотрицательными, записанными в десятичной
|
||||
системе счисления. Булевские значения кодируются следующим образом:
|
||||
0=off=no соответствует выключенному параметру, 1=on=yes - включённому.
|
||||
|
||||
Известные параметры:
|
||||
Известные параметры:
|
||||
|
||||
timeout=<число секунд> задаёт время ожидания в экране выбора параметров.
|
||||
Если таймаут больше 9, используется значение 9. Значение по умолчанию 5.
|
||||
timeout=<число секунд> задаёт время ожидания в экране выбора параметров.
|
||||
Если таймаут больше 9, используется значение 9. Значение по умолчанию 5.
|
||||
|
||||
resolution=<ширина>*<высота> или <ширина>x<высота> задаёт желаемое
|
||||
разрешение графического режима. Если такого графического режима,
|
||||
устраивающего систему, не найдено, параметр игнорируется. По умолчанию
|
||||
пробуются последовательно разрешения 1024*768, 800*600, 640*480.
|
||||
resolution=<ширина>*<высота> или <ширина>x<высота> задаёт желаемое
|
||||
разрешение графического режима. Если такого графического режима,
|
||||
устраивающего систему, не найдено, параметр игнорируется. По умолчанию
|
||||
пробуются последовательно разрешения 1024*768, 800*600, 640*480.
|
||||
|
||||
vbemode=<номер видеорежима VBE> задаёт желаемый графический режим.
|
||||
Если такой режим не существует или не устраивает систему, параметр
|
||||
игнорируется. Параметр более приоритетен, чем resolution. Умолчального
|
||||
значения нет.
|
||||
vbemode=<номер видеорежима VBE> задаёт желаемый графический режим.
|
||||
Если такой режим не существует или не устраивает систему, параметр
|
||||
игнорируется. Параметр более приоритетен, чем resolution. Умолчального
|
||||
значения нет.
|
||||
|
||||
vrr=<включить VRR> - булевский параметр. Умолчальное значение 0.
|
||||
vrr=<включить VRR> - булевский параметр. Умолчальное значение 0.
|
||||
|
||||
biosdisks=<включить доступ к дискам через BIOS> - булевский параметр.
|
||||
Умолчальное значение 1.
|
||||
biosdisks=<включить доступ к дискам через BIOS> - булевский параметр.
|
||||
Умолчальное значение 1.
|
||||
|
||||
imgfrom=<источник рамдиска>. 1 - грузить дискету, 2 - грузить файл
|
||||
kolibri.img, находящийся рядом с первичным загрузчиком. Умолчальное
|
||||
значение 1 при загрузке с дискеты и 2 в противном случае.
|
||||
imgfrom=<источник рамдиска>. 1 - грузить дискету, 2 - грузить файл
|
||||
kolibri.img, находящийся рядом с первичным загрузчиком. Умолчальное
|
||||
значение 1 при загрузке с дискеты и 2 в противном случае.
|
||||
|
@ -24,68 +24,68 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Спецификация на первичный загрузчик KordOS.
|
||||
Загрузчик должен предоставлять следующие сервисы:
|
||||
1. При загрузке компьютера, получив управление от BIOS'а, загружать
|
||||
файл loader из папки kord по адресу 1000:0000.
|
||||
Размер файла loader не превосходит 30000h = 192 Kb.
|
||||
2. При этом устанавливать следующие регистры:
|
||||
ax идентифицирует устройство:
|
||||
al = тип:
|
||||
'f' - флопик
|
||||
Спецификация на первичный загрузчик KordOS.
|
||||
Загрузчик должен предоставлять следующие сервисы:
|
||||
1. При загрузке компьютера, получив управление от BIOS'а, загружать
|
||||
файл loader из папки kord по адресу 1000:0000.
|
||||
Размер файла loader не превосходит 30000h = 192 Kb.
|
||||
2. При этом устанавливать следующие регистры:
|
||||
ax идентифицирует устройство:
|
||||
al = тип:
|
||||
'f' - флопик
|
||||
'h' - HDD
|
||||
'c' - CD/DVD
|
||||
'u' - USB флешка
|
||||
'?' - неизвестное устройство
|
||||
ah = номер устройства (среди всех устройств фиксированного типа)
|
||||
bx = тип файловой системы:
|
||||
'u' - USB флешка
|
||||
'?' - неизвестное устройство
|
||||
ah = номер устройства (среди всех устройств фиксированного типа)
|
||||
bx = тип файловой системы:
|
||||
'12' = FAT12
|
||||
'16' = FAT16
|
||||
'32' = FAT32
|
||||
'nt' = NTFS
|
||||
'is' = ISO-9660
|
||||
ds:si = far-указатель на callback-сервис
|
||||
3. Предоставлять callback-сервис для вторичного загрузчика - far-процедуру:
|
||||
на входе: ax = запрашиваемая функция
|
||||
на выходе: CF=1, если функция не поддерживается; CF=0 иначе
|
||||
Загрузчик может разрушать все регистры, включая сегментные,
|
||||
за исключением ss и sp.
|
||||
4. Всегда должна поддерживаться callback-функция 1:
|
||||
назначение: прочитать файл, расположенный на загрузочном устройстве
|
||||
на входе: ax = 1, ds:di = указатель на информационную структуру:
|
||||
dw:dw far-указатель на буфер,
|
||||
первое слово - смещение, второе - сегмент
|
||||
dw максимальное число 4Kb-блоков для чтения (0x1000 байт)
|
||||
должно быть ненулевым и строго меньше 0x100
|
||||
ASCIIZ имя файла в формате "<папка1>/<папка2>/<файл>"
|
||||
Если имя файла содержит символы из старшей половины
|
||||
ASCIIZ-таблицы или не является 8.3-именем (в смысле, одна из компонент
|
||||
имени файла имеет имя длиннее 8 символов или расширение длиннее 3),
|
||||
загрузчик может не найти такой файл, даже если он есть
|
||||
(а может и найти).
|
||||
на выходе: bx = статус:
|
||||
0 = успешно
|
||||
1 = файл оказался слишком большим, буфер заполнен целиком
|
||||
и есть ещё данные файла
|
||||
2 = файл не найден
|
||||
3 = произошла ошибка чтения
|
||||
dx:ax = размер файла или FFFF:FFFF, если файл не найден
|
||||
5. Всегда должна поддерживаться callback-функция 2:
|
||||
назначение: продолжить чтение файла, частично загруженного функцией 1
|
||||
на входе: ax = 2, ds:di = указатель на информационную структуру:
|
||||
dw:dw far-указатель на буфер,
|
||||
первое слово - смещение, второе - сегмент
|
||||
dw максимальное число 4Kb-блоков для чтения (0x1000 байт)
|
||||
должно быть ненулевым и строго меньше 0x100
|
||||
на выходе: bx = статус:
|
||||
0 = успешно
|
||||
1 = файл оказался слишком большим, буфер заполнен целиком
|
||||
и есть ещё данные файла
|
||||
3 = произошла ошибка чтения
|
||||
dx:ax = размер файла
|
||||
Функцию можно вызывать только в случае, когда последний вызов функции
|
||||
1 и все последующие вызовы функции 2 вернули bx=1 (иными словами,
|
||||
только для продолжения загрузки файла, который уже был частично
|
||||
загружен, но ещё не загружен полностью).
|
||||
Загрузчик может быть уверен, что данные в областях памяти 0-9000 и
|
||||
60000-A0000 не будут модифицированы ядром.
|
||||
ds:si = far-указатель на callback-сервис
|
||||
3. Предоставлять callback-сервис для вторичного загрузчика - far-процедуру:
|
||||
на входе: ax = запрашиваемая функция
|
||||
на выходе: CF=1, если функция не поддерживается; CF=0 иначе
|
||||
Загрузчик может разрушать все регистры, включая сегментные,
|
||||
за исключением ss и sp.
|
||||
4. Всегда должна поддерживаться callback-функция 1:
|
||||
назначение: прочитать файл, расположенный на загрузочном устройстве
|
||||
на входе: ax = 1, ds:di = указатель на информационную структуру:
|
||||
dw:dw far-указатель на буфер,
|
||||
первое слово - смещение, второе - сегмент
|
||||
dw максимальное число 4Kb-блоков для чтения (0x1000 байт)
|
||||
должно быть ненулевым и строго меньше 0x100
|
||||
ASCIIZ имя файла в формате "<папка1>/<папка2>/<файл>"
|
||||
Если имя файла содержит символы из старшей половины
|
||||
ASCIIZ-таблицы или не является 8.3-именем (в смысле, одна из компонент
|
||||
имени файла имеет имя длиннее 8 символов или расширение длиннее 3),
|
||||
загрузчик может не найти такой файл, даже если он есть
|
||||
(а может и найти).
|
||||
на выходе: bx = статус:
|
||||
0 = успешно
|
||||
1 = файл оказался слишком большим, буфер заполнен целиком
|
||||
и есть ещё данные файла
|
||||
2 = файл не найден
|
||||
3 = произошла ошибка чтения
|
||||
dx:ax = размер файла или FFFF:FFFF, если файл не найден
|
||||
5. Всегда должна поддерживаться callback-функция 2:
|
||||
назначение: продолжить чтение файла, частично загруженного функцией 1
|
||||
на входе: ax = 2, ds:di = указатель на информационную структуру:
|
||||
dw:dw far-указатель на буфер,
|
||||
первое слово - смещение, второе - сегмент
|
||||
dw максимальное число 4Kb-блоков для чтения (0x1000 байт)
|
||||
должно быть ненулевым и строго меньше 0x100
|
||||
на выходе: bx = статус:
|
||||
0 = успешно
|
||||
1 = файл оказался слишком большим, буфер заполнен целиком
|
||||
и есть ещё данные файла
|
||||
3 = произошла ошибка чтения
|
||||
dx:ax = размер файла
|
||||
Функцию можно вызывать только в случае, когда последний вызов функции
|
||||
1 и все последующие вызовы функции 2 вернули bx=1 (иными словами,
|
||||
только для продолжения загрузки файла, который уже был частично
|
||||
загружен, но ещё не загружен полностью).
|
||||
Загрузчик может быть уверен, что данные в областях памяти 0-9000 и
|
||||
60000-A0000 не будут модифицированы ядром.
|
||||
|
@ -24,368 +24,368 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Нет повести печальнее на свете,
|
||||
Чем повесть о заклинившем Reset'е...
|
||||
Нет повести печальнее на свете,
|
||||
Чем повесть о заклинившем Reset'е...
|
||||
|
||||
Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает
|
||||
Windows, для носителей с размером сектора 512 байт.
|
||||
Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает
|
||||
Windows, для носителей с размером сектора 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Требования для работы:
|
||||
1) Все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
4) Пути к используемым файлам не должны содержать символических ссылок NTFS
|
||||
(жёсткие ссылки допускаются).
|
||||
5) Используемые файлы не должны быть сжатыми или разреженными файлами
|
||||
(актуально для NTFS, для FAT выполнено автоматически).
|
||||
Требования для работы:
|
||||
1) Все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
4) Пути к используемым файлам не должны содержать символических ссылок NTFS
|
||||
(жёсткие ссылки допускаются).
|
||||
5) Используемые файлы не должны быть сжатыми или разреженными файлами
|
||||
(актуально для NTFS, для FAT выполнено автоматически).
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки проверялись на валидность 08.08.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
|
||||
спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys
|
||||
и file://C:/ntldr либо file://C:/bootmgr
|
||||
неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543
|
||||
официальная спецификация расширения 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
|
||||
официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx
|
||||
официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx
|
||||
формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx
|
||||
Документация в тему (ссылки проверялись на валидность 08.08.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
|
||||
спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys
|
||||
и file://C:/ntldr либо file://C:/bootmgr
|
||||
неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543
|
||||
официальная спецификация расширения 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
|
||||
официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx
|
||||
официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx
|
||||
формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx
|
||||
|
||||
=====================================================================
|
||||
|
||||
Схема используемой памяти:
|
||||
600-2000 код загрузчика (и данные)
|
||||
2000-3000 стек
|
||||
3000-3200 сектор MBR
|
||||
3200-3400 бутсектор логического диска
|
||||
3400-3C00 информация о кэше для таблиц FAT16/FAT32:
|
||||
для FAT16 - массив на 0x100 байт, каждый байт равен
|
||||
0 или 1 в зависимости от того, загружен ли
|
||||
соответствующий сектор таблицы FAT16;
|
||||
для FAT32 - 100h входов по 8 байт: 4 байта
|
||||
(две ссылки - вперёд и назад) для организации L2-списка
|
||||
всех прочитанных секторов в порядке возрастания
|
||||
последнего времени использования + 4 байта для номера
|
||||
сектора; при переполнении кэша выкидывается элемент из
|
||||
головы списка, то есть тот, к которому дольше всех
|
||||
не было обращений
|
||||
3400-3440 информация о кэше для файловых записей NTFS в
|
||||
таком же формате, как и кэш для FAT32, но на 8 входов
|
||||
3480-34C0 заголовки для кэшей записей индекса NTFS
|
||||
3500-3D00 информация о кэшах записей индекса NTFS: с каждой
|
||||
файловой записью связан свой кэш для
|
||||
соответствующего индекса
|
||||
4000-8000 место для информации об атрибутах для NTFS
|
||||
60000-80000 таблица FAT12 / место под таблицу FAT16 /
|
||||
кэш для таблицы FAT32 / кэш для структур NTFS
|
||||
80000-90000 текущий рассматриваемый кластер
|
||||
90000-92000 FAT: кэш для корневой папки
|
||||
92000-... FAT: кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
Схема используемой памяти:
|
||||
600-2000 код загрузчика (и данные)
|
||||
2000-3000 стек
|
||||
3000-3200 сектор MBR
|
||||
3200-3400 бутсектор логического диска
|
||||
3400-3C00 информация о кэше для таблиц FAT16/FAT32:
|
||||
для FAT16 - массив на 0x100 байт, каждый байт равен
|
||||
0 или 1 в зависимости от того, загружен ли
|
||||
соответствующий сектор таблицы FAT16;
|
||||
для FAT32 - 100h входов по 8 байт: 4 байта
|
||||
(две ссылки - вперёд и назад) для организации L2-списка
|
||||
всех прочитанных секторов в порядке возрастания
|
||||
последнего времени использования + 4 байта для номера
|
||||
сектора; при переполнении кэша выкидывается элемент из
|
||||
головы списка, то есть тот, к которому дольше всех
|
||||
не было обращений
|
||||
3400-3440 информация о кэше для файловых записей NTFS в
|
||||
таком же формате, как и кэш для FAT32, но на 8 входов
|
||||
3480-34C0 заголовки для кэшей записей индекса NTFS
|
||||
3500-3D00 информация о кэшах записей индекса NTFS: с каждой
|
||||
файловой записью связан свой кэш для
|
||||
соответствующего индекса
|
||||
4000-8000 место для информации об атрибутах для NTFS
|
||||
60000-80000 таблица FAT12 / место под таблицу FAT16 /
|
||||
кэш для таблицы FAT32 / кэш для структур NTFS
|
||||
80000-90000 текущий рассматриваемый кластер
|
||||
90000-92000 FAT: кэш для корневой папки
|
||||
92000-... FAT: кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется
|
||||
размещением команды install=c:\kordldr.win в первой строке config.sys;
|
||||
при этом основной загрузчик системы загружает kordldr.win как обычный
|
||||
com-файл, в какой-то сегмент по смещению 100h и передаёт управление
|
||||
в начало кода (xxxx:0100).
|
||||
0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется
|
||||
добавлением строки наподобие c:\kordldr.win="KordOS" в секцию
|
||||
[operating systems] файла boot.ini; если загружаемый файл имеет размер
|
||||
не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS'
|
||||
(в случае kordldr.win так и есть), то основной загрузчик каждой из
|
||||
этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт
|
||||
управление на адрес 0D00:0256.
|
||||
0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями
|
||||
с базой данных основного загрузчика через bcdedit и подробно описана в
|
||||
инструкции к kordldr.win; основной загрузчик загружает целиком
|
||||
kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода.
|
||||
1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная
|
||||
им программа окажется в свою очередь загрузчиком, и в этом случае
|
||||
kordldr.win оказывается в условиях, когда основной загрузчик уже
|
||||
установил какое-то окружение, в частности, перехватил некоторые
|
||||
прерывания. Поэтому перед остальными действиями загрузчик должен
|
||||
восстановить систему в начальное состояние. (При загрузке под
|
||||
NT-линейкой такой проблемы не возникает, поскольку там основной
|
||||
загрузчик ничего в системе не трогает.) Поэтому перед собственно
|
||||
инициализацией KordOS при работе из-под DOS/9x производятся
|
||||
дополнительные действия. Первым делом kordldr проверяет, какой из
|
||||
случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт
|
||||
управление не на начало кода): определяет значение ip (команда call
|
||||
помещает в стек адрес следующей после call инструкции, команда pop si
|
||||
выталкивает его в регистр si), и если оно равно 100h, то kordldr
|
||||
загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения
|
||||
у пользователя (поскольку в этой схеме kordldr загружается всегда,
|
||||
он должен оставить возможность продолжить загрузку DOS/9x). Если
|
||||
пользователь хочет продолжить обычную загрузку, kordldr завершается.
|
||||
Иначе используется тот факт, что при выдаче прерывания перезагрузки
|
||||
int 19h система предварительно снимает все свои перехваты BIOSовских
|
||||
прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что
|
||||
kordldr устанавливает свой обработчик трассировочного прерывания,
|
||||
устанавливает флаг трассировки и передаёт управление DOSовскому
|
||||
обработчику. Обработчик трассировочного прерывания ничего не делает
|
||||
до тех пор, пока следующей инструкцией не оказывается int 19h, а
|
||||
в этот момент отбирает управление и продолжает загрузку KordOS.
|
||||
При этом BIOSовские обработчики восстановлены за исключением,
|
||||
быть может, прерывания таймера int 8, которое, возможно, восстановлено
|
||||
до команды jmp far на оригинальный обработчик. В последнем случае его
|
||||
нужно восстановить явно.
|
||||
2. Загрузчик перемещает свой код на адрес 0000:0600.
|
||||
3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0,
|
||||
настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы
|
||||
все данные можно было адресовать через [bp+N] с однобайтовым N
|
||||
(в дальнейшем они так и будут адресоваться для освобождения ds и
|
||||
экономии на размере кода). Разрешает прерывания на случай, если
|
||||
они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся
|
||||
с весёлой рожицы (символ с ASCII-кодом 2).
|
||||
4. Определяет характеристики жёсткого диска, указанного в качестве
|
||||
загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h),
|
||||
если LBA не поддерживается, то определяет геометрию - число дорожек
|
||||
и число секторов на дорожке (функция 8 прерывания 13h), эти параметры
|
||||
нужны функции чтения с диска.
|
||||
5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска.
|
||||
Цель цикла - для каждого логического диска попытаться загрузиться с
|
||||
него (действия по загрузке с конкретного логического диска начинаются
|
||||
с метки not_extended), при ошибке загрузки управление передаётся
|
||||
назад этому циклу (метка next_partition), и поиск подходящего раздела
|
||||
продолжается. На выходе заполняется одна переменная partition_start,
|
||||
имеющая смысл начала текущего рассматриваемого логического диска,
|
||||
но по ходу дела из-за приколов таблиц разделов используются ещё четыре
|
||||
переменных. cur_partition_ofs - фактически счётчик цикла, формально
|
||||
указатель на текущий вход в текущей загрузочной записи. Сама
|
||||
загрузочная запись считывается в память начиная с адреса 3000h.
|
||||
Три оставшихся нужны для правильной работы с расширенными разделами.
|
||||
В каждой загрузочной записи помещается не более 4 записей о разделах.
|
||||
Поэтому главной загрузочной записи, размещающейся в первом физическом
|
||||
секторе диска, может не хватить, и обычно создаётся так называемый
|
||||
расширенный раздел с расширенными загрузочными записями, формат
|
||||
которых почти идентичен главной. Расширенный раздел может быть только
|
||||
один, но в нём может быть много логических дисков и расширенных
|
||||
загрузочных записей. Расширенные загрузочные записи организованы
|
||||
в односвязный список, в каждой такой записи первый вход указывает
|
||||
на соответствующий логический диск, а второй - на следующую расширенную
|
||||
загрузочную запись.
|
||||
При этом в главной загрузочной записи все адреса разделов являются
|
||||
абсолютными номерами секторов. В расширенных же записях адреса разделов
|
||||
относительны, причём с разными базами: адрес логического диска
|
||||
указывается относительно расширенной записи, а адрес следующей
|
||||
расширенной записи указывается относительно начала расширенного
|
||||
раздела. Такой разнобой выглядит несколько странно, но имеет место
|
||||
быть. Три оставшихся переменных содержат: extended_part_start -
|
||||
начало расширенного раздела; extended_parent - текущая рассматриваемая
|
||||
расширенная загрузочная запись; extended_part_cur - следующая
|
||||
загрузочная запись для рассмотрения.
|
||||
Цикл выглядит так: просматриваются все разделы, указанные в текущей
|
||||
(главной или расширенной) загрузочной записи; для нормальных разделов
|
||||
(они же логические диски) происходит переход на not_extended, где
|
||||
устанавливается partition_start и начинается собственно загрузка
|
||||
(последующие шаги); при встрече с разделом, тип которого указывает
|
||||
на расширенность (5 или 0xF), код запоминает начало этого раздела
|
||||
(в главной загрузочной записи такой тип означает расширенный раздел,
|
||||
в расширенной - только указатель на следующую расширенную запись,
|
||||
в обоих случаях он может встретиться только один раз в данной записи);
|
||||
когда код доходит до конца списка, все нормальные разделы, описываемые
|
||||
в этой записи, уже просмотрены, так что код с чистой совестью переходит
|
||||
к следующей расширенной записи. Если он её не встретил, значит, уже
|
||||
все логические разделы были подвергнуты попыткам загрузиться, и все
|
||||
безрезультатно, так что выводится ругательство и работа останавливается
|
||||
Основной процесс загрузки.
|
||||
0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется
|
||||
размещением команды install=c:\kordldr.win в первой строке config.sys;
|
||||
при этом основной загрузчик системы загружает kordldr.win как обычный
|
||||
com-файл, в какой-то сегмент по смещению 100h и передаёт управление
|
||||
в начало кода (xxxx:0100).
|
||||
0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется
|
||||
добавлением строки наподобие c:\kordldr.win="KordOS" в секцию
|
||||
[operating systems] файла boot.ini; если загружаемый файл имеет размер
|
||||
не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS'
|
||||
(в случае kordldr.win так и есть), то основной загрузчик каждой из
|
||||
этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт
|
||||
управление на адрес 0D00:0256.
|
||||
0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями
|
||||
с базой данных основного загрузчика через bcdedit и подробно описана в
|
||||
инструкции к kordldr.win; основной загрузчик загружает целиком
|
||||
kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода.
|
||||
1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная
|
||||
им программа окажется в свою очередь загрузчиком, и в этом случае
|
||||
kordldr.win оказывается в условиях, когда основной загрузчик уже
|
||||
установил какое-то окружение, в частности, перехватил некоторые
|
||||
прерывания. Поэтому перед остальными действиями загрузчик должен
|
||||
восстановить систему в начальное состояние. (При загрузке под
|
||||
NT-линейкой такой проблемы не возникает, поскольку там основной
|
||||
загрузчик ничего в системе не трогает.) Поэтому перед собственно
|
||||
инициализацией KordOS при работе из-под DOS/9x производятся
|
||||
дополнительные действия. Первым делом kordldr проверяет, какой из
|
||||
случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт
|
||||
управление не на начало кода): определяет значение ip (команда call
|
||||
помещает в стек адрес следующей после call инструкции, команда pop si
|
||||
выталкивает его в регистр si), и если оно равно 100h, то kordldr
|
||||
загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения
|
||||
у пользователя (поскольку в этой схеме kordldr загружается всегда,
|
||||
он должен оставить возможность продолжить загрузку DOS/9x). Если
|
||||
пользователь хочет продолжить обычную загрузку, kordldr завершается.
|
||||
Иначе используется тот факт, что при выдаче прерывания перезагрузки
|
||||
int 19h система предварительно снимает все свои перехваты BIOSовских
|
||||
прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что
|
||||
kordldr устанавливает свой обработчик трассировочного прерывания,
|
||||
устанавливает флаг трассировки и передаёт управление DOSовскому
|
||||
обработчику. Обработчик трассировочного прерывания ничего не делает
|
||||
до тех пор, пока следующей инструкцией не оказывается int 19h, а
|
||||
в этот момент отбирает управление и продолжает загрузку KordOS.
|
||||
При этом BIOSовские обработчики восстановлены за исключением,
|
||||
быть может, прерывания таймера int 8, которое, возможно, восстановлено
|
||||
до команды jmp far на оригинальный обработчик. В последнем случае его
|
||||
нужно восстановить явно.
|
||||
2. Загрузчик перемещает свой код на адрес 0000:0600.
|
||||
3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0,
|
||||
настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы
|
||||
все данные можно было адресовать через [bp+N] с однобайтовым N
|
||||
(в дальнейшем они так и будут адресоваться для освобождения ds и
|
||||
экономии на размере кода). Разрешает прерывания на случай, если
|
||||
они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся
|
||||
с весёлой рожицы (символ с ASCII-кодом 2).
|
||||
4. Определяет характеристики жёсткого диска, указанного в качестве
|
||||
загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h),
|
||||
если LBA не поддерживается, то определяет геометрию - число дорожек
|
||||
и число секторов на дорожке (функция 8 прерывания 13h), эти параметры
|
||||
нужны функции чтения с диска.
|
||||
5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска.
|
||||
Цель цикла - для каждого логического диска попытаться загрузиться с
|
||||
него (действия по загрузке с конкретного логического диска начинаются
|
||||
с метки not_extended), при ошибке загрузки управление передаётся
|
||||
назад этому циклу (метка next_partition), и поиск подходящего раздела
|
||||
продолжается. На выходе заполняется одна переменная partition_start,
|
||||
имеющая смысл начала текущего рассматриваемого логического диска,
|
||||
но по ходу дела из-за приколов таблиц разделов используются ещё четыре
|
||||
переменных. cur_partition_ofs - фактически счётчик цикла, формально
|
||||
указатель на текущий вход в текущей загрузочной записи. Сама
|
||||
загрузочная запись считывается в память начиная с адреса 3000h.
|
||||
Три оставшихся нужны для правильной работы с расширенными разделами.
|
||||
В каждой загрузочной записи помещается не более 4 записей о разделах.
|
||||
Поэтому главной загрузочной записи, размещающейся в первом физическом
|
||||
секторе диска, может не хватить, и обычно создаётся так называемый
|
||||
расширенный раздел с расширенными загрузочными записями, формат
|
||||
которых почти идентичен главной. Расширенный раздел может быть только
|
||||
один, но в нём может быть много логических дисков и расширенных
|
||||
загрузочных записей. Расширенные загрузочные записи организованы
|
||||
в односвязный список, в каждой такой записи первый вход указывает
|
||||
на соответствующий логический диск, а второй - на следующую расширенную
|
||||
загрузочную запись.
|
||||
При этом в главной загрузочной записи все адреса разделов являются
|
||||
абсолютными номерами секторов. В расширенных же записях адреса разделов
|
||||
относительны, причём с разными базами: адрес логического диска
|
||||
указывается относительно расширенной записи, а адрес следующей
|
||||
расширенной записи указывается относительно начала расширенного
|
||||
раздела. Такой разнобой выглядит несколько странно, но имеет место
|
||||
быть. Три оставшихся переменных содержат: extended_part_start -
|
||||
начало расширенного раздела; extended_parent - текущая рассматриваемая
|
||||
расширенная загрузочная запись; extended_part_cur - следующая
|
||||
загрузочная запись для рассмотрения.
|
||||
Цикл выглядит так: просматриваются все разделы, указанные в текущей
|
||||
(главной или расширенной) загрузочной записи; для нормальных разделов
|
||||
(они же логические диски) происходит переход на not_extended, где
|
||||
устанавливается partition_start и начинается собственно загрузка
|
||||
(последующие шаги); при встрече с разделом, тип которого указывает
|
||||
на расширенность (5 или 0xF), код запоминает начало этого раздела
|
||||
(в главной загрузочной записи такой тип означает расширенный раздел,
|
||||
в расширенной - только указатель на следующую расширенную запись,
|
||||
в обоих случаях он может встретиться только один раз в данной записи);
|
||||
когда код доходит до конца списка, все нормальные разделы, описываемые
|
||||
в этой записи, уже просмотрены, так что код с чистой совестью переходит
|
||||
к следующей расширенной записи. Если он её не встретил, значит, уже
|
||||
все логические разделы были подвергнуты попыткам загрузиться, и все
|
||||
безрезультатно, так что выводится ругательство и работа останавливается
|
||||
(jmp $).
|
||||
Может возникнуть вопрос, зачем нужна такая сложная схема и почему
|
||||
нельзя узнать нужный логический диск заранее или хотя бы ограничиться
|
||||
первым попавшимся логическим диском, не крутя цикл. Так вот, вариант
|
||||
с предварительным определением нужного раздела в данном случае не
|
||||
используется, поскольку повлёк бы за собой нетривиальные лишние
|
||||
действия по установке (в текущем виде установку можно провести вручную,
|
||||
и она сводится к указанию системному загрузчику на существование
|
||||
kordldr); кстати, в альтернативной версии загрузки после
|
||||
Windows-загрузчика, когда установка осуществляется не вручную, а
|
||||
специальной программой под Windows, используется модифицированная
|
||||
версия, в которой как раз начальный физический сектор нужного раздела
|
||||
прописывается установщиком. Сам kordldr не может установить, с какого
|
||||
раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан
|
||||
быть файлом на диске C:\). Вариант с первым попавшимся логическим
|
||||
диском был реализован в первой версии загрузчика, но по ходу дела
|
||||
обнаружилось, что таки нужно крутить цикл: во-вторых, может быть
|
||||
приятным, что сама система может стоять вовсе не на системном C:\, а и
|
||||
на других дисках; во-первых, диск C: может и не быть первым логическим
|
||||
разделом - Vista любит создавать скрытый первичный раздел перед
|
||||
системным, и тогда диск C: становится вторым логическим.
|
||||
6. Извещает пользователя о том, что происходит попытка загрузки с очередного
|
||||
логического диска.
|
||||
7. Читает первый сектор логического диска и определяет файловую систему.
|
||||
И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе
|
||||
и должно совпадать с характеристикой физического носителя, то есть
|
||||
200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число
|
||||
секторов в кластере и должно быть степенью двойки.
|
||||
Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со
|
||||
смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано
|
||||
быть ненулевым).
|
||||
Критерий FAT: загрузчик вычисляет число кластеров, определяет
|
||||
предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению
|
||||
+38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29).
|
||||
После определения типа файловой системы извещает пользователя об
|
||||
определённом типе. Если файловая система не распознана, выдаёт
|
||||
соответствующее сообщение и переходит к следующему логическому диску.
|
||||
8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '12'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT12-обработчик; считывает в память всю
|
||||
таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке
|
||||
чтения пытается использовать другие копии FAT.
|
||||
8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '16'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (массив байт с возможными значениями 0 и 1,
|
||||
означающими, был ли уже загружен соответствующий сектор - всего в
|
||||
таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не
|
||||
загружен, все байты нулевые.
|
||||
8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '32'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (формат информации описан выше, в распределении
|
||||
используемой загрузчиком памяти) - ни один сектор ещё не загружен.
|
||||
8г. Общее для FAT-томов: определяет значения служебных переменных
|
||||
root_start (первый сектор корневого каталога в FAT12/16, игнорируется
|
||||
при обработке FAT32-томов), data_start (начало данных с поправкой,
|
||||
вводимой для того, чтобы кластер N начинался с сектора
|
||||
N*sectors_per_cluster+data_start), root_clus (первый кластер корневого
|
||||
каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию
|
||||
загрузки файла на FAT-обработчик.
|
||||
8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу 'nt'; определяет значение служебной переменной frs_size
|
||||
(размер в байтах файловой записи, File Record Segment), для полной
|
||||
корректности проверяет, что это значение (равное 0x400 байт на всех
|
||||
реальных NTFS-томах - единственный способ изменить его заключается
|
||||
в пересоздании всех системных структур вручную) не превосходит 0x1000
|
||||
и кратно размеру сектора 0x200 байт; инициализирует кэш файловых
|
||||
записей - ничего ещё не загружено; считывает первый кластер $MFT
|
||||
и загружает информацию о расположении на диске всей таблицы $MFT
|
||||
(атрибут 0x80, $Data); устанавливает указатель на функцию загрузки
|
||||
файла на NTFS-обработчик.
|
||||
9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного
|
||||
загрузчика. При обнаружении ошибки переходит на обработчик ошибок с
|
||||
соответствующим сообщением.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск),
|
||||
ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h),
|
||||
может быть изменён путём модификации константы в исходнике или
|
||||
специальным установщиком), bx=идентификатор файловой системы (берётся
|
||||
из стека, куда ранее был засунут на шаге 8), ds:si=указатель на
|
||||
callback-функцию.
|
||||
11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000.
|
||||
Может возникнуть вопрос, зачем нужна такая сложная схема и почему
|
||||
нельзя узнать нужный логический диск заранее или хотя бы ограничиться
|
||||
первым попавшимся логическим диском, не крутя цикл. Так вот, вариант
|
||||
с предварительным определением нужного раздела в данном случае не
|
||||
используется, поскольку повлёк бы за собой нетривиальные лишние
|
||||
действия по установке (в текущем виде установку можно провести вручную,
|
||||
и она сводится к указанию системному загрузчику на существование
|
||||
kordldr); кстати, в альтернативной версии загрузки после
|
||||
Windows-загрузчика, когда установка осуществляется не вручную, а
|
||||
специальной программой под Windows, используется модифицированная
|
||||
версия, в которой как раз начальный физический сектор нужного раздела
|
||||
прописывается установщиком. Сам kordldr не может установить, с какого
|
||||
раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан
|
||||
быть файлом на диске C:\). Вариант с первым попавшимся логическим
|
||||
диском был реализован в первой версии загрузчика, но по ходу дела
|
||||
обнаружилось, что таки нужно крутить цикл: во-вторых, может быть
|
||||
приятным, что сама система может стоять вовсе не на системном C:\, а и
|
||||
на других дисках; во-первых, диск C: может и не быть первым логическим
|
||||
разделом - Vista любит создавать скрытый первичный раздел перед
|
||||
системным, и тогда диск C: становится вторым логическим.
|
||||
6. Извещает пользователя о том, что происходит попытка загрузки с очередного
|
||||
логического диска.
|
||||
7. Читает первый сектор логического диска и определяет файловую систему.
|
||||
И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе
|
||||
и должно совпадать с характеристикой физического носителя, то есть
|
||||
200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число
|
||||
секторов в кластере и должно быть степенью двойки.
|
||||
Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со
|
||||
смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано
|
||||
быть ненулевым).
|
||||
Критерий FAT: загрузчик вычисляет число кластеров, определяет
|
||||
предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению
|
||||
+38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29).
|
||||
После определения типа файловой системы извещает пользователя об
|
||||
определённом типе. Если файловая система не распознана, выдаёт
|
||||
соответствующее сообщение и переходит к следующему логическому диску.
|
||||
8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '12'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT12-обработчик; считывает в память всю
|
||||
таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке
|
||||
чтения пытается использовать другие копии FAT.
|
||||
8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '16'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (массив байт с возможными значениями 0 и 1,
|
||||
означающими, был ли уже загружен соответствующий сектор - всего в
|
||||
таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не
|
||||
загружен, все байты нулевые.
|
||||
8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу '32'; устанавливает указатель на функцию получения следующего
|
||||
в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию
|
||||
о кэше секторов FAT (формат информации описан выше, в распределении
|
||||
используемой загрузчиком памяти) - ни один сектор ещё не загружен.
|
||||
8г. Общее для FAT-томов: определяет значения служебных переменных
|
||||
root_start (первый сектор корневого каталога в FAT12/16, игнорируется
|
||||
при обработке FAT32-томов), data_start (начало данных с поправкой,
|
||||
вводимой для того, чтобы кластер N начинался с сектора
|
||||
N*sectors_per_cluster+data_start), root_clus (первый кластер корневого
|
||||
каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию
|
||||
загрузки файла на FAT-обработчик.
|
||||
8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы -
|
||||
константу 'nt'; определяет значение служебной переменной frs_size
|
||||
(размер в байтах файловой записи, File Record Segment), для полной
|
||||
корректности проверяет, что это значение (равное 0x400 байт на всех
|
||||
реальных NTFS-томах - единственный способ изменить его заключается
|
||||
в пересоздании всех системных структур вручную) не превосходит 0x1000
|
||||
и кратно размеру сектора 0x200 байт; инициализирует кэш файловых
|
||||
записей - ничего ещё не загружено; считывает первый кластер $MFT
|
||||
и загружает информацию о расположении на диске всей таблицы $MFT
|
||||
(атрибут 0x80, $Data); устанавливает указатель на функцию загрузки
|
||||
файла на NTFS-обработчик.
|
||||
9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного
|
||||
загрузчика. При обнаружении ошибки переходит на обработчик ошибок с
|
||||
соответствующим сообщением.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск),
|
||||
ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h),
|
||||
может быть изменён путём модификации константы в исходнике или
|
||||
специальным установщиком), bx=идентификатор файловой системы (берётся
|
||||
из стека, куда ранее был засунут на шаге 8), ds:si=указатель на
|
||||
callback-функцию.
|
||||
11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Чтение файла:
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:dat.
|
||||
2. Разбирает переданные параметры и вызывает процедуру загрузки файла.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Чтение файла:
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:dat.
|
||||
2. Разбирает переданные параметры и вызывает процедуру загрузки файла.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
|
||||
Вспомогательные процедуры.
|
||||
Процедура чтения секторов (read):
|
||||
на входе должно быть установлено:
|
||||
Вспомогательные процедуры.
|
||||
Процедура чтения секторов (read):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:dat
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор (относительно начала логического диска)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
||||
устройстве, прибавляя номер первого сектора логического диска,
|
||||
найденный при переборе дисков.
|
||||
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.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор (относительно начала логического диска)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
|
||||
устройстве, прибавляя номер первого сектора логического диска,
|
||||
найденный при переборе дисков.
|
||||
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.
|
||||
|
||||
Процедура обработки ошибок (find_error_si и find_error_sp):
|
||||
на входе: указатель на сообщение об ошибке в si либо на верхушке стека
|
||||
0. Если вызывается find_error_si, она помещает переданный указатель в стек.
|
||||
1. Если ошибка произошла в процессе работы callback-функции, то
|
||||
(метка error_in_callback) обработчик просто возвращает управление
|
||||
вызвавшему коду, рапортуя о ненайденном файле.
|
||||
2. Если же ошибка произошла до передачи управления вторичному загрузчику,
|
||||
обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>"
|
||||
и (восстановив стек) переходит к следующему логическому диску.
|
||||
Процедура обработки ошибок (find_error_si и find_error_sp):
|
||||
на входе: указатель на сообщение об ошибке в si либо на верхушке стека
|
||||
0. Если вызывается find_error_si, она помещает переданный указатель в стек.
|
||||
1. Если ошибка произошла в процессе работы callback-функции, то
|
||||
(метка error_in_callback) обработчик просто возвращает управление
|
||||
вызвавшему коду, рапортуя о ненайденном файле.
|
||||
2. Если же ошибка произошла до передачи управления вторичному загрузчику,
|
||||
обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>"
|
||||
и (восстановив стек) переходит к следующему логическому диску.
|
||||
|
||||
Процедура чтения файла/атрибута по известному размещению на диске
|
||||
Процедура чтения файла/атрибута по известному размещению на диске
|
||||
(read_file_chunk):
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на информацию о размещении
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
ecx = лимит числа секторов для чтения, старшее слово должно быть 0
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Определяет, является ли атрибут резидентным (возможно только в NTFS
|
||||
и означает, что данные файла/атрибута уже были целиком прочитаны при
|
||||
обработке информации о файле) или нерезидентным (означает, что данные
|
||||
хранятся где-то на диске, и имеется информация о том, где именно).
|
||||
2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует
|
||||
данные по месту назначения (с учётом указанного лимита).
|
||||
3. Для нерезидентных атрибутов информация состоит из пар <размер очередного
|
||||
фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура
|
||||
читает фрагменты, пока файл не закончится или пока не будет достигнут
|
||||
указанный лимит.
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на информацию о размещении
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
ecx = лимит числа секторов для чтения, старшее слово должно быть 0
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
флаг CF установлен, если возникла ошибка чтения
|
||||
1. Определяет, является ли атрибут резидентным (возможно только в NTFS
|
||||
и означает, что данные файла/атрибута уже были целиком прочитаны при
|
||||
обработке информации о файле) или нерезидентным (означает, что данные
|
||||
хранятся где-то на диске, и имеется информация о том, где именно).
|
||||
2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует
|
||||
данные по месту назначения (с учётом указанного лимита).
|
||||
3. Для нерезидентных атрибутов информация состоит из пар <размер очередного
|
||||
фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура
|
||||
читает фрагменты, пока файл не закончится или пока не будет достигнут
|
||||
указанный лимит.
|
||||
|
||||
Процедура просмотра кэша (cache_lookup):
|
||||
на входе должно быть установлено:
|
||||
eax = искомое значение
|
||||
ss:si = указатель на структуру-заголовок кэша
|
||||
на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение
|
||||
было только что добавлено, и сброшен, если оно уже было в кэше.
|
||||
1. Просматривает кэш в поисках указанного значения. Если значение найдено
|
||||
(при этом флаг CF оказывается сброшенным), переходит к шагу 4.
|
||||
2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в
|
||||
голове двусвязного списка), иначе добавляет к кэшу ещё один вход.
|
||||
3. Устанавливает в полученном входе указанное значение. Устанавливает флаг
|
||||
CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5.
|
||||
4. Удаляет вход из списка.
|
||||
5. Добавляет сектор в конец списка (самый новый вход).
|
||||
Процедура просмотра кэша (cache_lookup):
|
||||
на входе должно быть установлено:
|
||||
eax = искомое значение
|
||||
ss:si = указатель на структуру-заголовок кэша
|
||||
на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение
|
||||
было только что добавлено, и сброшен, если оно уже было в кэше.
|
||||
1. Просматривает кэш в поисках указанного значения. Если значение найдено
|
||||
(при этом флаг CF оказывается сброшенным), переходит к шагу 4.
|
||||
2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в
|
||||
голове двусвязного списка), иначе добавляет к кэшу ещё один вход.
|
||||
3. Устанавливает в полученном входе указанное значение. Устанавливает флаг
|
||||
CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5.
|
||||
4. Удаляет вход из списка.
|
||||
5. Добавляет сектор в конец списка (самый новый вход).
|
||||
|
@ -26,393 +26,393 @@
|
||||
|
||||
Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.?
|
||||
|
||||
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
||||
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
||||
либо ISO-9660, либо UDF.)
|
||||
Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660.
|
||||
(ISO-9660 и её расширения - стандарт для CD; DVD может использовать
|
||||
либо ISO-9660, либо UDF.)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Требования для работы:
|
||||
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор и все используемые файлы должны быть читабельны.
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 452K свободной базовой памяти.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки проверялись на валидность 14.09.2008):
|
||||
стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
|
||||
стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf
|
||||
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
||||
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
||||
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
||||
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
||||
Документация в тему (ссылки проверялись на валидность 14.09.2008):
|
||||
стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
|
||||
стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf
|
||||
официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
|
||||
то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
|
||||
описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
|
||||
официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
|
||||
|
||||
=====================================================================
|
||||
|
||||
Схема используемой памяти:
|
||||
1000-1800 временный буфер для чтения одиночных секторов
|
||||
...-7C00 стек
|
||||
7C00-8400 код бутсектора
|
||||
8400-8A00 информация о кэше для папок: массив входов следующего
|
||||
формата:
|
||||
dw следующий элемент в L2-списке закэшированных папок,
|
||||
упорядоченном по времени использования
|
||||
(голова списка - самый старый);
|
||||
dw предыдущий элемент в том же списке;
|
||||
dd первый сектор папки;
|
||||
dw размер папки в байтах;
|
||||
dw сегмент кэша
|
||||
60000-... содержимое Path Table, если она используется
|
||||
+ кэш для папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area
|
||||
Схема используемой памяти:
|
||||
1000-1800 временный буфер для чтения одиночных секторов
|
||||
...-7C00 стек
|
||||
7C00-8400 код бутсектора
|
||||
8400-8A00 информация о кэше для папок: массив входов следующего
|
||||
формата:
|
||||
dw следующий элемент в L2-списке закэшированных папок,
|
||||
упорядоченном по времени использования
|
||||
(голова списка - самый старый);
|
||||
dw предыдущий элемент в том же списке;
|
||||
dd первый сектор папки;
|
||||
dw размер папки в байтах;
|
||||
dw сегмент кэша
|
||||
60000-... содержимое Path Table, если она используется
|
||||
+ кэш для папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip
|
||||
равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает
|
||||
дальний прыжок на самого себя с целью получить cs=0 (в некоторых
|
||||
местах используется адресация переменных загрузчика через cs, поскольку
|
||||
и ds, и es могут быть заняты под другие сегменты).
|
||||
2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом)
|
||||
и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления
|
||||
и разрешённые прерывания. Сохраняет идентификатор загрузочного диска
|
||||
в специальную переменную.
|
||||
3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять
|
||||
LBA-функции.
|
||||
4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту
|
||||
ISO9660 со смещения 10h начинается цепочка описателей тома,
|
||||
завершающаяся специальным описателем (Volume Descriptor Set
|
||||
Terminator). Код по очереди считывает все сектора, пока не наткнётся
|
||||
либо на искомый описатель, либо на терминатор. Во втором случае
|
||||
выдаётся соответствующее сообщение, и загрузка прекращается.
|
||||
Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD
|
||||
располагается в последней сессии. И спецификация ElTorito загрузочного
|
||||
CD оперирует также с последней сессией. Однако на практике оказывается,
|
||||
что: во-первых, реальные BIOSы не понимают мультисессионных CD и
|
||||
всегда используют первую сессию; во-вторых, BIOSовский int 13h просто
|
||||
не позволяет получить информацию о последней сессии. В связи с этим
|
||||
загрузчик также использует первую сессию. (В-третьих, в одной из BIOS
|
||||
обнаружилась заготовка, которая в случае запроса сектора 10h, в котором
|
||||
во всех нормальных случаях и располагается PVD, перенаправляет его
|
||||
на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с
|
||||
последней сессии, то благодаря заготовке загрузчик без всяких
|
||||
модификаций также читал бы последнюю сессию.)
|
||||
5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во
|
||||
внутренние переменные: размер логического блока (согласно спецификации,
|
||||
должен быть степенью двойки от 512 до размера логического сектора,
|
||||
равного 2048 для CD и DVD); положение на диске корневой папки;
|
||||
вычисляет число блоков в секторе (из предыдущего примечания следует,
|
||||
что оно всегда целое и само является степенью двойки).
|
||||
6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет
|
||||
размер пространства, которое может использовать загрузчик (от
|
||||
адреса 6000:0000 до конца доступной памяти).
|
||||
7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит
|
||||
базовую информацию обо всех папках на диске. Если таблица слишком
|
||||
велика (больше 62K или больше половины доступной памяти), то она
|
||||
игнорируется. Если таблица путей недоступна, то запрос типа
|
||||
dir1/dir2/dir3/file приведёт к последовательному разбору корневой
|
||||
папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать
|
||||
саму таблицу путей (где записано положение папки dir1/dir2/dir3)
|
||||
и папку dir3. Если таблица загружена, то соответственно уменьшается
|
||||
объём оставшейся доступной памяти и увеличивается указатель на
|
||||
свободную область.
|
||||
8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7
|
||||
доступная память отводится под этот кэш).
|
||||
9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке
|
||||
печатает соответствующее сообщение и прекращает загрузку с CD.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует
|
||||
тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is'
|
||||
идентифицирует файловую систему ISO-9660; ds:si указывает на
|
||||
callback-функцию, которую может вызывать вторичный загрузчик.
|
||||
11. Передаёт управление вторичному загрузчику, совершая дальний прыжок
|
||||
на адрес, куда kord/loader был загружен.
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip
|
||||
равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает
|
||||
дальний прыжок на самого себя с целью получить cs=0 (в некоторых
|
||||
местах используется адресация переменных загрузчика через cs, поскольку
|
||||
и ds, и es могут быть заняты под другие сегменты).
|
||||
2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом)
|
||||
и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления
|
||||
и разрешённые прерывания. Сохраняет идентификатор загрузочного диска
|
||||
в специальную переменную.
|
||||
3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять
|
||||
LBA-функции.
|
||||
4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту
|
||||
ISO9660 со смещения 10h начинается цепочка описателей тома,
|
||||
завершающаяся специальным описателем (Volume Descriptor Set
|
||||
Terminator). Код по очереди считывает все сектора, пока не наткнётся
|
||||
либо на искомый описатель, либо на терминатор. Во втором случае
|
||||
выдаётся соответствующее сообщение, и загрузка прекращается.
|
||||
Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD
|
||||
располагается в последней сессии. И спецификация ElTorito загрузочного
|
||||
CD оперирует также с последней сессией. Однако на практике оказывается,
|
||||
что: во-первых, реальные BIOSы не понимают мультисессионных CD и
|
||||
всегда используют первую сессию; во-вторых, BIOSовский int 13h просто
|
||||
не позволяет получить информацию о последней сессии. В связи с этим
|
||||
загрузчик также использует первую сессию. (В-третьих, в одной из BIOS
|
||||
обнаружилась заготовка, которая в случае запроса сектора 10h, в котором
|
||||
во всех нормальных случаях и располагается PVD, перенаправляет его
|
||||
на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с
|
||||
последней сессии, то благодаря заготовке загрузчик без всяких
|
||||
модификаций также читал бы последнюю сессию.)
|
||||
5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во
|
||||
внутренние переменные: размер логического блока (согласно спецификации,
|
||||
должен быть степенью двойки от 512 до размера логического сектора,
|
||||
равного 2048 для CD и DVD); положение на диске корневой папки;
|
||||
вычисляет число блоков в секторе (из предыдущего примечания следует,
|
||||
что оно всегда целое и само является степенью двойки).
|
||||
6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет
|
||||
размер пространства, которое может использовать загрузчик (от
|
||||
адреса 6000:0000 до конца доступной памяти).
|
||||
7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит
|
||||
базовую информацию обо всех папках на диске. Если таблица слишком
|
||||
велика (больше 62K или больше половины доступной памяти), то она
|
||||
игнорируется. Если таблица путей недоступна, то запрос типа
|
||||
dir1/dir2/dir3/file приведёт к последовательному разбору корневой
|
||||
папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать
|
||||
саму таблицу путей (где записано положение папки dir1/dir2/dir3)
|
||||
и папку dir3. Если таблица загружена, то соответственно уменьшается
|
||||
объём оставшейся доступной памяти и увеличивается указатель на
|
||||
свободную область.
|
||||
8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7
|
||||
доступная память отводится под этот кэш).
|
||||
9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке
|
||||
печатает соответствующее сообщение и прекращает загрузку с CD.
|
||||
10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует
|
||||
тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is'
|
||||
идентифицирует файловую систему ISO-9660; ds:si указывает на
|
||||
callback-функцию, которую может вызывать вторичный загрузчик.
|
||||
11. Передаёт управление вторичному загрузчику, совершая дальний прыжок
|
||||
на адрес, куда kord/loader был загружен.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика (callback):
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
||||
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
||||
запросах на продолжение загрузки файла).
|
||||
Функция обратного вызова для вторичного загрузчика (callback):
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
Перенаправляет запрос соответствующей локальной процедуре (load_file при
|
||||
первом запросе на загрузку файла, loadloop.loadnew при последующих
|
||||
запросах на продолжение загрузки файла).
|
||||
|
||||
Вспомогательные процедуры.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения секторов (read_sectors):
|
||||
на входе должно быть установлено:
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор
|
||||
cx = число секторов
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
||||
число читаемых секторов не превосходило 7Fh (требование спецификации
|
||||
Процедура чтения секторов (read_sectors):
|
||||
на входе должно быть установлено:
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = стартовый сектор
|
||||
cx = число секторов
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации
|
||||
число читаемых секторов не превосходило 7Fh (требование спецификации
|
||||
EDD BIOS).
|
||||
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||||
итерации) до 7Fh.
|
||||
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||||
клали).
|
||||
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
||||
устанавливает CF=1 и выходит из процедуры.
|
||||
Очищает стек от пакета, сформированного на предыдущем шаге.
|
||||
5. В соответствии с числом прочитанных на текущей итерации секторов
|
||||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||||
работу, иначе возвращается на шаг 2.
|
||||
2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
|
||||
итерации) до 7Fh.
|
||||
3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
|
||||
push, причём в обратном порядке: стек - структура LIFO, и данные в
|
||||
стеке хранятся в обратном порядке по отношению к тому, как их туда
|
||||
клали).
|
||||
4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек,
|
||||
устанавливает CF=1 и выходит из процедуры.
|
||||
Очищает стек от пакета, сформированного на предыдущем шаге.
|
||||
5. В соответствии с числом прочитанных на текущей итерации секторов
|
||||
корректирует текущий сектор, число оставшихся секторов и указатель на
|
||||
буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
|
||||
работу, иначе возвращается на шаг 2.
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
|
||||
Процедура загрузки файла (load_file):
|
||||
на входе:
|
||||
ds:di = указатель на информационную структуру, описанную в спецификации
|
||||
на загрузчик, а также в комментариях к коду
|
||||
на выходе:
|
||||
bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть,
|
||||
2=файл не найден, 3=ошибка чтения
|
||||
dx:ax = размер файла, 0xFFFFFFFF, если файл не найден
|
||||
1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице,
|
||||
иначе переходит сразу к шагу 4, установив eax = начальный блок
|
||||
корневой папки.
|
||||
2. Устанавливает es:di на начало таблицы путей. Ограничение на размер
|
||||
гарантирует, что вся таблица помещается в сегменте 6000h.
|
||||
Инициализирует dx (в котором будет хранится номер текущего входа в
|
||||
таблице, считая с 1), cx (размер оставшегося участка таблицы),
|
||||
bx (номер входа, соответствующего родительской папке для текущего
|
||||
рассматриваемого участка пути).
|
||||
3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы
|
||||
таблицы путей упорядочены (подробно о порядке написано в спецификации),
|
||||
так что если родительский элемент для очередного входа больше нужного,
|
||||
то нужного входа в таблице нет совсем, и в этом случае происходит
|
||||
выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент,
|
||||
соответствующий очередной папке в запрошенном пути, то на рассмотрение
|
||||
выносится следующая компонента пути. Если эта компонента последняя,
|
||||
то осталось найти файл в папке, и код переходит к пункту 4,
|
||||
установив eax = начальный блок этой папки. Если же нет, то эта
|
||||
компонента должна задавать имя папки, и код возвращается к пункту 3,
|
||||
скорректировав указатель на имя ds:si и номер родительского входа bx.
|
||||
4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax
|
||||
и указатель на имя файла относительно этой папки в ds:si. Если
|
||||
папку искали по таблице путей, то имя файла уже не содержит подпапок;
|
||||
если же нет, то подпапки вполне возможны.
|
||||
5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый
|
||||
из которых задаётся отдельным входом в папке. Информация обо всех
|
||||
таких кусках при просмотре папки запоминается в области, начинающейся
|
||||
с адреса 0000:2000. Переменная cur_desc_end содержит указатель на
|
||||
конец этой области, он же указатель, куда будет помещена информация
|
||||
при обнаружении следующего входа. (Папки, согласно спецификации,
|
||||
должны задаваться одним куском.)
|
||||
6. Код сначала ищет запрошенную папку в кэше папок.
|
||||
7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка,
|
||||
отсортированного по давности последнего обращения и код переходит к
|
||||
п.15. (Следующим действием станет добавление папки в конец списка.)
|
||||
8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать
|
||||
с диска. Сначала загружается первый сектор (физический сектор,
|
||||
содержащий первый логический блок). При ошибке ввода/вывода
|
||||
происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF.
|
||||
Первый элемент папки содержит информацию о самой этой папке, конкретно
|
||||
загрузчик интересуется её размером.
|
||||
9. Если размер папки слишком большой (больше или равен 64K либо больше половины
|
||||
общего размера кэша), то кэшироваться она не будет. В этом случае код
|
||||
считывает папку посекторно во временный буфер (0000:1000) и посекторно
|
||||
сканирует на наличие запрошенного имени, пока не найдёт такого имени
|
||||
или пока не кончатся данные. (Цикл начинается со сканирования,
|
||||
поскольку первая часть данных уже прочитана.) В конце код переходит
|
||||
к п.17.
|
||||
10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно
|
||||
обеспечить достаточное количество свободного места. Для этого может
|
||||
понадобиться выкинуть какое-то количество старых данных (цикл
|
||||
parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря,
|
||||
свободное пространство окажется разорванным на несколько фрагментов.
|
||||
Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает
|
||||
все следующие за ней данные назад по памяти и соответственно
|
||||
корректирует информацию о местонахождении данных в информации о кэше.
|
||||
При этом новое пространство всегда добавляется в конец доступной
|
||||
памяти. Цикл выкидывания продолжается, пока не освободится место,
|
||||
достаточное для хранения папки. Из-за ограничений на размер кэшируемых
|
||||
папок в конце концов место найдётся.
|
||||
11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы
|
||||
организуются в единый список свободных элементов; если он непуст,
|
||||
то очередной элемент берётся из этого списка; если же пуст, то
|
||||
берётся совсем новый элемент из области памяти, предназначенной для
|
||||
элементов кэша.
|
||||
12. В новом элементе заполняются поля начального блока, сегмента с данными,
|
||||
размера в байтах.
|
||||
13. Уже прочитанные данные первого физического сектора пересылаются на
|
||||
законное место в кэше.
|
||||
14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся
|
||||
данные с диска. При ошибке чтения, как и раньше, происходит выход из
|
||||
процедуры с bx=3, ax=dx=0xFFFF.
|
||||
15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов
|
||||
кэша.
|
||||
16. Загрузчик ищет запрошенное имя в загруженных данных папки.
|
||||
(Из-за ограничений на размер кэшируемой папки все данные располагаются
|
||||
в одном сегменте.)
|
||||
17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено
|
||||
никаких кусков файла, то cur_desc_end такой же, каким был вначале.
|
||||
В этом случае процедура рапортует о ненайденном файле и выходит.
|
||||
18. (filefound) Пропускает текущую компоненту имени. Если она была не последней
|
||||
(то есть подпапкой, в которой нужно производить дальнейший поиск),
|
||||
то код проверяет, что найденный вход - действительно подпапка,
|
||||
устанавливает новый стартовый блок и возвращается к п.4.
|
||||
Если же последней, то код проверяет, что найденный вход - регулярный
|
||||
файл и начинает загрузку файла.
|
||||
19. Нормализует указатель, по которому требуется прочитать файл. Под
|
||||
нормализацией понимается преобразование типа
|
||||
1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса,
|
||||
но гарантирует отсутствие переполнений: в приведённом примере попытка
|
||||
переслать 400h байт по rep movsb приведёт к тому, что последние 8
|
||||
байт запишутся не в нужное место, а на 64K раньше. Далее нормализация
|
||||
будет производиться после каждой пересылки. В cur_limit помещает
|
||||
предельный размер для чтения в байтах.
|
||||
20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты
|
||||
(пункты 21-27).
|
||||
21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое
|
||||
нужно пропустить с начала фрагмента.
|
||||
22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего
|
||||
шага, либо напрямую из callback-процедуры при запросе на продолжение
|
||||
чтения. Для этого и нужна вышеупомянутая переменная [cur_start] -
|
||||
при продолжении чтения, прервавшегося из-за конца буфера посередине
|
||||
фрагмента, там будет записано соответствующее значение.
|
||||
23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента
|
||||
и максимальной длины остатка. Если второе строго меньше, то
|
||||
запоминает, что файл слишком большой и прочитан только частично.
|
||||
Определяет новое значение числа прочитанных байт во фрагменте
|
||||
для возможных будущих вызовов [cur_start].
|
||||
24. Переводит пропускаемое число байт в число логических блоков и байт
|
||||
в первом блоке, последнее число записывает в переменную [first_byte],
|
||||
откуда её позднее достанет read_many_bytes.with_first.
|
||||
25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код
|
||||
определяет начальный блок фрагмента и вызывает вспомогательную функцию
|
||||
чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения)
|
||||
и выходит из цикла к п.28.
|
||||
26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала
|
||||
код пропускает нужное количество непрерывных частей, а потом
|
||||
в цикле загружает непрерывные части с помощью той же функции,
|
||||
в промежутках между частями увеличивая номер начального блока.
|
||||
Пока не кончится фрагмент или пока не наберётся запрошенное число байт.
|
||||
При ошибке чтения делает то же самое, что и в предыдущем случае.
|
||||
27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер
|
||||
ещё не достигнут, переходит к следующему фрагменту и п.20. В противном
|
||||
случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли
|
||||
переполнение в п.23.
|
||||
28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех
|
||||
фрагментов.
|
||||
Процедура загрузки файла (load_file):
|
||||
на входе:
|
||||
ds:di = указатель на информационную структуру, описанную в спецификации
|
||||
на загрузчик, а также в комментариях к коду
|
||||
на выходе:
|
||||
bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть,
|
||||
2=файл не найден, 3=ошибка чтения
|
||||
dx:ax = размер файла, 0xFFFFFFFF, если файл не найден
|
||||
1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице,
|
||||
иначе переходит сразу к шагу 4, установив eax = начальный блок
|
||||
корневой папки.
|
||||
2. Устанавливает es:di на начало таблицы путей. Ограничение на размер
|
||||
гарантирует, что вся таблица помещается в сегменте 6000h.
|
||||
Инициализирует dx (в котором будет хранится номер текущего входа в
|
||||
таблице, считая с 1), cx (размер оставшегося участка таблицы),
|
||||
bx (номер входа, соответствующего родительской папке для текущего
|
||||
рассматриваемого участка пути).
|
||||
3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы
|
||||
таблицы путей упорядочены (подробно о порядке написано в спецификации),
|
||||
так что если родительский элемент для очередного входа больше нужного,
|
||||
то нужного входа в таблице нет совсем, и в этом случае происходит
|
||||
выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент,
|
||||
соответствующий очередной папке в запрошенном пути, то на рассмотрение
|
||||
выносится следующая компонента пути. Если эта компонента последняя,
|
||||
то осталось найти файл в папке, и код переходит к пункту 4,
|
||||
установив eax = начальный блок этой папки. Если же нет, то эта
|
||||
компонента должна задавать имя папки, и код возвращается к пункту 3,
|
||||
скорректировав указатель на имя ds:si и номер родительского входа bx.
|
||||
4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax
|
||||
и указатель на имя файла относительно этой папки в ds:si. Если
|
||||
папку искали по таблице путей, то имя файла уже не содержит подпапок;
|
||||
если же нет, то подпапки вполне возможны.
|
||||
5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый
|
||||
из которых задаётся отдельным входом в папке. Информация обо всех
|
||||
таких кусках при просмотре папки запоминается в области, начинающейся
|
||||
с адреса 0000:2000. Переменная cur_desc_end содержит указатель на
|
||||
конец этой области, он же указатель, куда будет помещена информация
|
||||
при обнаружении следующего входа. (Папки, согласно спецификации,
|
||||
должны задаваться одним куском.)
|
||||
6. Код сначала ищет запрошенную папку в кэше папок.
|
||||
7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка,
|
||||
отсортированного по давности последнего обращения и код переходит к
|
||||
п.15. (Следующим действием станет добавление папки в конец списка.)
|
||||
8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать
|
||||
с диска. Сначала загружается первый сектор (физический сектор,
|
||||
содержащий первый логический блок). При ошибке ввода/вывода
|
||||
происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF.
|
||||
Первый элемент папки содержит информацию о самой этой папке, конкретно
|
||||
загрузчик интересуется её размером.
|
||||
9. Если размер папки слишком большой (больше или равен 64K либо больше половины
|
||||
общего размера кэша), то кэшироваться она не будет. В этом случае код
|
||||
считывает папку посекторно во временный буфер (0000:1000) и посекторно
|
||||
сканирует на наличие запрошенного имени, пока не найдёт такого имени
|
||||
или пока не кончатся данные. (Цикл начинается со сканирования,
|
||||
поскольку первая часть данных уже прочитана.) В конце код переходит
|
||||
к п.17.
|
||||
10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно
|
||||
обеспечить достаточное количество свободного места. Для этого может
|
||||
понадобиться выкинуть какое-то количество старых данных (цикл
|
||||
parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря,
|
||||
свободное пространство окажется разорванным на несколько фрагментов.
|
||||
Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает
|
||||
все следующие за ней данные назад по памяти и соответственно
|
||||
корректирует информацию о местонахождении данных в информации о кэше.
|
||||
При этом новое пространство всегда добавляется в конец доступной
|
||||
памяти. Цикл выкидывания продолжается, пока не освободится место,
|
||||
достаточное для хранения папки. Из-за ограничений на размер кэшируемых
|
||||
папок в конце концов место найдётся.
|
||||
11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы
|
||||
организуются в единый список свободных элементов; если он непуст,
|
||||
то очередной элемент берётся из этого списка; если же пуст, то
|
||||
берётся совсем новый элемент из области памяти, предназначенной для
|
||||
элементов кэша.
|
||||
12. В новом элементе заполняются поля начального блока, сегмента с данными,
|
||||
размера в байтах.
|
||||
13. Уже прочитанные данные первого физического сектора пересылаются на
|
||||
законное место в кэше.
|
||||
14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся
|
||||
данные с диска. При ошибке чтения, как и раньше, происходит выход из
|
||||
процедуры с bx=3, ax=dx=0xFFFF.
|
||||
15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов
|
||||
кэша.
|
||||
16. Загрузчик ищет запрошенное имя в загруженных данных папки.
|
||||
(Из-за ограничений на размер кэшируемой папки все данные располагаются
|
||||
в одном сегменте.)
|
||||
17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено
|
||||
никаких кусков файла, то cur_desc_end такой же, каким был вначале.
|
||||
В этом случае процедура рапортует о ненайденном файле и выходит.
|
||||
18. (filefound) Пропускает текущую компоненту имени. Если она была не последней
|
||||
(то есть подпапкой, в которой нужно производить дальнейший поиск),
|
||||
то код проверяет, что найденный вход - действительно подпапка,
|
||||
устанавливает новый стартовый блок и возвращается к п.4.
|
||||
Если же последней, то код проверяет, что найденный вход - регулярный
|
||||
файл и начинает загрузку файла.
|
||||
19. Нормализует указатель, по которому требуется прочитать файл. Под
|
||||
нормализацией понимается преобразование типа
|
||||
1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса,
|
||||
но гарантирует отсутствие переполнений: в приведённом примере попытка
|
||||
переслать 400h байт по rep movsb приведёт к тому, что последние 8
|
||||
байт запишутся не в нужное место, а на 64K раньше. Далее нормализация
|
||||
будет производиться после каждой пересылки. В cur_limit помещает
|
||||
предельный размер для чтения в байтах.
|
||||
20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты
|
||||
(пункты 21-27).
|
||||
21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое
|
||||
нужно пропустить с начала фрагмента.
|
||||
22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего
|
||||
шага, либо напрямую из callback-процедуры при запросе на продолжение
|
||||
чтения. Для этого и нужна вышеупомянутая переменная [cur_start] -
|
||||
при продолжении чтения, прервавшегося из-за конца буфера посередине
|
||||
фрагмента, там будет записано соответствующее значение.
|
||||
23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента
|
||||
и максимальной длины остатка. Если второе строго меньше, то
|
||||
запоминает, что файл слишком большой и прочитан только частично.
|
||||
Определяет новое значение числа прочитанных байт во фрагменте
|
||||
для возможных будущих вызовов [cur_start].
|
||||
24. Переводит пропускаемое число байт в число логических блоков и байт
|
||||
в первом блоке, последнее число записывает в переменную [first_byte],
|
||||
откуда её позднее достанет read_many_bytes.with_first.
|
||||
25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код
|
||||
определяет начальный блок фрагмента и вызывает вспомогательную функцию
|
||||
чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения)
|
||||
и выходит из цикла к п.28.
|
||||
26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала
|
||||
код пропускает нужное количество непрерывных частей, а потом
|
||||
в цикле загружает непрерывные части с помощью той же функции,
|
||||
в промежутках между частями увеличивая номер начального блока.
|
||||
Пока не кончится фрагмент или пока не наберётся запрошенное число байт.
|
||||
При ошибке чтения делает то же самое, что и в предыдущем случае.
|
||||
27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер
|
||||
ещё не достигнут, переходит к следующему фрагменту и п.20. В противном
|
||||
случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли
|
||||
переполнение в п.23.
|
||||
28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех
|
||||
фрагментов.
|
||||
|
||||
Процедура проверки, является ли текущая компонента имени файла последней
|
||||
Процедура проверки, является ли текущая компонента имени файла последней
|
||||
(is_last_component):
|
||||
на входе: ds:si = указатель на имя
|
||||
на выходе: флаг CF установлен, если есть последующие компоненты
|
||||
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
||||
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
||||
и выходит.
|
||||
на входе: ds:si = указатель на имя
|
||||
на выходе: флаг CF установлен, если есть последующие компоненты
|
||||
В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый,
|
||||
то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF
|
||||
и выходит.
|
||||
|
||||
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
||||
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
||||
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
||||
таблицы путей для test_filename1, папки для test_filename2
|
||||
на выходе: CF установлен, если имена не совпадают
|
||||
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
||||
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
||||
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
||||
возможно только в ситуации типа имени "filename.ext" и элемента
|
||||
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
||||
именами в папке отсортированы по убыванию версий);
|
||||
несовпадение символов - означает, что имена не совпадают;
|
||||
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
||||
файла, и в зависимости от этого принимать решение о совпадении.
|
||||
Процедуры проверки на совпадение текущей компоненты имени файла с именем
|
||||
текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки):
|
||||
на входе: ds:si = указатель на имя, es:di = указатель на элемент
|
||||
таблицы путей для test_filename1, папки для test_filename2
|
||||
на выходе: CF установлен, если имена не совпадают
|
||||
В цикле проверяет совпадение приведённых к верхнему регистру очередных символов
|
||||
имён файла и элемента. Условия выхода из цикла: закончилось имя файла
|
||||
в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение
|
||||
возможно только в ситуации типа имени "filename.ext" и элемента
|
||||
"filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми
|
||||
именами в папке отсортированы по убыванию версий);
|
||||
несовпадение символов - означает, что имена не совпадают;
|
||||
закончилось имя элемента - нужно проверить, закончилось ли при этом имя
|
||||
файла, и в зависимости от этого принимать решение о совпадении.
|
||||
|
||||
Процедура приведения символа в верхний регистр (toupper):
|
||||
на входе: ASCII-символ
|
||||
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
||||
нему неприменимо)
|
||||
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
||||
остальные символы не трогает.
|
||||
Процедура приведения символа в верхний регистр (toupper):
|
||||
на входе: ASCII-символ
|
||||
на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к
|
||||
нему неприменимо)
|
||||
Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A',
|
||||
остальные символы не трогает.
|
||||
|
||||
Процедура поиска файла в данных папки (scan_for_filename_in_sector):
|
||||
на входе:
|
||||
ds:si = указатель на имя файла
|
||||
es:bx = указатель на начало данных папки
|
||||
es:dx = указатель на конец данных папки
|
||||
на выходе:
|
||||
CF сброшен, если найден финальный фрагмент файла
|
||||
(и дальше сканировать папку не нужно)
|
||||
в область для информации о фрагментах файла записывается найденное
|
||||
В цикле просматривает все входы папки, пропуская те, у которых установлен
|
||||
бит Associated (это специальные входы, дополняющие основные). Если
|
||||
имя очередного входа совпадает с именем файла, то запоминает новый
|
||||
фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent),
|
||||
то код выходит с CF=0. Если достигнут конец данных, то код выходит
|
||||
с CF=1. Если очередной вход нулевой (первый байт настоящего входа
|
||||
содержит длину и не может быть нулём), то процедура переходит к
|
||||
рассмотрению следующего логического блока. При этом потенциально
|
||||
возможно переполнение при добавлении размера блока; поскольку такой
|
||||
сценарий означает, что процедура вызвана для кэшированной папки
|
||||
с размером почти 64K и началом данных bx=0 (это свойство вызывающего
|
||||
кода), а размер блока - степень двойки, то после переполнения всегда
|
||||
bx=0, так что это можно обнаружить по взведённому ZF после сложения;
|
||||
в этом случае также происходит выход (а после переполнения CF=1).
|
||||
Процедура поиска файла в данных папки (scan_for_filename_in_sector):
|
||||
на входе:
|
||||
ds:si = указатель на имя файла
|
||||
es:bx = указатель на начало данных папки
|
||||
es:dx = указатель на конец данных папки
|
||||
на выходе:
|
||||
CF сброшен, если найден финальный фрагмент файла
|
||||
(и дальше сканировать папку не нужно)
|
||||
в область для информации о фрагментах файла записывается найденное
|
||||
В цикле просматривает все входы папки, пропуская те, у которых установлен
|
||||
бит Associated (это специальные входы, дополняющие основные). Если
|
||||
имя очередного входа совпадает с именем файла, то запоминает новый
|
||||
фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent),
|
||||
то код выходит с CF=0. Если достигнут конец данных, то код выходит
|
||||
с CF=1. Если очередной вход нулевой (первый байт настоящего входа
|
||||
содержит длину и не может быть нулём), то процедура переходит к
|
||||
рассмотрению следующего логического блока. При этом потенциально
|
||||
возможно переполнение при добавлении размера блока; поскольку такой
|
||||
сценарий означает, что процедура вызвана для кэшированной папки
|
||||
с размером почти 64K и началом данных bx=0 (это свойство вызывающего
|
||||
кода), а размер блока - степень двойки, то после переполнения всегда
|
||||
bx=0, так что это можно обнаружить по взведённому ZF после сложения;
|
||||
в этом случае также происходит выход (а после переполнения CF=1).
|
||||
|
||||
Процедура перевода логического блока в номер сектора:
|
||||
на входе: eax = логический блок
|
||||
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
||||
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
||||
блоков в секторе, хранящееся во внутренней переменной).
|
||||
Процедура перевода логического блока в номер сектора:
|
||||
на входе: eax = логический блок
|
||||
на выходе: eax = физический сектор, dx = номер логического блока в секторе
|
||||
Осуществляет обычное деление 32-битного числа на 32-битное (число логических
|
||||
блоков в секторе, хранящееся во внутренней переменной).
|
||||
|
||||
Процедура загрузки физического сектора, содержащего указанный логический блок
|
||||
Процедура загрузки физического сектора, содержащего указанный логический блок
|
||||
(load_phys_sector_for_lb_force):
|
||||
на входе: eax = логический блок;
|
||||
si - индикатор, задающий, следует ли читать данные в случае,
|
||||
если логический блок начинается с начала физического:
|
||||
si = 0 - не нужно, si ненулевой - нужно
|
||||
на выходе:
|
||||
физический сектор загружен по адресу 0000:1000
|
||||
si указывает на данные логического блока
|
||||
CF установлен при ошибке чтения
|
||||
Преобразует предыдущей процедурой номер логического блока в номер физического
|
||||
сектора и номер логического блока внутри сектора; если последняя
|
||||
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
||||
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
||||
и читает сектор.
|
||||
на входе: eax = логический блок;
|
||||
si - индикатор, задающий, следует ли читать данные в случае,
|
||||
если логический блок начинается с начала физического:
|
||||
si = 0 - не нужно, si ненулевой - нужно
|
||||
на выходе:
|
||||
физический сектор загружен по адресу 0000:1000
|
||||
si указывает на данные логического блока
|
||||
CF установлен при ошибке чтения
|
||||
Преобразует предыдущей процедурой номер логического блока в номер физического
|
||||
сектора и номер логического блока внутри сектора; если последняя
|
||||
величина нулевая и никаких действий в этом случае не запрошено (si=0),
|
||||
то ничего и не делает; иначе устанавливает si в соответствии с ней
|
||||
и читает сектор.
|
||||
|
||||
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
||||
(read_many_bytes и read_many_bytes.with_first):
|
||||
на входе:
|
||||
eax = логический блок
|
||||
esi = число байт для чтения
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
cur_limit = размер буфера (не меньше esi)
|
||||
на выходе:
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
если произошла ошибка чтения, флаг CF установлен
|
||||
cur_limit соответствующим образом уменьшен
|
||||
Отличие двух процедур: вторая дополнительно принимает во внимание переменную
|
||||
[first_byte], начиная чтение первого блока со смещения [first_byte];
|
||||
соответственно, первая читает блок с начала, обнуляя [first_byte]
|
||||
при входе.
|
||||
1. Отдельно считывает первый физический сектор во временную область 0000:1000,
|
||||
если первый логический блок начинается не с начала сектора. При
|
||||
ошибке чтения выходит из процедуры.
|
||||
2. Пересылает нужную часть данных (возможно, 0 байт), прочитанных в п.1,
|
||||
в буфер. Нормализует указатель на буфер.
|
||||
3. Если все необходимые данные уже прочитаны, выходит из процедуры.
|
||||
4. Дальнейшие данные находятся в нескольких физических секторах, при этом,
|
||||
возможно, последний сектор считывать нужно не целиком.
|
||||
5. Если в буфере есть место для считывания всех секторов, то сразу читаются
|
||||
все сектора, после чего указатель на буфер нужным образом уменьшается.
|
||||
6. Если же нет, то считываются все сектора, кроме последнего, после чего
|
||||
последний сектор считывается отдельно во временную область, и уже
|
||||
оттуда нужная часть данных копируется в буфер.
|
||||
Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков
|
||||
(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. Если же нет, то считываются все сектора, кроме последнего, после чего
|
||||
последний сектор считывается отдельно во временную область, и уже
|
||||
оттуда нужная часть данных копируется в буфер.
|
||||
|
@ -24,337 +24,337 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Встречаются вирус и FAT.
|
||||
- Привет, ты кто?
|
||||
- Я? Вирус.
|
||||
- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
|
||||
Встречаются вирус и FAT.
|
||||
- Привет, ты кто?
|
||||
- Я? Вирус.
|
||||
- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
|
||||
|
||||
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны.
|
||||
2) Минимальный процессор - 80186.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны.
|
||||
2) Минимальный процессор - 80186.
|
||||
3) В системе должно быть как минимум 592K свободной базовой памяти.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Документация в тему (ссылки валидны на момент написания этого файла, 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
|
||||
Документация в тему (ссылки валидны на момент написания этого файла, 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
|
||||
|
||||
=====================================================================
|
||||
|
||||
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
|
||||
занимает 12 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x17EE = 6126 байт. Вся таблица помещается в памяти.
|
||||
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
|
||||
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
|
||||
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
|
||||
на практике нужна только небольшая её часть. Поэтому место в памяти
|
||||
резервируется, но данные считываются только в момент, когда к ним
|
||||
действительно идёт обращение.
|
||||
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
|
||||
занимает 12 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x17EE = 6126 байт. Вся таблица помещается в памяти.
|
||||
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
|
||||
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
|
||||
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
|
||||
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
|
||||
на практике нужна только небольшая её часть. Поэтому место в памяти
|
||||
резервируется, но данные считываются только в момент, когда к ним
|
||||
действительно идёт обращение.
|
||||
|
||||
Схема используемой памяти:
|
||||
...-7C00 стек
|
||||
7C00-7E00 код бутсектора
|
||||
7E00-8200 вспомогательный файл загрузчика (kordldr.f1x)
|
||||
8200-8300 список загруженных секторов таблицы FAT16
|
||||
(1 = соответствующий сектор загружен)
|
||||
60000-80000 загруженная таблица FAT12 / место для таблицы FAT16
|
||||
80000-90000 текущий кластер текущей рассматриваемой папки
|
||||
90000-92000 кэш для корневой папки
|
||||
92000-... кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
Схема используемой памяти:
|
||||
...-7C00 стек
|
||||
7C00-7E00 код бутсектора
|
||||
7E00-8200 вспомогательный файл загрузчика (kordldr.f1x)
|
||||
8200-8300 список загруженных секторов таблицы FAT16
|
||||
(1 = соответствующий сектор загружен)
|
||||
60000-80000 загруженная таблица FAT12 / место для таблицы FAT16
|
||||
80000-90000 текущий кластер текущей рассматриваемой папки
|
||||
90000-92000 кэш для корневой папки
|
||||
92000-... кэш для некорневых папок (каждой папке отводится
|
||||
2000h байт = 100h входов, одновременно в кэше
|
||||
может находиться не более 7 папок;
|
||||
точный размер определяется размером доступной
|
||||
физической памяти - как правило, непосредственно
|
||||
перед A0000 размещается EBDA, Extended BIOS Data Area)
|
||||
|
||||
=====================================================================
|
||||
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
||||
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
||||
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
||||
это освобождает ds и экономит на размере кода).
|
||||
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
||||
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
||||
сообщением об отсутствии LBA.
|
||||
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
||||
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
||||
предполагает уже существующие данные корректными.
|
||||
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
|
||||
и начальный сектор данных. Кладёт их в стек; впоследствии они
|
||||
всегда будут лежать в стеке и адресоваться через bp.
|
||||
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
|
||||
секторов - минимум из размера корневой папки, указанного в BPB, и 16
|
||||
(размер кэша для корневой папки - 2000h байт = 16 секторов).
|
||||
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
|
||||
он оказывается папкой, или если файл имеет нулевую длину -
|
||||
переходит на код обработки ошибок с сообщением о
|
||||
ненайденном загрузчике.
|
||||
Замечание: на этом этапе загрузки искать можно только в корневой
|
||||
папке и только имена, заданные в формате файловой системе FAT
|
||||
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
||||
быть заглавными, при необходимости имя и расширение дополняются
|
||||
пробелами, разделяющей точки нет, завершающего нуля нет).
|
||||
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
|
||||
ему управление. При этом в регистрах dx:ax оказывается абсолютный
|
||||
номер первого сектора kordldr.f1x, а в cx - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
Основной процесс загрузки.
|
||||
Точка входа (start): получает управление от BIOS при загрузке, при этом
|
||||
dl содержит идентификатор диска, с которого идёт загрузка
|
||||
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
|
||||
кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
|
||||
бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
|
||||
это освобождает ds и экономит на размере кода).
|
||||
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
|
||||
прерывания 13h. Если нет, переходит на код обработки ошибок с
|
||||
сообщением об отсутствии LBA.
|
||||
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
|
||||
записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
|
||||
предполагает уже существующие данные корректными.
|
||||
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
|
||||
и начальный сектор данных. Кладёт их в стек; впоследствии они
|
||||
всегда будут лежать в стеке и адресоваться через bp.
|
||||
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
|
||||
секторов - минимум из размера корневой папки, указанного в BPB, и 16
|
||||
(размер кэша для корневой папки - 2000h байт = 16 секторов).
|
||||
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
|
||||
он оказывается папкой, или если файл имеет нулевую длину -
|
||||
переходит на код обработки ошибок с сообщением о
|
||||
ненайденном загрузчике.
|
||||
Замечание: на этом этапе загрузки искать можно только в корневой
|
||||
папке и только имена, заданные в формате файловой системе FAT
|
||||
(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
|
||||
быть заглавными, при необходимости имя и расширение дополняются
|
||||
пробелами, разделяющей точки нет, завершающего нуля нет).
|
||||
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
|
||||
ему управление. При этом в регистрах dx:ax оказывается абсолютный
|
||||
номер первого сектора kordldr.f1x, а в cx - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения секторов (read_sectors и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения секторов (read_sectors и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
dx:ax = стартовый сектор (относительно начала логического диска
|
||||
для read_sectors, относительно начала данных для read_sectors2)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
||||
в номер относительно начала логического диска, прибавляя номер сектора
|
||||
начала данных, хранящийся в стеке как [bp-8].
|
||||
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.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
dx:ax = стартовый сектор (относительно начала логического диска
|
||||
для read_sectors, относительно начала данных для read_sectors2)
|
||||
cx = число секторов (должно быть больше нуля)
|
||||
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
|
||||
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
|
||||
в номер относительно начала логического диска, прибавляя номер сектора
|
||||
начала данных, хранящийся в стеке как [bp-8].
|
||||
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.
|
||||
|
||||
Процедура поиска элемента по имени в уже прочитанных данных папки
|
||||
Процедура поиска элемента по имени в уже прочитанных данных папки
|
||||
(scan_for_filename):
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
|
||||
3 на расширение, все буквы заглавные, если имя/расширение
|
||||
короче, оно дополняется до максимума пробелами)
|
||||
es = сегмент данных папки
|
||||
cx = число элементов в прочитанных данных
|
||||
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
|
||||
(ZF=1, если либо найден запрошенный элемент, либо достигнут
|
||||
конец папки); CF определяет, удалось ли найти элемент с искомым именем
|
||||
(CF=1, если не удалось); если удалось, то es:di указывает на него.
|
||||
scan_for_filename считает, что данные папки размещаются начиная с es:0.
|
||||
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
|
||||
проверяет имена.
|
||||
на входе должно быть установлено:
|
||||
ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
|
||||
3 на расширение, все буквы заглавные, если имя/расширение
|
||||
короче, оно дополняется до максимума пробелами)
|
||||
es = сегмент данных папки
|
||||
cx = число элементов в прочитанных данных
|
||||
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
|
||||
(ZF=1, если либо найден запрошенный элемент, либо достигнут
|
||||
конец папки); CF определяет, удалось ли найти элемент с искомым именем
|
||||
(CF=1, если не удалось); если удалось, то es:di указывает на него.
|
||||
scan_for_filename считает, что данные папки размещаются начиная с es:0.
|
||||
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
|
||||
проверяет имена.
|
||||
|
||||
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
|
||||
на входе должно быть установлено:
|
||||
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
|
||||
сканирует элементы; если по результатам сканирования обнаруживает,
|
||||
что нужно читать папку дальше, то считывает не более 0x10000 = 64K
|
||||
байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
|
||||
не вылезти за пределы используемой памяти, во-вторых, сканирование
|
||||
предполагает, что все обрабатываемые элементы располагаются в одном
|
||||
сегменте) и продолжает цикл.
|
||||
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
|
||||
кончились элементы в папке (судя по числу элементов, указанному в BPB);
|
||||
очередной элемент папки сигнализирует о конце (первый байт нулевой).
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
|
||||
сканирует элементы; если по результатам сканирования обнаруживает,
|
||||
что нужно читать папку дальше, то считывает не более 0x10000 = 64K
|
||||
байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
|
||||
не вылезти за пределы используемой памяти, во-вторых, сканирование
|
||||
предполагает, что все обрабатываемые элементы располагаются в одном
|
||||
сегменте) и продолжает цикл.
|
||||
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
|
||||
кончились элементы в папке (судя по числу элементов, указанному в BPB);
|
||||
очередной элемент папки сигнализирует о конце (первый байт нулевой).
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Работа вспомогательного загрузчика kordldr.f1x:
|
||||
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
||||
В зависимости от этого устанавливает смещения используемых процедур
|
||||
бутсектора. Критерий проверки: scan_for_filename должна начинаться
|
||||
с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
|
||||
с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
|
||||
именно такую форму).
|
||||
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
||||
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
||||
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
||||
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
||||
Замечание: этот размер не может превосходить 0A0000h байт и
|
||||
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
||||
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
||||
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
|
||||
спецификации от Microsoft (версия 1.03 спецификации датирована,
|
||||
к слову, 06 декабря 2000 года), разрядность FAT определяется
|
||||
исключительно числом кластеров: максимальное число кластеров на
|
||||
FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
|
||||
может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
|
||||
а число 0xFF7 не может быть корректным номером кластера.
|
||||
Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
|
||||
по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
|
||||
явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
|
||||
FAT12-том, в результате получается, что последний кластер
|
||||
(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
|
||||
[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
|
||||
[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
|
||||
лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
|
||||
в соответствии со спецификацией. Linux при определении FAT12/FAT16
|
||||
честно следует спецификации.
|
||||
Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
|
||||
Microsoft если и будет исправлять ошибки, то согласно собственному
|
||||
описанию.
|
||||
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
|
||||
Если размер, указанный в BPB, превосходит 12 секторов,
|
||||
это означает, что заявленный размер слишком большой (это не считается
|
||||
ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
|
||||
заведомо влезает в такой объём данных).
|
||||
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
|
||||
FAT не загружен (они будут подгружаться позднее, когда понадобятся
|
||||
и только те, которые понадобятся).
|
||||
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
||||
kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
|
||||
значения регистров на входе в kordldr.f1x.
|
||||
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
|
||||
найден, или оказался папкой, или оказался слишком большим, то переходит
|
||||
на код обработки ошибок из бутсектора с сообщением
|
||||
Работа вспомогательного загрузчика kordldr.f1x:
|
||||
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
|
||||
В зависимости от этого устанавливает смещения используемых процедур
|
||||
бутсектора. Критерий проверки: scan_for_filename должна начинаться
|
||||
с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
|
||||
с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
|
||||
именно такую форму).
|
||||
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
|
||||
адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
|
||||
ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
|
||||
место должно быть, отсюда ограничение в 592 Kb (94000h байт).
|
||||
Замечание: этот размер не может превосходить 0A0000h байт и
|
||||
на практике оказывается немного (на 1-2 килобайта) меньшим из-за
|
||||
наличия дополнительной области данных BIOS "вверху" базовой памяти.
|
||||
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
|
||||
спецификации от Microsoft (версия 1.03 спецификации датирована,
|
||||
к слову, 06 декабря 2000 года), разрядность FAT определяется
|
||||
исключительно числом кластеров: максимальное число кластеров на
|
||||
FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
|
||||
может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
|
||||
а число 0xFF7 не может быть корректным номером кластера.
|
||||
Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
|
||||
по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
|
||||
явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
|
||||
FAT12-том, в результате получается, что последний кластер
|
||||
(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
|
||||
[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
|
||||
[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
|
||||
лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
|
||||
в соответствии со спецификацией. Linux при определении FAT12/FAT16
|
||||
честно следует спецификации.
|
||||
Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
|
||||
Microsoft если и будет исправлять ошибки, то согласно собственному
|
||||
описанию.
|
||||
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
|
||||
Если размер, указанный в BPB, превосходит 12 секторов,
|
||||
это означает, что заявленный размер слишком большой (это не считается
|
||||
ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
|
||||
заведомо влезает в такой объём данных).
|
||||
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
|
||||
FAT не загружен (они будут подгружаться позднее, когда понадобятся
|
||||
и только те, которые понадобятся).
|
||||
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
|
||||
kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
|
||||
значения регистров на входе в kordldr.f1x.
|
||||
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 (номер диска).
|
||||
Устанавливает bx='12', если тип файловой системы - FAT12, и
|
||||
bx='16' в случае FAT16. Устанавливает si=смещение функции обратного
|
||||
вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
|
||||
9. Передаёт управление по адресу 1000:0000.
|
||||
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
||||
и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
|
||||
по-прежнему нет.
|
||||
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
|
||||
Это нужно, чтобы последующие обращения к коду бутсектора в случае
|
||||
ошибок чтения не выводил соответствующее сообщение с последующей
|
||||
перезагрузкой, а рапортовал об ошибке чтения, которую мог бы
|
||||
как-нибудь обработать вторичный загрузчик.
|
||||
8. Если загрузочный диск имеет идентификатор меньше 0x80,
|
||||
то устанавливает al='f' ("floppy"), ah=идентификатор диска,
|
||||
иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
|
||||
Устанавливает bx='12', если тип файловой системы - FAT12, и
|
||||
bx='16' в случае FAT16. Устанавливает si=смещение функции обратного
|
||||
вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
|
||||
9. Передаёт управление по адресу 1000:0000.
|
||||
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:7C00, а -8 берётся от того, что
|
||||
инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
|
||||
и они должны сохраняться в неизменности.
|
||||
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
|
||||
и вызывает нужную вспомогательную процедуру.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
|
||||
ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
|
||||
кодом должна указывать на 0:7C00, а -8 берётся от того, что
|
||||
инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
|
||||
и они должны сохраняться в неизменности.
|
||||
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
|
||||
и вызывает нужную вспомогательную процедуру.
|
||||
3. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
|
||||
Вспомогательные процедуры kordldr.f1x.
|
||||
Процедура получения следующего кластера в FAT (get_next_cluster):
|
||||
1. Вспоминает разрядность FAT, вычисленную ранее.
|
||||
Для FAT12:
|
||||
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
|
||||
вся таблица FAT.
|
||||
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
|
||||
слова, задающего следующий кластер. Загружает слово по этому адресу.
|
||||
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
|
||||
располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
|
||||
на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
|
||||
надо.
|
||||
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
|
||||
номера нормальных кластеров меньше, и флаг CF устанавливается;
|
||||
специальные значения EOF и BadClus сбрасывают флаг CF.
|
||||
Для FAT16:
|
||||
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
|
||||
в таблице FAT.
|
||||
3. Если сектор ещё не загружен, то загружает его.
|
||||
4. Вычисляет смещение данных для конкретного кластера относительно начала
|
||||
сектора.
|
||||
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
|
||||
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
|
||||
CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
|
||||
Вспомогательные процедуры kordldr.f1x.
|
||||
Процедура получения следующего кластера в FAT (get_next_cluster):
|
||||
1. Вспоминает разрядность FAT, вычисленную ранее.
|
||||
Для FAT12:
|
||||
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
|
||||
вся таблица FAT.
|
||||
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
|
||||
слова, задающего следующий кластер. Загружает слово по этому адресу.
|
||||
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
|
||||
располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
|
||||
на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
|
||||
надо.
|
||||
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
|
||||
номера нормальных кластеров меньше, и флаг CF устанавливается;
|
||||
специальные значения EOF и BadClus сбрасывают флаг CF.
|
||||
Для FAT16:
|
||||
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
|
||||
в таблице FAT.
|
||||
3. Если сектор ещё не загружен, то загружает его.
|
||||
4. Вычисляет смещение данных для конкретного кластера относительно начала
|
||||
сектора.
|
||||
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
|
||||
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
|
||||
CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
|
||||
|
||||
Процедура загрузки файла (load_file):
|
||||
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
||||
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
||||
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
||||
(больше 8 символов в имени, больше 3 символов в расширении или
|
||||
больше одной точки), возвращается с ошибкой.
|
||||
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
|
||||
папки используется процедура из бутсектора. Для остальных папок:
|
||||
a) Проверяет, есть ли такая папка в кэше некорневых папок.
|
||||
(Идентификация папок осуществляется по номеру начального кластера.)
|
||||
Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
|
||||
выкидывает папку, к которой дольше всего не было обращений. (Для
|
||||
каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
|
||||
определяющая его номер при сортировке по давности последнего обращения.
|
||||
При обращении к какому-то элементу его метка становится нулевой,
|
||||
а те метки, которые меньше старого значения, увеличиваются на единицу.)
|
||||
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
||||
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
||||
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
||||
процедуры с ошибкой.
|
||||
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
||||
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
||||
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
||||
и просматривает в нём все элементы. Работает, пока не случится одно из
|
||||
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
||||
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
||||
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
||||
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
||||
запрошенном имени должен быть файлом, все промежуточные - папками.
|
||||
Если текущий компонент имени - промежуточный, продвигает текущую
|
||||
рассматриваемую папку и возвращается к пункту 2.
|
||||
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
Процедура загрузки файла (load_file):
|
||||
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
|
||||
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
|
||||
разделяются символом '/') в FAT-формат 8+3. Если это невозможно
|
||||
(больше 8 символов в имени, больше 3 символов в расширении или
|
||||
больше одной точки), возвращается с ошибкой.
|
||||
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
|
||||
папки используется процедура из бутсектора. Для остальных папок:
|
||||
a) Проверяет, есть ли такая папка в кэше некорневых папок.
|
||||
(Идентификация папок осуществляется по номеру начального кластера.)
|
||||
Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
|
||||
выкидывает папку, к которой дольше всего не было обращений. (Для
|
||||
каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
|
||||
определяющая его номер при сортировке по давности последнего обращения.
|
||||
При обращении к какому-то элементу его метка становится нулевой,
|
||||
а те метки, которые меньше старого значения, увеличиваются на единицу.)
|
||||
б) Просматривает в поисках запрошенного имени все элементы из кэша,
|
||||
используя процедуру из бутсектора. Если обнаруживает искомый элемент,
|
||||
переходит к шагу 4. Если обнаруживает конец папки, возвращается из
|
||||
процедуры с ошибкой.
|
||||
в) В цикле считывает папку посекторно. При этом пропускает начальные
|
||||
секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
|
||||
прочитанный сектор копирует в кэш, если там ещё остаётся место,
|
||||
и просматривает в нём все элементы. Работает, пока не случится одно из
|
||||
трёх событий: найден искомый элемент; кончились кластеры (судя по
|
||||
цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
|
||||
(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
|
||||
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
|
||||
запрошенном имени должен быть файлом, все промежуточные - папками.
|
||||
Если текущий компонент имени - промежуточный, продвигает текущую
|
||||
рассматриваемую папку и возвращается к пункту 2.
|
||||
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
|
@ -24,310 +24,310 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
Читай между строк - там никогда не бывает опечаток.
|
||||
Читай между строк - там никогда не бывает опечаток.
|
||||
|
||||
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
|
||||
|
||||
=====================================================================
|
||||
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
|
||||
выбор осуществляется установкой константы use_lba в первой строке исходника.
|
||||
Требования для работы:
|
||||
1) Сам бутсектор, первая копия FAT и все используемые файлы
|
||||
должны быть читабельны. (Если дело происходит на носителе с разбиением на
|
||||
разделы и загрузочный код в MBR достаточно умный, то читабельности резервной
|
||||
копии бутсектора (сектор номер 6 на томе) достаточно вместо читабельности
|
||||
самого бутсектора).
|
||||
2) Минимальный процессор - 80386.
|
||||
3) В системе должно быть как минимум 584K свободной базовой памяти.
|
||||
Есть две версии в зависимости от того, поддерживает ли носитель 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
|
||||
Документация в тему (ссылки проверялись на валидность 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)
|
||||
Схема используемой памяти:
|
||||
...-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, сохраняет и его в стек для последующего обращения через
|
||||
Основной процесс загрузки.
|
||||
Точка входа (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 - число считанных секторов
|
||||
(равное размеру кластера).
|
||||
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. Для подстраховки зацикливается.
|
||||
Вспомогательные процедуры бутсектора.
|
||||
Код обработки ошибок (err):
|
||||
1. Выводит строку с сообщением об ошибке.
|
||||
2. Выводит строку "Press any key...".
|
||||
3. Ждёт нажатия any key.
|
||||
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
|
||||
5. Для подстраховки зацикливается.
|
||||
|
||||
Процедура чтения кластера (read_cluster):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения кластера (read_cluster):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = номер кластера
|
||||
на выходе: ecx = число прочитанных секторов (размер кластера),
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
eax и старшие слова других 32-битных регистров разрушаются
|
||||
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
|
||||
и переходит к следующей процедуре.
|
||||
es:bx = указатель на начало буфера, куда будут прочитаны данные
|
||||
eax = номер кластера
|
||||
на выходе: ecx = число прочитанных секторов (размер кластера),
|
||||
es:bx указывает на конец буфера, в который были прочитаны данные,
|
||||
eax и старшие слова других 32-битных регистров разрушаются
|
||||
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
|
||||
и переходит к следующей процедуре.
|
||||
|
||||
Процедура чтения секторов (read_sectors32 и read_sectors2):
|
||||
на входе должно быть установлено:
|
||||
Процедура чтения секторов (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.
|
||||
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):
|
||||
на входе должно быть установлено:
|
||||
Процедура поиска элемента в папке (lookup_in_dir):
|
||||
на входе должно быть установлено:
|
||||
ss:bp = 0:7C00
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
eax = начальный кластер папки
|
||||
ds:si = указатель на имя файла в формате FAT (см. выше)
|
||||
eax = начальный кластер папки
|
||||
bx = 0
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
|
||||
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
|
||||
для продвижения по цепочке кластеров - описанную далее процедуру
|
||||
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
|
||||
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
|
||||
если чтение прервётся раньше) не перекрываются последующими чтениями
|
||||
(это будет использовано позднее, в системе кэширования из kordldr.f32).
|
||||
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
|
||||
кончились элементы в папке (первый байт очередного элемента нулевой);
|
||||
кончились данные папки в соответствии с цепочкой кластеров из FAT.
|
||||
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
|
||||
CF сброшен и es:di указывает на элемент папки
|
||||
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
|
||||
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
|
||||
для продвижения по цепочке кластеров - описанную далее процедуру
|
||||
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
|
||||
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
|
||||
если чтение прервётся раньше) не перекрываются последующими чтениями
|
||||
(это будет использовано позднее, в системе кэширования из kordldr.f32).
|
||||
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
|
||||
кончились элементы в папке (первый байт очередного элемента нулевой);
|
||||
кончились данные папки в соответствии с цепочкой кластеров из FAT.
|
||||
|
||||
Процедура вывода на экран ASCIIZ-строки (out_string):
|
||||
на входе: ds:si -> строка
|
||||
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
|
||||
Процедура вывода на экран 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. Если файл не
|
||||
найден, или оказался папкой, или оказался слишком большим, то переходит
|
||||
на код обработки ошибок из бутсектора с сообщением
|
||||
Работа вспомогательного загрузчика 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.
|
||||
Замечание: на этом этапе имя файла уже можно указывать вместе с путём
|
||||
и в формате 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. Восстанавливает стек вызывающего кода и возвращает управление.
|
||||
Функция обратного вызова для вторичного загрузчика:
|
||||
предоставляет возможность чтения файла.
|
||||
Вход и выход описаны в спецификации на загрузчик.
|
||||
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, то оно задаёт номер следующего кластера в цепочке;
|
||||
в противном случае цепочка закончилась.
|
||||
Вспомогательные процедуры 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 и считывает все кластеры в указанный
|
||||
при вызове буфер последовательными вызовами функции бутсектора;
|
||||
при этом если несколько кластеров файла расположены на диске
|
||||
последовательно, то их чтение объединяется в одну операцию.
|
||||
Следит за тем, чтобы не превысить указанный при вызове процедуры
|
||||
лимит числа секторов для чтения.
|
||||
Процедура загрузки файла (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.
|
||||
Процедура продолжения загрузки файла (continue_load_file): встроена
|
||||
внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
|
||||
сохранённые из load_file) и продолжает шаг 5.
|
||||
|
@ -24,7 +24,7 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
;Тут определены все сообщения, которые нужны в процессе отладки, и совсем не нужны в рабочей копии программы.
|
||||
;Тут определены все сообщения, которые нужны в процессе отладки, и совсем не нужны в рабочей копии программы.
|
||||
If DEBUG
|
||||
cseg_msg db ' - Adress of code segment',0
|
||||
stack_msg db 'Set stack & segments is have completed',0
|
||||
|
@ -38,7 +38,7 @@
|
||||
use16
|
||||
org 0x0
|
||||
jmp start
|
||||
include 'sl_equ.inc' ; в файле размещены все equ предопределения
|
||||
include 'sl_equ.inc' ; в файле размещены все equ предопределения
|
||||
include 'boot_st.inc'
|
||||
include 'debug_msg.inc' ;here is message from debug
|
||||
include 'parse_dat.inc'
|
||||
@ -49,8 +49,8 @@ include 'parse_any.inc'
|
||||
include 'parse_def_sect.inc'
|
||||
include 'parse_err.inc'
|
||||
|
||||
file_data dw 0x0,ini_data_ ;формат: смещение: сегмент т.к. используется les
|
||||
size_data dw 16 ;16 блоков по 4 кб т.е предел до 64 кб
|
||||
file_data dw 0x0,ini_data_ ;формат: смещение: сегмент т.к. используется les
|
||||
size_data dw 16 ;16 блоков по 4 кб т.е предел до 64 кб
|
||||
name_ini_f db 'kord/startos.ini',0
|
||||
|
||||
;////////////
|
||||
@ -86,7 +86,7 @@ start:
|
||||
call printplain
|
||||
mov al, '#'
|
||||
mov cx, 80
|
||||
;input cx=size al=char будет вывден символ сколько раз указано в cx
|
||||
;input cx=size al=char будет вывден символ сколько раз указано в cx
|
||||
@@:
|
||||
call putchar
|
||||
loop @b
|
||||
@ -94,7 +94,7 @@ start:
|
||||
if DEBUG
|
||||
pushad
|
||||
mov ax, cs
|
||||
shl eax, 4 ; в десятичной системе адрес сегмента
|
||||
shl eax, 4 ; в десятичной системе адрес сегмента
|
||||
mov cx, 0xa
|
||||
mov di, cseg_msg
|
||||
call decode
|
||||
@ -162,7 +162,7 @@ cpugood:
|
||||
|
||||
|
||||
; Load startos.ini
|
||||
mov cx, loop_read_startos_file ;кол-во попыток чтения файла конфигурации startos.ini
|
||||
mov cx, loop_read_startos_file ;кол-во попыток чтения файла конфигурации startos.ini
|
||||
align 4
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Load startos.ini ;
|
||||
@ -254,32 +254,32 @@ table_15_87:
|
||||
db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0
|
||||
|
||||
fat12_buffer:
|
||||
.BS_jmpBoot db 0x90,0x90,0x90 ;3 байта NOP инструкция - ничего не делать
|
||||
.BS_OEMName db 'K SyS 64' ;8 байт
|
||||
.BPB_BytsPerSec dw 512 ;кол-во байтов в секторе может быть любое 512 1024 2048 4096 2 байта
|
||||
.BPB_SecPerClus db 0x1 ;кол-во секторов в кластере
|
||||
.BPB_RsvdSecCnt dw 0x1 ;для FAt12/16 только 1, для FAT32 обычно 32
|
||||
.BPB_NumFATs db 0x1 ;кол-во фат таблиц, на тот случай если будет сброс на дискету образа рам диска
|
||||
.BPB_RootEntCnt dw 512 ;для мак совместимости с fat16
|
||||
.BPB_TotSec16 dw 0x0 ;кл-во секторов
|
||||
.BS_jmpBoot db 0x90,0x90,0x90 ;3 байта NOP инструкция - ничего не делать
|
||||
.BS_OEMName db 'K SyS 64' ;8 байт
|
||||
.BPB_BytsPerSec dw 512 ;кол-во байтов в секторе может быть любое 512 1024 2048 4096 2 байта
|
||||
.BPB_SecPerClus db 0x1 ;кол-во секторов в кластере
|
||||
.BPB_RsvdSecCnt dw 0x1 ;для FAt12/16 только 1, для FAT32 обычно 32
|
||||
.BPB_NumFATs db 0x1 ;кол-во фат таблиц, на тот случай если будет сброс на дискету образа рам диска
|
||||
.BPB_RootEntCnt dw 512 ;для мак совместимости с fat16
|
||||
.BPB_TotSec16 dw 0x0 ;кл-во секторов
|
||||
.BPB_Media db 0xF0
|
||||
.BPB_FATSz16 dw 0x0
|
||||
.BPB_SecPerTrk dw 0x0 ;содержит геометрию диска для RAMFS на как бы без разницы, пока пустое поле, позже внести реальные значения.
|
||||
.BPB_SecPerTrk dw 0x0 ;содержит геометрию диска для RAMFS на как бы без разницы, пока пустое поле, позже внести реальные значения.
|
||||
.BPB_NumHeads dw 0x0
|
||||
.BPB_HiddSec dd 0x0 ;кол-во скрытых секторов
|
||||
.BPB_HiddSec dd 0x0 ;кол-во скрытых секторов
|
||||
.BPB_TotSec32 dd 0x0
|
||||
.BS_DrvNum db 'R' ;от слова RAM
|
||||
.BS_DrvNum db 'R' ;от слова RAM
|
||||
.BS_Reserved1 db 0x0
|
||||
.BS_BootSig db 0x29
|
||||
.BS_VolID db 'RFKS'
|
||||
.BS_VolLab db 'RAM DISK FS' ;11 символов
|
||||
.BS_FilSysType db 'FAT12 ' ;8 символов
|
||||
;62 байта структура fat12.
|
||||
.BS_VolLab db 'RAM DISK FS' ;11 символов
|
||||
.BS_FilSysType db 'FAT12 ' ;8 символов
|
||||
;62 байта структура fat12.
|
||||
db (512-($-fat12_buffer))dup(0x90)
|
||||
|
||||
|
||||
|
||||
;структура для дирректории fat
|
||||
;структура для дирректории fat
|
||||
struc FAT_32_entry ;Byte Directory Entry Structure
|
||||
{
|
||||
.DIR_Name rb 11
|
||||
@ -297,21 +297,21 @@ struc FAT_32_entry ;Byte Directory Entry Structure
|
||||
|
||||
|
||||
}
|
||||
;Тут будут распологатсья данные, которые затруднительно распологать в стековой области....
|
||||
;Тут будут распологатсья данные, которые затруднительно распологать в стековой области....
|
||||
;;;
|
||||
;timer
|
||||
shot_name_fat rb 11 ;временный буфер для fat12, в нем храняться имена файлов приведенные к правилам FAT /* вдальнейшем перенести в стэк
|
||||
shot_name_fat rb 11 ;временный буфер для fat12, в нем храняться имена файлов приведенные к правилам FAT /* вдальнейшем перенести в стэк
|
||||
|
||||
if DEBUG
|
||||
rb 1 ;нужен для отладки и вывода имени файла после преобразования
|
||||
rb 1 ;нужен для отладки и вывода имени файла после преобразования
|
||||
dest_name_fat db 24 dup('_');12
|
||||
db 0x0
|
||||
end if
|
||||
|
||||
value_timeout rw 1 ;value to timeout
|
||||
old_timer rd 1 ;старое значение вектора таймера
|
||||
start_timer rd 1 ;значение таймера
|
||||
timer_ rd 1 ;новое значение вектора таймера т.е. SL
|
||||
old_timer rd 1 ;старое значение вектора таймера
|
||||
start_timer rd 1 ;значение таймера
|
||||
timer_ rd 1 ;новое значение вектора таймера т.е. SL
|
||||
start_stack rw 1 ;save stack
|
||||
save_bp_from_timer rw 1 ;save bp from timer
|
||||
|
||||
|
@ -905,7 +905,7 @@ flat assembler version 1.68 (65535 kilobytes memory)
|
||||
0E58: E2 FC loop @b
|
||||
0E5A: BF E0 01 mov di, 480
|
||||
0E5D: B4 0E mov ah, color_sym_yellow
|
||||
0E5F: B0 C4 mov al, 'Ä'
|
||||
0E5F: B0 C4 mov al, 'Д'
|
||||
0E61: B9 3D 00 mov cx, 61
|
||||
0E64: F3 rep
|
||||
0E65: AB stosw
|
||||
|
@ -24,31 +24,31 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
; Модуль парсинга - это стандартный компонент, встраиваемый во вторичный загрузчик.
|
||||
; Данный модуль позволяет стандартно произвести разбор ini файла
|
||||
; (и с использованием полученных данных ОС будет загружаться дальше).
|
||||
; В начале найдем открывающий "[" - это будет указывать на начало
|
||||
; секции. Поддерживается 1 секция это [loader], остальные секции могут иметь
|
||||
; любые имена, но они должны быть заключены в в скобки []
|
||||
; Модуль парсинга - это стандартный компонент, встраиваемый во вторичный загрузчик.
|
||||
; Данный модуль позволяет стандартно произвести разбор ini файла
|
||||
; (и с использованием полученных данных ОС будет загружаться дальше).
|
||||
; В начале найдем открывающий "[" - это будет указывать на начало
|
||||
; секции. Поддерживается 1 секция это [loader], остальные секции могут иметь
|
||||
; любые имена, но они должны быть заключены в в скобки []
|
||||
macro use_parse
|
||||
{
|
||||
;input cx=size of ini file
|
||||
parse_start:
|
||||
;es:di as 2000:0000 new segment
|
||||
;установим указатель на загруженный блок
|
||||
;установим указатель на загруженный блок
|
||||
enter 256, 0 ;set 16 byte for current task in stack
|
||||
;we are is not use bp because bp is pointer on array 16 byte
|
||||
mov word [save_bp_from_timer], bp ;save point to own data array
|
||||
mov save_cx, cx ;it's placed size of ini file
|
||||
les di, dword [file_data]
|
||||
;обнулим все переменные выделенные из стека
|
||||
;обнулим все переменные выделенные из стека
|
||||
;init flag
|
||||
xor ax, ax
|
||||
mov status_flag, ax
|
||||
;set data size
|
||||
mov info_real_mode_size, ini_data_ +0x1000 ;изменим значение занятости памяти
|
||||
mov info_real_mode_size, ini_data_ +0x1000 ;изменим значение занятости памяти
|
||||
|
||||
;поиск начала блока.
|
||||
;поиск начала блока.
|
||||
;///////////check [loader]
|
||||
cld
|
||||
|
||||
@ -63,23 +63,23 @@ parse_start:
|
||||
|
||||
.start:
|
||||
call get_firs_sym ;get first symbol on new line
|
||||
.first_ret: ;первый возврат
|
||||
.first_ret: ;первый возврат
|
||||
; jcxz .end_file ;.end_loader ;found or not found parametrs in section exit in section
|
||||
test cx, cx
|
||||
jz error.not_loader
|
||||
cmp al, '['
|
||||
jz .parse_loader
|
||||
jmp .start
|
||||
;////// проверка на наличее секции loader
|
||||
;////// проверка на наличее секции loader
|
||||
use_parse_loader
|
||||
;pause
|
||||
if DEBUG
|
||||
xor ax, ax
|
||||
int 16h
|
||||
end if
|
||||
;////// вывод графического экрана, выбор, секции под дефолту
|
||||
;////// вывод графического экрана, выбор, секции под дефолту
|
||||
use_any_sec
|
||||
;парсинг выбраной или дефолтной секции т.е. разбор параметров выполнение сценария
|
||||
;парсинг выбраной или дефолтной секции т.е. разбор параметров выполнение сценария
|
||||
use_parse_def_sect
|
||||
|
||||
;//////////////////
|
||||
|
@ -24,7 +24,7 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
;тут распологается модуль с помощью которого будут парситься все остальные секции
|
||||
;тут распологается модуль с помощью которого будут парситься все остальные секции
|
||||
color_sym_black equ 0
|
||||
color_sym_blue equ 1
|
||||
color_sym_green equ 2
|
||||
@ -40,12 +40,12 @@ color_sym_yellow equ 14
|
||||
|
||||
macro use_any_sec
|
||||
{
|
||||
;узнаем работу предыдущего шага т.е. чему = timeout, если он 0, то визуальная часть не будет отображена на дисплее с выбором загрузочных секций.
|
||||
;иначе мы ее должны отобразить и ждать заявленое время для выбора и конигурирования пукнктов секции от пользователя.
|
||||
;узнаем работу предыдущего шага т.е. чему = timeout, если он 0, то визуальная часть не будет отображена на дисплее с выбором загрузочных секций.
|
||||
;иначе мы ее должны отобразить и ждать заявленое время для выбора и конигурирования пукнктов секции от пользователя.
|
||||
|
||||
if DEBUG
|
||||
pusha
|
||||
mov ax, word [value_timeout];идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти
|
||||
mov ax, word [value_timeout];идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти
|
||||
; mov ax,cx
|
||||
mov cx, 0x0a
|
||||
mov di, show_db1
|
||||
@ -62,7 +62,7 @@ end if
|
||||
test ax, ax
|
||||
jz .parse_run_only
|
||||
|
||||
;отобразим полный список всех найденых секций.
|
||||
;отобразим полный список всех найденых секций.
|
||||
if DEBUG
|
||||
pusha
|
||||
mov si, show_all_sect
|
||||
@ -70,7 +70,7 @@ if DEBUG
|
||||
popa
|
||||
end if
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
|
||||
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
|
||||
out 0x60, al
|
||||
xor cx, cx
|
||||
.wait_loop: ; variant 2
|
||||
@ -102,7 +102,7 @@ end if
|
||||
mov dword [start_timer], eax
|
||||
mov word [timer_], newtimer
|
||||
mov word [timer_+2], cs
|
||||
;установить свое прерывание на таймер т.е. код будет перрываться ~18 раз в сек и переходить на обработчик
|
||||
;установить свое прерывание на таймер т.е. код будет перрываться ~18 раз в сек и переходить на обработчик
|
||||
cli
|
||||
push 0
|
||||
pop es
|
||||
@ -112,7 +112,7 @@ end if
|
||||
pop dword [es:8*4]
|
||||
sti
|
||||
|
||||
;процедура формирования буфера для скролинга секций
|
||||
;процедура формирования буфера для скролинга секций
|
||||
;if DEBUG
|
||||
; pusha
|
||||
; mov ax,point_default
|
||||
@ -130,20 +130,20 @@ end if
|
||||
; int 0x16
|
||||
; popa
|
||||
;end if
|
||||
;;;;;;;;;;;;;размер предыдущей сеции установим =0
|
||||
;;;;;;;;;;;;;размер предыдущей сеции установим =0
|
||||
mov save_descript_size, 18
|
||||
;отобразить black screen
|
||||
;отобразить black screen
|
||||
show_bl_sc ;es=0xb800
|
||||
.show_all_scr:
|
||||
get_frame_buffer ;es=0x2000
|
||||
;отображение секций
|
||||
;отображение секций
|
||||
call show_bl_sc_sect ;es=0xb800
|
||||
;отобразить активный курсор
|
||||
;отобразить активный курсор
|
||||
.show_active_cursor:
|
||||
show_act_cursor
|
||||
show_descript ;макрос по отображению описания секции
|
||||
show_descript ;макрос по отображению описания секции
|
||||
|
||||
;отобразить Press any key ....
|
||||
;отобразить Press any key ....
|
||||
mov eax, dword [old_timer]
|
||||
cmp eax, dword [timer_]
|
||||
jz .interrupt_16
|
||||
@ -151,7 +151,7 @@ show_descript ;
|
||||
show_timer_message
|
||||
mov word [start_stack], sp
|
||||
.interrupt_16:
|
||||
xor ax, ax ;получим информацию о том что нажато
|
||||
xor ax, ax ;получим информацию о том что нажато
|
||||
int 0x16
|
||||
;check on change
|
||||
mov ebx, dword [old_timer]
|
||||
@ -161,7 +161,7 @@ show_timer_message
|
||||
cli
|
||||
push 0
|
||||
pop es
|
||||
; mov eax,dword [old_timer] ; восстановим прежднее прерывание
|
||||
; mov eax,dword [old_timer] ; восстановим прежднее прерывание
|
||||
mov [es:8*4], ebx
|
||||
mov dword [timer_], ebx
|
||||
sti
|
||||
@ -172,7 +172,7 @@ clear_timer_msg
|
||||
@@:
|
||||
call clean_active_cursor ;clean old cursor ;es=0xb800
|
||||
|
||||
cmp ah, 0x48 ;реакция системы на события
|
||||
cmp ah, 0x48 ;реакция системы на события
|
||||
jz .up
|
||||
cmp ah, 0x50
|
||||
jz .down
|
||||
@ -188,9 +188,9 @@ clear_timer_msg
|
||||
cmp al, 0xD
|
||||
jnz .show_active_cursor
|
||||
|
||||
jmp .end_show_all ;парсинг секции которая указана в point_default
|
||||
jmp .end_show_all ;парсинг секции которая указана в point_default
|
||||
.up:
|
||||
mov si, point_to_point_def ;значение указателя
|
||||
mov si, point_to_point_def ;значение указателя
|
||||
add si, 2
|
||||
lea ax, point_to_hframe
|
||||
|
||||
@ -208,8 +208,8 @@ clear_timer_msg
|
||||
|
||||
|
||||
.down:
|
||||
mov si, point_to_point_def ;значение указателя
|
||||
mov ax, point_to_eframe ;указатель на последний элемент
|
||||
mov si, point_to_point_def ;значение указателя
|
||||
mov ax, point_to_eframe ;указатель на последний элемент
|
||||
sub si, 2
|
||||
cmp si, ax
|
||||
jb @f
|
||||
@ -255,7 +255,7 @@ clear_timer_msg
|
||||
|
||||
|
||||
|
||||
; тут мы будем парсить только дефолтную секцию и выполнять ее ничего не предлагая пользователю из диалогов.
|
||||
; тут мы будем парсить только дефолтную секцию и выполнять ее ничего не предлагая пользователю из диалогов.
|
||||
.parse_run_only:
|
||||
if DEBUG
|
||||
pusha
|
||||
@ -286,7 +286,7 @@ end if
|
||||
macro show_bl_sc
|
||||
{
|
||||
;;;;;;;;;;;;;;;
|
||||
;очистим экран и выведем меню
|
||||
;очистим экран и выведем меню
|
||||
; draw frames
|
||||
xor ax, ax
|
||||
if DEBUG
|
||||
@ -324,7 +324,7 @@ end if
|
||||
;;;;;;;;;;;;;;;;;;;;;;; show '__________________________'
|
||||
mov di, 480
|
||||
mov ah, color_sym_yellow
|
||||
mov al, 'Д'
|
||||
mov al, 'Д'
|
||||
mov cx, 61
|
||||
rep stosw
|
||||
;;;;;;;;;;;;;;;;;;;;;;; show 'Select section'
|
||||
@ -418,7 +418,7 @@ macro get_frame_buffer
|
||||
mov si, di ;point frame
|
||||
mov bx, cx
|
||||
mov dx, size_show_section
|
||||
; mov point_to_hframe,di ; внесем значение, так подстраховка не более
|
||||
; mov point_to_hframe,di ; внесем значение, так подстраховка не более
|
||||
|
||||
mov al, byte [es:di]
|
||||
push word .first_ret_bl_sc
|
||||
@ -443,14 +443,14 @@ macro get_frame_buffer
|
||||
|
||||
.start_bl:
|
||||
call get_firs_sym ;get first symbol on new line
|
||||
.first_ret_bl_sc: ;первый возврат
|
||||
.first_ret_bl_sc: ;первый возврат
|
||||
test cx, cx
|
||||
jz error.correct_exit_bl ;critical error not found default point it's not possible because it's param chacking before
|
||||
.analisist_al:
|
||||
cmp al, '['
|
||||
jnz .start_bl
|
||||
;просматриваем ini файл с начала в поисках секции указаной как default
|
||||
;поиск фрейма в котором содержиться значение default
|
||||
;просматриваем ini файл с начала в поисках секции указаной как default
|
||||
;поиск фрейма в котором содержиться значение default
|
||||
.found_sect_bl:
|
||||
cmp di, point_loader
|
||||
jz .start_bl
|
||||
@ -464,12 +464,12 @@ macro get_frame_buffer
|
||||
|
||||
|
||||
.save_point_def:
|
||||
;итак далее мы должны заполнить frame буфер адресов секций, что бы потом по нему быстро перемещаться не вычисляя снова адреса
|
||||
mov di, si ;указатель на начало
|
||||
;итак далее мы должны заполнить frame буфер адресов секций, что бы потом по нему быстро перемещаться не вычисляя снова адреса
|
||||
mov di, si ;указатель на начало
|
||||
mov cx, bx
|
||||
lea si, point_to_hframe
|
||||
mov dx, size_show_section+1 ;т.к. у нас структура содержит размер между первым и вторым указателем, то нам нужно на 1 адрес больше обсчитать секций.
|
||||
;переходим на обработку значения указателя
|
||||
mov dx, size_show_section+1 ;т.к. у нас структура содержит размер между первым и вторым указателем, то нам нужно на 1 адрес больше обсчитать секций.
|
||||
;переходим на обработку значения указателя
|
||||
mov al, byte [es:di]
|
||||
push word .first_ret_mfb
|
||||
cmp al, ' '
|
||||
@ -480,7 +480,7 @@ macro get_frame_buffer
|
||||
|
||||
.start_mfb:
|
||||
call get_firs_sym ;get first symbol on new line
|
||||
.first_ret_mfb: ;первый возврат
|
||||
.first_ret_mfb: ;первый возврат
|
||||
jcxz .val_buff_comp ;.end_loader ;found or not found parametrs in section exit in section
|
||||
cmp al, '['
|
||||
jnz .start_mfb
|
||||
@ -509,7 +509,7 @@ macro get_frame_buffer
|
||||
|
||||
macro show_act_cursor
|
||||
{
|
||||
;отображение курсора по умолчанию
|
||||
;отображение курсора по умолчанию
|
||||
lea si, point_to_hframe
|
||||
mov di, 962-160
|
||||
mov ax, point_default
|
||||
@ -554,7 +554,7 @@ end if
|
||||
}
|
||||
|
||||
macro show_descript
|
||||
;Этот макрос показывает краткое описание, если оно есть у секции в пункте
|
||||
;Этот макрос показывает краткое описание, если оно есть у секции в пункте
|
||||
;Section description
|
||||
{
|
||||
local .start_p_sh_d
|
||||
@ -568,14 +568,14 @@ local .show_mess_prev_eq
|
||||
mov si, point_to_point_def
|
||||
pop es
|
||||
sub si, 2
|
||||
mov cx, [si] ;загрузим указатель наследующию секцию
|
||||
sub cx, di ;вот теперь имеем истиный размер
|
||||
;di - указатель на дефолтную секцию т.е. выбранную cx - размер области. для просмотра
|
||||
mov cx, [si] ;загрузим указатель наследующию секцию
|
||||
sub cx, di ;вот теперь имеем истиный размер
|
||||
;di - указатель на дефолтную секцию т.е. выбранную cx - размер области. для просмотра
|
||||
|
||||
.start_p_sh_d:
|
||||
call get_firs_sym ;get first symbol on new line
|
||||
test cx, cx
|
||||
jz .exit ;нету? ну ладно - следующее значение тогда )
|
||||
jz .exit ;нету? ну ладно - следующее значение тогда )
|
||||
cmp al, 'd'
|
||||
jnz .start_p_sh_d
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -591,7 +591,7 @@ local .show_mess_prev_eq
|
||||
sub bx, parse_descript_e - parse_descript;correct cx
|
||||
add bx, cx
|
||||
mov cx, bx
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; разбор аля ' = '
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; разбор аля ' = '
|
||||
mov ax, 0x3d20 ;cut al=' ' ah='='
|
||||
repe scasb
|
||||
jcxz .rest_value_loop_sh_d ;not found param timeout
|
||||
@ -602,10 +602,10 @@ local .show_mess_prev_eq
|
||||
repe scasb ;cut ' '
|
||||
inc cx
|
||||
dec di
|
||||
;;;;;;;;;;;;;;;;;;;;di указывает на строчку, которую нам нужно выводить.
|
||||
;строчка будет выводиться блоками по 37 символов.
|
||||
;настроим куда будем выводить т.е. начало
|
||||
;es:di - указывают на строчку из которой мы берем символ, ds:si куда будем выводить
|
||||
;;;;;;;;;;;;;;;;;;;;di указывает на строчку, которую нам нужно выводить.
|
||||
;строчка будет выводиться блоками по 37 символов.
|
||||
;настроим куда будем выводить т.е. начало
|
||||
;es:di - указывают на строчку из которой мы берем символ, ds:si куда будем выводить
|
||||
push di
|
||||
pop si
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
;Тут представленны теги, для сравнения
|
||||
;Тут представленны теги, для сравнения
|
||||
parse_loader db '[loader]'
|
||||
parse_loader_e:
|
||||
parse_l_timeout db 'timeout'
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@ error:
|
||||
mov cx, bx
|
||||
jmp ret_on_ch ;return
|
||||
|
||||
;///// îøèáêà ïðè íàõîäæåíèè äëèííû ñåêöèè â ïàðàìåòðå default
|
||||
;///// ошибка при находжении длинны секции в параметре default
|
||||
.error_get_size_d_sect:
|
||||
leave ;clear array in stack
|
||||
mov si, not_found_def_sect
|
||||
@ -42,7 +42,7 @@ error:
|
||||
mov si, not_found_sec_loader
|
||||
jmp err_show_ini
|
||||
|
||||
.default_eq_loader: ;êðèòè÷åñêàÿ îøèáêà default ñåêöèÿ = loader
|
||||
.default_eq_loader: ;критическая ошибка default секция = loader
|
||||
leave
|
||||
mov si, default_eq_loader
|
||||
jmp err_show_ini
|
||||
|
@ -24,10 +24,10 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
;блок макросов по обработке секции [loader]
|
||||
;входные данные:
|
||||
;es:di - указатель на секцию начинающиюся с '[' встечающиюся после 0хa
|
||||
;cx - счетчик кол-во байт для проверке в кадре
|
||||
;блок макросов по обработке секции [loader]
|
||||
;входные данные:
|
||||
;es:di - указатель на секцию начинающиюся с '[' встечающиюся после 0хa
|
||||
;cx - счетчик кол-во байт для проверке в кадре
|
||||
;
|
||||
macro use_parse_loader
|
||||
{
|
||||
@ -35,16 +35,16 @@ macro use_parse_loader
|
||||
;//////////////////
|
||||
;/ parse [loader]
|
||||
;//////////////////
|
||||
mov bx, cx ;cохраним в регистры значения счетчика и указателя
|
||||
mov bx, cx ;cохраним в регистры значения счетчика и указателя
|
||||
mov ax, di
|
||||
|
||||
; mov word [bp-4],.start ;is alredy set, see up
|
||||
mov si, parse_loader
|
||||
mov cx, parse_loader_e - parse_loader
|
||||
repe cmpsb
|
||||
jnz error.rest_value ;цепочка не совпала :( перейдем далее т.е. будем снова искать))
|
||||
jnz error.rest_value ;цепочка не совпала :( перейдем далее т.е. будем снова искать))
|
||||
|
||||
;сохраним указательна loader, что бы потом больше его не искать
|
||||
;сохраним указательна loader, что бы потом больше его не искать
|
||||
mov point_loader, ax
|
||||
sub bx, parse_loader_e - parse_loader;correct cx
|
||||
add bx, cx
|
||||
@ -63,7 +63,7 @@ end if
|
||||
mov dx, di
|
||||
@@:
|
||||
call get_firs_sym
|
||||
jcxz .loader_f_end ;.end_loader ; end даже если мы не нашли секцию предположим что секция [loader] стоит в конце
|
||||
jcxz .loader_f_end ;.end_loader ; end даже если мы не нашли секцию предположим что секция [loader] стоит в конце
|
||||
cmp al, '['
|
||||
jnz @b
|
||||
|
||||
@ -74,7 +74,7 @@ end if
|
||||
;//timeout=5
|
||||
;//default=main
|
||||
; mov di,dx ;set pointer on section [loader] i think it's not need
|
||||
mov cx, bx ;set counter for parsing section [loader] cx= кол-ву символов в секции [loader]
|
||||
mov cx, bx ;set counter for parsing section [loader] cx= кол-ву символов в секции [loader]
|
||||
mov ret_on_ch, .get_next_str; return point
|
||||
;;;;;;; parse timeout & default
|
||||
.get_next_str:
|
||||
@ -82,7 +82,7 @@ end if
|
||||
|
||||
test cx, cx
|
||||
jz .end_loader
|
||||
; jcxz .end_loader ;завершение парсинга значений timeout & default
|
||||
; jcxz .end_loader ;завершение парсинга значений timeout & default
|
||||
cmp al, 't'
|
||||
jz .loader_timeout
|
||||
cmp al, 'd'
|
||||
@ -96,7 +96,7 @@ end if
|
||||
mov cx, parse_l_default_e - parse_l_default
|
||||
repe cmpsb
|
||||
|
||||
jnz error.rest_value ;is not compare цепочка не совпала
|
||||
jnz error.rest_value ;is not compare цепочка не совпала
|
||||
|
||||
sub bx, parse_l_default_e - parse_l_default;correct cx
|
||||
add bx, cx
|
||||
@ -105,7 +105,7 @@ end if
|
||||
test status_flag, flag_found_default
|
||||
jz .correct_is_not_set
|
||||
|
||||
mov si, found_equal_default ;мы нашли что флаг уже установлен, информируем
|
||||
mov si, found_equal_default ;мы нашли что флаг уже установлен, информируем
|
||||
call printplain
|
||||
jmp .get_next_str
|
||||
|
||||
@ -121,12 +121,12 @@ end if
|
||||
repe scasb ;cut ' '
|
||||
inc cx
|
||||
dec di
|
||||
;сейчас es:di указывают на название секции, имя секции по дефолту не должно быть loader т.е. иначе возможно зацикливание
|
||||
;установим указатель si на это значение и сначала проверим
|
||||
;сейчас es:di указывают на название секции, имя секции по дефолту не должно быть loader т.е. иначе возможно зацикливание
|
||||
;установим указатель si на это значение и сначала проверим
|
||||
|
||||
;получение длинны секции
|
||||
; cx=bx содержит длинну остатка секции
|
||||
; di=ax указатель на текущию секцию
|
||||
;получение длинны секции
|
||||
; cx=bx содержит длинну остатка секции
|
||||
; di=ax указатель на текущию секцию
|
||||
mov bx, cx
|
||||
mov dx, di
|
||||
|
||||
@ -135,7 +135,7 @@ end if
|
||||
inc di
|
||||
dec cx
|
||||
test cx, cx
|
||||
jz error.error_get_size_d_sect ;переход на обработку ошибки по нахождению длины дефолтной секции
|
||||
jz error.error_get_size_d_sect ;переход на обработку ошибки по нахождению длины дефолтной секции
|
||||
cmp al, ' '
|
||||
jz @b
|
||||
cmp al, 0xd
|
||||
@ -146,13 +146,13 @@ end if
|
||||
;
|
||||
inc cx ;correct cx
|
||||
mov ax, bx
|
||||
sub bx, cx ; в bx длина секции которая определена по дефолту
|
||||
sub bx, cx ; в bx длина секции которая определена по дефолту
|
||||
mov save_cx_d, bx
|
||||
mov di, dx
|
||||
|
||||
mov cx, bx ;set size default section
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;проверка на =loader
|
||||
;save in reg point and счетчик
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;проверка на =loader
|
||||
;save in reg point and счетчик
|
||||
;check on loader
|
||||
mov bx, ax
|
||||
mov ax, dx
|
||||
@ -160,22 +160,22 @@ end if
|
||||
mov si, parse_loader
|
||||
inc si ;set only loader and 6 char in counter
|
||||
repe cmpsb
|
||||
jnz .check_section ;цепочка не совпала :( перейдем далее )) значит не исключение
|
||||
jnz .check_section ;цепочка не совпала :( перейдем далее )) значит не исключение
|
||||
|
||||
jmp error.default_eq_loader ;error критическая ошибка т.е. в дефолте присутствует имя [loader]
|
||||
jmp error.default_eq_loader ;error критическая ошибка т.е. в дефолте присутствует имя [loader]
|
||||
|
||||
.check_section: ;поиск соответствующей секции нам нужно будет узнать адрес этой секции
|
||||
.check_section: ;поиск соответствующей секции нам нужно будет узнать адрес этой секции
|
||||
mov cx, bx
|
||||
mov di, ax
|
||||
|
||||
;/////////////////////////////
|
||||
; mov ret_on_ch,.start_d ;set return
|
||||
mov si, di ;установим указатель на нашу секцию, которая по дефолту
|
||||
mov si, di ;установим указатель на нашу секцию, которая по дефолту
|
||||
|
||||
push di ;save point di
|
||||
|
||||
push cx ;save cx
|
||||
;установим указатель es:di на начало ini файла
|
||||
;установим указатель es:di на начало ini файла
|
||||
mov cx, save_cx ;it's placed size of ini file
|
||||
les di, dword [file_data]
|
||||
|
||||
@ -190,13 +190,13 @@ end if
|
||||
|
||||
.start_d:
|
||||
call get_firs_sym ;get first symbol on new line
|
||||
.first_ret_d: ;первый возврат
|
||||
.first_ret_d: ;первый возврат
|
||||
jcxz .correct_exit ;.end_loader ;found or not found parametrs in section exit in section
|
||||
cmp al, '['
|
||||
jz .found_sect_d
|
||||
jmp .start_d
|
||||
;просматриваем ini файл с начала в поисках секции указаной как default
|
||||
;идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти
|
||||
;просматриваем ini файл с начала в поисках секции указаной как default
|
||||
;идет проверка на наличее значения timeout, для более быстрой работы, этот параметр должен быть уже обработан,т.е. в этом случае при его =0 будет сформирован указатель только на дефолтную секцию, иначе информация будет собрана по всем секциям и составлены указатели в блоке памяти
|
||||
.found_sect_d:
|
||||
|
||||
;check on name section
|
||||
@ -215,9 +215,9 @@ end if
|
||||
pop ds
|
||||
pop si
|
||||
|
||||
jnz .not_compare_d_s ;цепочка не совпала :( перейдем далее )) значит не исключение
|
||||
jnz .not_compare_d_s ;цепочка не совпала :( перейдем далее )) значит не исключение
|
||||
cmp byte[es:di], ']'
|
||||
jnz .not_compare_d_s ;нет в конце нашей секции завершающего символа :(
|
||||
jnz .not_compare_d_s ;нет в конце нашей секции завершающего символа :(
|
||||
|
||||
|
||||
|
||||
@ -243,7 +243,7 @@ end if
|
||||
jmp .start_d
|
||||
|
||||
.correct_exit:
|
||||
pop cx ;восстановим значение счетчика
|
||||
pop cx ;восстановим значение счетчика
|
||||
pop di
|
||||
|
||||
|
||||
@ -272,7 +272,7 @@ end if
|
||||
test status_flag, flag_found_timeout
|
||||
jz .correct_is_not_set_t
|
||||
|
||||
mov si, found_equal_timeout ;мы нашли что флаг уже установлен, информируем
|
||||
mov si, found_equal_timeout ;мы нашли что флаг уже установлен, информируем
|
||||
call printplain
|
||||
jmp .get_next_str
|
||||
|
||||
@ -288,7 +288,7 @@ end if
|
||||
inc cx
|
||||
dec di
|
||||
;get timeout value
|
||||
;2 знакa может быть обработано т.е. значение от 0 до 99 секунд
|
||||
;2 знакa может быть обработано т.е. значение от 0 до 99 секунд
|
||||
push cx
|
||||
xor bx, bx
|
||||
mov cx, 2
|
||||
|
@ -23,12 +23,12 @@
|
||||
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
; Предопределения
|
||||
DEBUG equ 1 ;компиляция с отладочной информацией =1 без отладочной инфорации =0
|
||||
loop_read_startos_file equ 3 ;кол-во попыток считать через callback сервис файл конфигурации блок2
|
||||
root_dir_entry_count equ 224 ;кол-во элементов в корневой дирректории
|
||||
;point_to_fat_struc equ 0xA000 ;временный буфер, куда будет размещена Fat таблица, и затем перенесена за 1 мб
|
||||
ini_data_ equ 0x2000 ;файл где размещен файл сценария загрузки, там происходит синтаксический разбор
|
||||
; Предопределения
|
||||
DEBUG equ 1 ;компиляция с отладочной информацией =1 без отладочной инфорации =0
|
||||
loop_read_startos_file equ 3 ;кол-во попыток считать через callback сервис файл конфигурации блок2
|
||||
root_dir_entry_count equ 224 ;кол-во элементов в корневой дирректории
|
||||
;point_to_fat_struc equ 0xA000 ;временный буфер, куда будет размещена Fat таблица, и затем перенесена за 1 мб
|
||||
ini_data_ equ 0x2000 ;файл где размещен файл сценария загрузки, там происходит синтаксический разбор
|
||||
size_show_section equ 18
|
||||
default_timeout_value equ 5 ;default value to timeout is will was some errors
|
||||
flag_found_default equ 0x1 ;default value is found
|
||||
@ -38,15 +38,15 @@ flag_found_RS equ 0x2 ;found RS value
|
||||
flag_found_GTRFMS equ 0x4 ;found type RamFS
|
||||
flag_found_RamdiskSector equ 0x8 ;found RamdiskSector
|
||||
flag_found_RamdiskCluster equ 0x16 ;found RamdiskCluster
|
||||
;statick data эти данные не предопределяются в течении выполнения всей программы.
|
||||
;statick data эти данные не предопределяются в течении выполнения всей программы.
|
||||
save_cx equ word [bp-2] ;save cx size ini file
|
||||
ret_on_ch equ word [bp-4] ;point to return разрушаемое значение
|
||||
ret_on_ch equ word [bp-4] ;point to return разрушаемое значение
|
||||
save_cx_d equ word [bp-6] ;save cx - size default section and working section
|
||||
status_flag equ word [bp-8] ;status flag
|
||||
point_loader equ word [bp-10]
|
||||
point_default equ word [bp-12] ;point to default
|
||||
|
||||
;данные которые зависимы от ветки выполнения и которые могут быть переопределены в процессе выполнения программы.
|
||||
;данные которые зависимы от ветки выполнения и которые могут быть переопределены в процессе выполнения программы.
|
||||
point_to_hframe equ word [bp-14] ;point on start frame (for change section)
|
||||
point_to_1 equ word [bp-16]
|
||||
point_to_2 equ word [bp-18]
|
||||
@ -73,26 +73,26 @@ point_to_eframe equ word [bp-56] ;point on point frame
|
||||
|
||||
|
||||
|
||||
; тут расположено временное хранилище для cx и di при переходе на следующий буфер при поиске секций
|
||||
find_sec_di equ word [bp-58] ;тут будет храниться di
|
||||
info_real_mode_size equ word [bp-60];тут храниться информация о занятой области т.е. размер, можно узнать сколько осталось места вычислив
|
||||
free_ad_memory equ word [bp-62] ;сколько у нас расширенной памяти для формирования рам диска и загрузки модулей
|
||||
show_errors_sect equ word [bp-64] ;переменая которая хранит биты ошибок для каждой логической секции.
|
||||
save_descript_size equ word [bp-66] ;save descript size previos section сохраним размер предыдущей секции которую выводили
|
||||
; тут расположено временное хранилище для cx и di при переходе на следующий буфер при поиске секций
|
||||
find_sec_di equ word [bp-58] ;тут будет храниться di
|
||||
info_real_mode_size equ word [bp-60];тут храниться информация о занятой области т.е. размер, можно узнать сколько осталось места вычислив
|
||||
free_ad_memory equ word [bp-62] ;сколько у нас расширенной памяти для формирования рам диска и загрузки модулей
|
||||
show_errors_sect equ word [bp-64] ;переменая которая хранит биты ошибок для каждой логической секции.
|
||||
save_descript_size equ word [bp-66] ;save descript size previos section сохраним размер предыдущей секции которую выводили
|
||||
save_ramdisksize equ dword [bp-70] ;save size of ramdisk in byte
|
||||
save_file_size equ dword [bp-74] ;save size of reading file
|
||||
set_ramfs equ word [bp-76] ;определенный тип файловой системы,нужно для формирования рам диска
|
||||
point_next_fat_str equ word [bp-78] ;указатель на следующий элемент fat таблицы
|
||||
size_root_dir equ word [bp-80] ;кол-во элементов в секторах по 512 байт корневой директории
|
||||
firstDataSect equ word [bp-82] ;первый сектор данных в сеторах от 0
|
||||
DataClasters equ word [bp-84] ;размер массива доступной для записи данных в кластерах.
|
||||
point_to_free_root equ word [bp-86] ;указатель на следующий пустую запись в рут дир
|
||||
point_to_dest_file_name equ word [bp-88] ;указывает на начало имени файла назначения. в формате es:point_to_dest_file_name, где es =0x2000
|
||||
data_offset equ word [bp-90] ;смещение в кластерах для записанных данных т.е перекинутых за 1-й мб
|
||||
first_input equ word [bp-92] ;поле для флагов в преобразовании имени.
|
||||
save_di_RAMDISK equ word [bp-94] ;сохраним di -указателя при обработке секции
|
||||
save_cx_RAMDISK equ word [bp-96] ;сохраним размер остатка секции
|
||||
status_flag_loader_f equ word [bp-98] ;сохраним результат выполенения загрузки файла
|
||||
set_ramfs equ word [bp-76] ;определенный тип файловой системы,нужно для формирования рам диска
|
||||
point_next_fat_str equ word [bp-78] ;указатель на следующий элемент fat таблицы
|
||||
size_root_dir equ word [bp-80] ;кол-во элементов в секторах по 512 байт корневой директории
|
||||
firstDataSect equ word [bp-82] ;первый сектор данных в сеторах от 0
|
||||
DataClasters equ word [bp-84] ;размер массива доступной для записи данных в кластерах.
|
||||
point_to_free_root equ word [bp-86] ;указатель на следующий пустую запись в рут дир
|
||||
point_to_dest_file_name equ word [bp-88] ;указывает на начало имени файла назначения. в формате es:point_to_dest_file_name, где es =0x2000
|
||||
data_offset equ word [bp-90] ;смещение в кластерах для записанных данных т.е перекинутых за 1-й мб
|
||||
first_input equ word [bp-92] ;поле для флагов в преобразовании имени.
|
||||
save_di_RAMDISK equ word [bp-94] ;сохраним di -указателя при обработке секции
|
||||
save_cx_RAMDISK equ word [bp-96] ;сохраним размер остатка секции
|
||||
status_flag_loader_f equ word [bp-98] ;сохраним результат выполенения загрузки файла
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;данные которые используются при обработке секции, т.е. после нажатия Enter, уже не возможно вернуться в первоначальный экран
|
||||
;для возврата, необходимо перезапустить полностью код т.е. стартовать с 0х1000:0000
|
||||
;данные которые используются при обработке секции, т.е. после нажатия Enter, уже не возможно вернуться в первоначальный экран
|
||||
;для возврата, необходимо перезапустить полностью код т.е. стартовать с 0х1000:0000
|
||||
|
@ -24,7 +24,7 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
; тут описываются процедуры которые используются в secondary loader
|
||||
; тут описываются процедуры которые используются в secondary loader
|
||||
color_sym_black equ 0
|
||||
color_sym_blue equ 1
|
||||
color_sym_green equ 2
|
||||
@ -40,7 +40,7 @@ color_sym_yellow equ 14
|
||||
color_sym_white equ 15
|
||||
if DEBUG
|
||||
decode:
|
||||
;input eax - число, es:di куда писать, cx=10
|
||||
;input eax - число, es:di куда писать, cx=10
|
||||
cmp eax, ecx
|
||||
jb @f
|
||||
xor edx, edx
|
||||
@ -198,7 +198,7 @@ show_name_section:
|
||||
dec di
|
||||
|
||||
|
||||
;все вырезали и все готово для вывода имени секции ))
|
||||
;все вырезали и все готово для вывода имени секции ))
|
||||
push es
|
||||
pop ds
|
||||
|
||||
@ -249,52 +249,52 @@ end if
|
||||
pop si
|
||||
ret
|
||||
|
||||
.not_name_sec_fb: ;нет имени в названии секции - значит так и скажем об этом
|
||||
.not_name_sec_fb: ;нет имени в названии секции - значит так и скажем об этом
|
||||
push cs
|
||||
pop ds
|
||||
mov di, default_section_name
|
||||
jmp .def_sect_name
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;процедура поиска вверх следующей секции
|
||||
;в point_default содержиться указатель на дефаулт секцию, и в пределах врейма мы бегаем по заранее пропарсеными значениям указателей
|
||||
;для того что бы отобразить и пропарсить следующий фрейм, нам нужно получить за пердыдущий или следующий указатель
|
||||
;процедура поиска вверх следующей секции
|
||||
;в point_default содержиться указатель на дефаулт секцию, и в пределах врейма мы бегаем по заранее пропарсеными значениям указателей
|
||||
;для того что бы отобразить и пропарсить следующий фрейм, нам нужно получить за пердыдущий или следующий указатель
|
||||
find_before_sect:
|
||||
mov di, point_default
|
||||
.e:
|
||||
push ini_data_
|
||||
pop es
|
||||
mov cx, di ;предположим будем просматривать к началу, текущая позиция di = сколько символов от начала документа имеется
|
||||
mov bx, cx ;копия
|
||||
mov cx, di ;предположим будем просматривать к началу, текущая позиция di = сколько символов от начала документа имеется
|
||||
mov bx, cx ;копия
|
||||
|
||||
;настроили указатель на дефаулт секцию
|
||||
;будем искать вверх
|
||||
;настроили указатель на дефаулт секцию
|
||||
;будем искать вверх
|
||||
.find_start_section:
|
||||
std ;установка флага направления - будем просматирвать к началу нашего ини файла
|
||||
;будем искать начало секции т.е. '[' этот символ
|
||||
std ;установка флага направления - будем просматирвать к началу нашего ини файла
|
||||
;будем искать начало секции т.е. '[' этот символ
|
||||
mov al, 0xa
|
||||
repnz scasb ;просканируем на наличее символа начала секции
|
||||
jcxz .go_ ;мы просмотрели до начала файла, но так и ничего не нашли ;(( по тихому выйдем )
|
||||
repnz scasb ;просканируем на наличее символа начала секции
|
||||
jcxz .go_ ;мы просмотрели до начала файла, но так и ничего не нашли ;(( по тихому выйдем )
|
||||
|
||||
mov find_sec_di, di ;сохраним данные
|
||||
mov find_sec_di, di ;сохраним данные
|
||||
mov cx, di ;
|
||||
|
||||
sub bx, cx
|
||||
mov cx, bx ;в сx значение - кол-во символов
|
||||
mov cx, bx ;в сx значение - кол-во символов
|
||||
cld
|
||||
call get_firs_sym
|
||||
.ret_go:
|
||||
jcxz ._not_section ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
jcxz ._not_section ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
|
||||
cmp di, point_loader; секцию loader мы не заносим иначе крах
|
||||
cmp di, point_loader; секцию loader мы не заносим иначе крах
|
||||
jz ._not_section
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
cmp al, '['
|
||||
jnz ._not_section
|
||||
mov point_default, di
|
||||
.exit_scan_sect:
|
||||
ret
|
||||
;;;;;;;; восстановим значения и продолжим поиски начала секции которая нас устроит ))
|
||||
;;;;;;;; восстановим значения и продолжим поиски начала секции которая нас устроит ))
|
||||
._not_section:
|
||||
mov di, find_sec_di
|
||||
mov cx, di
|
||||
@ -302,7 +302,7 @@ find_before_sect:
|
||||
jmp .find_start_section
|
||||
.go_:
|
||||
cld
|
||||
mov cx, bx ;в сx значение - кол-во символов
|
||||
mov cx, bx ;в сx значение - кол-во символов
|
||||
|
||||
mov al, byte [es:di]
|
||||
push word .f_go
|
||||
@ -313,11 +313,11 @@ find_before_sect:
|
||||
jmp get_firs_sym.first_sp
|
||||
|
||||
.f_go:
|
||||
jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
|
||||
cmp di, point_loader; секцию loader мы не заносим иначе крах
|
||||
cmp di, point_loader; секцию loader мы не заносим иначе крах
|
||||
jz .exit_scan_sect
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
cmp al, '['
|
||||
jnz .exit_scan_sect
|
||||
mov point_default, di
|
||||
@ -336,14 +336,14 @@ find_next_sect:
|
||||
mov di, point_default
|
||||
push ini_data_
|
||||
pop es
|
||||
mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется
|
||||
sub cx, di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало
|
||||
mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется
|
||||
sub cx, di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало
|
||||
jmp .let_s_go
|
||||
.h:
|
||||
push ini_data_
|
||||
pop es
|
||||
mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется
|
||||
; sub cx,di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало
|
||||
mov cx, save_cx;di ;предположим будем просматривать к концу, текущая позиция di = сколько символов от начала документа имеется
|
||||
; sub cx,di ;сейчас в cx остаток т.е. сколько можно крутить до конца и не вылазить на начало
|
||||
|
||||
mov al, byte [es:di]
|
||||
push word .let_s_go_ret
|
||||
@ -356,17 +356,17 @@ find_next_sect:
|
||||
|
||||
|
||||
|
||||
;настроили указатель на дефаулт секцию
|
||||
;будем искать вниз
|
||||
;настроили указатель на дефаулт секцию
|
||||
;будем искать вниз
|
||||
.let_s_go:
|
||||
call get_firs_sym
|
||||
.let_s_go_ret:
|
||||
jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
jcxz .exit_scan_sect ; в данном случае имеем конструкцию 0xa ... ; hello [секция] обломс ищем далее
|
||||
cmp al, '['
|
||||
jnz .let_s_go
|
||||
cmp di, point_loader
|
||||
jz .let_s_go
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
;все удачно мы нашли вхождение секции предыдущей
|
||||
mov point_default, di
|
||||
.exit_scan_sect:
|
||||
ret
|
||||
@ -374,8 +374,8 @@ find_next_sect:
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;clean old cursor
|
||||
clean_active_cursor:
|
||||
;не изменяет значение ax
|
||||
;отображение курсора по умолчанию
|
||||
;не изменяет значение ax
|
||||
;отображение курсора по умолчанию
|
||||
lea si, point_to_hframe
|
||||
mov di, 962-160
|
||||
mov dx, point_default
|
||||
@ -405,7 +405,7 @@ end if
|
||||
pop ax
|
||||
ret
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;установка таймера и отображение счетчика времени
|
||||
;установка таймера и отображение счетчика времени
|
||||
gettime:
|
||||
mov ah, 0
|
||||
int 1Ah
|
||||
@ -462,13 +462,13 @@ newtimer:
|
||||
mov dword [timer_], eax
|
||||
mov sp, word [start_stack]
|
||||
mov bp, word [save_bp_from_timer]
|
||||
;;не восстановленый стек :(
|
||||
;;не восстановленый стек :(
|
||||
sti
|
||||
jmp parse_start.parse_run_only
|
||||
|
||||
|
||||
.decode:
|
||||
;input ax - число, es:di куда писать, bx=10
|
||||
;input ax - число, es:di куда писать, bx=10
|
||||
cmp ax, bx
|
||||
jb @f
|
||||
xor dx, dx
|
||||
@ -485,9 +485,9 @@ newtimer:
|
||||
ret
|
||||
|
||||
show_bl_sc_sect:
|
||||
;1) отображение списка секций. Если секция не имет имя - ошибка - вывод Section unname
|
||||
;проверка на наличее имени.
|
||||
;входные данные es:di -указатель на секцию - cx размер секции
|
||||
;1) отображение списка секций. Если секция не имет имя - ошибка - вывод Section unname
|
||||
;проверка на наличее имени.
|
||||
;входные данные es:di -указатель на секцию - cx размер секции
|
||||
; push bp
|
||||
mov bx, point_to_eframe
|
||||
lea si, point_to_hframe
|
||||
|
@ -24,15 +24,15 @@
|
||||
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
;*****************************************************************************
|
||||
|
||||
; это комментарий
|
||||
; это комментарий
|
||||
[loader]
|
||||
; секция [loader] содержит параметры загрузчика
|
||||
; в течение timeout секунд загрузчик будет ждать реакции пользователя,
|
||||
; если её не последует, будет загружена конфигурация, указанная в default
|
||||
; секция [loader] содержит параметры загрузчика
|
||||
; в течение timeout секунд загрузчик будет ждать реакции пользователя,
|
||||
; если её не последует, будет загружена конфигурация, указанная в default
|
||||
timeout=5
|
||||
default=kolibri_EE
|
||||
; прочие секции - по одной на каждую конфигурацию
|
||||
; и по одной на каждый вторичный модуль
|
||||
; прочие секции - по одной на каждую конфигурацию
|
||||
; и по одной на каждый вторичный модуль
|
||||
[main]
|
||||
name="Kord OS v 0.00001"
|
||||
descript="This is x64 OS microkernel"
|
||||
|
@ -403,7 +403,7 @@ align 4
|
||||
align 4
|
||||
.no_mouseunder:
|
||||
shl ebx, 9
|
||||
lea ebx, [ebx+ebx*4] ; умножение на 5
|
||||
lea ebx, [ebx+ebx*4] ; умножение на 5
|
||||
lea edx, [ebx+ecx*4] ; + x*BytesPerPixel (Vesa2.0 32)
|
||||
mov edi, edx
|
||||
add edi, [LFBAddress] ; + LFB address
|
||||
@ -501,7 +501,7 @@ VGA_draw_bar_1:
|
||||
add eax, [temp.cx]
|
||||
and eax, 0xfff8
|
||||
shl ebx, 9
|
||||
lea ebx, [ebx+ebx*4]; умножение на 5
|
||||
lea ebx, [ebx+ebx*4]; умножение на 5
|
||||
lea ebx, [ebx+eax*4] ; + x*BytesPerPixel (Vesa2.0 32)
|
||||
mov esi, ebx
|
||||
add esi, [LFBAddress] ; + LFB address
|
||||
|
Loading…
Reference in New Issue
Block a user