Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 3554 → Rev 3555

/kernel/branches/Kolibri-acpi/blkdev/cd_drv.inc
9,21 → 9,21
 
 
;**********************************************************
; Íåïîñðåäñòâåííàÿ ðàáîòà ñ óñòðîéñòâîì Ñ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_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
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
jz @@NextRetr
jmp .wait
@@:
; Çàäåðæêà íà 2,5 ñåêóíäû
; Задержка на 2,5 секунды
; mov EAX,[timer_ticks]
; add EAX,50 ;250
;@@Wait:
161,53 → 161,53
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
214,13 → 214,13
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
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
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
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
 
 
;***********************************************
;* ÏÎÑËÀÒÜ ÓÑÒÐÎÉÑÒÂÓ 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
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
; 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
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
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
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
@@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,16 → 597,16
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
613,21 → 613,21
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
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,55 → 661,69
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]
sub eax, [timer_ATAPI_check]
cmp eax, 100
jb .no
.yes:
xor eax, eax
inc eax
ret
.no:
xor eax, eax
ret
endp
 
align 4
check_ATAPI_device_event:
pusha
853,78 → 867,78
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
/kernel/branches/Kolibri-acpi/blkdev/disk.inc
5,7 → 5,7
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 2257 $
$Revision: 3460 $
 
; =============================================================================
; ================================= Constants =================================
/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc
5,7 → 5,7
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 2140 $
$Revision: 3284 $
 
; This function is intended to replace the old 'hd_read' function when
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
397,7 → 397,7
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
/kernel/branches/Kolibri-acpi/blkdev/fdc.inc
37,9 → 37,9
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:
/kernel/branches/Kolibri-acpi/blkdev/flp_drv.inc
9,12 → 9,12
 
 
;**********************************************************
; Íåïîñðåäñòâåííàÿ ðàáîòà ñ êîíòðîëëåðîì ãèáêîãî äèñêà
; Непосредственная работа с контроллером гибкого диска
;**********************************************************
; Àâòîð èñõîäíîãî òåêñòà Êóëàêîâ Âëàäèìèð Ãåííàäüåâè÷.
; Àäàïòàöèÿ è äîðàáîòêà 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
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
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_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
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
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,17 → 182,17
ret
 
;*********************************************
;* ÎÁÐÀÁÎÒ×ÈÊ ÏÐÅÐÛÂÀÍÈß ÎÒ ÊÎÍÒÐÎËËÅÐÀ ÍÃÌÄ *
;* ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА НГМД *
;*********************************************
FDCInterrupt:
; Óñòàíîâèòü ôëàã ïðåðûâàíè
; Установить флаг прерывани
mov [FDD_IntFlag], 1
ret
 
 
;******************************************
;* ÓÑÒÀÍÎÂÈÒÜ ÍÎÂÛÉ ÎÁÐÀÁÎÒ×ÈÊ ÏÐÅÐÛÂÀÍÈÉ *
;* ÍÃÌÄ *
;* УСТАНОВИТЬ НОВЫЙ ОБРАБОТЧИК ПРЕРЫВАНИЙ *
;* НГМД *
;******************************************
SetUserInterrupts:
mov [fdc_irq_func], FDCInterrupt
199,28 → 199,28
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
ret
 
;*********************************
;* ÂÊËÞ×ÈÒÜ ÌÎÒÎÐ ÄÈÑÊÎÂÎÄÀ "A:" *
;* ВКЛЮЧИТЬ МОТОР ДИСКОВОДА "A:" *
;*********************************
FDDMotorON:
pusha
237,11 → 237,11
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
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
ret
 
;*****************************************
;* ÑÎÕÐÀÍÅÍÈÅ ÓÊÀÇÀÒÅËß ÂÐÅÌÅÍÈ *
;* СОХРАНЕНИЕ УКАЗАТЕЛЯ ВРЕМЕНИ *
;*****************************************
save_timer_fdd_motor:
mov eax, [timer_ticks]
282,8 → 282,26
ret
 
;*****************************************
;* ÏÐÎÂÅÐÊÀ ÇÀÄÅÐÆÊÈ ÂÛÊËÞ×ÅÍÈß ÌÎÒÎÐÀ *
;* ПРОВЕРКА ЗАДЕРЖКИ ВЫКЛЮЧЕНИЯ МОТОРА *
;*****************************************
proc check_fdd_motor_status_has_work?
cmp [flp_status], 0
jnz .yes
cmp [fdd_motor_status], 0
jz .no
mov eax, [timer_ticks]
sub eax, [timer_fdd_motor]
cmp eax, 500
jb .no
.yes:
xor eax, eax
inc eax
ret
.no:
xor eax, eax
ret
endp
 
align 4
check_fdd_motor_status:
cmp [fdd_motor_status], 0
300,7 → 318,7
ret
 
;**********************************
;* ÂÛÊËÞ×ÈÒÜ ÌÎÒÎÐ ÄÈÑÊÎÂÎÄÀ *
;* ВЫКЛЮЧИТЬ МОТОР ДИСКОВОДА *
;**********************************
FDDMotorOFF:
push AX
314,35 → 332,35
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
353,30 → 371,30
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
383,24 → 401,24
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:
409,27 → 427,27
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
440,19 → 458,19
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
467,21 → 485,21
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
489,11 → 507,11
je @@Exit_2
cmp [FDC_Status], 1
je @@Err_3
; Òðîåêðàòíîå ïîâòîðåíèå ÷òåíè
; Троекратное повторение чтени
inc [ReadRepCounter]
cmp [ReadRepCounter], 3
jb @@ReadSector_1
; Òðîåêðàòíîå ïîâòîðåíèå ðåêàëèáðîâêè
; Троекратное повторение рекалибровки
call RecalibrateFDD
call SeekTrack
inc [RecalRepCounter]
509,27 → 527,27
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
540,19 → 558,19
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
566,21 → 584,21
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
588,11 → 606,11
je @@Exit_4
cmp [FDC_Status], 1
je @@Err_4
; Òðîåêðàòíîå ïîâòîðåíèå ÷òåíè
; Троекратное повторение чтени
inc [ReadRepCounter]
cmp [ReadRepCounter], 3
jb @@WriteSector_1
; Òðîåêðàòíîå ïîâòîðåíèå ðåêàëèáðîâêè
; Троекратное повторение рекалибровки
call RecalibrateFDD
call SeekTrack
inc [RecalRepCounter]
607,7 → 625,7
ret
 
;*********************************************
;* ÏÎËÓ×ÈÒÜ ÈÍÔÎÐÌÀÖÈÞ Î ÐÅÇÓËÜÒÀÒÅ ÎÏÅÐÀÖÈÈ *
;* ПОЛУЧИТЬ ИНФОРМАЦИЮ О РЕЗУЛЬТАТЕ ОПЕРАЦИИ *
;*********************************************
GetStatusInfo:
push AX
/kernel/branches/Kolibri-acpi/blkdev/hd_drv.inc
109,28 → 109,28
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
/kernel/branches/Kolibri-acpi/blkdev/ide_cache.inc
52,7 → 52,7
cmp [dma_hdd], 1
jnz .nodma
@@:
; Ž¡ê¥¤¨­ï¥¬ § ¯¨áì 楯®çª¨ ¯®á«¥¤®¢ â¥«ì­ëå ᥪâ®à®¢ ¢ ®¤­® ®¡à é¥­¨¥ ª ¤¨áªã
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
cmp ecx, 1
jz .nonext
cmp dword [esi+8+4], 2
/kernel/branches/Kolibri-acpi/blkdev/rd.inc
346,10 → 346,10
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
; 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
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, 'à'-''
sub al, 0xE0-0x90
.ret:
ret
.rus1:
1601,8 → 1601,26
cmp ecx, eax
jb .scan_cont
; found!
; If creating a directory, allocate one data cluster now and fail immediately
; if this is impossible. This prevents from creating an invalid directory entry
; on a full disk.
; yup, the argument is quite non-intuitive... but what should I do if
; the entire function uses such arguments? BTW, it refers to al from pushad,
; which in turn is filled with 0 in fs_RamdiskRewrite and 1 in fs_RamdiskCreateFolder.
push esi ecx
cmp byte [esp+24+12+20+28], 0
jz .no.preallocate.folder.data
mov ecx, 2849
mov edi, RAMDISK_FAT
xor eax, eax
repnz scasw
jz @f
add esp, 24
jmp .disk_full
@@:
mov [esp+24+12+20+20], edi ; store the cluster somewhere
.no.preallocate.folder.data:
; calculate name checksum
push esi ecx
mov esi, [esp+8+8]
mov ecx, 11
xor eax, eax
1672,6 → 1690,13
mov byte [edi+11], 10h ; attributes: folder
mov ecx, 32*2
mov edx, edi
push edx
push ecx
push edi
add edi, 26 ; edi points to low word of cluster
push edi
mov edi, [esp+16+20+20]
jmp .doit2
.doit:
push edx
push ecx
1686,6 → 1711,7
xor eax, eax
repnz scasw
jnz .disk_full2
.doit2:
dec edi
dec edi
 
/kernel/branches/Kolibri-acpi/boot/bootcode.inc
428,7 → 428,7
.nopci:
; \end{Mario79}
 
mov al, 0xf6 ; Ñáðîñ êëàâèàòóðû, ðàçðåøèòü ñêàíèðîâàíèå
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
out 0x60, al
xor cx, cx
wait_loop: ; variant 2
847,21 → 847,21
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
/kernel/branches/Kolibri-acpi/boot/booten.inc
89,11 → 89,11
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
/kernel/branches/Kolibri-acpi/boot/bootet.inc
15,87 → 15,87
 
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
/kernel/branches/Kolibri-acpi/boot/bootge.inc
89,11 → 89,11
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
/kernel/branches/Kolibri-acpi/boot/bootru.inc
15,87 → 15,83
 
 
d80x25_bottom:
db 186,' KolibriOS ®á­®¢ ­  ­  MenuetOS ¨ … …„Ž‘’€‚‹Ÿ…’ ˆ'
db 'Š€Šˆ• ƒ€A’ˆ‰. ',186
db 186,' ®¤à®¡­¥¥ ᬮâà¨â¥ ¢ ä ©«¥ 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: "
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 " ¦¨¬¨â¥ «î¡ãî ª« ¢¨èã, ¤«ï ¯¥à¥å®¤  ¢ ¢ë¡®à ०¨¬®¢.",0
badsect db 13,10,186," Žè¨¡ª  - „¨áª¥â  ¯®¢à¥¦¤¥­ . ®¯à®¡ã©â¥ ¤àã£ãî.",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
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 db " ¦¬¨â¥ [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
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
/kernel/branches/Kolibri-acpi/boot/bootsp.inc
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
/kernel/branches/Kolibri-acpi/boot/bootvesa.inc
78,7 → 78,7
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
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
/kernel/branches/Kolibri-acpi/boot/ru.inc
11,9 → 11,9
; 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
/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/after_win/kordldr.win.txt
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. Добавляет сектор в конец списка (самый новый вход).
/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/cdfs/bootsect.txt
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. Если же нет, то считываются все сектора, кроме последнего, после чего
последний сектор считывается отдельно во временную область, и уже
оттуда нужная часть данных копируется в буфер.
/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat1x/bootsect.txt
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.
/kernel/branches/Kolibri-acpi/bootloader/extended_primary_loader/fat32/bootsect.txt
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.
/kernel/branches/Kolibri-acpi/bootloader/readme
5,25 → 5,25
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
‡ £à㧮ç­ë© ᥪâ®à ¤«ï Ž‘ Š®«¨¡à¨ (FAT12, ¤¨áª¥â )
Загрузочный сектор для ОС Колибри (FAT12, дискета)
 
- Ž¯¨á ­¨¥
®§¢®«ï¥â § £à㦠âì 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
 
---------------------------------------------------------------------
/kernel/branches/Kolibri-acpi/build.bat
1,6 → 1,6
@echo off
cls
set languages=en ru ge et
set languages=en ru ge et sp
set drivers=com_mouse emu10k1x fm801 infinity sis sound viasound vt823x
set targets=all kernel drivers clean
 
/kernel/branches/Kolibri-acpi/bus/usb/ehci.inc
0,0 → 1,1911
; Code for EHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial
; development, but there are no reasons to keep it here.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; EHCI register declarations.
; Part 1. Capability registers.
; Base is MMIO from the PCI space.
EhciCapLengthReg = 0
EhciVersionReg = 2
EhciStructParamsReg = 4
EhciCapParamsReg = 8
EhciPortRouteReg = 0Ch
; Part 2. Operational registers.
; Base is (base for part 1) + (value of EhciCapLengthReg).
EhciCommandReg = 0
EhciStatusReg = 4
EhciInterruptReg = 8
EhciFrameIndexReg = 0Ch
EhciCtrlDataSegReg = 10h
EhciPeriodicListReg = 14h
EhciAsyncListReg = 18h
EhciConfigFlagReg = 40h
EhciPortsReg = 44h
 
; Possible values of ehci_pipe.NextQH.Type bitfield.
EHCI_TYPE_ITD = 0 ; isochronous transfer descriptor
EHCI_TYPE_QH = 1 ; queue head
EHCI_TYPE_SITD = 2 ; split-transaction isochronous TD
EHCI_TYPE_FSTN = 3 ; frame span traversal node
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
 
; Hardware part of EHCI general transfer descriptor.
struct ehci_hardware_td
NextTD dd ?
; Bit 0 is Terminate bit, 1 = there is no next TD.
; Bits 1-4 must be zero.
; With masked 5 lower bits, this is the physical address of the next TD, if any.
AlternateNextTD dd ?
; Similar to NextTD, used if the transfer terminates with a short packet.
Token dd ?
; 1. Lower byte is Status field:
; bit 0 = ping state for USB2 endpoints, ERR handshake signal for USB1 endpoints
; bit 1 = split transaction state, meaningless for USB2 endpoints
; bit 2 = missed micro-frame
; bit 3 = transaction error
; bit 4 = babble detected
; bit 5 = data buffer error
; bit 6 = halted
; bit 7 = active
; 2. Next two bits (bits 8-9) are PID code, 0 = OUT, 1 = IN, 2 = SETUP.
; 3. Next two bits (bits 10-11) is ErrorCounter. Initialized as 3, decremented
; on each error; if it goes to zero, transaction is stopped.
; 4. Next 3 bits (bits 12-14) are CurrentPage field.
; 5. Next bit (bit 15) is InterruptOnComplete bit.
; 6. Next 15 bits (bits 16-30) are TransferLength field,
; number of bytes to transfer.
; 7. Upper bit (bit 31) is DataToggle bit.
BufferPointers rd 5
; The buffer to be transferred can be spanned on up to 5 physical pages.
; The first item of this array is the physical address of the first byte in
; the buffer, other items are physical addresses of next pages. Lower 12 bits
; in other items must be set to zero; ehci_pipe.Overlay reuses some of them.
BufferPointersHigh rd 5
; Upper dwords of BufferPointers for controllers with 64-bit memory access.
; Always zero.
ends
 
; EHCI general transfer descriptor.
; * The structure describes transfers to be performed on Control, Bulk or
; Interrupt endpoints.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 52 bytes and corresponds to
; the Queue Element Transfer Descriptor from EHCI specification.
; * The hardware requires 32-bytes alignment of the hardware part, so
; the entire descriptor must be 32-bytes aligned. Since the allocator
; (usb_allocate_common) allocates memory sequentially from page start
; (aligned on 0x1000 bytes), size of the structure must be divisible by 32.
; * The hardware also requires that the hardware part must not cross page
; boundary; the allocator satisfies this automatically.
struct ehci_gtd ehci_hardware_td
Flags dd ?
; Copy of flags from the call to usb_*_transfer_async.
SoftwarePart rd sizeof.usb_gtd/4
; Software part, common for all controllers.
rd 3 ; padding
ends
 
if sizeof.ehci_gtd mod 32
.err ehci_gtd must be 32-bytes aligned
end if
 
; EHCI-specific part of a pipe descriptor.
; * This structure corresponds to the Queue Head from the EHCI specification.
; * The hardware requires 32-bytes alignment of the hardware part.
; Since the allocator (usb_allocate_common) allocates memory sequentially
; from page start (aligned on 0x1000 bytes), size of the structure must be
; divisible by 32.
; * The hardware requires also that the hardware part must not cross page
; boundary; the allocator satisfies this automatically.
struct ehci_pipe
NextQH dd ?
; 1. First bit (bit 0) is Terminate bit, 1 = there is no next QH.
; 2. Next two bits (bits 1-2) are Type field of the next QH,
; one of EHCI_TYPE_* constants.
; 3. Next two bits (bits 3-4) are reserved, must be zero.
; 4. With masked 5 lower bits, this is the physical address of the next object
; to be processed, usually next QH.
Token dd ?
; 1. Lower 7 bits are DeviceAddress field. This is the address of the
; target device on the USB bus.
; 2. Next bit (bit 7) is Inactivate-on-next-transaction bit. Can be nonzero
; only for interrupt/isochronous USB1 endpoints.
; 3. Next 4 bits (bits 8-11) are Endpoint field. This is the target endpoint
; number.
; 4. Next 2 bits (bits 12-13) are EndpointSpeed field, one of EHCI_SPEED_*.
; 5. Next bit (bit 14) is DataToggleControl bit,
; 0 = use DataToggle bit from QH, 1 = from TD.
; 6. Next bit (bit 15) is Head-of-reclamation-list. The head of Control list
; has 1 here, all other QHs have zero.
; 7. Next 11 bits (bits 16-26) are MaximumPacketLength field for the target
; endpoint.
; 8. Next bit (bit 27) is ControlEndpoint bit, must be 1 for USB1 control
; endpoints and 0 for all others.
; 9. Upper 4 bits (bits 28-31) are NakCountReload field.
; Zero for USB1 endpoints, zero for periodic endpoints.
; For control/bulk USB2 endpoints, the code sets it to 4,
; which is rather arbitrary.
Flags dd ?
; 1. Lower byte is S-mask, each bit corresponds to one microframe per frame;
; bit is set <=> enable transactions in this microframe.
; 2. Next byte is C-mask, each bit corresponds to one microframe per frame;
; bit is set <=> enable complete-split transactions in this microframe.
; Meaningful only for USB1 endpoints.
; 3. Next 14 bits give address of the target device as hub:port, bits 16-22
; are the USB address of the hub, bits 23-29 are the port number.
; Meaningful only for USB1 endpoints.
; 4. Upper 2 bits define number of consequetive transactions per micro-frame
; which host is allowed to permit for this endpoint.
; For control/bulk endpoints, it must be 1.
; For periodic endpoints, the value is taken from the endpoint descriptor.
HeadTD dd ?
; The physical address of the first TD for this pipe.
; Lower 5 bits must be zero.
Overlay ehci_hardware_td ?
; Working area for the current TD, if there is any.
; When TD is retired, it is written to that TD and Overlay is loaded
; from the new TD, if any.
BaseList dd ?
; Pointer to head of the corresponding pipe list.
SoftwarePart rd sizeof.usb_pipe/4
; Software part, common for all controllers.
rd 2 ; padding
ends
 
if sizeof.ehci_pipe mod 32
.err ehci_pipe must be 32-bytes aligned
end if
 
; This structure describes the static head of every list of pipes.
; The hardware requires 32-bytes alignment of this structure.
; All instances of this structure are located sequentially in ehci_controller,
; ehci_controller is page-aligned, so it is sufficient to make this structure
; 32-bytes aligned and verify that the first instance is 32-bytes aligned
; inside ehci_controller.
; The hardware also requires that 44h bytes (size of 64-bit Queue Head
; Descriptor) starting at the beginning of this structure must not cross page
; boundary. If not, most hardware still behaves correctly (in fact, the last
; dword can have any value and this structure is never written), but on some
; hardware some things just break in mysterious ways.
struct ehci_static_ep
; Hardware fields are the same as in ehci_pipe.
; Only NextQH and Overlay.Token are actually used.
; NB: some emulators ignore Token.Halted bit (probably assuming that it is set
; only when device fails and emulation never fails) and always follow
; [Alternate]NextTD when they see that OverlayToken.Active bit is zero;
; so it is important to also set [Alternate]NextTD to 1.
NextQH dd ?
Token dd ?
Flags dd ?
HeadTD dd ?
NextTD dd ?
AlternateNextTD dd ?
OverlayToken dd ?
NextList dd ?
SoftwarePart rd sizeof.usb_static_ep/4
Bandwidths rw 8
dd ?
ends
 
if sizeof.ehci_static_ep mod 32
.err ehci_static_ep must be 32-bytes aligned
end if
 
if ehci_static_ep.OverlayToken <> ehci_pipe.Overlay.Token
.err ehci_static_ep.OverlayToken misplaced
end if
 
; EHCI-specific part of controller data.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 4096 bytes and corresponds to
; the Periodic Frame List from the EHCI specification.
; * The hardware requires page-alignment of the hardware part, so
; the entire descriptor must be page-aligned.
; This structure is allocated with kernel_alloc (see usb_init_controller),
; this gives page-aligned data.
; * The controller is described by both ehci_controller and usb_controller
; structures, for each controller there is one ehci_controller and one
; usb_controller structure. These structures are located sequentially
; in the memory: beginning from some page start, there is ehci_controller
; structure - this enforces hardware alignment requirements - and then
; usb_controller structure.
; * The code keeps pointer to usb_controller structure. The ehci_controller
; structure is addressed as [ptr + ehci_controller.field - sizeof.ehci_controller].
struct ehci_controller
; ------------------------------ hardware fields ------------------------------
FrameList rd 1024
; Entry n corresponds to the head of the frame list to be executed in
; the frames n,n+1024,n+2048,n+3096,...
; The first bit of each entry is Terminate bit, 1 = the frame is empty.
; Bits 1-2 are Type field, one of EHCI_TYPE_* constants.
; Bits 3-4 must be zero.
; With masked 5 lower bits, the entry is a physical address of the first QH/TD
; to be executed.
; ------------------------------ software fields ------------------------------
; Every list has the static head, which is an always halted QH.
; The following fields are static heads, one per list:
; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list.
IntEDs ehci_static_ep
rb 62 * sizeof.ehci_static_ep
; Beware.
; Two following strings ensure that 44h bytes at any static head
; do not cross page boundary. Without that, the code "works on my machine"...
; but fails on some hardware in seemingly unrelated ways.
; One hardware TD (without any software fields) fit in the rest of the page.
ehci_controller.ControlDelta = 2000h - (ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep)
StopQueueTD ehci_hardware_td
; Used as AlternateNextTD for transfers when short packet is considered
; as an error; short packet must stop the queue in this case, not advance
; to the next transfer.
rb ehci_controller.ControlDelta - sizeof.ehci_hardware_td
; Padding for page-alignment.
ControlED ehci_static_ep
BulkED ehci_static_ep
MMIOBase1 dd ?
; Virtual address of memory-mapped area with part 1 of EHCI registers EhciXxxReg.
MMIOBase2 dd ?
; Pointer inside memory-mapped area MMIOBase1; points to part 2 of EHCI registers.
StructuralParams dd ?
; Copy of EhciStructParamsReg value.
CapabilityParams dd ?
; Copy of EhciCapParamsReg value.
DeferredActions dd ?
; Bitmask of events from EhciStatusReg which were observed by the IRQ handler
; and needs to be processed in the IRQ thread.
ends
 
if ehci_controller.IntEDs mod 32
.err Static endpoint descriptors must be 32-bytes aligned inside ehci_controller
end if
 
; Description of #HCI-specific data and functions for
; controller-independent code.
; Implements the structure usb_hardware_func from hccommon.inc for EHCI.
iglobal
align 4
ehci_hardware_func:
dd 'EHCI'
dd sizeof.ehci_controller
dd ehci_init
dd ehci_process_deferred
dd ehci_set_device_address
dd ehci_get_device_address
dd ehci_port_disable
dd ehci_new_port.reset
dd ehci_set_endpoint_packet_size
dd ehci_alloc_pipe
dd ehci_free_pipe
dd ehci_init_pipe
dd ehci_unlink_pipe
dd ehci_alloc_td
dd ehci_free_td
dd ehci_alloc_transfer
dd ehci_insert_transfer
dd ehci_new_device
endg
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and
; EHCI-specific parts of software structures.
; eax = pointer to ehci_controller to be initialized
; [ebp-4] = pcidevice
proc ehci_init
; inherit some variables from the parent (usb_init_controller)
.devfn equ ebp - 4
.bus equ ebp - 3
; 1. Store pointer to ehci_controller for further use.
push eax
mov edi, eax
mov esi, eax
; 2. Initialize ehci_controller.FrameList.
; Note that FrameList is located in the beginning of ehci_controller,
; so esi and edi now point to ehci_controller.FrameList.
; First 32 entries of FrameList contain physical addresses
; of first 32 Periodic static heads, further entries duplicate these.
; See the description of structures for full info.
; 2a. Get physical address of first static head.
; Note that 1) it is located in the beginning of a page
; and 2) first 32 static heads fit in the same page,
; so one call to get_phys_addr without correction of lower 12 bits
; is sufficient.
if (ehci_controller.IntEDs / 0x1000) <> ((ehci_controller.IntEDs + 32 * sizeof.ehci_static_ep) / 0x1000)
.err assertion failed
end if
if (ehci_controller.IntEDs mod 0x1000) <> 0
.err assertion failed
end if
add eax, ehci_controller.IntEDs
call get_phys_addr
; 2b. Fill first 32 entries.
inc eax
inc eax ; set Type to EHCI_TYPE_QH
push 32
pop ecx
mov edx, ecx
@@:
stosd
add eax, sizeof.ehci_static_ep
loop @b
; 2c. Fill the rest entries.
mov ecx, 1024 - 32
rep movsd
; 3. Initialize static heads ehci_controller.*ED.
; Use the loop over groups: first group consists of first 32 Periodic
; descriptors, next group consists of next 16 Periodic descriptors,
; ..., last group consists of the last Periodic descriptor.
; 3a. Prepare for the loop.
; make esi point to the second group, other registers are already set.
add esi, 32*4 + 32*sizeof.ehci_static_ep
; 3b. Loop over groups. On every iteration:
; edx = size of group, edi = pointer to the current group,
; esi = pointer to the next group.
.init_static_eds:
; 3c. Get the size of next group.
shr edx, 1
; 3d. Exit the loop if there is no next group.
jz .init_static_eds_done
; 3e. Initialize the first half of the current group.
; Advance edi to the second half.
push esi
call ehci_init_static_ep_group
pop esi
; 3f. Initialize the second half of the current group
; with the same values.
; Advance edi to the next group, esi/eax to the next of the next group.
call ehci_init_static_ep_group
jmp .init_static_eds
.init_static_eds_done:
; 3g. Initialize the last static head.
xor esi, esi
call ehci_init_static_endpoint
; While we are here, initialize StopQueueTD.
if (ehci_controller.StopQueueTD <> ehci_controller.IntEDs + 63 * sizeof.ehci_static_ep)
.err assertion failed
end if
inc [edi+ehci_hardware_td.NextTD] ; 0 -> 1
inc [edi+ehci_hardware_td.AlternateNextTD] ; 0 -> 1
; leave other fields as zero, including Active bit
; 3i. Initialize the head of Control list.
add edi, ehci_controller.ControlDelta
lea esi, [edi+sizeof.ehci_static_ep]
call ehci_init_static_endpoint
or byte [edi-sizeof.ehci_static_ep+ehci_static_ep.Token+1], 80h
; 3j. Initialize the head of Bulk list.
sub esi, sizeof.ehci_static_ep
call ehci_init_static_endpoint
; 4. Create a virtual memory area to talk with the controller.
; 4a. Enable memory & bus master access.
mov ch, [.bus]
mov cl, 1
mov eax, ecx
mov bh, [.devfn]
mov bl, 4
call pci_read_reg
or al, 6
xchg eax, ecx
call pci_write_reg
; 4b. Read memory base address.
mov ah, [.bus]
mov al, 2
mov bl, 10h
call pci_read_reg
; DEBUGF 1,'K : phys MMIO %x\n',eax
and al, not 0Fh
; 4c. Create mapping for physical memory. 200h bytes are always sufficient.
stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE
test eax, eax
jz .fail
; DEBUGF 1,'K : MMIO %x\n',eax
if ehci_controller.MMIOBase1 <> ehci_controller.BulkED + sizeof.ehci_static_ep
.err assertion failed
end if
stosd ; fill ehci_controller.MMIOBase1
movzx ecx, byte [eax+EhciCapLengthReg]
mov edx, [eax+EhciCapParamsReg]
mov ebx, [eax+EhciStructParamsReg]
add eax, ecx
if ehci_controller.MMIOBase2 <> ehci_controller.MMIOBase1 + 4
.err assertion failed
end if
stosd ; fill ehci_controller.MMIOBase2
if ehci_controller.StructuralParams <> ehci_controller.MMIOBase2 + 4
.err assertion failed
end if
if ehci_controller.CapabilityParams <> ehci_controller.StructuralParams + 4
.err assertion failed
end if
mov [edi], ebx ; fill ehci_controller.StructuralParams
mov [edi+4], edx ; fill ehci_controller.CapabilityParams
DEBUGF 1,'K : HCSPARAMS=%x, HCCPARAMS=%x\n',ebx,edx
and ebx, 15
mov [edi+usb_controller.NumPorts+sizeof.ehci_controller-ehci_controller.StructuralParams], ebx
mov edi, eax
; now edi = MMIOBase2
; 6. Transfer the controller to a known state.
; 6b. Stop the controller if it is running.
push 10
pop ecx
test dword [edi+EhciStatusReg], 1 shl 12
jnz .stopped
and dword [edi+EhciCommandReg], not 1
@@:
push 1
pop esi
call delay_ms
test dword [edi+EhciStatusReg], 1 shl 12
jnz .stopped
loop @b
dbgstr 'Failed to stop EHCI controller'
jmp .fail_unmap
.stopped:
; 6c. Reset the controller. Wait up to 50 ms checking status every 1 ms.
or dword [edi+EhciCommandReg], 2
push 50
pop ecx
@@:
push 1
pop esi
call delay_ms
test dword [edi+EhciCommandReg], 2
jz .reset_ok
loop @b
dbgstr 'Failed to reset EHCI controller'
jmp .fail_unmap
.reset_ok:
; 7. Configure the controller.
pop esi ; restore the pointer saved at step 1
add esi, sizeof.ehci_controller
; 7a. If the controller is 64-bit, say to it that all structures are located
; in first 4G.
test byte [esi+ehci_controller.CapabilityParams-sizeof.ehci_controller], 1
jz @f
mov dword [edi+EhciCtrlDataSegReg], 0
@@:
; 7b. Hook interrupt and enable appropriate interrupt sources.
mov ah, [.bus]
mov al, 0
mov bh, [.devfn]
mov bl, 3Ch
call pci_read_reg
; al = IRQ
DEBUGF 1,'K : attaching to IRQ %x\n',al
movzx eax, al
stdcall attach_int_handler, eax, ehci_irq, esi
; mov dword [edi+EhciStatusReg], 111111b ; clear status
; disable Frame List Rollover interrupt, enable all other sources
mov dword [edi+EhciInterruptReg], 110111b
; 7c. Inform the controller of the address of periodic lists head.
lea eax, [esi-sizeof.ehci_controller]
call get_phys_addr
mov dword [edi+EhciPeriodicListReg], eax
; 7d. Inform the controller of the address of asynchronous lists head.
lea eax, [esi+ehci_controller.ControlED-sizeof.ehci_controller]
call get_phys_addr
mov dword [edi+EhciAsyncListReg], eax
; 7e. Configure operational details and run the controller.
mov dword [edi+EhciCommandReg], \
(1 shl 16) + \ ; interrupt threshold = 1 microframe = 0.125ms
(0 shl 11) + \ ; disable Async Park Mode
(0 shl 8) + \ ; zero Async Park Mode Count
(1 shl 5) + \ ; Async Schedule Enable
(1 shl 4) + \ ; Periodic Schedule Enable
(0 shl 2) + \ ; 1024 elements in FrameList
1 ; Run
; 7f. Route all ports to this controller, not companion controllers.
mov dword [edi+EhciConfigFlagReg], 1
DEBUGF 1,'K : EHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts]
; 8. Apply port power, if needed, and disable all ports.
xor ecx, ecx
@@:
mov dword [edi+EhciPortsReg+ecx*4], 1000h ; Port Power enabled, all other bits disabled
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb @b
test byte [esi+ehci_controller.StructuralParams-sizeof.ehci_controller], 10h
jz @f
push esi
push 20
pop esi
call delay_ms
pop esi
@@:
; 9. Return pointer to usb_controller.
xchg eax, esi
ret
; On error, pop the pointer saved at step 1 and return zero.
; Note that the main code branch restores the stack at step 7 and never fails
; after step 7.
.fail_unmap:
pop eax
push eax
stdcall free_kernel_space, [eax+ehci_controller.MMIOBase1]
.fail:
pop ecx
xor eax, eax
ret
endp
 
; Helper procedure for step 3 of ehci_init, see comments there.
; Initializes the static head of one list.
; esi = pointer to the "next" list, edi = pointer to head to initialize.
; Advances edi to the next head, keeps esi.
proc ehci_init_static_endpoint
xor eax, eax
inc eax ; set Terminate bit
mov [edi+ehci_static_ep.NextTD], eax
mov [edi+ehci_static_ep.AlternateNextTD], eax
test esi, esi
jz @f
mov eax, esi
call get_phys_addr
inc eax
inc eax ; set Type to EHCI_TYPE_QH
@@:
mov [edi+ehci_static_ep.NextQH], eax
mov [edi+ehci_static_ep.NextList], esi
mov byte [edi+ehci_static_ep.OverlayToken], 1 shl 6 ; halted
add edi, ehci_static_ep.SoftwarePart
call usb_init_static_endpoint
add edi, sizeof.ehci_static_ep - ehci_static_ep.SoftwarePart
ret
endp
 
; Helper procedure for step 3 of ehci_init, see comments there.
; Initializes one half of group of static heads.
; edx = size of the next group = half of size of the group,
; edi = pointer to the group, esi = pointer to the next group.
; Advances esi, edi to next group, keeps edx.
proc ehci_init_static_ep_group
push edx
@@:
call ehci_init_static_endpoint
add esi, sizeof.ehci_static_ep
dec edx
jnz @b
pop edx
ret
endp
 
; Controller-specific pre-initialization function: take ownership from BIOS.
; Some BIOSes, although not all of them, use USB controllers themselves
; to support USB flash drives. In this case,
; we must notify the BIOS that we don't need that emulation and know how to
; deal with USB devices.
proc ehci_kickoff_bios
; 1. Get the physical address of MMIO registers.
mov ah, [esi+PCIDEV.bus]
mov bh, [esi+PCIDEV.devfn]
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh
; 2. Create mapping for physical memory. 200h bytes are always sufficient.
stdcall map_io_mem, eax, 200h, PG_SW+PG_NOCACHE
test eax, eax
jz .nothing
push eax ; push argument for step 8
; 3. Some BIOSes enable controller interrupts as a result of giving
; controller away. At this point the system knows nothing about how to serve
; EHCI interrupts, so such an interrupt will send the system into an infinite
; loop handling the same IRQ again and again. Thus, we need to block EHCI
; interrupts. We can't do this at the controller level until step 5,
; because the controller is currently owned by BIOS, so we block all hardware
; interrupts on this processor until step 5.
pushf
cli
; 4. Take the ownership over the controller.
; 4a. Locate take-ownership capability in the PCI configuration space.
; Limit the loop with 100h iterations; since the entire configuration space is
; 100h bytes long, hitting this number of iterations means that something is
; corrupted.
; Use a value from MMIO as a starting point.
mov edx, [eax+EhciCapParamsReg]
DEBUGF 1,'K : edx=%x\n',edx
movzx edi, byte [eax+EhciCapLengthReg]
add edi, eax
push 0
mov bl, dh ; get Extended Capabilities Pointer
test bl, bl
jz .has_ownership2
cmp bl, 40h
jb .no_capability
.look_bios_handoff:
test bl, 3
jnz .no_capability
; In each iteration, read the current dword,
mov ah, [esi+PCIDEV.bus]
mov al, 2
mov bh, [esi+PCIDEV.devfn]
call pci_read_reg
; check, whether the capability ID is take-ownership ID = 1,
cmp al, 1
jz .found_bios_handoff
; if not, advance to next-capability link and continue loop.
dec byte [esp]
jz .no_capability
mov bl, ah
cmp bl, 40h
jae .look_bios_handoff
.no_capability:
dbgstr 'warning: cannot locate take-ownership capability'
jmp .has_ownership2
.found_bios_handoff:
; 4b. Check whether BIOS has ownership.
; Some BIOSes release ownership before loading OS, but forget to unwatch for
; change-ownership requests; they cannot handle ownership request, so
; such a request sends the system into infinite loop of handling the same SMI
; over and over. Avoid this.
inc ebx
inc ebx
test eax, 0x10000
jz .has_ownership
; 4c. Request ownership.
inc ebx
mov cl, 1
mov ah, [esi+PCIDEV.bus]
mov al, 0
call pci_write_reg
; 4d. Some BIOSes set ownership flag, but forget to watch for change-ownership
; requests; if so, there is no sense in waiting.
inc ebx
mov ah, [esi+PCIDEV.bus]
mov al, 2
call pci_read_reg
dec ebx
dec ebx
test ah, 20h
jz .force_ownership
; 4e. Wait for result no more than 1 s, checking for status every 1 ms.
; If successful, go to 5.
mov dword [esp], 1000
@@:
mov ah, [esi+PCIDEV.bus]
mov al, 0
call pci_read_reg
test al, 1
jz .has_ownership
push esi
push 1
pop esi
call delay_ms
pop esi
dec dword [esp]
jnz @b
dbgstr 'warning: taking EHCI ownership from BIOS timeout'
.force_ownership:
; 4f. BIOS has not responded within the timeout.
; Let's just clear BIOS ownership flag and hope that everything will be ok.
mov ah, [esi+PCIDEV.bus]
mov al, 0
mov cl, 0
call pci_write_reg
.has_ownership:
; 5. Just in case clear all SMI event sources except change-ownership.
dbgstr 'has_ownership'
inc ebx
inc ebx
mov ah, [esi+PCIDEV.bus]
mov al, 2
mov ecx, eax
call pci_read_reg
and ax, 2000h
xchg eax, ecx
call pci_write_reg
.has_ownership2:
pop ecx
; 6. Disable all controller interrupts until the system will be ready to
; process them.
mov dword [edi+EhciInterruptReg], 0
; 7. Now we can unblock interrupts in the processor.
popf
; 8. Release memory mapping created in step 2 and return.
call free_kernel_space
.nothing:
ret
endp
 
; IRQ handler for EHCI controllers.
ehci_irq.noint:
spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock]
; Not our interrupt: restore registers and return zero.
xor eax, eax
pop edi esi ebx
ret
 
proc ehci_irq
push ebx esi edi ; save registers to be cdecl
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.controller dd ?
end virtual
; 1. ebx will hold whether some deferred processing is needed,
; that cannot be done from the interrupt handler. Initialize to zero.
xor ebx, ebx
; 2. Get the mask of events which should be processed.
mov esi, [.controller]
mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller]
spin_lock_irqsave [esi+usb_controller.WaitSpinlock]
mov eax, [edi+EhciStatusReg]
; DEBUGF 1,'K : [%d] EHCI status %x\n',[timer_ticks],eax
; 3. Check whether that interrupt has been generated by our controller.
; (One IRQ can be shared by several devices.)
and eax, [edi+EhciInterruptReg]
jz .noint
; 4. Clear the events we know of.
; Note that this should be done before processing of events:
; new events could arise while we are processing those, this way we won't lose
; them (the controller would generate another interrupt after completion
; of this one).
; DEBUGF 1,'K : EHCI interrupt: status = %x\n',eax
mov [edi+EhciStatusReg], eax
; 5. Sanity check.
test al, 10h
jz @f
DEBUGF 1,'K : something terrible happened with EHCI %x (%x)\n',esi,al
@@:
; We can't do too much from an interrupt handler. Inform the processing thread
; that it should perform appropriate actions.
or [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], eax
spin_unlock_irqrestore [esi+usb_controller.WaitSpinlock]
inc ebx
call usb_wakeup_if_needed
; 6. Interrupt processed; return non-zero.
mov al, 1
pop edi esi ebx ; restore used registers to be cdecl
ret
endp
 
; This procedure is called from usb_set_address_callback
; and stores USB device address in the ehci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
proc ehci_set_device_address
mov byte [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart], cl
call usb_subscribe_control
ret
endp
 
; This procedure returns USB device address from the ehci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = endpoint address
proc ehci_get_device_address
mov eax, [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart]
and eax, 7Fh
ret
endp
 
; This procedure is called from usb_set_address_callback
; if the device does not accept SET_ADDRESS command and needs
; to be disabled at the port level.
; in: esi -> usb_controller, ecx = port (zero-based)
proc ehci_port_disable
mov eax, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller]
and dword [eax+EhciPortsReg+ecx*4], not (4 or 2Ah)
ret
endp
 
; This procedure is called from usb_get_descr8_callback when
; the packet size for zero endpoint becomes known and
; stores the packet size in ehci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
proc ehci_set_endpoint_packet_size
mov eax, [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart]
and eax, not (0x7FF shl 16)
shl ecx, 16
or eax, ecx
mov [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart], eax
; Wait until hardware cache is evicted.
call usb_subscribe_control
ret
endp
 
uglobal
align 4
; Data for memory allocator, see memory.inc.
ehci_ep_first_page dd ?
ehci_ep_mutex MUTEX
ehci_gtd_first_page dd ?
ehci_gtd_mutex MUTEX
endg
 
; This procedure allocates memory for pipe.
; Both hardware+software parts must be allocated, returns pointer to usb_pipe
; (software part).
proc ehci_alloc_pipe
push ebx
mov ebx, ehci_ep_mutex
stdcall usb_allocate_common, sizeof.ehci_pipe
test eax, eax
jz @f
add eax, ehci_pipe.SoftwarePart
@@:
pop ebx
ret
endp
 
; This procedure frees memory for pipe allocated by ehci_alloc_pipe.
; void stdcall with one argument = pointer to usb_pipe.
proc ehci_free_pipe
virtual at esp
dd ? ; return address
.ptr dd ?
end virtual
sub [.ptr], ehci_pipe.SoftwarePart
jmp usb_free_common
endp
 
; This procedure is called from API usb_open_pipe and processes
; the controller-specific part of this API. See docs.
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
proc ehci_init_pipe
virtual at ebp+8
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
; 1. Zero all fields in the hardware part.
push eax ecx
sub edi, ehci_pipe.SoftwarePart
xor eax, eax
push ehci_pipe.SoftwarePart/4
pop ecx
rep stosd
pop ecx eax
; 2. Setup PID in the first TD and make sure that the it is not active.
xor edx, edx
test byte [.endpoint], 80h
setnz dh
mov [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], edx
mov [eax+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], 1
mov [eax+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], 1
; 3. Store physical address of the first TD.
sub eax, ehci_gtd.SoftwarePart
call get_phys_addr
mov [edi+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart], eax
; 4. Fill ehci_pipe.Flags except for S- and C-masks.
; Copy location from the config pipe.
mov eax, [ecx+ehci_pipe.Flags-ehci_pipe.SoftwarePart]
and eax, 3FFF0000h
; Use 1 requests per microframe for control/bulk endpoints,
; use value from the endpoint descriptor for periodic endpoints
push 1
pop edx
test [.type], 1
jz @f
mov edx, [.maxpacket]
shr edx, 11
inc edx
@@:
shl edx, 30
or eax, edx
mov [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart], eax
; 5. Fill ehci_pipe.Token.
mov eax, [ecx+ehci_pipe.Token-ehci_pipe.SoftwarePart]
; copy following fields from the config pipe:
; DeviceAddress, EndpointSpeed, ControlEndpoint if new type is control
mov ecx, eax
and eax, 307Fh
and ecx, 8000000h
or ecx, 4000h
mov edx, [.endpoint]
and edx, 15
shl edx, 8
or eax, edx
mov edx, [.maxpacket]
shl edx, 16
or eax, edx
; for control endpoints, use DataToggle from TD, otherwise use DataToggle from QH
cmp [.type], CONTROL_PIPE
jnz @f
or eax, ecx
@@:
; for control/bulk USB2 endpoints, set NakCountReload to 4
test eax, USB_SPEED_HS shl 12
jz .nonak
cmp [.type], CONTROL_PIPE
jz @f
cmp [.type], BULK_PIPE
jnz .nonak
@@:
or eax, 40000000h
.nonak:
mov [edi+ehci_pipe.Token-ehci_pipe.SoftwarePart], eax
; 5. Select the corresponding list and insert to the list.
; 5a. Use Control list for control pipes, Bulk list for bulk pipes.
lea edx, [esi+ehci_controller.ControlED.SoftwarePart-sizeof.ehci_controller]
cmp [.type], BULK_PIPE
jb .insert ; control pipe
lea edx, [esi+ehci_controller.BulkED.SoftwarePart-sizeof.ehci_controller]
jz .insert ; bulk pipe
.interrupt_pipe:
; 5b. For interrupt pipes, let the scheduler select the appropriate list
; and the appropriate microframe(s) (which goes to S-mask and C-mask)
; based on the current bandwidth distribution and the requested bandwidth.
; There are two schedulers, one for high-speed devices,
; another for split transactions.
; This could fail if the requested bandwidth is not available;
; if so, return an error.
test word [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart+2], 3FFFh
jnz .interrupt_fs
call ehci_select_hs_interrupt_list
jmp .interrupt_common
.interrupt_fs:
call ehci_select_fs_interrupt_list
.interrupt_common:
test edx, edx
jz .return0
mov word [edi+ehci_pipe.Flags-ehci_pipe.SoftwarePart], ax
.insert:
mov [edi+ehci_pipe.BaseList-ehci_pipe.SoftwarePart], edx
; Insert to the head of the corresponding list.
; Note: inserting to the head guarantees that the list traverse in
; ehci_process_updated_schedule, once started, will not interact with new pipes.
; However, we still need to ensure that links in the new pipe (edi.NextVirt)
; are initialized before links to the new pipe (edx.NextVirt).
; 5c. Insert in the list of virtual addresses.
mov ecx, [edx+usb_pipe.NextVirt]
mov [edi+usb_pipe.NextVirt], ecx
mov [edi+usb_pipe.PrevVirt], edx
mov [ecx+usb_pipe.PrevVirt], edi
mov [edx+usb_pipe.NextVirt], edi
; 5d. Insert in the hardware list: copy previous NextQH to the new pipe,
; store the physical address of the new pipe to previous NextQH.
mov ecx, [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart]
mov [edi+ehci_pipe.NextQH-ehci_pipe.SoftwarePart], ecx
lea eax, [edi-ehci_pipe.SoftwarePart]
call get_phys_addr
inc eax
inc eax
mov [edx+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], eax
; 6. Return with nonzero eax.
ret
.return0:
xor eax, eax
ret
endp
 
; This function is called from ehci_process_deferred when
; a new device was connected at least USB_CONNECT_DELAY ticks
; and therefore is ready to be configured.
; ecx = port, esi -> ehci_controller, edi -> EHCI MMIO
proc ehci_new_port
; 1. If the device operates at low-speed, just release it to a companion.
mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x port %d state is %x\n',[timer_ticks],esi,ecx,eax
mov edx, eax
and ah, 0Ch
cmp ah, 4
jz .low_speed
; 2. Devices operating at full-speed and high-speed must now have ah == 8.
; Some broken hardware asserts both D+ and D- even after initial decoupling;
; if so, stop initialization here, no sense in further actions.
cmp ah, 0Ch
jz .se1
; 3. If another port is resetting right now, mark this port as 'reset pending'
; and return.
bts [esi+usb_controller.PendingPorts], ecx
cmp [esi+usb_controller.ResettingPort], -1
jnz .nothing
btr [esi+usb_controller.PendingPorts], ecx
; Otherwise, fall through to ohci_new_port.reset.
 
; This function is called from ehci_new_port and usb_test_pending_port.
; It starts reset signalling for the port. Note that in USB first stages
; of configuration can not be done for several ports in parallel.
.reset:
push edi
mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller]
mov eax, [edi+EhciPortsReg+ecx*4]
; 1. Store information about resetting hub (roothub) and port.
and [esi+usb_controller.ResettingHub], 0
mov [esi+usb_controller.ResettingPort], cl
; 2. Initiate reset signalling.
or ah, 1
and al, not (4 or 2Ah)
mov [edi+EhciPortsReg+ecx*4], eax
; 3. Store the current time and set status to 1 = reset signalling active.
mov eax, [timer_ticks]
mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 1
; dbgstr 'high-speed or full-speed device, resetting'
DEBUGF 1,'K : [%d] EHCI %x: port %d has HS or FS device, resetting\n',[timer_ticks],esi,ecx
pop edi
.nothing:
ret
.low_speed:
; dbgstr 'low-speed device, releasing'
DEBUGF 1,'K : [%d] EHCI %x: port %d has LS device, releasing\n',[timer_ticks],esi,ecx
or dh, 20h
and dl, not 2Ah
mov [edi+EhciPortsReg+ecx*4], edx
ret
.se1:
dbgstr 'SE1 after connect debounce. Broken hardware?'
ret
endp
 
; This procedure is called from several places in main USB code
; and allocates required packets for the given transfer.
; ebx = pipe, other parameters are passed through the stack:
; buffer,size = data to transfer
; flags = same as in usb_open_pipe: bit 0 = allow short transfer, other bits reserved
; td = pointer to the current end-of-queue descriptor
; direction =
; 0000b for normal transfers,
; 1000b for control SETUP transfer,
; 1101b for control OUT transfer,
; 1110b for control IN transfer
; returns eax = pointer to the new end-of-queue descriptor
; (not included in the queue itself) or 0 on error
proc ehci_alloc_transfer stdcall uses edi, \
buffer:dword, size:dword, flags:dword, td:dword, direction:dword
locals
origTD dd ?
packetSize dd ? ; must be last variable, see usb_init_transfer
endl
; 1. Save original value of td:
; it will be useful for rollback if something would fail.
mov eax, [td]
mov [origTD], eax
; One transfer descriptor can describe up to 5 pages.
; In the worst case (when the buffer is something*1000h+0FFFh)
; this corresponds to 4001h bytes. If the requested size is
; greater, we should split the transfer into several descriptors.
; Boundaries to split must be multiples of endpoint transfer size
; to avoid short packets except in the end of the transfer,
; 4000h is always a good value.
; 2. While the remaining data cannot fit in one descriptor,
; allocate full descriptors (of maximal possible size).
mov edi, 4000h
mov [packetSize], edi
.fullpackets:
cmp [size], edi
jbe .lastpacket
call ehci_alloc_packet
test eax, eax
jz .fail
mov [td], eax
add [buffer], edi
sub [size], edi
jmp .fullpackets
; 3. The remaining data can fit in one packet;
; allocate the last descriptor with size = size of remaining data.
.lastpacket:
mov eax, [size]
mov [packetSize], eax
call ehci_alloc_packet
test eax, eax
jz .fail
; 9. Update flags in the last packet.
mov edx, [flags]
mov [ecx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], edx
; 10. Fill AlternateNextTD field in all allocated TDs.
; If the caller says that short transfer is ok, the queue must advance to
; the next descriptor, which is in eax.
; Otherwise, the queue should stop, so make AlternateNextTD point to
; always-inactive descriptor StopQueueTD.
push eax
test dl, 1
jz .disable_short
sub eax, ehci_gtd.SoftwarePart
jmp @f
.disable_short:
mov eax, [ebx+usb_pipe.Controller]
add eax, ehci_controller.StopQueueTD - sizeof.ehci_controller
@@:
call get_phys_addr
mov edx, [origTD]
@@:
cmp edx, [esp]
jz @f
mov [edx+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], eax
mov edx, [edx+usb_gtd.NextVirt]
jmp @b
@@:
pop eax
ret
.fail:
mov edi, ehci_hardware_func
mov eax, [td]
stdcall usb_undo_tds, [origTD]
xor eax, eax
ret
endp
 
; Helper procedure for ehci_alloc_transfer.
; Allocates and initializes one transfer descriptor.
; ebx = pipe, other parameters are passed through the stack;
; fills the current last descriptor and
; returns eax = next descriptor (not filled).
proc ehci_alloc_packet
; inherit some variables from the parent ehci_alloc_transfer
virtual at ebp-8
.origTD dd ?
.packetSize dd ?
rd 2
.buffer dd ?
.transferSize dd ?
.Flags dd ?
.td dd ?
.direction dd ?
end virtual
; 1. Allocate the next TD.
call ehci_alloc_td
test eax, eax
jz .nothing
; 2. Initialize controller-independent parts of both TDs.
push eax
call usb_init_transfer
pop eax
; 3. Copy PID to the new descriptor.
mov edx, [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart]
mov [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], edx
mov [eax+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], 1
mov [eax+ehci_gtd.AlternateNextTD-ehci_gtd.SoftwarePart], 1
; 4. Save the returned value (next descriptor).
push eax
; 5. Store the physical address of the next descriptor.
sub eax, ehci_gtd.SoftwarePart
call get_phys_addr
mov [ecx+ehci_gtd.NextTD-ehci_gtd.SoftwarePart], eax
; 6. For zero-length transfers, store zero in all fields for buffer addresses.
; Otherwise, fill them with real values.
xor eax, eax
mov [ecx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], eax
repeat 10
mov [ecx+ehci_gtd.BufferPointers-ehci_gtd.SoftwarePart+(%-1)*4], eax
end repeat
cmp [.packetSize], eax
jz @f
mov eax, [.buffer]
call get_phys_addr
mov [ecx+ehci_gtd.BufferPointers-ehci_gtd.SoftwarePart], eax
and eax, 0xFFF
mov edx, [.packetSize]
add edx, eax
sub edx, 0x1000
jbe @f
mov eax, [.buffer]
add eax, 0x1000
call get_pg_addr
mov [ecx+ehci_gtd.BufferPointers+4-ehci_gtd.SoftwarePart], eax
sub edx, 0x1000
jbe @f
mov eax, [.buffer]
add eax, 0x2000
call get_pg_addr
mov [ecx+ehci_gtd.BufferPointers+8-ehci_gtd.SoftwarePart], eax
sub edx, 0x1000
jbe @f
mov eax, [.buffer]
add eax, 0x3000
call get_pg_addr
mov [ecx+ehci_gtd.BufferPointers+12-ehci_gtd.SoftwarePart], eax
sub edx, 0x1000
jbe @f
mov eax, [.buffer]
add eax, 0x4000
call get_pg_addr
mov [ecx+ehci_gtd.BufferPointers+16-ehci_gtd.SoftwarePart], eax
@@:
; 7. Fill Token field:
; set Status = 0 (inactive, ehci_insert_transfer would mark everything active);
; keep current PID if [.direction] is zero, use two lower bits of [.direction]
; otherwise shifted as (0|1|2) -> (2|0|1);
; set error counter to 3;
; set current page to 0;
; do not interrupt on complete (ehci_insert_transfer sets this bit where needed);
; set DataToggle to bit 2 of [.direction].
mov eax, [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart]
and eax, 300h ; keep PID code
mov edx, [.direction]
test edx, edx
jz .haspid
and edx, 3
dec edx
jns @f
add edx, 3
@@:
mov ah, dl
mov edx, [.direction]
and edx, not 3
shl edx, 29
or eax, edx
.haspid:
or eax, 0C00h
mov edx, [.packetSize]
shl edx, 16
or eax, edx
mov [ecx+ehci_gtd.Token-ehci_gtd.SoftwarePart], eax
; 4. Restore the returned value saved in step 2.
pop eax
.nothing:
ret
endp
 
; This procedure is called from several places in main USB code
; and activates the transfer which was previously allocated by
; ehci_alloc_transfer.
; ecx -> last descriptor for the transfer, ebx -> usb_pipe
proc ehci_insert_transfer
or byte [ecx+ehci_gtd.Token+1-ehci_gtd.SoftwarePart], 80h ; set IOC bit
mov eax, [esp+4]
.activate:
or byte [eax+ehci_gtd.Token-ehci_gtd.SoftwarePart], 80h ; set Active bit
cmp eax, ecx
mov eax, [eax+usb_gtd.NextVirt]
jnz .activate
ret
endp
 
; This function is called from ehci_process_deferred when
; reset signalling for a new device needs to be finished.
proc ehci_port_reset_done
movzx ecx, [esi+usb_controller.ResettingPort]
and dword [edi+EhciPortsReg+ecx*4], not 12Ah
mov eax, [timer_ticks]
mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 2
DEBUGF 1,'K : [%d] EHCI %x: reset port %d done\n',[timer_ticks],esi,ecx
ret
endp
 
; This function is called from ehci_process_deferred when
; a new device has been reset, recovered after reset and needs to be configured.
proc ehci_port_init
; 1. Get the status and set it to zero.
; If reset has been failed (device disconnected during reset),
; continue to next device (if there is one).
xor eax, eax
xchg al, [esi+usb_controller.ResettingStatus]
test al, al
js usb_test_pending_port
; 2. Get the port status. High-speed devices should be now enabled,
; full-speed devices are left disabled;
; if the port is disabled, release it to a companion and continue to
; next device (if there is one).
movzx ecx, [esi+usb_controller.ResettingPort]
mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x status of port %d is %x\n',[timer_ticks],esi,ecx,eax
test al, 4
jnz @f
; DEBUGF 1,'K : USB port disabled after reset, status = %x\n',eax
dbgstr 'releasing to companion'
or ah, 20h
mov [edi+EhciPortsReg+ecx*4], eax
jmp usb_test_pending_port
@@:
; 3. Call the worker procedure to notify the protocol layer
; about new EHCI device. It is high-speed.
push USB_SPEED_HS
pop eax
call ehci_new_device
test eax, eax
jnz .nothing
; 4. If something at the protocol layer has failed
; (no memory, no bus address), disable the port and stop the initialization.
.disable_exit:
and dword [edi+EhciPortsReg+ecx*4], not (4 or 2Ah)
jmp usb_test_pending_port
.nothing:
ret
endp
 
; This procedure is called from ehci_port_init and from hub support code
; when a new device is connected and has been reset.
; It calls usb_new_device at the protocol layer with correct parameters.
; in: esi -> usb_controller, eax = speed.
proc ehci_new_device
push ebx ecx ; save used registers (ecx is important for ehci_port_init)
; 1. Store the speed for the protocol layer.
mov [esi+usb_controller.ResettingSpeed], al
; 2. Shift speed bits to the proper place in ehci_pipe.Token.
shl eax, 12
; 3. For high-speed devices, go to step 5 with edx = 0.
xor edx, edx
cmp ah, USB_SPEED_HS shl (12-8)
jz .common
; 4. For low-speed and full-speed devices, fill address:port
; of the last high-speed hub (the closest to the device hub)
; for split transactions, and set ControlEndpoint bit in eax;
; ehci_init_pipe assumes that the parent pipe is a control pipe.
movzx ecx, [esi+usb_controller.ResettingPort]
mov edx, [esi+usb_controller.ResettingHub]
push eax
.find_hs_hub:
mov eax, [edx+usb_hub.ConfigPipe]
mov eax, [eax+usb_pipe.DeviceData]
cmp [eax+usb_device_data.Speed], USB_SPEED_HS
jz .found_hs_hub
movzx ecx, [eax+usb_device_data.Port]
mov edx, [eax+usb_device_data.Hub]
jmp .find_hs_hub
.found_hs_hub:
mov edx, [edx+usb_hub.ConfigPipe]
inc ecx
mov edx, [edx+ehci_pipe.Token-ehci_pipe.SoftwarePart]
shl ecx, 23
and edx, 7Fh
shl edx, 16
or edx, ecx ; ehci_pipe.Flags
pop eax
or eax, 1 shl 27 ; ehci_pipe.Token
.common:
; 5. Create pseudo-pipe in the stack.
; See ehci_init_pipe: only .Controller, .Token, .Flags fields are used.
push esi ; ehci_pipe.SoftwarePart.Controller
mov ecx, esp
sub esp, ehci_pipe.SoftwarePart - ehci_pipe.Flags - 4
push edx ; ehci_pipe.Flags
push eax ; ehci_pipe.Token
; 6. Notify the protocol layer.
call usb_new_device
; 7. Cleanup the stack after step 5 and return.
add esp, ehci_pipe.SoftwarePart - ehci_pipe.Flags + 8
pop ecx ebx ; restore used registers
ret
endp
 
; This procedure is called in the USB thread from usb_thread_proc,
; processes regular actions and those actions which can't be safely done
; from interrupt handler.
; Returns maximal time delta before the next call.
proc ehci_process_deferred
push ebx edi ; save used registers to be stdcall
mov edi, [esi+ehci_controller.MMIOBase2-sizeof.ehci_controller]
; 1. Get the mask of events to process.
xor eax, eax
xchg eax, [esi+ehci_controller.DeferredActions-sizeof.ehci_controller]
push eax
; 2. Initialize the return value.
push -1
; Handle roothub events.
; 3a. Test whether there are such events.
test al, 4
jz .skip_roothub
; Status of some port has changed. Loop over all ports.
; 3b. Prepare for the loop: start from port 0.
xor ecx, ecx
.portloop:
; 3c. Get the port status and changes of it.
; If there are no changes, just continue to the next port.
mov eax, [edi+EhciPortsReg+ecx*4]
test al, 2Ah
jz .nextport
; 3d. Clear change bits and read the status again.
; (It is possible, although quite unlikely, that some event occurs between
; the first read and the clearing, invalidating the old status. If an event
; occurs after the clearing, we will not miss it, looking in the next scan.
mov [edi+EhciPortsReg+ecx*4], eax
mov ebx, eax
mov eax, [edi+EhciPortsReg+ecx*4]
DEBUGF 1,'K : [%d] EHCI %x: status of port %d changed to %x\n',[timer_ticks],esi,ecx,ebx
; 3e. Handle overcurrent.
; Note: that needs work.
test bl, 20h ; overcurrent change
jz .noovercurrent
test al, 10h ; overcurrent active
jz .noovercurrent
DEBUGF 1,'K : overcurrent at port %d\n',ecx
.noovercurrent:
; 3f. Handle changing of connection status.
test bl, 2
jz .nocsc
; There was a connect or disconnect event at this port.
; 3g. Disconnect the old device on this port, if any.
; If the port was resetting, indicate fail; later stages will process it.
cmp [esi+usb_controller.ResettingHub], 0
jnz @f
cmp cl, [esi+usb_controller.ResettingPort]
jnz @f
mov [esi+usb_controller.ResettingStatus], -1
@@:
bts [esi+usb_controller.NewDisconnected], ecx
; 3h. Change connected status. For the connection event, also store
; the connection time; any further processing is permitted only after
; USB_CONNECT_DELAY ticks.
test al, 1
jz .disconnect
mov eax, [timer_ticks]
mov [esi+usb_controller.ConnectedTime+ecx*4], eax
bts [esi+usb_controller.NewConnected], ecx
jmp .nextport
.disconnect:
btr [esi+usb_controller.NewConnected], ecx
jmp .nextport
.nocsc:
; 3i. Handle port disabling.
; Note: that needs work.
test al, 8
jz @f
test al, 4
jz @f
DEBUGF 1,'K : port %d disabled\n',ecx
@@:
; 3j. Continue the loop for the next port.
.nextport:
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop
.skip_roothub:
; 4. Process disconnect events. This should be done after step 3
; (which includes the first stage of disconnect processing).
call usb_disconnect_stage2
; 5. Check for previously connected devices.
; If there is a connected device which was connected less than
; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over.
; Otherwise, call ehci_new_port.
; This should be done after step 3.
xor ecx, ecx
cmp [esi+usb_controller.NewConnected], ecx
jz .skip_newconnected
.portloop2:
bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY
jge .connected
neg eax
cmp [esp], eax
jb .nextport2
mov [esp], eax
jmp .nextport2
.connected:
btr [esi+usb_controller.NewConnected], ecx
call ehci_new_port
jmp .portloop2
.noconnect:
.nextport2:
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop2
.skip_newconnected:
; 6. Process wait lists.
; 6a. Periodic endpoints.
; If a request is pending >8 microframes, satisfy it.
; If a request is pending <=8 microframes, schedule next wakeup in 0.01s.
mov eax, [esi+usb_controller.WaitPipeRequestPeriodic]
cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic]
jz .noperiodic
mov edx, [edi+EhciFrameIndexReg]
sub edx, [esi+usb_controller.StartWaitFrame]
and edx, 0x3FFF
cmp edx, 8
jbe @f
mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax
jmp .noperiodic
@@:
pop eax
push 1 ; wakeup in 0.01 sec for next test
.noperiodic:
; 6b. Asynchronous endpoints.
; Satisfy a request when InterruptOnAsyncAdvance fired.
test byte [esp+4], 20h
jz @f
dbgstr 'async advance int'
mov eax, [esi+usb_controller.WaitPipeRequestAsync]
mov [esi+usb_controller.ReadyPipeHeadAsync], eax
@@:
; Some hardware in some (rarely) conditions set the status bit,
; but just does not generate the corresponding interrupt.
; Force checking the status here.
mov eax, [esi+usb_controller.WaitPipeRequestAsync]
cmp [esi+usb_controller.ReadyPipeHeadAsync], eax
jz .noasync
spin_lock_irq [esi+usb_controller.WaitSpinlock]
mov edx, [edi+EhciStatusReg]
test dl, 20h
jz @f
mov dword [edi+EhciStatusReg], 20h
and dword [esi+ehci_controller.DeferredActions-sizeof.ehci_controller], not 20h
dbgstr 'warning: async advance int missed'
mov [esi+usb_controller.ReadyPipeHeadAsync], eax
jmp .async_unlock
@@:
cmp dword [esp], 100
jb .async_unlock
mov dword [esp], 100
.async_unlock:
spin_unlock_irq [esi+usb_controller.WaitSpinlock]
.noasync:
; 7. Finalize transfers processed by hardware.
; It is better to perform this step after step 4 (disconnect events),
; although not strictly obligatory. This way, an active transfer aborted
; due to disconnect would be handled with more specific USB_STATUS_CLOSED,
; not USB_STATUS_NORESPONSE.
test byte [esp+4], 3
jz @f
call ehci_process_updated_schedule
@@:
; 8. Test whether reset signalling has been started and should be stopped now.
; This must be done after step 7, because completion of some transfer could
; result in resetting a new port.
.test_reset:
; 8a. Test whether reset signalling is active.
cmp [esi+usb_controller.ResettingStatus], 1
jnz .no_reset_in_progress
; 8b. Yep. Test whether it should be stopped.
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_TIME
jge .reset_done
; 8c. Not yet, but initiate wakeup in -eax ticks and exit this step.
neg eax
cmp [esp], eax
jb .skip_reset
mov [esp], eax
jmp .skip_reset
.reset_done:
; 8d. Yep, call the worker function and proceed to 8e.
call ehci_port_reset_done
.no_reset_in_progress:
; 8e. Test whether reset process is done, either successful or failed.
cmp [esi+usb_controller.ResettingStatus], 0
jz .skip_reset
; 8f. Yep. Test whether it should be stopped.
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME
jge .reset_recovery_done
; 8g. Not yet, but initiate wakeup in -eax ticks and exit this step.
neg eax
cmp [esp], eax
jb .skip_reset
mov [esp], eax
jmp .skip_reset
.reset_recovery_done:
; 8h. Yep, call the worker function. This could initiate another reset,
; so return to the beginning of this step.
call ehci_port_init
jmp .test_reset
.skip_reset:
; 9. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 4 and 7 which could create new requests.
; 9a. Call the worker function.
call usb_process_wait_lists
; 9b. If it reports that an asynchronous endpoint should be removed,
; doorbell InterruptOnAsyncAdvance and schedule wakeup in 1s
; (sometimes it just does not fire).
test al, 1 shl CONTROL_PIPE
jz @f
mov edx, [esi+usb_controller.WaitPipeListAsync]
mov [esi+usb_controller.WaitPipeRequestAsync], edx
or dword [edi+EhciCommandReg], 1 shl 6
dbgstr 'async advance doorbell'
cmp dword [esp], 100
jb @f
mov dword [esp], 100
@@:
; 9c. If it reports that a periodic endpoint should be removed,
; save the current frame and schedule wakeup in 0.01 sec.
test al, 1 shl INTERRUPT_PIPE
jz @f
mov eax, [esi+usb_controller.WaitPipeListPeriodic]
mov [esi+usb_controller.WaitPipeRequestPeriodic], eax
mov edx, [edi+EhciFrameIndexReg]
mov [esi+usb_controller.StartWaitFrame], edx
mov dword [esp], 1 ; wakeup in 0.01 sec for next test
@@:
; 10. Pop the return value, restore the stack after step 1 and return.
pop eax
pop ecx
pop edi ebx ; restore used registers to be stdcall
ret
endp
 
; This procedure is called in the USB thread from ehci_process_deferred
; when EHCI IRQ handler has signalled that new IOC-packet was processed.
; It scans all lists for completed packets and calls ehci_process_finalized_td
; for those packets.
proc ehci_process_updated_schedule
; Important note: we cannot hold the list lock during callbacks,
; because callbacks sometimes open and/or close pipes and thus acquire/release
; the corresponding lock itself.
; Fortunately, pipes can be finally freed only by another step of
; ehci_process_deferred, so all pipes existing at the start of this function
; will be valid while this function is running. Some pipes can be removed
; from the corresponding list, some pipes can be inserted; insert/remove
; functions guarantee that traversing one list yields all pipes that were in
; that list at the beginning of the traversing (possibly with some new pipes,
; possibly without some new pipes, that doesn't matter).
push edi
; 1. Process all Periodic lists.
lea edi, [esi+ehci_controller.IntEDs-sizeof.ehci_controller+ehci_static_ep.SoftwarePart]
lea ebx, [esi+ehci_controller.IntEDs+63*sizeof.ehci_static_ep-sizeof.ehci_controller+ehci_static_ep.SoftwarePart]
@@:
call ehci_process_updated_list
cmp edi, ebx
jnz @b
; 2. Process the Control list.
add edi, ehci_controller.ControlDelta
call ehci_process_updated_list
; 3. Process the Bulk list.
call ehci_process_updated_list
; 4. Return.
pop edi
ret
endp
 
; This procedure is called from ehci_process_updated_schedule, see comments there.
; It processes one list, esi -> usb_controller, edi -> usb_static_ep,
; and advances edi to next head.
proc ehci_process_updated_list
push ebx
; 1. Perform the external loop over all pipes.
mov ebx, [edi+usb_static_ep.NextVirt]
.loop:
cmp ebx, edi
jz .done
; store pointer to the next pipe in the stack
push [ebx+usb_static_ep.NextVirt]
; 2. For every pipe, perform the internal loop over all descriptors.
; All descriptors are organized in the queue; we process items from the start
; of the queue until a) the last descriptor (not the part of the queue itself)
; or b) an active (not yet processed by the hardware) descriptor is reached.
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
mov ebx, [ebx+usb_pipe.LastTD]
push ebx
mov ebx, [ebx+usb_gtd.NextVirt]
.tdloop:
; 3. For every descriptor, test active flag and check for end-of-queue;
; if either of conditions holds, exit from the internal loop.
cmp ebx, [esp]
jz .tddone
cmp byte [ebx+ehci_gtd.Token-ehci_gtd.SoftwarePart], 0
js .tddone
; Release the queue lock while processing one descriptor:
; callback function could (and often would) schedule another transfer.
push ecx
call mutex_unlock
call ehci_process_updated_td
pop ecx
call mutex_lock
jmp .tdloop
.tddone:
call mutex_unlock
pop ebx
; End of internal loop, restore pointer to the next pipe
; and continue the external loop.
pop ebx
jmp .loop
.done:
pop ebx
add edi, sizeof.ehci_static_ep
ret
endp
 
; This procedure is called from ehci_process_updated_list, which is itself
; called from ehci_process_updated_schedule, see comments there.
; It processes one completed descriptor.
; in: ebx -> usb_gtd, out: ebx -> next usb_gtd.
proc ehci_process_updated_td
; mov eax, [ebx+usb_gtd.Pipe]
; cmp [eax+usb_pipe.Type], INTERRUPT_PIPE
; jnz @f
; DEBUGF 1,'K : finalized TD for pipe %x:\n',eax
; lea eax, [ebx-ehci_gtd.SoftwarePart]
; DEBUGF 1,'K : %x %x %x %x\n',[eax],[eax+4],[eax+8],[eax+12]
; DEBUGF 1,'K : %x %x %x %x\n',[eax+16],[eax+20],[eax+24],[eax+28]
;@@:
; 1. Remove this descriptor from the list of descriptors for this pipe.
call usb_unlink_td
; 2. Calculate actual number of bytes transferred.
mov eax, [ebx+ehci_gtd.Token-ehci_gtd.SoftwarePart]
lea edx, [eax+eax]
shr edx, 17
sub edx, [ebx+usb_gtd.Length]
neg edx
; 3. Check whether we need some special processing beyond notifying the driver.
; Transfer errors require special processing.
; Short packets require special processing if
; a) this is not the last descriptor for transfer stage
; (in this case we need to process subsequent descriptors for the stage too)
; or b) the caller considers short transfers to be an error.
; ehci_alloc_transfer sets bit 0 of ehci_gtd.Flags to 0 if short packet
; in this descriptor requires special processing and to 1 otherwise.
; If special processing is not needed, advance to 4 with ecx = 0.
; Otherwise, go to 6.
xor ecx, ecx
test al, 40h
jnz .error
test byte [ebx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], 1
jnz .notify
cmp edx, [ebx+usb_gtd.Length]
jnz .special
.notify:
; 4. Either the descriptor in ebx was processed without errors,
; or all necessary error actions were taken and ebx points to the last
; related descriptor.
; 4a. Test whether it is the last descriptor in the transfer
; <=> it has an associated callback.
mov eax, [ebx+usb_gtd.Callback]
test eax, eax
jz .nocallback
; 4b. It has an associated callback; call it with corresponding parameters.
stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .callback
.nocallback:
; 4c. It is an intermediate descriptor. Add its length to the length
; in the following descriptor.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.callback:
; 5. Free the current descriptor and return the next one.
push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx
pop ebx
ret
.error:
push ebx
sub ebx, ehci_gtd.SoftwarePart
DEBUGF 1,'K : TD failed:\n'
DEBUGF 1,'K : %x %x %x %x\n',[ebx],[ebx+4],[ebx+8],[ebx+12]
DEBUGF 1,'K : %x %x %x %x\n',[ebx+16],[ebx+20],[ebx+24],[ebx+28]
pop ebx
DEBUGF 1,'K : pipe now:\n'
mov ecx, [ebx+usb_gtd.Pipe]
sub ecx, ehci_pipe.SoftwarePart
DEBUGF 1,'K : %x %x %x %x\n',[ecx],[ecx+4],[ecx+8],[ecx+12]
DEBUGF 1,'K : %x %x %x %x\n',[ecx+16],[ecx+20],[ecx+24],[ecx+28]
DEBUGF 1,'K : %x %x %x %x\n',[ecx+32],[ecx+36],[ecx+40],[ecx+44]
.special:
; 6. Special processing is needed.
; 6a. Save the status and length.
push edx
push eax
; 6b. Traverse the list of descriptors looking for the final descriptor
; for this transfer. Free and unlink non-final descriptors.
; Final descriptor will be freed in step 5.
.look_final:
call usb_is_final_packet
jnc .found_final
push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx
pop ebx
call usb_unlink_td
jmp .look_final
.found_final:
; 6c. Restore the status saved in 6a and transform it to the error code.
; Notes:
; * any USB transaction error results in Halted bit; if it is not set,
; but we are here, it must be due to short packet;
; * babble is considered a fatal USB transaction error,
; other errors just lead to retrying the transaction;
; if babble is detected, return the corresponding error;
; * if several non-fatal errors have occured during transaction retries,
; all corresponding bits are set. In this case, return some error code,
; the order is quite arbitrary.
pop eax ; status
push USB_STATUS_UNDERRUN
pop ecx
test al, 40h ; not Halted?
jz .know_error
mov cl, USB_STATUS_OVERRUN
test al, 10h ; Babble detected?
jnz .know_error
mov cl, USB_STATUS_BUFOVERRUN
test al, 20h ; Data Buffer error?
jnz .know_error
mov cl, USB_STATUS_NORESPONSE
test al, 8 ; Transaction Error?
jnz .know_error
mov cl, USB_STATUS_STALL
.know_error:
; 6d. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets,
; it is not an error; in this case, go to 4 with ecx = 0.
cmp ecx, USB_STATUS_UNDERRUN
jnz @f
test byte [ebx+ehci_gtd.Flags-ehci_gtd.SoftwarePart], 1
jz @f
xor ecx, ecx
pop edx ; length
jmp .notify
@@:
; 6e. Abort the entire transfer.
; There are two cases: either there is only one transfer stage
; (everything except control transfers), then ebx points to the last TD and
; all previous TD were unlinked and dismissed (if possible),
; or there are several stages (a control transfer) and ebx points to the last
; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage,
; because Setup stage can not produce short packets); for Data stage, we need
; to unlink and free (if possible) one more TD and advance ebx to the next one.
cmp [ebx+usb_gtd.Callback], 0
jnz .normal
push ecx
push [ebx+usb_gtd.NextVirt]
stdcall ehci_free_td, ebx
pop ebx
call usb_unlink_td
pop ecx
.normal:
; 6f. For bulk/interrupt transfers we have no choice but halt the queue,
; the driver should intercede (through some API which is not written yet).
; Control pipes normally recover at the next SETUP transaction (first stage
; of any control transfer), so we hope on the best and just advance the queue
; to the next transfer. (According to the standard, "A control pipe may also
; support functional stall as well, but this is not recommended.").
mov edx, [ebx+usb_gtd.Pipe]
mov eax, [ebx+ehci_gtd.NextTD-ehci_gtd.SoftwarePart]
or al, 1
mov [edx+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart], eax
mov [edx+ehci_pipe.Overlay.AlternateNextTD-ehci_pipe.SoftwarePart], eax
cmp [edx+usb_pipe.Type], CONTROL_PIPE
jz .control
; Bulk/interrupt transfer; halt the queue.
mov [edx+ehci_pipe.Overlay.Token-ehci_pipe.SoftwarePart], 40h
pop edx
jmp .notify
; Control transfer.
.control:
and [edx+ehci_pipe.Overlay.Token-ehci_pipe.SoftwarePart], 0
dec [edx+ehci_pipe.Overlay.NextTD-ehci_pipe.SoftwarePart]
pop edx
jmp .notify
endp
 
; This procedure unlinks the pipe from the corresponding pipe list.
; esi -> usb_controller, ebx -> usb_pipe
proc ehci_unlink_pipe
cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE
jnz @f
test word [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart+2], 3FFFh
jnz .interrupt_fs
call ehci_hs_interrupt_list_unlink
jmp .interrupt_common
.interrupt_fs:
call ehci_fs_interrupt_list_unlink
.interrupt_common:
@@:
mov edx, [ebx+usb_pipe.NextVirt]
mov eax, [ebx+usb_pipe.PrevVirt]
mov [edx+usb_pipe.PrevVirt], eax
mov [eax+usb_pipe.NextVirt], edx
mov edx, esi
sub edx, eax
cmp edx, sizeof.ehci_controller
mov edx, [ebx+ehci_pipe.NextQH-ehci_pipe.SoftwarePart]
jb .prev_is_static
mov [eax+ehci_pipe.NextQH-ehci_pipe.SoftwarePart], edx
ret
.prev_is_static:
mov [eax+ehci_static_ep.NextQH-ehci_static_ep.SoftwarePart], edx
ret
endp
 
proc ehci_alloc_td
push ebx
mov ebx, ehci_gtd_mutex
stdcall usb_allocate_common, sizeof.ehci_gtd
test eax, eax
jz @f
add eax, ehci_gtd.SoftwarePart
@@:
pop ebx
ret
endp
 
; This procedure is called from several places from main USB code and
; frees all additional data associated with the transfer descriptor.
; EHCI has no additional data, so just free ehci_gtd structure.
proc ehci_free_td
sub dword [esp+4], ehci_gtd.SoftwarePart
jmp usb_free_common
endp
/kernel/branches/Kolibri-acpi/bus/usb/hccommon.inc
0,0 → 1,472
; USB Host Controller support code: hardware-independent part,
; common for all controller types.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; USB device must have at least 100ms of stable power before initializing can
; proceed; one timer tick is 10ms, so enforce delay in 10 ticks
USB_CONNECT_DELAY = 10
; USB requires at least 10 ms for reset signalling. Normally, this is one timer
; tick. However, it is possible that we start reset signalling in the end of
; interval between timer ticks and then we test time in the start of the next
; interval; in this case, the delta between [timer_ticks] is 1, but the real
; time passed is significantly less than 10 ms. To avoid this, we add an extra
; tick; this guarantees that at least 10 ms have passed.
USB_RESET_TIME = 2
; USB requires at least 10 ms of reset recovery, a delay between reset
; signalling and any commands to device. Add an extra tick for the same reasons
; as with the previous constant.
USB_RESET_RECOVERY_TIME = 2
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; Controller descriptor.
; This structure represents the common (controller-independent) part
; of a controller for the USB code. The corresponding controller-dependent
; part *hci_controller is located immediately before usb_controller.
struct usb_controller
; Two following fields organize all controllers in the global linked list.
Next dd ?
Prev dd ?
HardwareFunc dd ?
; Pointer to usb_hardware_func structure with controller-specific functions.
NumPorts dd ?
; Number of ports in the root hub.
SetAddressBuffer rb 8
; Buffer for USB control command SET_ADDRESS.
ExistingAddresses rd 128/32
; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating
; for new devices. Bit 0 is always set.
;
; The hardware is allowed to cache some data from hardware structures.
; Regular operations are designed considering this,
; but sometimes it is required to wait for synchronization of hardware cache
; with modified structures in memory.
; The code keeps two queues of pipes waiting for synchronization,
; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware
; cache is invalidated under different conditions for those types.
; Both queues are organized in the same way, as single-linked lists.
; There are three special positions: the head of list (new pipes are added
; here), the first pipe to be synchronized at the current iteration,
; the tail of list (all pipes starting from here are synchronized).
WaitPipeListAsync dd ?
WaitPipeListPeriodic dd ?
; List heads.
WaitPipeRequestAsync dd ?
WaitPipeRequestPeriodic dd ?
; Pending request to hardware to refresh cache for items from WaitPipeList*.
; (Pointers to some items in WaitPipeList* or NULLs).
ReadyPipeHeadAsync dd ?
ReadyPipeHeadPeriodic dd ?
; Items of RemovingList* which were released by hardware and are ready
; for further processing.
; (Pointers to some items in WaitPipeList* or NULLs).
NewConnected dd ?
; bit mask of recently connected ports of the root hub,
; bit set = a device was recently connected to the corresponding port;
; after USB_CONNECT_DELAY ticks of stable status these ports are moved to
; PendingPorts
NewDisconnected dd ?
; bit mask of disconnected ports of the root hub,
; bit set = a device in the corresponding port was disconnected,
; disconnect processing is required.
PendingPorts dd ?
; bit mask of ports which are ready to be initialized
ControlLock MUTEX ?
; mutex which guards all operations with control queue
BulkLock MUTEX ?
; mutex which guards all operations with bulk queue
PeriodicLock MUTEX ?
; mutex which guards all operations with periodic queues
WaitSpinlock:
; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList)
StartWaitFrame dd ?
; USB frame number when WaitPipeRequest* was registered.
ResettingHub dd ?
; Pointer to usb_hub responsible for the currently resetting port, if any.
; NULL for the root hub.
ResettingPort db ?
; Port that is currently resetting, 0-based.
ResettingSpeed db ?
; Speed of currently resetting device.
ResettingStatus db ?
; Status of port reset. 0 = no port is resetting, -1 = reset failed,
; 1 = reset in progress, 2 = reset recovery in progress.
rb 1 ; alignment
ResetTime dd ?
; Time when reset signalling or reset recovery has been started.
ConnectedTime rd 16
; Time, in timer ticks, when the port i has signalled the connect event.
; Valid only if bit i in NewConnected is set.
DevicesByPort rd 16
; Pointer to usb_pipe for zero endpoint (which serves as device handle)
; for each port.
ends
 
; Interface-specific data. Several interfaces of one device can operate
; independently, each is controlled by some driver and is identified by
; some driver-specific data passed as is to the driver.
struct usb_interface_data
DriverData dd ?
; Passed as is to the driver.
DriverFunc dd ?
; Pointer to USBSRV structure for the driver.
ends
 
; Device-specific data.
struct usb_device_data
PipeListLock MUTEX
; Lock guarding OpenedPipeList. Must be the first item of the structure,
; the code passes pointer to usb_device_data as is to mutex_lock/unlock.
OpenedPipeList rd 2
; List of all opened pipes for the device.
; Used when the device is disconnected, so all pipes should be closed.
ClosedPipeList rd 2
; List of all closed, but still valid pipes for the device.
; A pipe closed with USBClosePipe is just deallocated,
; but a pipe closed due to disconnect must remain valid until driver-provided
; disconnect handler returns; this list links all such pipes to deallocate them
; after disconnect processing.
NumPipes dd ?
; Number of not-yet-closed pipes.
Hub dd ?
; NULL if connected to the root hub, pointer to usb_hub otherwise.
Port db ?
; Port on the hub, zero-based.
DeviceDescrSize db ?
; Size of device descriptor.
NumInterfaces db ?
; Number of interfaces.
Speed db ?
; Device speed, one of USB_SPEED_*.
ConfigDataSize dd ?
; Total size of data associated with the configuration descriptor
; (including the configuration descriptor itself);
Interfaces dd ?
; Offset from the beginning of this structure to Interfaces field.
; Variable-length fields:
; DeviceDescriptor:
; device descriptor starts here
; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize
; configuration descriptor with all associated data
; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4)
; array of NumInterfaces elements of type usb_interface_data
ends
 
usb_device_data.DeviceDescriptor = sizeof.usb_device_data
 
; Description of controller-specific data and functions.
struct usb_hardware_func
ID dd ? ; '*HCI'
DataSize dd ? ; sizeof(*hci_controller)
Init dd ?
; Initialize controller-specific part of controller data.
; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn
; out: eax = 0 <=> failed, otherwise eax -> usb_controller
ProcessDeferred dd ?
; Called regularly from the main loop of USB thread
; (either due to timeout from a previous call, or due to explicit wakeup).
; in: esi -> usb_controller
; out: eax = maximum timeout for next call (-1 = infinity)
SetDeviceAddress dd ?
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
GetDeviceAddress dd ?
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = address
PortDisable dd ?
; Disable the given port in the root hub.
; in: esi -> usb_controller, ecx = port (zero-based)
InitiateReset dd ?
; Start reset signalling on the given port.
; in: esi -> usb_controller, ecx = port (zero-based)
SetEndpointPacketSize dd ?
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
AllocPipe dd ?
; out: eax = pointer to allocated usb_pipe
FreePipe dd ?
; void stdcall with one argument = pointer to previously allocated usb_pipe
InitPipe dd ?
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
UnlinkPipe dd ?
; esi -> usb_controller, ebx -> usb_pipe
AllocTD dd ?
; out: eax = pointer to allocated usb_gtd
FreeTD dd ?
; void stdcall with one argument = pointer to previously allocated usb_gtd
AllocTransfer dd ?
; Allocate and initialize one stage of a transfer.
; ebx -> usb_pipe, other parameters are passed through the stack:
; buffer,size = data to transfer
; flags = same as in usb_open_pipe:
; bit 0 = allow short transfer, other bits reserved
; td = pointer to the current end-of-queue descriptor
; direction =
; 0000b for normal transfers,
; 1000b for control SETUP transfer,
; 1101b for control OUT transfer,
; 1110b for control IN transfer
; returns eax = pointer to the new end-of-queue descriptor
; (not included in the queue itself) or 0 on error
InsertTransfer dd ?
; Activate previously initialized transfer (maybe with multiple stages).
; esi -> usb_controller, ebx -> usb_pipe,
; [esp+4] -> first usb_gtd for the transfer,
; ecx -> last descriptor for the transfer
NewDevice dd ?
; Initiate configuration of a new device (create pseudo-pipe describing that
; device and call usb_new_device).
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
ends
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
; Initializes one controller, called by usb_init for every controller.
; edi -> usb_hardware_func, eax -> PCIDEV structure for the device.
proc usb_init_controller
push ebp
mov ebp, esp
; 1. Store in the stack PCI coordinates and save pointer to PCIDEV:
; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs.
push dword [eax+PCIDEV.devfn]
push eax
; 2. Allocate *hci_controller + usb_controller.
mov ebx, [edi+usb_hardware_func.DataSize]
add ebx, sizeof.usb_controller
stdcall kernel_alloc, ebx
test eax, eax
jz .nothing
; 3. Zero-initialize both structures.
push edi eax
mov ecx, ebx
shr ecx, 2
xchg edi, eax
xor eax, eax
rep stosd
; 4. Initialize usb_controller structure,
; except data known only to controller-specific code (like NumPorts)
; and link fields
; (this structure will be inserted to the overall list at step 6).
dec eax
mov [edi+usb_controller.ExistingAddresses+4-sizeof.usb_controller], eax
mov [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax
mov [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax
mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port
dec eax ; don't allocate zero address
mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax
lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller]
call mutex_init
add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock
call mutex_init
add ecx, usb_controller.BulkLock - usb_controller.ControlLock
call mutex_init
pop eax edi
mov [eax+ebx-sizeof.usb_controller+usb_controller.HardwareFunc], edi
push eax
; 5. Call controller-specific initialization.
; If failed, free memory allocated in step 2 and return.
call [edi+usb_hardware_func.Init]
test eax, eax
jz .fail
pop ecx
; 6. Insert the controller to the global list.
xchg eax, ebx
mov ecx, usb_controllers_list_mutex
call mutex_lock
mov edx, usb_controllers_list
mov eax, [edx+usb_controller.Prev]
mov [ebx+usb_controller.Next], edx
mov [ebx+usb_controller.Prev], eax
mov [edx+usb_controller.Prev], ebx
mov [eax+usb_controller.Next], ebx
call mutex_unlock
; 7. Wakeup USB thread to call ProcessDeferred.
call usb_wakeup
.nothing:
; 8. Restore pointer to PCIDEV saved in step 1 and return.
pop eax
leave
ret
.fail:
call kernel_free
jmp .nothing
endp
 
; Helper function, calculates physical address including offset in page.
proc get_phys_addr
push ecx
mov ecx, eax
and ecx, 0xFFF
call get_pg_addr
add eax, ecx
pop ecx
ret
endp
 
; Put the given control pipe in the wait list;
; called when the pipe structure is changed and a possible hardware cache
; needs to be synchronized. When it will be known that the cache is updated,
; usb_subscription_done procedure will be called.
proc usb_subscribe_control
cmp [ebx+usb_pipe.NextWait], -1
jnz @f
mov eax, [esi+usb_controller.WaitPipeListAsync]
mov [ebx+usb_pipe.NextWait], eax
mov [esi+usb_controller.WaitPipeListAsync], ebx
@@:
ret
endp
 
; Called after synchronization of hardware cache with software changes.
; Continues process of device enumeration based on when it was delayed
; due to call to usb_subscribe_control.
proc usb_subscription_done
mov eax, [ebx+usb_pipe.DeviceData]
cmp [eax+usb_device_data.DeviceDescrSize], 0
jz usb_after_set_address
jmp usb_after_set_endpoint_size
endp
 
; This function is called when a new device has either passed
; or failed first stages of configuration, so the next device
; can enter configuration process.
proc usb_test_pending_port
mov [esi+usb_controller.ResettingPort], -1
cmp [esi+usb_controller.PendingPorts], 0
jz .nothing
bsf ecx, [esi+usb_controller.PendingPorts]
btr [esi+usb_controller.PendingPorts], ecx
mov eax, [esi+usb_controller.HardwareFunc]
jmp [eax+usb_hardware_func.InitiateReset]
.nothing:
ret
endp
 
; This procedure is regularly called from controller-specific ProcessDeferred,
; it checks whether there are disconnected events and if so, process them.
proc usb_disconnect_stage2
bsf ecx, [esi+usb_controller.NewDisconnected]
jz .nothing
lock btr [esi+usb_controller.NewDisconnected], ecx
btr [esi+usb_controller.PendingPorts], ecx
xor ebx, ebx
xchg ebx, [esi+usb_controller.DevicesByPort+ecx*4]
test ebx, ebx
jz usb_disconnect_stage2
call usb_device_disconnected
jmp usb_disconnect_stage2
.nothing:
ret
endp
 
; Initial stage of disconnect processing: called when device is disconnected.
proc usb_device_disconnected
; Loop over all pipes, close everything, wait until hardware reacts.
; The final handling is done in usb_pipe_closed.
push ebx
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_lock
lea eax, [ecx+usb_device_data.OpenedPipeList-usb_pipe.NextSibling]
push eax
mov ebx, [eax+usb_pipe.NextSibling]
.pipe_loop:
call usb_close_pipe_nolock
mov ebx, [ebx+usb_pipe.NextSibling]
cmp ebx, [esp]
jnz .pipe_loop
pop eax
pop ebx
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_unlock
ret
endp
 
; Called from controller-specific ProcessDeferred,
; processes wait-pipe-done notifications,
; returns whether there are more items in wait queues.
; in: esi -> usb_controller
; out: eax = bitmask of pipe types with non-empty wait queue
proc usb_process_wait_lists
xor edx, edx
push edx
call usb_process_one_wait_list
jnc @f
or byte [esp], 1 shl CONTROL_PIPE
@@:
push 4
pop edx
call usb_process_one_wait_list
jnc @f
or byte [esp], 1 shl INTERRUPT_PIPE
@@:
xor edx, edx
call usb_process_one_wait_list
jnc @f
or byte [esp], 1 shl CONTROL_PIPE
@@:
pop eax
ret
endp
 
; Helper procedure for usb_process_wait_lists;
; does the same for one wait queue.
; in: esi -> usb_controller,
; edx=0 for *Async, edx=4 for *Periodic list
; out: CF = issue new request
proc usb_process_one_wait_list
; 1. Check whether there is a pending request. If so, do nothing.
mov ebx, [esi+usb_controller.WaitPipeRequestAsync+edx]
cmp ebx, [esi+usb_controller.ReadyPipeHeadAsync+edx]
clc
jnz .nothing
; 2. Check whether there are new data. If so, issue a new request.
cmp ebx, [esi+usb_controller.WaitPipeListAsync+edx]
stc
jnz .nothing
test ebx, ebx
jz .nothing
; 3. Clear all lists.
xor ecx, ecx
mov [esi+usb_controller.WaitPipeListAsync+edx], ecx
mov [esi+usb_controller.WaitPipeRequestAsync+edx], ecx
mov [esi+usb_controller.ReadyPipeHeadAsync+edx], ecx
; 4. Loop over all pipes from the wait list.
.pipe_loop:
; For every pipe:
; 5. Save edx and next pipe in the list.
push edx
push [ebx+usb_pipe.NextWait]
; 6. If USB_FLAG_EXTRA_WAIT is set, reinsert the pipe to the list and continue.
test [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
jz .process
mov eax, [esi+usb_controller.WaitPipeListAsync+edx]
mov [ebx+usb_pipe.NextWait], eax
mov [esi+usb_controller.WaitPipeListAsync+edx], ebx
jmp .continue
.process:
; 7. Call the handler depending on USB_FLAG_CLOSED.
or [ebx+usb_pipe.NextWait], -1
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
jz .nodisconnect
call usb_pipe_closed
jmp .continue
.nodisconnect:
call usb_subscription_done
.continue:
; 8. Restore edx and next pipe saved in step 5 and continue the loop.
pop ebx
pop edx
test ebx, ebx
jnz .pipe_loop
.check_new_work:
; 9. Set CF depending on whether WaitPipeList* is nonzero.
cmp [esi+usb_controller.WaitPipeListAsync+edx], 1
cmc
.nothing:
ret
endp
/kernel/branches/Kolibri-acpi/bus/usb/hub.inc
0,0 → 1,1237
; Support for USB (non-root) hubs:
; powering up/resetting/disabling ports,
; watching for adding/removing devices.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; Hub constants
; USB hub descriptor type
USB_HUB_DESCRIPTOR = 29h
 
; Features for CLEAR_FEATURE commands to the hub.
C_HUB_LOCAL_POWER = 0
C_HUB_OVER_CURRENT = 1
 
; Bits in result of GET_STATUS command for a port.
; Also suitable for CLEAR_FEATURE/SET_FEATURE commands, where applicable,
; except TEST/INDICATOR.
PORT_CONNECTION = 0
PORT_ENABLE = 1
PORT_SUSPEND = 2
PORT_OVER_CURRENT = 3
PORT_RESET = 4
PORT_POWER = 8
PORT_LOW_SPEED = 9
PORT_HIGH_SPEED = 10
PORT_TEST_BIT = 11
PORT_INDICATOR_BIT = 12
C_PORT_CONNECTION = 16
C_PORT_ENABLE = 17
C_PORT_SUSPEND = 18
C_PORT_OVER_CURRENT = 19
C_PORT_RESET = 20
PORT_TEST_FEATURE = 21
PORT_INDICATOR_FEATURE = 22
 
; Internal constants
; Bits in usb_hub.Actions
HUB_WAIT_POWERED = 1
; ports were powered, wait until power is stable
HUB_WAIT_CONNECT = 2
; some device was connected, wait initial debounce interval
HUB_RESET_IN_PROGRESS = 4
; reset in progress, so buffer for config requests is owned
; by reset process; this includes all stages from initial disconnect test
; to end of setting address (fail on any stage should lead to disabling port,
; which requires a config request)
HUB_RESET_WAITING = 8
; the port is ready for reset, but another device somewhere on the bus
; is resetting. Implies HUB_RESET_IN_PROGRESS
HUB_RESET_SIGNAL = 10h
; reset signalling is active for some port in the hub
; Implies HUB_RESET_IN_PROGRESS
HUB_RESET_RECOVERY = 20h
; reset recovery is active for some port in the hub
; Implies HUB_RESET_IN_PROGRESS
 
; Well, I think that those 5 flags WAIT_CONNECT and RESET_* require additional
; comments. So that is the overview of what happens with a new device assuming
; no errors.
; * device is connected;
; * hub notifies us about connect event; after some processing
; usb_hub_port_change finally processes that event, setting the flag
; HUB_WAIT_CONNECT and storing time when the device was connected;
; * 100 ms delay;
; * usb_hub_process_deferred clears HUB_WAIT_CONNECT,
; sets HUB_RESET_IN_PROGRESS, stores the port index in ConfigBuffer and asks
; the hub whether there was a disconnect event for that port during those
; 100 ms (on the hardware level notifications are obtained using polling
; with some intervals, so it is possible that the corresponding notification
; has not arrived yet);
; * usb_hub_connect_port_status checks that there was no disconnect event
; and sets HUB_RESET_WAITING flag (HUB_RESET_IN_PROGRESS is still set,
; ConfigBuffer still contains the port index);
; * usb_hub_process_deferred checks whether there is another device currently
; resetting. If so, it waits until reset is done
; (with HUB_RESET_WAITING and HUB_RESET_IN_PROGRESS bits set);
; * usb_hub_process_deferred clears HUB_RESET_WAITING, sets HUB_RESET_SIGNAL
; and initiates reset signalling on the port;
; * usb_hub_process_deferred checks the status every tick;
; when reset signalling is stopped by the hub, usb_hub_resetting_port_status
; callback clears HUB_RESET_SIGNAL and sets HUB_RESET_RECOVERY;
; * 10 ms (at least) delay;
; * usb_hub_process_deferred clears HUB_RESET_RECOVERY and notifies other code
; that the new device is ready to be configured;
; * when it is possible to reset another device, the protocol layer
; clears HUB_RESET_IN_PROGRESS bit.
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; This structure contains all used data for one hub.
struct usb_hub
; All configured hubs are organized in the global usb_hub_list.
; Two following fields give next/prev items in that list.
; While the hub is unconfigured, they point to usb_hub itself.
Next dd ?
Prev dd ?
Controller dd ?
; Pointer to usb_controller for the bus.
;
; Handles of two pipes: configuration control pipe for zero endpoint opened by
; the common code and status interrupt pipe opened by us.
ConfigPipe dd ?
StatusPipe dd ?
NumPorts dd ?
; Number of downstream ports; from 1 to 255.
Actions dd ?
; Bitfield with HUB_* constants.
PoweredOnTime dd ?
; Time (in ticks) when all downstream ports were powered up.
ResetTime dd ?
; Time (in ticks) when the current port was reset;
; when a port is resetting, contains the last tick of status check;
; when reset recovery for a port is active, contains the time when
; reset was completed.
;
; There are two possible reasons for configuration requests:
; synchronous, when certain time is passed after something,
; and asynchronous, when the hub is notifying about some change and
; config request needs to be issued in order to query details.
; Use two different buffers to avoid unnecessary dependencies.
ConfigBuffer rb 8
; Buffer for configuration requests for synchronous events.
ChangeConfigBuffer rb 8
; Buffer for configuration requests for status changes.
AccStatusChange db ?
; Accumulated status change. See 11.12.3 of USB2 spec or comments in code.
HubCharacteristics dw ?
; Copy of usb_hub_descr.wHubCharacteristics.
PowerOnInterval db ?
; Copy of usb_hub_descr.bPwrOn2PwrGood.
;
; Two following fields are written at once by GET_STATUS request
; and must remain in this order.
StatusData dw ?
; Bitfield with 1 shl PORT_* indicating status of the current port.
StatusChange dw ?
; Bitfield with 1 shl PORT_* indicating change in status of the current port.
; Two following fields are written at once by GET_STATUS request
; and must remain in this order.
; The meaning is the same as of StatusData/StatusChange; two following fields
; are used by the synchronous requests to avoid unnecessary interactions with
; the asynchronous handler.
ResetStatusData dw ?
ResetStatusChange dw ?
StatusChangePtr dd ?
; Pointer to StatusChangeBuf.
ConnectedDevicesPtr dd ?
; Pointer to ConnectedDevices.
ConnectedTimePtr dd ?
; Pointer to ConnectedTime.
;
; Variable-length parts:
; DeviceRemovable rb (NumPorts+8)/8
; Bit i+1 = device at port i (zero-based) is non-removable.
; StatusChangeBuf rb (NumPorts+8)/8
; Buffer for status interrupt pipe. Bit 0 = hub status change,
; other bits = status change of the corresponding ports.
; ConnectedDevices rd NumPorts
; Pointers to config pipes for connected devices or zero if no device connected.
; ConnectedTime rd NumPorts
; For initial debounce interval:
; time (in ticks) when a device was connected at that port.
; Normally: -1
ends
 
; Hub descriptor.
struct usb_hub_descr usb_descr
bNbrPorts db ?
; Number of downstream ports.
wHubCharacteristics dw ?
; Bit 0: 0 = all ports are powered at once, 1 = individual port power switching
; Bit 1: reserved, must be zero
; Bit 2: 1 = the hub is part of a compound device
; Bits 3-4: 00 = global overcurrent protection,
; 01 = individual port overcurrent protection,
; 1x = no overcurrent protection
; Bits 5-6: Transaction Translator Think Time, 8*(value+1) full-speed bit times
; Bit 7: 1 = port indicators supported
; Other bits are reserved.
bPwrOn2PwrGood db ?
; Time in 2ms intervals between powering up a port and a port becoming ready.
bHubContrCurrent db ?
; Maximum current requirements of the Hub Controller electronics in mA.
; DeviceRemovable - variable length
; Bit 0 is reserved, bit i+1 = device at port i is non-removable.
; PortPwrCtrlMask - variable length
; Obsolete, exists for compatibility. We ignore it.
ends
 
iglobal
align 4
; Implementation of struct USBFUNC for hubs.
usb_hub_callbacks:
dd usb_hub_callbacks_end - usb_hub_callbacks
dd usb_hub_init
dd usb_hub_disconnect
usb_hub_callbacks_end:
usb_hub_pseudosrv dd usb_hub_callbacks
endg
 
; This procedure is called when new hub is detected.
; It initializes the device.
; Technically, initialization implies sending several USB queries,
; so it is split in several procedures. The first is usb_hub_init,
; other are callbacks which will be called at some time in the future,
; when the device will respond.
; edx = usb_interface_descr, ecx = length rest
proc usb_hub_init
push ebx esi ; save used registers to be stdcall
virtual at esp
rd 2 ; saved registers
dd ? ; return address
.pipe dd ? ; handle of the config pipe
.config dd ? ; pointer to usb_config_descr
.interface dd ? ; pointer to usb_interface_descr
end virtual
; Hubs use one IN interrupt endpoint for polling the device
; 1. Locate the descriptor of the interrupt endpoint.
; Loop over all descriptors owned by this interface.
.lookep:
; 1a. Skip the current descriptor.
movzx eax, [edx+usb_descr.bLength]
add edx, eax
sub ecx, eax
jb .errorep
; 1b. Length of data left must be at least sizeof.usb_endpoint_descr.
cmp ecx, sizeof.usb_endpoint_descr
jb .errorep
; 1c. If we have found another interface descriptor but not found our endpoint,
; this is an error: all subsequent descriptors belong to that interface
; (or further interfaces).
cmp [edx+usb_endpoint_descr.bDescriptorType], USB_INTERFACE_DESCR
jz .errorep
; 1d. Ignore all interface-related descriptors except endpoint descriptor.
cmp [edx+usb_endpoint_descr.bDescriptorType], USB_ENDPOINT_DESCR
jnz .lookep
; 1e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr.
cmp [edx+usb_endpoint_descr.bLength], sizeof.usb_endpoint_descr
jb .errorep
; 1f. Ignore all endpoints except for INTERRUPT IN.
cmp [edx+usb_endpoint_descr.bEndpointAddress], 0
jge .lookep
mov al, [edx+usb_endpoint_descr.bmAttributes]
and al, 3
cmp al, INTERRUPT_PIPE
jnz .lookep
; We have located the descriptor for INTERRUPT IN endpoint,
; the pointer is in edx.
; 2. Allocate memory for the hub descriptor.
; Maximum length (assuming 255 downstream ports) is 40 bytes.
; 2a. Save registers.
push edx
; 2b. Call the allocator.
push 40
pop eax
call malloc
; 2c. Restore registers.
pop ecx
; 2d. If failed, say something to the debug board and return error.
test eax, eax
jz .nomemory
; 2e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov.
xchg esi, eax
; 3. Open a pipe for the status endpoint with descriptor found in step 1.
mov ebx, [.pipe]
movzx eax, [ecx+usb_endpoint_descr.bEndpointAddress]
movzx edx, [ecx+usb_endpoint_descr.bInterval]
movzx ecx, [ecx+usb_endpoint_descr.wMaxPacketSize]
stdcall usb_open_pipe, ebx, eax, ecx, INTERRUPT_PIPE, edx
; If failed, free the memory allocated in step 2,
; say something to the debug board and return error.
test eax, eax
jz .free
; 4. Send control query for the hub descriptor,
; pass status pipe as a callback parameter,
; allow short packets.
mov dword [esi], 0xA0 + \ ; class-specific request
(USB_GET_DESCRIPTOR shl 8) + \
(0 shl 16) + \ ; descriptor index 0
(USB_HUB_DESCRIPTOR shl 24)
mov dword [esi+4], 40 shl 16
stdcall usb_control_async, ebx, esi, esi, 40, usb_hub_got_config, eax, 1
; 5. If failed, free the memory allocated in step 2,
; say something to the debug board and return error.
test eax, eax
jz .free
; Otherwise, return 1. usb_hub_got_config will overwrite it later.
xor eax, eax
inc eax
jmp .nothing
.free:
xchg eax, esi
call free
jmp .return0
.errorep:
dbgstr 'Invalid config descriptor for a hub'
jmp .return0
.nomemory:
dbgstr 'No memory for USB hub data'
.return0:
xor eax, eax
.nothing:
pop esi ebx ; restore used registers to be stdcall
retn 12
endp
 
; This procedure is called when the request for the hub descriptor initiated
; by usb_hub_init is finished, either successfully or unsuccessfully.
proc usb_hub_got_config stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
push ebx ; save used registers to be stdcall
; 1. If failed, say something to the debug board, free the buffer
; and stop the initialization.
cmp [status], 0
jnz .invalid
; 2. The length must be at least sizeof.usb_hub_descr.
; Note that [length] includes 8 bytes of setup packet.
cmp [length], 8 + sizeof.usb_hub_descr
jb .invalid
; 3. Sanity checks for the hub descriptor.
mov eax, [buffer]
if USB_DUMP_DESCRIPTORS
mov ecx, [length]
sub ecx, 8
DEBUGF 1,'K : hub config:'
push eax
@@:
DEBUGF 1,' %x',[eax]:2
inc eax
dec ecx
jnz @b
DEBUGF 1,'\n'
pop eax
end if
cmp [eax+usb_hub_descr.bLength], sizeof.usb_hub_descr
jb .invalid
cmp [eax+usb_hub_descr.bDescriptorType], USB_HUB_DESCRIPTOR
jnz .invalid
movzx ecx, [eax+usb_hub_descr.bNbrPorts]
test ecx, ecx
jz .invalid
; 4. We use sizeof.usb_hub_descr bytes plus DeviceRemovable info;
; size of DeviceRemovable is (NumPorts+1) bits, this gives
; floor(NumPorts/8)+1 bytes. Check that all data are present in the
; descriptor and were successfully read.
mov edx, ecx
shr edx, 3
add edx, sizeof.usb_hub_descr + 1
cmp [eax+usb_hub_descr.bLength], dl
jb .invalid
sub [length], 8
cmp [length], edx
jb .invalid
; 5. Allocate the memory for usb_hub structure.
; Total size of variable-length data is ALIGN_UP(2*(floor(NumPorts/8)+1),4)+8*NumPorts.
lea edx, [sizeof.usb_hub+(edx-sizeof.usb_hub_descr)*2+3]
and edx, not 3
lea eax, [edx+ecx*8]
push ecx edx
call malloc
pop edx ecx
test eax, eax
jz .nomemory
xchg eax, ebx
; 6. Fill usb_hub structure.
mov [ebx+usb_hub.NumPorts], ecx
add edx, ebx
mov [ebx+usb_hub.ConnectedDevicesPtr], edx
mov eax, [pipe]
mov [ebx+usb_hub.ConfigPipe], eax
mov edx, [eax+usb_pipe.Controller]
mov [ebx+usb_hub.Controller], edx
mov eax, [calldata]
mov [ebx+usb_hub.StatusPipe], eax
push esi edi
mov esi, [buffer]
; The following commands load bNbrPorts, wHubCharacteristics, bPwrOn2PwrGood.
mov edx, dword [esi+usb_hub_descr.bNbrPorts]
mov dl, 0
; The following command zeroes AccStatusChange and stores
; HubCharacteristics and PowerOnInterval.
mov dword [ebx+usb_hub.AccStatusChange], edx
xor eax, eax
mov [ebx+usb_hub.Actions], eax
; Copy DeviceRemovable data.
lea edi, [ebx+sizeof.usb_hub]
add esi, sizeof.usb_hub_descr
mov edx, ecx
shr ecx, 3
inc ecx
rep movsb
mov [ebx+usb_hub.StatusChangePtr], edi
; Zero ConnectedDevices.
mov edi, [ebx+usb_hub.ConnectedDevicesPtr]
mov ecx, edx
rep stosd
mov [ebx+usb_hub.ConnectedTimePtr], edi
; Set ConnectedTime to -1.
dec eax
mov ecx, edx
rep stosd
pop edi esi
; 7. Replace value of 1 returned from usb_hub_init to the real value.
; Note: hubs are part of the core USB code, so this code can work with
; internals of other parts. Another way, the only possible one for external
; drivers, is to use two memory allocations: one (returned from AddDevice and
; fixed after that) for pointer, another for real data. That would work also,
; but wastes one allocation.
mov eax, [pipe]
mov eax, [eax+usb_pipe.DeviceData]
add eax, [eax+usb_device_data.Interfaces]
.scan:
cmp [eax+usb_interface_data.DriverData], 1
jnz @f
cmp [eax+usb_interface_data.DriverFunc], usb_hub_pseudosrv - USBSRV.usb_func
jz .scan_found
@@:
add eax, sizeof.usb_interface_data
jmp .scan
.scan_found:
mov [eax+usb_interface_data.DriverData], ebx
; 8. Insert the hub structure to the tail of the overall list of all hubs.
mov ecx, usb_hubs_list
mov edx, [ecx+usb_hub.Prev]
mov [ecx+usb_hub.Prev], ebx
mov [edx+usb_hub.Next], ebx
mov [ebx+usb_hub.Prev], edx
mov [ebx+usb_hub.Next], ecx
; 9. Start powering up all ports.
DEBUGF 1,'K : found hub with %d ports\n',[ebx+usb_hub.NumPorts]
lea eax, [ebx+usb_hub.ConfigBuffer]
xor ecx, ecx
mov dword [eax], 23h + \ ; class-specific request to hub port
(USB_SET_FEATURE shl 8) + \
(PORT_POWER shl 16)
mov edx, [ebx+usb_hub.NumPorts]
mov dword [eax+4], edx
stdcall usb_control_async, [ebx+usb_hub.ConfigPipe], eax, ecx, ecx, usb_hub_port_powered, ebx, ecx
.freebuf:
; 10. Free the buffer for hub descriptor and return.
mov eax, [buffer]
call free
pop ebx ; restore used registers to be stdcall
ret
.nomemory:
dbgstr 'No memory for USB hub data'
jmp .freebuf
.invalid:
dbgstr 'Invalid hub descriptor'
jmp .freebuf
endp
 
; This procedure is called when the request to power up some port is completed,
; either successfully or unsuccessfully.
proc usb_hub_port_powered stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. Check whether the operation was successful.
; If not, say something to the debug board and ssstop the initialization.
cmp [status], 0
jnz .invalid
; 2. Check whether all ports were powered.
; If so, go to 4. Otherwise, proceed to 3.
mov eax, [calldata]
dec dword [eax+usb_hub.ConfigBuffer+4]
jz .done
; 3. Power up the next port and return.
lea edx, [eax+usb_hub.ConfigBuffer]
xor ecx, ecx
stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, ecx, usb_hub_port_powered, eax, ecx
.nothing:
ret
.done:
; 4. All ports were powered.
; The hub requires some delay until power will be stable, the delay value
; is provided in the hub descriptor; we have copied that value to
; usb_hub.PowerOnInterval. Note the time and set the corresponding flag
; for usb_hub_process_deferred.
mov ecx, [timer_ticks]
mov [eax+usb_hub.PoweredOnTime], ecx
or [eax+usb_hub.Actions], HUB_WAIT_POWERED
jmp .nothing
.invalid:
dbgstr 'Error while powering hub ports'
jmp .nothing
endp
 
; Requests notification about any changes in hub/ports configuration.
; Called when initial configuration is done and when a previous notification
; has been processed.
proc usb_hub_wait_change
mov ecx, [eax+usb_hub.NumPorts]
shr ecx, 3
inc ecx
stdcall usb_normal_transfer_async, [eax+usb_hub.StatusPipe], \
[eax+usb_hub.StatusChangePtr], ecx, usb_hub_changed, eax, 1
ret
endp
 
; This procedure is called when something has changed on the hub.
proc usb_hub_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; DEBUGF 1,'K : [%d] int pipe for hub %x\n',[timer_ticks],[calldata]
; 1. Check whether our request has failed.
; If so, say something to the debug board and stop processing notifications.
xor ecx, ecx
cmp [status], ecx
jnz .failed
; 2. If no data were retrieved, restart waiting.
mov eax, [calldata]
cmp [length], ecx
jz .continue
; 3. If size of data retrieved is less than maximal, pad with zeroes;
; this corresponds to 'state of other ports was not changed'
mov ecx, [eax+usb_hub.NumPorts]
shr ecx, 3
inc ecx
sub ecx, [length]
push eax edi
mov edi, [buffer]
add edi, [length]
xor eax, eax
rep stosb
pop edi eax
.restart:
; State of some elements of the hub was changed.
; Find the first element that was changed,
; ask the hub about nature of the change,
; clear the corresponding change,
; reask the hub about status+change (it is possible that another change
; occurs between the first ask and clearing the change; we won't see that
; change, so we need to query the status after clearing the change),
; continue two previous steps until nothing changes,
; process all changes which were registered.
; When all changes for one element will be processed, return to here and look
; for other changed elements.
mov edx, [eax+usb_hub.StatusChangePtr]
; We keep all observed changes in the special var usb_hub.AccStatusChange;
; it will be logical OR of all observed StatusChange's.
; 4. No observed changes yet, zero usb_hub.AccStatusChange.
xor ecx, ecx
mov [eax+usb_hub.AccStatusChange], cl
; 5. Test whether there was a change in the hub itself.
; If so, query hub state.
btr dword [edx], ecx
jnc .no_hub_change
.next_hub_change:
; DEBUGF 1,'K : [%d] querying status of hub %x\n',[timer_ticks],eax
lea edx, [eax+usb_hub.ChangeConfigBuffer]
lea ecx, [eax+usb_hub.StatusData]
mov dword [edx], 0A0h + \ ; class-specific request from hub itself
(USB_GET_STATUS shl 8)
mov dword [edx+4], 4 shl 16 ; get 4 bytes
stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_status, eax, 0
jmp .nothing
.no_hub_change:
; 6. Find the first port with changed state and clear the corresponding bit
; (so next scan after .restart will not consider this port again).
; If found, go to 8. Otherwise, advance to 7.
inc ecx
.test_port_change:
btr [edx], ecx
jc .found_port_change
inc ecx
cmp ecx, [eax+usb_hub.NumPorts]
jbe .test_port_change
.continue:
; 7. All changes have been processed. Wait for next notification.
call usb_hub_wait_change
.nothing:
ret
.found_port_change:
mov dword [eax+usb_hub.ChangeConfigBuffer+4], ecx
.next_port_change:
; 8. Query port state. Continue work in usb_hub_port_status callback.
; movzx ecx, [eax+usb_hub.ChangeConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] querying status of hub %x port %d\n',[timer_ticks],eax,ecx
lea edx, [eax+usb_hub.ChangeConfigBuffer]
mov dword [edx], 0A3h + \ ; class-specific request from hub port
(USB_GET_STATUS shl 8)
mov byte [edx+6], 4 ; data length = 4 bytes
lea ecx, [eax+usb_hub.StatusData]
stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_port_status, eax, 0
jmp .nothing
.failed:
cmp [status], USB_STATUS_CLOSED
jz .nothing
dbgstr 'Querying hub notification failed'
jmp .nothing
endp
 
; This procedure is called when the request of hub status is completed,
; either successfully or unsuccessfully.
proc usb_hub_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. Check whether our request has failed.
; If so, say something to the debug board and stop processing notifications.
cmp [status], 0
jnz .failed
; 2. Accumulate observed changes.
mov eax, [calldata]
mov dl, byte [eax+usb_hub.StatusChange]
or [eax+usb_hub.AccStatusChange], dl
.next_change:
; 3. Find the first change. If found, advance to 4. Otherwise, go to 5.
mov cl, C_HUB_OVER_CURRENT
btr dword [eax+usb_hub.StatusChange], 1
jc .clear_hub_change
mov cl, C_HUB_LOCAL_POWER
btr dword [eax+usb_hub.StatusChange], 0
jnc .final
.clear_hub_change:
; 4. Clear the change and continue in usb_hub_change_cleared callback.
lea edx, [eax+usb_hub.ChangeConfigBuffer]
mov dword [edx], 20h + \ ; class-specific request to hub itself
(USB_CLEAR_FEATURE shl 8)
mov [edx+2], cl ; feature selector
and dword [edx+4], 0
stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_change_cleared, eax, 0
.nothing:
ret
.final:
; 5. All changes cleared and accumulated, now process them.
; Note: that needs work.
DEBUGF 1,'K : hub status %x\n',[eax+usb_hub.AccStatusChange]:2
test [eax+usb_hub.AccStatusChange], 1
jz .no_local_power
test [eax+usb_hub.StatusData], 1
jz .local_power_lost
dbgstr 'Hub local power is now good'
jmp .no_local_power
.local_power_lost:
dbgstr 'Hub local power is now lost'
.no_local_power:
test [eax+usb_hub.AccStatusChange], 2
jz .no_overcurrent
test [eax+usb_hub.StatusData], 2
jz .no_overcurrent
dbgstr 'Hub global overcurrent'
.no_overcurrent:
; 6. Process possible changes for other ports.
jmp usb_hub_changed.restart
.failed:
dbgstr 'Querying hub status failed'
jmp .nothing
endp
 
; This procedure is called when the request to clear hub change is completed,
; either successfully or unsuccessfully.
proc usb_hub_change_cleared stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. Check whether our request has failed.
; If so, say something to the debug board and stop processing notifications.
cmp [status], 0
jnz .failed
; 2. If there is a change which was observed, but not yet cleared,
; go to the code which clears it.
mov eax, [calldata]
cmp [eax+usb_hub.StatusChange], 0
jnz usb_hub_status.next_change
; 3. Otherwise, go to the code which queries the status.
jmp usb_hub_changed.next_hub_change
.failed:
dbgstr 'Clearing hub change failed'
ret
endp
 
; This procedure is called when the request of port status is completed,
; either successfully or unsuccessfully.
proc usb_hub_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. Check whether our request has failed.
; If so, say something to the debug board and stop processing notifications.
cmp [status], 0
jnz .failed
; 2. Accumulate observed changes.
mov eax, [calldata]
; movzx ecx, [eax+usb_hub.ChangeConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.StatusChange]:4
mov dl, byte [eax+usb_hub.StatusChange]
or [eax+usb_hub.AccStatusChange], dl
.next_change:
; 3. Find the first change. If found, advance to 4. Otherwise, go to 5.
; Ignore change in reset status; it is cleared by synchronous code
; (usb_hub_process_deferred), so avoid unnecessary interference.
; mov cl, C_PORT_RESET
btr dword [eax+usb_hub.StatusChange], PORT_RESET
; jc .clear_port_change
mov cl, C_PORT_OVER_CURRENT
btr dword [eax+usb_hub.StatusChange], PORT_OVER_CURRENT
jc .clear_port_change
mov cl, C_PORT_SUSPEND
btr dword [eax+usb_hub.StatusChange], PORT_SUSPEND
jc .clear_port_change
mov cl, C_PORT_ENABLE
btr dword [eax+usb_hub.StatusChange], PORT_ENABLE
jc .clear_port_change
mov cl, C_PORT_CONNECTION
btr dword [eax+usb_hub.StatusChange], PORT_CONNECTION
jnc .final
.clear_port_change:
; 4. Clear the change and continue in usb_hub_port_changed callback.
call usb_hub_clear_port_change
jmp .nothing
.final:
; All changes cleared and accumulated, now process them.
movzx ecx, byte [eax+usb_hub.ChangeConfigBuffer+4]
dec ecx
DEBUGF 1,'K : final: hub %x port %d status %x change %x\n',eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.AccStatusChange]:2
; 5. Process connect/disconnect events.
; 5a. Test whether there is such event.
test byte [eax+usb_hub.AccStatusChange], 1 shl PORT_CONNECTION
jz .nodisconnect
; 5b. If there was a connected device, notify the main code about disconnect.
push ebx
mov edx, [eax+usb_hub.ConnectedDevicesPtr]
xor ebx, ebx
xchg ebx, [edx+ecx*4]
test ebx, ebx
jz @f
push eax ecx
call usb_device_disconnected
pop ecx eax
@@:
pop ebx
; 5c. If the disconnect event corresponds to the port which is currently
; resetting, then another request from synchronous code could be in the fly,
; so aborting reset immediately would lead to problems with those requests.
; Thus, just set the corresponding status and let the synchronous code process.
test byte [eax+usb_hub.Actions], (HUB_RESET_SIGNAL or HUB_RESET_RECOVERY)
jz @f
mov edx, [eax+usb_hub.Controller]
cmp [edx+usb_controller.ResettingPort], cl
jnz @f
mov [edx+usb_controller.ResettingStatus], -1
@@:
; 5d. If the current status is 'connected', store the current time as connect
; time and set the corresponding bit for usb_hub_process_deferred.
; Otherwise, set connect time to -1.
; If current time is -1, pretend that the event occured one tick later and
; store zero.
mov edx, [eax+usb_hub.ConnectedTimePtr]
test byte [eax+usb_hub.StatusData], 1 shl PORT_CONNECTION
jz .disconnected
or [eax+usb_hub.Actions], HUB_WAIT_CONNECT
push eax
call usb_hub_store_connected_time
pop eax
jmp @f
.disconnected:
or dword [edx+ecx*4], -1
@@:
.nodisconnect:
; 6. Process port disabling.
test [eax+usb_hub.AccStatusChange], 1 shl PORT_ENABLE
jz .nodisable
test byte [eax+usb_hub.StatusData], 1 shl PORT_ENABLE
jnz .nodisable
; Note: that needs work.
dbgstr 'Port disabled'
.nodisable:
; 7. Process port overcurrent.
test [eax+usb_hub.AccStatusChange], 1 shl PORT_OVER_CURRENT
jz .noovercurrent
test byte [eax+usb_hub.StatusData], 1 shl PORT_OVER_CURRENT
jz .noovercurrent
; Note: that needs work.
dbgstr 'Port over-current'
.noovercurrent:
; 8. Process possible changes for other ports.
jmp usb_hub_changed.restart
.failed:
dbgstr 'Querying port status failed'
.nothing:
ret
endp
 
; Helper procedure to store current time in ConnectedTime,
; advancing -1 to zero if needed.
proc usb_hub_store_connected_time
mov eax, [timer_ticks]
; transform -1 to 0, leave other values as is
cmp eax, -1
sbb eax, -1
mov [edx+ecx*4], eax
ret
endp
 
; Helper procedure for several parts of hub code.
; Sends a request to clear the given feature of the port.
; eax -> usb_hub, cl = feature;
; as is should be called from async code, sync code should set
; edx to ConfigBuffer and call usb_hub_clear_port_change.buffer;
; port number (1-based) should be filled in [edx+4] by previous requests.
proc usb_hub_clear_port_change
lea edx, [eax+usb_hub.ChangeConfigBuffer]
.buffer:
; push edx
; movzx edx, byte [edx+4]
; dec edx
; DEBUGF 1,'K : [%d] hub %x port %d clear feature %d\n',[timer_ticks],eax,edx,cl
; pop edx
mov dword [edx], 23h + \ ; class-specific request to hub port
(USB_CLEAR_FEATURE shl 8)
mov byte [edx+2], cl
and dword [edx+4], 0xFF
stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, edx, 0, usb_hub_port_changed, eax, 0
ret
endp
 
; This procedure is called when the request to clear port change is completed,
; either successfully or unsuccessfully.
proc usb_hub_port_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. Check whether our request has failed.
; If so, say something to the debug board and stop processing notifications.
cmp [status], 0
jnz .failed
; 2. If the request was originated by synchronous code, no further processing
; is required.
mov eax, [calldata]
lea edx, [eax+usb_hub.ConfigBuffer]
cmp [buffer], edx
jz .nothing
; 3. If there is a change which was observed, but not yet cleared,
; go to the code which clears it.
cmp [eax+usb_hub.StatusChange], 0
jnz usb_hub_port_status.next_change
; 4. Otherwise, go to the code which queries the status.
jmp usb_hub_changed.next_port_change
.failed:
dbgstr 'Clearing port change failed'
.nothing:
ret
endp
 
; This procedure is called in the USB thread from usb_thread_proc,
; contains synchronous code which should be activated at certain time
; (e.g. reset a recently connected device after debounce interval 100ms).
; Returns the number of ticks when it should be called next time.
proc usb_hub_process_deferred
; 1. Top-of-stack will contain return value; initialize to infinite timeout.
push -1
; 2. If wait for stable power is active, then
; either reschedule wakeup (if time is not over)
; or start processing notifications.
test byte [esi+usb_hub.Actions], HUB_WAIT_POWERED
jz .no_powered
movzx eax, [esi+usb_hub.PowerOnInterval]
; three following instructions are equivalent to edx = ceil(eax / 5) + 1
; 1 extra tick is added to make sure that the interval is at least as needed
; (it is possible that PoweredOnTime was set just before timer interrupt, and
; this test goes on just after timer interrupt)
add eax, 9
; two following instructions are equivalent to edx = floor(eax / 5)
; for any 0 <= eax < 40000000h
mov ecx, 33333334h
mul ecx
mov eax, [timer_ticks]
sub eax, [esi+usb_hub.PoweredOnTime]
sub eax, edx
jge .powered_on
neg eax
pop ecx
push eax
jmp .no_powered
.powered_on:
and [esi+usb_hub.Actions], not HUB_WAIT_POWERED
mov eax, esi
call usb_hub_wait_change
.no_powered:
; 3. If reset is pending, check whether we can start it and start it, if so.
test byte [esi+usb_hub.Actions], HUB_RESET_WAITING
jz .no_wait_reset
mov eax, [esi+usb_hub.Controller]
cmp [eax+usb_controller.ResettingPort], -1
jnz .no_wait_reset
call usb_hub_initiate_reset
.no_wait_reset:
; 4. If reset signalling is active, wait for end of reset signalling
; and schedule wakeup in 1 tick.
test byte [esi+usb_hub.Actions], HUB_RESET_SIGNAL
jz .no_resetting_port
; It has no sense to query status several times per tick.
mov eax, [timer_ticks]
cmp eax, [esi+usb_hub.ResetTime]
jz @f
mov [esi+usb_hub.ResetTime], eax
movzx ecx, byte [esi+usb_hub.ConfigBuffer+4]
mov eax, usb_hub_resetting_port_status
call usb_hub_query_port_status
@@:
pop eax
push 1
.no_resetting_port:
; 5. If reset recovery is active and time is not over, reschedule wakeup.
test byte [esi+usb_hub.Actions], HUB_RESET_RECOVERY
jz .no_reset_recovery
mov eax, [timer_ticks]
sub eax, [esi+usb_hub.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME
jge .reset_done
neg eax
cmp [esp], eax
jb @f
mov [esp], eax
@@:
jmp .no_reset_recovery
.reset_done:
; 6. If reset recovery is active and time is over, clear 'reset recovery' flag,
; notify other code about a new device and let it do further steps.
; If that fails, stop reset process for this port and disable that port.
and [esi+usb_hub.Actions], not HUB_RESET_RECOVERY
; Bits 9-10 of port status encode port speed.
; If PORT_LOW_SPEED is set, the device is low-speed. Otherwise,
; PORT_HIGH_SPEED bit distinguishes full-speed and high-speed devices.
; This corresponds to values of USB_SPEED_FS=0, USB_SPEED_LS=1, USB_SPEED_HS=2.
mov eax, dword [esi+usb_hub.ResetStatusData]
shr eax, PORT_LOW_SPEED
and eax, 3
test al, 1
jz @f
mov al, 1
@@:
; movzx ecx, [esi+usb_hub.ConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] hub %x port %d speed %d\n',[timer_ticks],esi,ecx,eax
push esi
mov esi, [esi+usb_hub.Controller]
cmp [esi+usb_controller.ResettingStatus], -1
jz .disconnected_while_reset
mov edx, [esi+usb_controller.HardwareFunc]
call [edx+usb_hardware_func.NewDevice]
pop esi
test eax, eax
jnz .no_reset_recovery
mov eax, esi
call usb_hub_disable_resetting_port
jmp .no_reset_recovery
.disconnected_while_reset:
pop esi
mov eax, esi
call usb_hub_reset_aborted
.no_reset_recovery:
; 7. Handle recent connection events.
; Note: that should be done after step 6, because step 6 can clear
; HUB_RESET_IN_PROGRESS flag.
; 7a. Test whether there is such an event pending. If no, skip this step.
test byte [esi+usb_hub.Actions], HUB_WAIT_CONNECT
jz .no_wait_connect
; 7b. If we have started reset process for another port in the same hub,
; skip this step: the buffer for config requests can be used for that port.
test byte [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS
jnz .no_wait_connect
; 7c. Clear flag 'there are connection events which should be processed'.
; If there are another connection events, this flag will be set again.
and [esi+usb_hub.Actions], not HUB_WAIT_CONNECT
; 7d. Prepare for loop over all ports.
xor ecx, ecx
.test_wait_connect:
; 7e. For every port test for recent connection event.
; If none, continue the loop for the next port.
mov edx, [esi+usb_hub.ConnectedTimePtr]
mov eax, [edx+ecx*4]
cmp eax, -1
jz .next_wait_connect
or [esi+usb_hub.Actions], HUB_WAIT_CONNECT
; 7f. Test whether initial delay is over.
sub eax, [timer_ticks]
neg eax
sub eax, USB_CONNECT_DELAY
jge .connect_delay_over
; 7g. The initial delay is not over;
; set the corresponding flag again, reschedule wakeup and continue the loop.
neg eax
cmp [esp], eax
jb @f
mov [esp], eax
@@:
jmp .next_wait_connect
.connect_delay_over:
; The initial delay is over.
; It is possible that there was disconnect event during that delay, probably
; with connect event after that. If so, we should restart the waiting. However,
; on the hardware level connect/disconnect events from hubs are implemented
; using polling with interval selected by the hub, so it is possible that
; we have not yet observed that disconnect event.
; Thus, we query port status+change data before all further processing.
; 7h. Send the request for status+change data.
push ecx
; Hub requests expect 1-based port number, not zero-based we operate with.
inc ecx
mov eax, usb_hub_connect_port_status
call usb_hub_query_port_status
pop ecx
; 3i. If request has been submitted successfully, set the flag
; 'reset in progress, config buffer is owned by reset process' and break
; from the loop.
test eax, eax
jz .next_wait_connect
or [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS
jmp .no_wait_connect
.next_wait_connect:
; 7j. Continue the loop for next port.
inc ecx
cmp ecx, [esi+usb_hub.NumPorts]
jb .test_wait_connect
.no_wait_connect:
; 8. Pop return value from top-of-stack and return.
pop eax
ret
endp
 
; Helper procedure for other code. Called when reset process is aborted.
proc usb_hub_reset_aborted
; Clear 'reset in progress' flag and test for other devices which could be
; waiting for reset.
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
push esi
mov esi, [eax+usb_hub.Controller]
call usb_test_pending_port
pop esi
ret
endp
 
; Helper procedure for usb_hub_process_deferred.
; Sends a request to query port status.
; esi -> usb_hub, eax = callback, ecx = 1-based port.
proc usb_hub_query_port_status
; dec ecx
; DEBUGF 1,'K : [%d] [main] hub %x port %d query status\n',[timer_ticks],esi,ecx
; inc ecx
add ecx, 4 shl 16 ; data length = 4
lea edx, [esi+usb_hub.ConfigBuffer]
mov dword [edx], 0A3h + \ ; class-specific request from hub port
(USB_GET_STATUS shl 8)
mov dword [edx+4], ecx
lea ecx, [esi+usb_hub.ResetStatusData]
stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, ecx, 4, eax, esi, 0
ret
endp
 
; This procedure is called when the request to query port status
; initiated by usb_hub_process_deferred for testing connection is completed,
; either successfully or unsuccessfully.
proc usb_hub_connect_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
push esi ; save used register to be stdcall
mov eax, [calldata]
mov esi, [pipe]
; movzx ecx, [eax+usb_hub.ConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] [connect test] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4
; 1. In any case, clear 'reset in progress' flag.
; If everything is ok, it would be set again.
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
; 2. If the request has failed, stop reset process.
cmp [status], 0
jnz .nothing
mov edx, [eax+usb_hub.ConnectedTimePtr]
movzx ecx, byte [eax+usb_hub.ConfigBuffer+4]
dec ecx
; 3. Test whether there was a disconnect event.
test byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION
jz .reset
; 4. There was a disconnect event.
; There is another handler of connect/disconnect events, usb_hub_port_status.
; However, we do not know whether it has already processed this event
; or it will process it sometime later.
; If ConnectedTime is -1, then another handler has already run,
; there was no connection event, so just leave the value as -1.
; Otherwise, there are two possibilities: either another handler has not yet
; run (which is quite likely), or there was a connection event and the other
; handler has run exactly while our request was processed (otherwise our
; request would not been submitted; this is quite unlikely due to timing
; requirements, but not impossible). In this case, set ConnectedTime to the
; current time: in the likely case it prevents usb_hub_process_deferred from immediate
; issuing of another requests (which would be just waste of time);
; in the unlikely case it is still correct (although slightly increases
; the debounce interval).
cmp dword [edx+ecx*4], -1
jz .nothing
call usb_hub_store_connected_time
jmp .nothing
.reset:
; 5. The device remained connected for the entire debounce interval;
; we can proceed with initialization.
; Clear connected time for this port and notify usb_hub_process_deferred that
; the new port is waiting for reset.
or dword [edx+ecx*4], -1
or [eax+usb_hub.Actions], HUB_RESET_IN_PROGRESS + HUB_RESET_WAITING
.nothing:
pop esi ; restore used register to be stdcall
ret
endp
 
; This procedure is called when the request to query port status
; initiated by usb_hub_process_deferred for testing reset status is completed,
; either successfully or unsuccessfully.
proc usb_hub_resetting_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; 1. If the request has failed, do nothing.
cmp [status], 0
jnz .nothing
; 2. If reset signalling is still active, do nothing.
mov eax, [calldata]
; movzx ecx, [eax+usb_hub.ConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : hub %x port %d ResetStatusData = %x change = %x\n',eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4
test byte [eax+usb_hub.ResetStatusData], 1 shl PORT_RESET
jnz .nothing
; 3. Store the current time to start reset recovery interval
; and clear 'reset signalling active' flag.
mov edx, [timer_ticks]
mov [eax+usb_hub.ResetTime], edx
and [eax+usb_hub.Actions], not HUB_RESET_SIGNAL
; 4. If the device has not been disconnected, set 'reset recovery active' bit.
; Otherwise, terminate reset process.
test byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION
jnz .disconnected
or [eax+usb_hub.Actions], HUB_RESET_RECOVERY
.common:
; In any case, clear change of resetting status.
lea edx, [eax+usb_hub.ConfigBuffer]
mov cl, C_PORT_RESET
call usb_hub_clear_port_change.buffer
.nothing:
ret
.disconnected:
call usb_hub_reset_aborted
jmp .common
endp
 
; Helper procedure for usb_hub_process_deferred. Initiates reset signalling
; on the current port (given by 1-based value [ConfigBuffer+4]).
; esi -> usb_hub, eax -> usb_controller
proc usb_hub_initiate_reset
; 1. Store hub+port data in the controller structure.
movzx ecx, [esi+usb_hub.ConfigBuffer+4]
dec ecx
mov [eax+usb_controller.ResettingPort], cl
mov [eax+usb_controller.ResettingHub], esi
; 2. Store the current time and set 'reset signalling active' flag.
mov eax, [timer_ticks]
mov [esi+usb_hub.ResetTime], eax
and [esi+usb_hub.Actions], not HUB_RESET_WAITING
or [esi+usb_hub.Actions], HUB_RESET_SIGNAL
; 3. Send request to the hub to initiate request signalling.
lea edx, [esi+usb_hub.ConfigBuffer]
; DEBUGF 1,'K : [%d] hub %x port %d initiate reset\n',[timer_ticks],esi,ecx
mov dword [edx], 23h + \
(USB_SET_FEATURE shl 8) + \
(PORT_RESET shl 16)
and dword [edx+4], 0xFF
stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_reset_started, esi, 0
test eax, eax
jnz @f
mov eax, esi
call usb_hub_reset_aborted
@@:
ret
endp
 
; This procedure is called when the request to start reset signalling initiated
; by usb_hub_initiate_reset is completed, either successfully or unsuccessfully.
proc usb_hub_reset_started stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; If the request is successful, do nothing.
; Otherwise, clear 'reset signalling' flag and abort reset process.
mov eax, [calldata]
; movzx ecx, [eax+usb_hub.ConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] hub %x port %d reset started\n',[timer_ticks],eax,ecx
cmp [status], 0
jz .nothing
and [eax+usb_hub.Actions], not HUB_RESET_SIGNAL
dbgstr 'Failed to reset hub port'
call usb_hub_reset_aborted
.nothing:
ret
endp
 
; This procedure is called by the protocol layer if something has failed during
; initial stages of the configuration process, so the device should be disabled
; at hub level.
proc usb_hub_disable_resetting_port
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
; movzx ecx, [eax+usb_hub.ConfigBuffer+4]
; dec ecx
; DEBUGF 1,'K : [%d] hub %x port %d disable\n',[timer_ticks],eax,ecx
lea edx, [eax+usb_hub.ConfigBuffer]
mov cl, PORT_ENABLE
jmp usb_hub_clear_port_change.buffer
endp
 
; This procedure is called when the hub is disconnected.
proc usb_hub_disconnect
virtual at esp
dd ? ; return address
.hubdata dd ?
end virtual
; 1. If the hub is disconnected during initial configuration,
; 1 is stored as hub data and there is nothing to do.
mov eax, [.hubdata]
cmp eax, 1
jz .nothing
; 2. Remove the hub from the overall list.
mov ecx, [eax+usb_hub.Next]
mov edx, [eax+usb_hub.Prev]
mov [ecx+usb_hub.Prev], edx
mov [edx+usb_hub.Next], ecx
; 3. If some child is in reset process, abort reset.
push esi
mov esi, [eax+usb_hub.Controller]
cmp [esi+usb_controller.ResettingHub], eax
jnz @f
cmp [esi+usb_controller.ResettingPort], -1
jz @f
push eax
call usb_test_pending_port
pop eax
@@:
pop esi
; 4. Loop over all children and notify other code that they were disconnected.
push ebx
xor ecx, ecx
.disconnect_children:
mov ebx, [eax+usb_hub.ConnectedDevicesPtr]
mov ebx, [ebx+ecx*4]
test ebx, ebx
jz @f
push eax ecx
call usb_device_disconnected
pop ecx eax
@@:
inc ecx
cmp ecx, [eax+usb_hub.NumPorts]
jb .disconnect_children
; 4. Free memory allocated for the hub data.
call free
pop ebx
.nothing:
retn 4
endp
/kernel/branches/Kolibri-acpi/bus/usb/init.inc
0,0 → 1,249
; Initialization of the USB subsystem.
; Provides usb_init procedure, includes all needed files.
 
; General notes:
; * There is one entry point for external kernel code: usb_init is called
; from initialization code and initializes USB subsystem.
; * There are several entry points for API; see the docs for description.
; * There are several functions which are called from controller-specific
; parts of USB subsystem. The most important is usb_new_device,
; which is called when a new device has been connected (over some time),
; has been reset and is ready to start configuring.
; * IRQ handlers are very restricted. They can not take any locks,
; since otherwise a deadlock is possible: imagine that a code has taken the
; lock and was interrupted by IRQ handler. Now IRQ handler would wait for
; releasing the lock, and a lock owner would wait for exiting IRQ handler
; to get the control.
; * Thus, there is the special USB thread which processes almost all activity.
; IRQ handlers do the minimal processing and wake this thread.
; * Also the USB thread wakes occasionally to process tasks which can be
; predicted without interrupts. These include e.g. a periodic roothub
; scanning in UHCI and initializing in USB_CONNECT_DELAY ticks
; after connecting a new device.
; * The main procedure of USB thread, usb_thread_proc, does all its work
; by querying usb_hardware_func.ProcessDeferred for every controller
; and usb_hub_process_deferred for every hub.
; ProcessDeferred does controller-specific actions and calculates the time
; when it should be invoked again, possibly infinite.
; usb_thread_proc selects the minimum from all times returned by
; ProcessDeferred and sleeps until this moment is reached or the thread
; is awakened by IRQ handler.
 
; Initializes the USB subsystem.
proc usb_init
; 1. Initialize all locks.
mov ecx, usb_controllers_list_mutex
call mutex_init
mov ecx, usb1_ep_mutex
call mutex_init
mov ecx, usb_gtd_mutex
call mutex_init
mov ecx, ehci_ep_mutex
call mutex_init
mov ecx, ehci_gtd_mutex
call mutex_init
; 2. Kick off BIOS from all USB controllers, calling the corresponding function
; *hci_kickoff_bios. Also count USB controllers for the next step.
; Note: USB1 companion(s) must go before the corresponding EHCI controller,
; otherwise BIOS could see a device moving from EHCI to a companion;
; first, this always wastes time;
; second, some BIOSes are buggy, do not expect that move and try to refer to
; previously-assigned controller instead of actual; sometimes that leads to
; hangoff.
; Thus, process controllers in PCI order.
mov esi, pcidev_list
push 0
.kickoff:
mov esi, [esi+PCIDEV.fd]
cmp esi, pcidev_list
jz .done_kickoff
cmp word [esi+PCIDEV.class+1], 0x0C03
jnz .kickoff
mov eax, uhci_kickoff_bios
cmp byte [esi+PCIDEV.class], 0x00
jz .do_kickoff
mov eax, ohci_kickoff_bios
cmp byte [esi+PCIDEV.class], 0x10
jz .do_kickoff
mov eax, ehci_kickoff_bios
cmp byte [esi+PCIDEV.class], 0x20
jnz .kickoff
.do_kickoff:
inc dword [esp]
call eax
jmp .kickoff
.done_kickoff:
pop eax
; 3. If no controllers were found, exit.
; Otherwise, run the USB thread.
test eax, eax
jz .nothing
call create_usb_thread
jz .nothing
; 4. Initialize all USB controllers, calling usb_init_controller for each.
; Note: USB1 companion(s) should go before the corresponding EHCI controller,
; although this is not strictly necessary (this way, a companion would not try
; to initialize high-speed device only to see a disconnect when EHCI takes
; control).
; Thus, process all EHCI controllers in the first loop, all USB1 controllers
; in the second loop. (One loop in reversed PCI order could also be used,
; but seems less natural.)
; 4a. Loop over all PCI devices, call usb_init_controller
; for all EHCI controllers.
mov eax, pcidev_list
.scan_ehci:
mov eax, [eax+PCIDEV.fd]
cmp eax, pcidev_list
jz .done_ehci
cmp [eax+PCIDEV.class], 0x0C0320
jnz .scan_ehci
mov edi, ehci_hardware_func
call usb_init_controller
jmp .scan_ehci
.done_ehci:
; 4b. Loop over all PCI devices, call usb_init_controller
; for all UHCI and OHCI controllers.
mov eax, pcidev_list
.scan_usb1:
mov eax, [eax+PCIDEV.fd]
cmp eax, pcidev_list
jz .done_usb1
mov edi, uhci_hardware_func
cmp [eax+PCIDEV.class], 0x0C0300
jz @f
mov edi, ohci_hardware_func
cmp [eax+PCIDEV.class], 0x0C0310
jnz .scan_usb1
@@:
call usb_init_controller
jmp .scan_usb1
.done_usb1:
.nothing:
ret
endp
 
uglobal
align 4
usb_event dd ?
endg
 
; Helper function for usb_init. Creates and initializes the USB thread.
proc create_usb_thread
; 1. Create the thread.
push edi
push 1
pop ebx
mov ecx, usb_thread_proc
xor edx, edx
call new_sys_threads
pop edi
; If failed, say something to the debug board and return with ZF set.
test eax, eax
jns @f
DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax
.clear:
xor eax, eax
jmp .nothing
@@:
; 2. Wait while the USB thread initializes itself.
@@:
call change_task
cmp [usb_event], 0
jz @b
; 3. If initialization failed, the USB thread sets [usb_event] to -1.
; Return with ZF set or cleared corresponding to the result.
cmp [usb_event], -1
jz .clear
.nothing:
ret
endp
 
; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero.
proc usb_wakeup_if_needed
test ebx, ebx
jz usb_wakeup.nothing
usb_wakeup:
xor edx, edx
mov eax, [usb_event]
mov ebx, [eax+EVENT.id]
xor esi, esi
call raise_event
.nothing:
ret
endp
 
; Main loop of the USB thread.
proc usb_thread_proc
; 1. Initialize: create event to allow wakeup by interrupt handlers.
xor esi, esi
mov ecx, MANUAL_DESTROY
call create_event
test eax, eax
jnz @f
; If failed, set [usb_event] to -1 and terminate myself.
dbgstr 'cannot create event for USB thread'
or [usb_event], -1
jmp sys_end
@@:
mov [usb_event], eax
push -1 ; initial timeout: infinite
usb_thread_wait:
; 2. Main loop: wait for either wakeup event or timeout.
pop ecx ; get timeout
mov eax, [usb_event]
mov ebx, [eax+EVENT.id]
call wait_event_timeout
push -1 ; default timeout: infinite
; 3. Main loop: call worker functions of all controllers;
; if some function schedules wakeup in timeout less than the current value,
; replace that value with the returned timeout.
mov esi, usb_controllers_list
@@:
mov esi, [esi+usb_controller.Next]
cmp esi, usb_controllers_list
jz .controllers_done
mov eax, [esi+usb_controller.HardwareFunc]
call [eax+usb_hardware_func.ProcessDeferred]
cmp [esp], eax
jb @b
mov [esp], eax
jmp @b
.controllers_done:
; 4. Main loop: call hub worker function for all hubs,
; similarly calculating minimum of all returned timeouts.
; When done, continue to 2.
mov esi, usb_hubs_list
@@:
mov esi, [esi+usb_hub.Next]
cmp esi, usb_hubs_list
jz usb_thread_wait
call usb_hub_process_deferred
cmp [esp], eax
jb @b
mov [esp], eax
jmp @b
endp
 
iglobal
align 4
usb_controllers_list:
dd usb_controllers_list
dd usb_controllers_list
usb_hubs_list:
dd usb_hubs_list
dd usb_hubs_list
endg
uglobal
align 4
usb_controllers_list_mutex MUTEX
endg
 
include "memory.inc"
include "hccommon.inc"
include "pipe.inc"
include "ohci.inc"
include "uhci.inc"
include "ehci.inc"
include "protocol.inc"
include "hub.inc"
include "scheduler.inc"
/kernel/branches/Kolibri-acpi/bus/usb/memory.inc
0,0 → 1,215
; Memory management for USB structures.
; Protocol layer uses the common kernel heap malloc/free.
; Hardware layer has special requirements:
; * memory blocks should be properly aligned
; * memory blocks should not cross page boundary
; Hardware layer allocates fixed-size blocks.
; Thus, the specific allocator is quite easy to write:
; allocate one page, split into blocks, maintain the single-linked
; list of all free blocks in each page.
 
; Note: size must be a multiple of required alignment.
 
; Data for one pool: dd pointer to the first page, MUTEX lock.
 
uglobal
; Structures in UHCI and OHCI have equal sizes.
; Thus, functions and data for allocating/freeing can be shared;
; we keep them here rather than in controller-specific files.
align 4
; Data for UHCI and OHCI endpoints pool.
usb1_ep_first_page dd ?
usb1_ep_mutex MUTEX
; Data for UHCI and OHCI general transfer descriptors pool.
usb_gtd_first_page dd ?
usb_gtd_mutex MUTEX
endg
 
; sanity check: structures in UHCI and OHCI should be the same for allocation
if (sizeof.ohci_pipe=sizeof.uhci_pipe)&(ohci_pipe.SoftwarePart=uhci_pipe.SoftwarePart)
 
; Allocates one endpoint structure for UHCI/OHCI.
; Returns pointer to software part (usb_pipe) in eax.
proc usb1_allocate_endpoint
push ebx
mov ebx, usb1_ep_mutex
stdcall usb_allocate_common, sizeof.ohci_pipe
test eax, eax
jz @f
add eax, ohci_pipe.SoftwarePart
@@:
pop ebx
ret
endp
 
; Free one endpoint structure for UHCI/OHCI.
; Stdcall with one argument, pointer to software part (usb_pipe).
proc usb1_free_endpoint
sub dword [esp+4], ohci_pipe.SoftwarePart
jmp usb_free_common
endp
 
else
; sanity check continued
.err allocate_endpoint/free_endpoint must be different for OHCI and UHCI
end if
 
; sanity check: structures in UHCI and OHCI should be the same for allocation
if (sizeof.ohci_gtd=sizeof.uhci_gtd)&(ohci_gtd.SoftwarePart=uhci_gtd.SoftwarePart)
 
; Allocates one general transfer descriptor structure for UHCI/OHCI.
; Returns pointer to software part (usb_gtd) in eax.
proc usb1_allocate_general_td
push ebx
mov ebx, usb_gtd_mutex
stdcall usb_allocate_common, sizeof.ohci_gtd
test eax, eax
jz @f
add eax, ohci_gtd.SoftwarePart
@@:
pop ebx
ret
endp
 
; Free one general transfer descriptor structure for UHCI/OHCI.
; Stdcall with one argument, pointer to software part (usb_gtd).
proc usb1_free_general_td
sub dword [esp+4], ohci_gtd.SoftwarePart
jmp usb_free_common
endp
 
else
; sanity check continued
.err allocate_general_td/free_general_td must be different for OHCI and UHCI
end if
 
; Allocator for fixed-size blocks: allocate a block.
; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
proc usb_allocate_common
push edi ; save used register to be stdcall
virtual at esp
dd ? ; saved edi
dd ? ; return address
.size dd ?
end virtual
; 1. Take the lock.
mov ecx, ebx
call mutex_lock
; 2. Find the first allocated page with a free block, if any.
; 2a. Initialize for the loop.
mov edx, ebx
.pageloop:
; 2b. Get the next page, keeping the current in eax.
mov eax, edx
mov edx, [edx-4]
; 2c. If there is no next page, we're out of luck; go to 4.
test edx, edx
jz .newpage
add edx, 0x1000
@@:
; 2d. Get the pointer to the first free block on this page.
; If there is no free block, continue to 2b.
mov eax, [edx-8]
test eax, eax
jz .pageloop
; 2e. Get the pointer to the next free block.
mov ecx, [eax]
; 2f. Update the pointer to the first free block from eax to ecx.
; Normally [edx-8] still contains eax, if so, atomically set it to ecx
; and proceed to 3.
; However, the price of simplicity of usb_free_common (in particular, it
; doesn't take the lock) is that [edx-8] could (rarely) be changed while
; we processed steps 2d+2e. If so, return to 2d and retry.
lock cmpxchg [edx-8], ecx
jnz @b
.return:
; 3. Release the lock taken in step 1 and return.
push eax
mov ecx, ebx
call mutex_unlock
pop eax
pop edi ; restore used register to be stdcall
ret 4
.newpage:
; 4. Allocate a new page.
push eax
stdcall kernel_alloc, 0x1000
pop edx
; If failed, say something to the debug board and return zero.
test eax, eax
jz .nomemory
; 5. Add the new page to the tail of list of allocated pages.
mov [edx-4], eax
; 6. Initialize two service dwords in the end of page:
; first free block is (start of page) + (block size)
; (we will return first block at (start of page), so consider it allocated),
; no next page.
mov edx, eax
lea edi, [eax+0x1000-8]
add edx, [.size]
mov [edi], edx
and dword [edi+4], 0
; 7. All blocks starting from edx are free; join them in a single-linked list.
@@:
mov ecx, edx
add edx, [.size]
mov [ecx], edx
cmp edx, edi
jbe @b
sub ecx, [.size]
and dword [ecx], 0
; 8. Return (start of page).
jmp .return
.nomemory:
dbgstr 'no memory for USB descriptor'
xor eax, eax
jmp .return
endp
 
; Allocator for fixed-size blocks: free a block.
proc usb_free_common
push ecx edx
virtual at esp
rd 2 ; saved registers
dd ? ; return address
.block dd ?
end virtual
; Insert the given block to the head of free blocks in this page.
mov ecx, [.block]
mov edx, ecx
or edx, 0xFFF
@@:
mov eax, [edx+1-8]
mov [ecx], eax
lock cmpxchg [edx+1-8], ecx
jnz @b
pop edx ecx
ret 4
endp
 
; Helper procedure for OHCI: translate physical address in ecx
; of some transfer descriptor to linear address.
proc usb_td_to_virt
; Traverse all pages used for transfer descriptors, looking for the one
; with physical address as in ecx.
mov eax, [usb_gtd_first_page]
@@:
test eax, eax
jz .zero
push eax
call get_pg_addr
sub eax, ecx
jz .found
cmp eax, -0x1000
ja .found
pop eax
mov eax, [eax+0x1000-4]
jmp @b
.found:
; When found, combine page address from eax with page offset from ecx.
pop eax
and ecx, 0xFFF
add eax, ecx
.zero:
ret
endp
/kernel/branches/Kolibri-acpi/bus/usb/ohci.inc
0,0 → 1,1601
; Code for OHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial
; development, but there are no reasons to keep it here.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; OHCI register declarations
; All of the registers should be read and written as Dwords.
; Partition 1. Control and Status registers.
OhciRevisionReg = 0
OhciControlReg = 4
OhciCommandStatusReg = 8
OhciInterruptStatusReg = 0Ch
OhciInterruptEnableReg = 10h
OhciInterruptDisableReg = 14h
; Partition 2. Memory Pointer registers.
OhciHCCAReg = 18h
OhciPeriodCurrentEDReg = 1Ch
OhciControlHeadEDReg = 20h
OhciControlCurrentEDReg = 24h
OhciBulkHeadEDReg = 28h
OhciBulkCurrentEDReg = 2Ch
OhciDoneHeadReg = 30h
; Partition 3. Frame Counter registers.
OhciFmIntervalReg = 34h
OhciFmRemainingReg = 38h
OhciFmNumberReg = 3Ch
OhciPeriodicStartReg = 40h
OhciLSThresholdReg = 44h
; Partition 4. Root Hub registers.
OhciRhDescriptorAReg = 48h
OhciRhDescriptorBReg = 4Ch
OhciRhStatusReg = 50h
OhciRhPortStatusReg = 54h
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
 
; OHCI-specific part of a pipe descriptor.
; * This structure corresponds to the Endpoint Descriptor aka ED from the OHCI
; specification.
; * The hardware requires 16-bytes alignment of the hardware part.
; Since the allocator (usb_allocate_common) allocates memory sequentially
; from page start (aligned on 0x1000 bytes), size of the structure must be
; divisible by 16.
struct ohci_pipe
; All addresses are physical.
Flags dd ?
; 1. Lower 7 bits (bits 0-6) are FunctionAddress. This is the USB address of
; the function containing the endpoint that this ED controls.
; 2. Next 4 bits (bits 7-10) are EndpointNumber. This is the USB address of
; the endpoint within the function.
; 3. Next 2 bits (bits 11-12) are Direction. This 2-bit field indicates the
; direction of data flow: 1 = IN, 2 = OUT. If neither IN nor OUT is
; specified, then the direction is determined from the PID field of the TD.
; For CONTROL endpoints, the transfer direction is different
; for different transfers, so the value of this field is 0
; (3 would have the same effect) and the actual direction
; of one transfer is encoded in the Transfer Descriptor.
; 4. Next bit (bit 13) is Speed bit. It indicates the speed of the endpoint:
; full-speed (S = 0) or low-speed (S = 1).
; 5. Next bit (bit 14) is sKip bit. When this bit is set, the hardware
; continues on to the next ED on the list without attempting access
; to the TD queue or issuing any USB token for the endpoint.
; Always cleared.
; 6. Next bit (bit 15) is Format bit. It must be 0 for Control, Bulk and
; Interrupt endpoints and 1 for Isochronous endpoints.
; 7. Next 11 bits (bits 16-26) are MaximumPacketSize. This field indicates
; the maximum number of bytes that can be sent to or received from the
; endpoint in a single data packet.
TailP dd ?
; Physical address of the tail descriptor in the TD queue.
; The descriptor itself is not in the queue. See also HeadP.
HeadP dd ?
; 1. First bit (bit 0) is Halted bit. This bit is set by the hardware to
; indicate that processing of the TD queue on the endpoint is halted.
; 2. Second bit (bit 1) is toggleCarry bit. Whenever a TD is retired, this
; bit is written to contain the last data toggle value from the retired TD.
; 3. Next two bits (bits 2-3) are reserved and always zero.
; 4. With masked 4 lower bits, this is HeadP itself: physical address of the
; head descriptor in the TD queue, that is, next TD to be processed for this
; endpoint. Note that a TD must be 16-bytes aligned.
; Empty queue is characterized by the condition HeadP == TailP.
NextED dd ?
; If nonzero, then this entry is a physical address of the next ED to be
; processed. See also the description before NextVirt field of the usb_pipe
; structure. Additionally to that description, the following is specific for
; the OHCI controller:
; * n=5, N=32, there are 32 "leaf" periodic lists.
; * The 1ms periodic list also serves Isochronous endpoints, which should be
; in the end of the list.
; * There is no "next" list for Bulk and Control lists, they are processed
; separately from others.
; * There is no "next" list for Periodic list for 1ms interval.
SoftwarePart rd sizeof.usb_pipe/4
; Software part, common for all controllers.
ends
 
if sizeof.ohci_pipe mod 16
.err ohci_pipe must be 16-bytes aligned
end if
 
; This structure describes the static head of every list of pipes.
; The hardware requires 16-bytes alignment of this structure.
; All instances of this structure are located sequentially in uhci_controller,
; uhci_controller is page-aligned, so it is sufficient to make this structure
; 16-bytes aligned and verify that the first instance is 16-bytes aligned
; inside uhci_controller.
struct ohci_static_ep
Flags dd ?
; Same as ohci_pipe.Flags.
; sKip bit is set, so the hardware ignores other fields except NextED.
dd ?
; Corresponds to ohci_pipe.TailP. Not used.
NextList dd ?
; Virtual address of the next list.
NextED dd ?
; Same as ohci_pipe.NextED.
SoftwarePart rd sizeof.usb_static_ep/4
; Software part, common for all controllers.
dd ?
; Padding for 16-bytes alignment.
ends
 
if sizeof.ohci_static_ep mod 16
.err ohci_static_ep must be 16-bytes aligned
end if
 
; OHCI-specific part of controller data.
; * The structure describes the memory area used for controller data,
; additionally to the registers of the controller.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 256 bytes and corresponds to
; the HCCA from OHCI specification.
; * The hardware requires 256-bytes alignment of the hardware part, so
; the entire descriptor must be 256-bytes aligned.
; This structure is allocated with kernel_alloc (see usb_init_controller),
; this gives page-aligned data.
; * The controller is described by both ohci_controller and usb_controller
; structures, for each controller there is one ohci_controller and one
; usb_controller structure. These structures are located sequentially
; in the memory: beginning from some page start, there is ohci_controller
; structure - this enforces hardware alignment requirements - and then
; usb_controller structure.
; * The code keeps pointer to usb_controller structure. The ohci_controller
; structure is addressed as [ptr + ohci_controller.field - sizeof.ohci_controller].
struct ohci_controller
; ------------------------------ hardware fields ------------------------------
InterruptTable rd 32
; Pointers to interrupt EDs. The hardware starts processing of periodic lists
; within the frame N from the ED pointed to by [InterruptTable+(N and 31)*4].
; See also the description of periodic lists inside ohci_pipe structure.
FrameNumber dw ?
; The current frame number. This field is written by hardware only.
; This field is read by ohci_process_deferred and ohci_irq to
; communicate when control/bulk processing needs to be temporarily
; stopped/restarted.
dw ?
; Padding. Written as zero at every update of FrameNumber.
DoneHead dd ?
; Physical pointer to the start of Done Queue.
; When the hardware updates this field, it sets bit 0 to one if there is
; unmasked interrupt pending.
rb 120
; Reserved for the hardware.
; ------------------------------ software fields ------------------------------
IntEDs ohci_static_ep
rb 62 * sizeof.ohci_static_ep
; Heads of 63 Periodic lists, see the description in usb_pipe.
ControlED ohci_static_ep
; Head of Control list, see the description in usb_pipe.
BulkED ohci_static_ep
; Head of Bulk list, see the description in usb_pipe.
MMIOBase dd ?
; Virtual address of memory-mapped area with OHCI registers OhciXxxReg.
PoweredUp db ?
; 1 in normal work, 0 during early phases of the initialization.
; This field is initialized to zero during memory allocation
; (see usb_init_controller), set to one by ohci_init when ports of the root hub
; are powered up, so connect/disconnect events can be handled.
rb 3 ; alignment
DoneList dd ?
; List of descriptors which were processed by the controller and now need
; to be finalized.
DoneListEndPtr dd ?
; Pointer to dword which should receive a pointer to the next item in DoneList.
; If DoneList is empty, this is a pointer to DoneList itself;
; otherwise, this is a pointer to NextTD field of the last item in DoneList.
ends
 
if ohci_controller.IntEDs mod 16
.err Static endpoint descriptors must be 16-bytes aligned inside ohci_controller
end if
 
; OHCI general transfer descriptor.
; * The structure describes transfers to be performed on Control, Bulk or
; Interrupt endpoints.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 16 bytes and corresponds to
; the General Transfer Descriptor aka general TD from OHCI specification.
; * The hardware requires 16-bytes alignment of the hardware part, so
; the entire descriptor must be 16-bytes aligned. Since the allocator
; (usb_allocate_common) allocates memory sequentially from page start
; (aligned on 0x1000 bytes), size of the structure must be divisible by 16.
struct ohci_gtd
; ------------------------------ hardware fields ------------------------------
; All addresses in this part are physical.
Flags dd ?
; 1. Lower 18 bits (bits 0-17) are ignored and not modified by the hardware.
; 2. Next bit (bit 18) is bufferRounding bit. If this bit is 0, then the last
; data packet must exactly fill the defined data buffer. If this bit is 1,
; then the last data packet may be smaller than the defined buffer without
; causing an error condition on the TD.
; 3. Next 2 bits (bits 19-20) are Direction field. This field indicates the
; direction of data flow. If the Direction field in the ED is OUT or IN,
; this field is ignored and the direction from the ED is used instead.
; Otherwise, 0 = SETUP, 1 = OUT, 2 = IN, 3 is reserved.
; 4. Next 3 bits (bits 21-23) are DelayInterrupt field. This field contains
; the interrupt delay count for this TD. When a TD is complete, the hardware
; may wait up to DelayInterrupt frames before generating an interrupt.
; If DelayInterrupt is 7 (maximum possible), then there is no interrupt
; associated with completion of this TD.
; 5. Next 2 bits (bits 24-25) are DataToggle field. This field is used to
; generate/compare the data PID value (DATA0 or DATA1). It is updated after
; each successful transmission/reception of a data packet. The bit 25
; is 0 when the data toggle value is acquired from the toggleCarry field in
; the ED and 1 when the data toggle value is taken from the bit 24.
; 6. Next 2 bits (bits 26-27) are ErrorCount field. For each transmission
; error, this value is incremented. If ErrorCount is 2 and another error
; occurs, the TD is retired with error. When a transaction completes without
; error, ErrorCount is reset to 0.
; 7. Upper 4 bits (bits 28-31) are ConditionCode field. This field contains
; the status of the last attempted transaction, one of USB_STATUS_* values.
CurBufPtr dd ?
; Physical address of the next memory location that will be accessed for
; transfer to/from the endpoint. 0 means zero-length data packet or that all
; bytes have been transferred.
NextTD dd ?
; This field has different meanings depending on the status of the descriptor.
; When the descriptor is queued for processing, but not yet processed:
; Physical address of the next TD for the endpoint.
; When the descriptor is processed by hardware, but not yet by software:
; Physical address of the previous processed TD.
; When the descriptor is processed by the IRQ handler, but not yet completed:
; Virtual pointer to the next processed TD.
BufEnd dd ?
; Physical address of the last byte in the buffer for this TD.
dd ? ; padding for 16-bytes alignment
SoftwarePart rd sizeof.usb_gtd/4
; Common part for all controllers.
ends
 
if sizeof.ohci_gtd mod 16
.err ohci_gtd must be 16-bytes aligned
end if
 
; OHCI isochronous transfer descriptor.
; * The structure describes transfers to be performed on Isochronous endpoints.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 32 bytes and corresponds to
; the Isochronous Transfer Descriptor aka isochronous TD from OHCI
; specification.
; * The hardware requires 32-bytes alignment of the hardware part, so
; the entire descriptor must be 32-bytes aligned.
; * The isochronous endpoints are not supported yet, so only hardware part is
; defined at the moment.
struct ohci_itd
StartingFrame dw ?
; This field contains the low order 16 bits of the frame number in which the
; first data packet of the Isochronous TD is to be sent.
Flags dw ?
; 1. Lower 5 bits (bits 0-4) are ignored and not modified by the hardware.
; 2. Next 3 bits (bits 5-7) are DelayInterrupt field.
; 3. Next 3 bits (bits 8-10) are FrameCount field. The TD describes
; FrameCount+1 data packets.
; 4. Next bit (bit 11) is ignored and not modified by the hardware.
; 5. Upper 4 bits (bits 12-15) are ConditionCode field. This field contains
; the completion code, one of USB_STATUS_* values, when the TD is moved to
; the Done Queue.
BufPage0 dd ?
; Lower 12 bits are ignored and not modified by the hardware.
; With masked 12 bits this field is the physical page containing all buffers.
NextTD dd ?
; Physical address of the next TD in the transfer queue.
BufEnd dd ?
; Physical address of the last byte in the buffer.
OffsetArray rw 8
; Initialized by software, read by hardware: Offset for packet 0..7.
; Used to determine size and starting address of an isochronous data packet.
; Written by hardware, read by software: PacketStatusWord for packet 0..7.
; Contains completion code and, if applicable, size received for an isochronous
; data packet.
ends
 
; Description of OHCI-specific data and functions for
; controller-independent code.
; Implements the structure usb_hardware_func from hccommon.inc for OHCI.
iglobal
align 4
ohci_hardware_func:
dd 'OHCI'
dd sizeof.ohci_controller
dd ohci_init
dd ohci_process_deferred
dd ohci_set_device_address
dd ohci_get_device_address
dd ohci_port_disable
dd ohci_new_port.reset
dd ohci_set_endpoint_packet_size
dd usb1_allocate_endpoint
dd usb1_free_endpoint
dd ohci_init_pipe
dd ohci_unlink_pipe
dd usb1_allocate_general_td
dd usb1_free_general_td
dd ohci_alloc_transfer
dd ohci_insert_transfer
dd ohci_new_device
endg
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and
; OHCI-specific parts of software structures.
; eax = pointer to ohci_controller to be initialized
; [ebp-4] = pcidevice
proc ohci_init
; inherit some variables from the parent (usb_init_controller)
.devfn equ ebp - 4
.bus equ ebp - 3
; 1. Store pointer to ohci_controller for further use.
push eax
mov edi, eax
; 2. Initialize hardware fields of ohci_controller.
; Namely, InterruptTable needs to be initialized with
; physical addresses of heads of first 32 Periodic lists.
; Note that all static heads fit in one page, so one call
; to get_pg_addr is sufficient.
if (ohci_controller.IntEDs / 0x1000) <> (ohci_controller.BulkED / 0x1000)
.err assertion failed
end if
if ohci_controller.IntEDs >= 0x1000
.err assertion failed
end if
lea esi, [eax+ohci_controller.IntEDs+32*sizeof.ohci_static_ep]
call get_pg_addr
add eax, ohci_controller.IntEDs
push 32
pop ecx
mov edx, ecx
@@:
stosd
add eax, sizeof.ohci_static_ep
loop @b
; 3. Initialize static heads ohci_controller.IntEDs, .ControlED, .BulkED.
; Use the loop over groups: first group consists of first 32 Periodic
; descriptors, next group consists of next 16 Periodic descriptors,
; ..., last group consists of the last Periodic descriptor.
; 3a. Prepare for the loop.
; make edi point to start of ohci_controller.IntEDs,
; other registers are already set.
; -128 fits in one byte, +128 does not fit.
sub edi, -128
; 3b. Loop over groups. On every iteration:
; edx = size of group, edi = pointer to the current group,
; esi = pointer to the next group, eax = physical address of the next group.
.init_static_eds:
; 3c. Get the size of the next group.
shr edx, 1
; 3d. Exit the loop if there is no next group.
jz .init_static_eds_done
; 3e. Initialize the first half of the current group.
; Advance edi to the second half.
push eax esi
call ohci_init_static_ep_group
pop esi eax
; 3f. Initialize the second half of the current group
; with the same values.
; Advance edi to the next group, esi/eax to the next of the next group.
call ohci_init_static_ep_group
jmp .init_static_eds
.init_static_eds_done:
; 3g. Initialize the head of the last Periodic list.
xor eax, eax
xor esi, esi
call ohci_init_static_endpoint
; 3i. Initialize the heads of Control and Bulk lists.
call ohci_init_static_endpoint
call ohci_init_static_endpoint
; 4. Create a virtual memory area to talk with the controller.
; 4a. Enable memory & bus master access.
mov ch, [.bus]
mov cl, 0
mov eax, ecx
mov bh, [.devfn]
mov bl, 4
call pci_read_reg
or al, 6
xchg eax, ecx
call pci_write_reg
; 4b. Read memory base address.
mov ah, [.bus]
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh
; 4c. Create mapping for physical memory. 256 bytes are sufficient.
stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE
test eax, eax
jz .fail
stosd ; fill ohci_controller.MMIOBase
xchg eax, edi
; now edi = MMIOBase
; 5. Reset the controller if needed.
; 5a. Check operational state.
; 0 = reset, 1 = resume, 2 = operational, 3 = suspended
mov eax, [edi+OhciControlReg]
and al, 3 shl 6
cmp al, 2 shl 6
jz .operational
; 5b. State is not operational, reset is needed.
.reset:
; 5c. Save FmInterval register.
pushd [edi+OhciFmIntervalReg]
; 5d. Issue software reset and wait up to 10ms, checking status every 1 ms.
push 1
pop ecx
push 10
pop edx
mov [edi+OhciCommandStatusReg], ecx
@@:
mov esi, ecx
call delay_ms
test [edi+OhciCommandStatusReg], ecx
jz .resetdone
dec edx
jnz @b
pop eax
dbgstr 'controller reset timeout'
jmp .fail_unmap
.resetdone:
; 5e. Restore FmInterval register.
pop eax
mov edx, eax
and edx, 3FFFh
jz .setfminterval
cmp dx, 2EDFh ; default value?
jnz @f ; assume that BIOS has configured the value
.setfminterval:
mov eax, 27792EDFh ; default value
@@:
mov [edi+OhciFmIntervalReg], eax
; 5f. Set PeriodicStart to 90% of FmInterval.
movzx eax, ax
; Two following lines are equivalent to eax = floor(eax * 0.9)
; for any 0 <= eax < 1C71C71Dh, which of course is far from maximum 0FFFFh.
mov edx, 0E6666667h
mul edx
mov [edi+OhciPeriodicStartReg], edx
.operational:
; 6. Setup controller registers.
pop esi ; restore pointer to ohci_controller saved in step 1
; 6a. Physical address of HCCA.
mov eax, esi
call get_pg_addr
mov [edi+OhciHCCAReg], eax
; 6b. Transition to operational state and clear all Enable bits.
mov cl, 2 shl 6
mov [edi+OhciControlReg], ecx
; 6c. Physical addresses of head of Control and Bulk lists.
if ohci_controller.BulkED >= 0x1000
.err assertion failed
end if
add eax, ohci_controller.ControlED
mov [edi+OhciControlHeadEDReg], eax
add eax, ohci_controller.BulkED - ohci_controller.ControlED
mov [edi+OhciBulkHeadEDReg], eax
; 6d. Zero Head registers: there are no active Control and Bulk descriptors yet.
xor eax, eax
; mov [edi+OhciPeriodCurrentEDReg], eax
mov [edi+OhciControlCurrentEDReg], eax
mov [edi+OhciBulkCurrentEDReg], eax
; mov [edi+OhciDoneHeadReg], eax
; 6e. Enable processing of all lists with control:bulk ratio = 1:1.
mov dword [edi+OhciControlReg], 10111100b
; 7. Get number of ports.
add esi, sizeof.ohci_controller
mov eax, [edi+OhciRhDescriptorAReg]
and eax, 0xF
mov [esi+usb_controller.NumPorts], eax
; 8. Initialize DoneListEndPtr to point to DoneList.
lea eax, [esi+ohci_controller.DoneList-sizeof.ohci_controller]
mov [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], eax
; 9. Hook interrupt.
mov ah, [.bus]
mov al, 0
mov bh, [.devfn]
mov bl, 3Ch
call pci_read_reg
; al = IRQ
movzx eax, al
stdcall attach_int_handler, eax, ohci_irq, esi
; 10. Enable controller interrupt on HcDoneHead writeback and RootHubStatusChange.
mov dword [edi+OhciInterruptEnableReg], 80000042h
DEBUGF 1,'K : OHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts]
; 11. Initialize ports of the controller.
; 11a. Initiate power up, disable all ports, clear all "changed" bits.
mov dword [edi+OhciRhStatusReg], 10000h ; SetGlobalPower
xor ecx, ecx
@@:
mov dword [edi+OhciRhPortStatusReg+ecx*4], 1F0101h ; SetPortPower+ClearPortEnable+clear "changed" bits
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb @b
; 11b. Wait for power up.
; VirtualBox has AReg == 0, delay_ms doesn't like zero value; ignore zero delay
push esi
mov esi, [edi+OhciRhDescriptorAReg]
shr esi, 24
add esi, esi
jz @f
call delay_ms
@@:
pop esi
; 11c. Ports are powered up; now it is ok to process connect/disconnect events.
mov [esi+ohci_controller.PoweredUp-sizeof.ohci_controller], 1
; IRQ handler doesn't accept connect/disconnect events before this point
; 11d. We could miss some events while waiting for powering up;
; scan all ports manually and check for connected devices.
xor ecx, ecx
.port_loop:
test dword [edi+OhciRhPortStatusReg+ecx*4], 1
jz .next_port
; There is a connected device; mark the port as 'connected'
; and save the connected time.
; Note that ConnectedTime must be set before 'connected' mark,
; otherwise the code in ohci_process_deferred could use incorrect time.
mov eax, [timer_ticks]
mov [esi+usb_controller.ConnectedTime+ecx*4], eax
lock bts [esi+usb_controller.NewConnected], ecx
.next_port:
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .port_loop
; 12. Return pointer to usb_controller.
xchg eax, esi
ret
.fail_unmap:
; On error after step 4, release the virtual memory area.
stdcall free_kernel_space, edi
.fail:
; On error, free the ohci_controller structure and return zero.
; Note that the pointer was placed in the stack at step 1.
; Note also that there can be no errors after step 8,
; where that pointer is popped from the stack.
pop ecx
.nothing:
xor eax, eax
ret
endp
 
; Helper procedure for step 3 of ohci_init.
; Initializes the static head of one list.
; eax = physical address of the "next" list, esi = pointer to the "next" list,
; edi = pointer to head to initialize.
; Advances edi to the next head, keeps eax/esi.
proc ohci_init_static_endpoint
mov byte [edi+ohci_static_ep.Flags+1], 1 shl (14 - 8) ; sKip this endpoint
mov [edi+ohci_static_ep.NextED], eax
mov [edi+ohci_static_ep.NextList], esi
add edi, ohci_static_ep.SoftwarePart
call usb_init_static_endpoint
add edi, sizeof.ohci_static_ep - ohci_static_ep.SoftwarePart
ret
endp
 
; Helper procedure for step 3 of ohci_init.
; Initializes one half of group of static heads.
; edx = size of the next group = half of size of the group,
; edi = pointer to the group, eax = physical address of the next group,
; esi = pointer to the next group.
; Advances eax, esi, edi to next group, keeps edx.
proc ohci_init_static_ep_group
push edx
@@:
call ohci_init_static_endpoint
add eax, sizeof.ohci_static_ep
add esi, sizeof.ohci_static_ep
dec edx
jnz @b
pop edx
ret
endp
 
; Controller-specific pre-initialization function: take ownership from BIOS.
; Some BIOSes, although not all of them, provide legacy emulation
; for USB keyboard and/or mice as PS/2-devices. In this case,
; we must notify the BIOS that we don't need that emulation and know how to
; deal with USB devices.
proc ohci_kickoff_bios
; 1. Get the physical address of MMIO registers.
mov ah, [esi+PCIDEV.bus]
mov bh, [esi+PCIDEV.devfn]
mov al, 2
mov bl, 10h
call pci_read_reg
and al, not 0Fh
; 2. Create mapping for physical memory. 256 bytes are sufficient.
stdcall map_io_mem, eax, 100h, PG_SW+PG_NOCACHE
test eax, eax
jz .nothing
; 3. Some BIOSes enable controller interrupts as a result of giving
; controller away. At this point the system knows nothing about how to serve
; OHCI interrupts, so such an interrupt will send the system into an infinite
; loop handling the same IRQ again and again. Thus, we need to block OHCI
; interrupts. We can't do this at the controller level until step 5,
; because the controller is currently owned by BIOS, so we block all hardware
; interrupts on this processor until step 5.
pushf
cli
; 4. Take the ownership over the controller.
; 4a. Check whether BIOS handles this controller at all.
mov edx, 100h
test dword [eax+OhciControlReg], edx
jz .has_ownership
; 4b. Send "take ownership" command to the BIOS.
; (This should generate SMI, BIOS should release its ownership in SMI handler.)
mov dword [eax+OhciCommandStatusReg], 8
; 4c. Wait for result no more than 50 ms, checking for status every 1 ms.
push 50
pop ecx
@@:
test dword [eax+OhciControlReg], edx
jz .has_ownership
push esi
push 1
pop esi
call delay_ms
pop esi
loop @b
dbgstr 'warning: taking OHCI ownership from BIOS timeout'
.has_ownership:
; 5. Disable all controller interrupts until the system will be ready to
; process them.
mov dword [eax+OhciInterruptDisableReg], 0C000007Fh
; 6. Now we can unblock interrupts in the processor.
popf
; 7. Release memory mapping created in step 2 and return.
stdcall free_kernel_space, eax
.nothing:
ret
endp
 
; IRQ handler for OHCI controllers.
ohci_irq.noint:
; Not our interrupt: restore registers and return zero.
xor eax, eax
pop edi esi ebx
ret
 
proc ohci_irq
push ebx esi edi ; save used registers to be cdecl
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.controller dd ?
end virtual
; 1. ebx will hold whether some deferred processing is needed,
; that cannot be done from the interrupt handler. Initialize to zero.
xor ebx, ebx
; 2. Get the mask of events which should be processed.
mov esi, [.controller]
mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller]
mov eax, [edi+OhciInterruptStatusReg]
; 3. Check whether that interrupt has been generated by our controller.
; (One IRQ can be shared by several devices.)
and eax, [edi+OhciInterruptEnableReg]
jz .noint
; 4. Get the physical pointer to the last processed descriptor.
; All processed descriptors form single-linked list from last to first
; with the help of NextTD field. The list is restarted every time when
; the controller writes to DoneHead, so grab the pointer now (before the next
; step) or it could be lost (the controller could write new value to DoneHead
; any time after WorkDone bit is cleared in OhciInterruptStatusReg).
mov ecx, [esi+ohci_controller.DoneHead-sizeof.ohci_controller]
and ecx, not 1
; 5. Clear the events we know of.
; Note that this should be done before processing of events:
; new events could arise while we are processing those, this way we won't lose
; them (the controller would generate another interrupt
; after completion of this one).
mov [edi+OhciInterruptStatusReg], eax
; 6. Save the mask of events for further reference.
push eax
; 7. Handle 'transfer is done' events.
; 7a. Test whether there are such events.
test al, 2
jz .skip_donehead
; There are some 'transfer is done' events, processed descriptors are linked
; through physical addresses in the reverse order.
; We can't do much in an interrupt handler, since callbacks could require
; waiting for locks and that can't be done in an interrupt handler.
; However, we can't also just defer all work to the USB thread, since
; it is possible that previous lists are not yet processed and it is hard
; to store unlimited number of list heads. Thus, we reverse the current list,
; append it to end of the previous list (if there is one) and defer other
; processing to the USB thread; this way there always is no more than one list
; (possibly joined from several controller-reported lists).
; The list traversal requires converting physical addresses to virtual pointers,
; so we may as well store pointers instead of physical addresses.
; 7b. Prepare for the reversing loop.
push ebx
xor ebx, ebx
test ecx, ecx
jz .tddone
call usb_td_to_virt
test eax, eax
jz .tddone
lea edx, [eax+ohci_gtd.NextTD]
; 7c. Reverse the list, converting physical to virtual. On every iteration:
; ecx = physical address of the current item
; eax = virtual pointer to the current item
; edx = virtual pointer to the last item.NextTD (first in the reverse list)
; ebx = virtual pointer to the next item (previous in the reverse list)
.tdloop:
mov ecx, [eax+ohci_gtd.NextTD]
mov [eax+ohci_gtd.NextTD], ebx
lea ebx, [eax+ohci_gtd.SoftwarePart]
test ecx, ecx
jz .tddone
call usb_td_to_virt
test eax, eax
jnz .tdloop
.tddone:
mov ecx, ebx
pop ebx
; 7d. The list is reversed,
; ecx = pointer to the first item, edx = pointer to the last item.NextTD.
; If the list is empty (unusual case), step 7 is done.
test ecx, ecx
jz .skip_donehead
; 7e. Otherwise, append this list to the end of previous one.
; Note that in theory the interrupt handler and the USB thread
; could execute in parallel.
.append_restart:
; Atomically get DoneListEndPtr in eax and set it to edx.
mov eax, [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller]
lock cmpxchg [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], edx
jnz .append_restart
; Store pointer to the new list.
; Note: we cannot perform any operations with [DoneListEndPtr]
; until we switch DoneListEndPtr to a new descriptor:
; it is possible that after first line of .append_restart loop
; ohci_process_deferred obtains the control, finishes processing
; of the old list, sets DoneListEndPtr to address of DoneList,
; frees all old descriptors, so eax would point to invalid location.
; This way, .append_restart loop would detect that DoneListEndPtr
; has changed, so eax needs to be re-read.
mov [eax], ecx
; 7f. Notify the USB thread that there is new work.
inc ebx
.skip_donehead:
; 8. Handle start-of-frame events.
; 8a. Test whether there are such events.
test byte [esp], 4
jz .skip_sof
; We enable SOF interrupt only when some pipes are waiting after changes.
spin_lock_irqsave [esi+usb_controller.WaitSpinlock]
; 8b. Make sure that there was at least one frame update
; since the request. If not, wait for the next SOF.
movzx eax, [esi+ohci_controller.FrameNumber-sizeof.ohci_controller]
cmp eax, [esi+usb_controller.StartWaitFrame]
jz .sof_unlock
; 8c. Copy WaitPipeRequest* to ReadyPipeHead*.
mov eax, [esi+usb_controller.WaitPipeRequestAsync]
mov [esi+usb_controller.ReadyPipeHeadAsync], eax
mov eax, [esi+usb_controller.WaitPipeRequestPeriodic]
mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax
; 8d. It is possible that pipe change is due to removal and
; Control/BulkCurrentED registers still point to one of pipes to be removed.
; The code responsible for disconnect events has temporarily stopped
; Control/Bulk processing, so it is safe to clear Control/BulkCurrentED.
; After that, restart processing.
xor edx, edx
mov [edi+OhciControlCurrentEDReg], edx
mov [edi+OhciBulkCurrentEDReg], edx
mov dword [edi+OhciCommandStatusReg], 6
or dword [edi+OhciControlReg], 30h
; 8e. Disable further interrupts on SOF.
; Note: OhciInterruptEnableReg/OhciInterruptDisableReg have unusual semantics.
mov dword [edi+OhciInterruptDisableReg], 4
; Notify the USB thread that there is new work (with pipes from ReadyPipeHead*).
inc ebx
.sof_unlock:
spin_unlock_irqrestore [esi+usb_controller.RemoveSpinlock]
.skip_sof:
; Handle roothub events.
; 9. Test whether there are such events.
test byte [esp], 40h
jz .skip_roothub
; 10. Check the status of the roothub itself.
; 10a. Global overcurrent?
test dword [edi+OhciRhStatusReg], 2
jz @f
; Note: this needs work.
dbgstr 'global overcurrent'
@@:
; 10b. Clear roothub events.
mov dword [edi+OhciRhStatusReg], 80020000h
; 11. Check the status of individual ports.
; Look for connect/disconnect and reset events.
; 11a. Prepare for the loop: start from port 0.
xor ecx, ecx
.portloop:
; 11b. Get the port status and changes of it.
; Accumulate change information.
; Look to "11.12.3 Port Change Information Processing" of the USB2 spec.
xor eax, eax
.accloop:
mov edx, [edi+OhciRhPortStatusReg+ecx*4]
xor ax, ax
or eax, edx
test edx, 1F0000h
jz .accdone
mov dword [edi+OhciRhPortStatusReg+ecx*4], 1F0000h
jmp .accloop
.accdone:
; debugging output, not needed for work
; test eax, 1F0000h
; jz @f
; DEBUGF 1,'K : ohci irq [%d] status of port %d is %x\n',[timer_ticks],ecx,eax
;@@:
; 11c. Ignore any events until all ports are powered up.
; They will be processed by ohci_init.
cmp [esi+ohci_controller.PoweredUp-sizeof.ohci_controller], 0
jz .nextport
; Handle changing of connection status.
test eax, 10000h
jz .nocsc
; There was a connect or disconnect event at this port.
; 11d. Disconnect the old device on this port, if any.
; if the port was resetting, indicate fail and signal
cmp cl, [esi+usb_controller.ResettingPort]
jnz @f
mov [esi+usb_controller.ResettingStatus], -1
inc ebx
@@:
lock bts [esi+usb_controller.NewDisconnected], ecx
; notify the USB thread that new work is waiting
inc ebx
; 11e. Change connected status. For the connection event, also
; store the connection time; any further processing is permitted only
; after USB_CONNECT_DELAY ticks.
test al, 1
jz .disconnect
; Note: ConnectedTime must be stored before setting the 'connected' bit,
; otherwise ohci_process_deferred could use an old time.
mov eax, [timer_ticks]
mov [esi+usb_controller.ConnectedTime+ecx*4], eax
lock bts [esi+usb_controller.NewConnected], ecx
jmp .nextport
.disconnect:
lock btr [esi+usb_controller.NewConnected], ecx
jmp .nextport
.nocsc:
; 11f. Process 'reset done' events.
test eax, 100000h
jz .nextport
test al, 10h
jnz .nextport
mov edx, [timer_ticks]
mov [esi+usb_controller.ResetTime], edx
mov [esi+usb_controller.ResettingStatus], 2
inc ebx
.nextport:
; 11g. Continue the loop for the next port.
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop
.skip_roothub:
; 12. Restore the stack after step 6.
pop eax
; 13. Notify the USB thread if some deferred processing is required.
call usb_wakeup_if_needed
; 14. Interrupt processed; return something non-zero.
mov al, 1
pop edi esi ebx ; restore used registers to be stdcall
ret
endp
 
; This procedure is called from usb_set_address_callback
; and stores USB device address in the ohci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
proc ohci_set_device_address
mov byte [ebx+ohci_pipe.Flags-ohci_pipe.SoftwarePart], cl
; Wait until the hardware will forget the old value.
call usb_subscribe_control
ret
endp
 
; This procedure returns USB device address from the usb_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = endpoint address
proc ohci_get_device_address
mov eax, [ebx+ohci_pipe.Flags-ohci_pipe.SoftwarePart]
and eax, 7Fh
ret
endp
 
; This procedure is called from usb_set_address_callback
; if the device does not accept SET_ADDRESS command and needs
; to be disabled at the port level.
; in: esi -> usb_controller, ecx = port
proc ohci_port_disable
mov edx, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller]
mov dword [edx+OhciRhPortStatusReg+ecx*4], 1
ret
endp
 
; This procedure is called from usb_get_descr8_callback when
; the packet size for zero endpoint becomes known and
; stores the packet size in ohci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
proc ohci_set_endpoint_packet_size
mov byte [ebx+ohci_pipe.Flags+2-ohci_pipe.SoftwarePart], cl
; Wait until the hardware will forget the old value.
call usb_subscribe_control
ret
endp
 
; This procedure is called from API usb_open_pipe and processes
; the controller-specific part of this API. See docs.
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
proc ohci_init_pipe
virtual at ebp+8
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
; 1. Initialize the queue of transfer descriptors: empty.
sub eax, ohci_gtd.SoftwarePart
call get_phys_addr
mov [edi+ohci_pipe.TailP-ohci_pipe.SoftwarePart], eax
mov [edi+ohci_pipe.HeadP-ohci_pipe.SoftwarePart], eax
; 2. Generate ohci_pipe.Flags, see the description in ohci_pipe.
mov eax, [ecx+ohci_pipe.Flags-ohci_pipe.SoftwarePart]
and eax, 0x207F ; keep Speed bit and FunctionAddress
mov edx, [.endpoint]
and edx, 15
shl edx, 7
or eax, edx
mov [edi+ohci_pipe.Flags-ohci_pipe.SoftwarePart], eax
mov eax, [.maxpacket]
mov word [edi+ohci_pipe.Flags+2-ohci_pipe.SoftwarePart], ax
cmp [.type], CONTROL_PIPE
jz @f
test byte [.endpoint], 80h
setnz al
inc eax
shl al, 3
or byte [edi+ohci_pipe.Flags+1-ohci_pipe.SoftwarePart], al
@@:
; 3. Insert the new pipe to the corresponding list of endpoints.
; 3a. Use Control list for control pipes, Bulk list for bulk pipes.
lea edx, [esi+ohci_controller.ControlED.SoftwarePart-sizeof.ohci_controller]
cmp [.type], BULK_PIPE
jb .insert ; control pipe
lea edx, [esi+ohci_controller.BulkED.SoftwarePart-sizeof.ohci_controller]
jz .insert ; bulk pipe
.interrupt_pipe:
; 3b. For interrupt pipes, let the scheduler select the appropriate list
; based on the current bandwidth distribution and the requested bandwidth.
; This could fail if the requested bandwidth is not available;
; if so, return an error.
lea edx, [esi + ohci_controller.IntEDs - sizeof.ohci_controller]
lea eax, [esi + ohci_controller.IntEDs + 32*sizeof.ohci_static_ep - sizeof.ohci_controller]
push 64
pop ecx
call usb1_select_interrupt_list
test edx, edx
jz .return0
; 3c. Insert endpoint at edi to the head of list in edx.
; Inserting to tail would work as well,
; but let's be consistent with other controllers.
.insert:
mov ecx, [edx+usb_pipe.NextVirt]
mov [edi+usb_pipe.NextVirt], ecx
mov [edi+usb_pipe.PrevVirt], edx
mov [ecx+usb_pipe.PrevVirt], edi
mov [edx+usb_pipe.NextVirt], edi
mov ecx, [edx+ohci_pipe.NextED-ohci_pipe.SoftwarePart]
mov [edi+ohci_pipe.NextED-ohci_pipe.SoftwarePart], ecx
lea eax, [edi-ohci_pipe.SoftwarePart]
call get_phys_addr
mov [edx+ohci_pipe.NextED-ohci_pipe.SoftwarePart], eax
; 4. Return something non-zero.
ret
.return0:
xor eax, eax
ret
endp
 
; This function is called from ohci_process_deferred when
; a new device was connected at least USB_CONNECT_DELAY ticks
; and therefore is ready to be configured.
; ecx = port, esi -> usb_controller
proc ohci_new_port
; test whether we are configuring another port
; if so, postpone configuring and return
bts [esi+usb_controller.PendingPorts], ecx
cmp [esi+usb_controller.ResettingPort], -1
jnz .nothing
btr [esi+usb_controller.PendingPorts], ecx
; fall through to ohci_new_port.reset
 
; This function is called from usb_test_pending_port.
; It starts reset signalling for the port. Note that in USB first stages
; of configuration can not be done for several ports in parallel.
.reset:
; reset port
and [esi+usb_controller.ResettingHub], 0
mov [esi+usb_controller.ResettingPort], cl
; Note: setting status must be the last action:
; it is possible that the device has been disconnected
; after timeout of USB_CONNECT_DELAY but before call to ohci_new_port.
; In this case, ohci_irq would not set reset status to 'failed',
; because ohci_irq would not know that this port is to be reset.
; However, the hardware would generate another interrupt
; in a response to reset a disconnected port, and this time
; ohci_irq knows that it needs to generate 'reset failed' event
; (because ResettingPort is now filled).
push edi
mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller]
mov dword [edi+OhciRhPortStatusReg+ecx*4], 10h
pop edi
.nothing:
ret
endp
 
; This procedure is called from the several places in main USB code
; and allocates required packets for the given transfer.
; ebx = pipe, other parameters are passed through the stack:
; buffer,size = data to transfer
; flags = same as in usb_open_pipe: bit 0 = allow short transfer, other bits reserved
; td = pointer to the current end-of-queue descriptor
; direction =
; 0000b for normal transfers,
; 1000b for control SETUP transfer,
; 1101b for control OUT transfer,
; 1110b for control IN transfer
; returns eax = pointer to the new end-of-queue descriptor
; (not included in the queue itself) or 0 on error
proc ohci_alloc_transfer stdcall uses edi, \
buffer:dword, size:dword, flags:dword, td:dword, direction:dword
locals
origTD dd ?
packetSize dd ? ; must be the last variable, see usb_init_transfer
endl
; 1. Save original value of td:
; it will be useful for rollback if something would fail.
mov eax, [td]
mov [origTD], eax
; One transfer descriptor can describe up to two pages.
; In the worst case (when the buffer is something*1000h+0FFFh)
; this corresponds to 1001h bytes. If the requested size is
; greater, we should split the transfer into several descriptors.
; Boundaries to split must be multiples of endpoint transfer size
; to avoid short packets except in the end of the transfer,
; 1000h is always a good value.
; 2. While the remaining data cannot fit in one packet,
; allocate page-sized descriptors.
mov edi, 1000h
mov [packetSize], edi
.fullpackets:
cmp [size], edi
jbe .lastpacket
call ohci_alloc_packet
test eax, eax
jz .fail
mov [td], eax
add [buffer], edi
sub [size], edi
jmp .fullpackets
; 3. The remaining data can fit in one descriptor;
; allocate the last descriptor with size = size of remaining data.
.lastpacket:
mov eax, [size]
mov [packetSize], eax
call ohci_alloc_packet
test eax, eax
jz .fail
; 4. Enable an immediate interrupt on completion of the last packet.
and byte [ecx+ohci_gtd.Flags+2-ohci_gtd.SoftwarePart], not (7 shl (21-16))
; 5. If a short transfer is ok for a caller, set the corresponding bit in
; the last descriptor, but not in others.
; Note: even if the caller says that short transfers are ok,
; all packets except the last one are marked as 'must be complete':
; if one of them will be short, the software intervention is needed
; to skip remaining packets; ohci_process_finalized_td will handle this
; transparently to the caller.
test [flags], 1
jz @f
or byte [ecx+ohci_gtd.Flags+2-ohci_gtd.SoftwarePart], 1 shl (18-16)
@@:
ret
.fail:
mov edi, ohci_hardware_func
mov eax, [td]
stdcall usb_undo_tds, [origTD]
xor eax, eax
ret
endp
 
; Helper procedure for ohci_alloc_transfer.
; Allocates and initializes one transfer descriptor.
; ebx = pipe, other parameters are passed through the stack;
; fills the current last descriptor and
; returns eax = next descriptor (not filled).
proc ohci_alloc_packet
; inherit some variables from the parent ohci_alloc_transfer
virtual at ebp-8
.origTD dd ?
.packetSize dd ?
rd 2
.buffer dd ?
.transferSize dd ?
.Flags dd ?
.td dd ?
.direction dd ?
end virtual
; 1. Allocate the next TD.
call usb1_allocate_general_td
test eax, eax
jz .nothing
; 2. Initialize controller-independent parts of both TDs.
push eax
call usb_init_transfer
pop eax
; 3. Save the returned value (next descriptor).
push eax
; 4. Store the physical address of the next descriptor.
sub eax, ohci_gtd.SoftwarePart
call get_phys_addr
mov [ecx+ohci_gtd.NextTD-ohci_gtd.SoftwarePart], eax
; 5. For zero-length transfers, store zero in both fields for buffer addresses.
; Otherwise, fill them with real values.
xor eax, eax
mov [ecx+ohci_gtd.CurBufPtr-ohci_gtd.SoftwarePart], eax
mov [ecx+ohci_gtd.BufEnd-ohci_gtd.SoftwarePart], eax
cmp [.packetSize], eax
jz @f
mov eax, [.buffer]
call get_phys_addr
mov [ecx+ohci_gtd.CurBufPtr-ohci_gtd.SoftwarePart], eax
mov eax, [.buffer]
add eax, [.packetSize]
dec eax
call get_phys_addr
mov [ecx+ohci_gtd.BufEnd-ohci_gtd.SoftwarePart], eax
@@:
; 6. Generate Flags field:
; - set bufferRounding (bit 18) to zero = disallow short transfers;
; for the last transfer in a row, ohci_alloc_transfer would set the real value;
; - set Direction (bits 19-20) to lower 2 bits of [.direction];
; - set DelayInterrupt (bits 21-23) to 7 = do not generate interrupt;
; for the last transfer in a row, ohci_alloc_transfer would set the real value;
; - set DataToggle (bits 24-25) to next 2 bits of [.direction];
; - set ConditionCode (bits 28-31) to 1111b as a indicator that there was no
; attempts to perform this transfer yet;
; - zero all other bits.
mov eax, [.direction]
mov edx, eax
and eax, 3
shl eax, 19
and edx, (3 shl 2)
shl edx, 24 - 2
lea eax, [eax + edx + (7 shl 21) + (15 shl 28)]
mov [ecx+ohci_gtd.Flags-ohci_gtd.SoftwarePart], eax
; 7. Restore the returned value saved in step 3.
pop eax
.nothing:
ret
endp
 
; This procedure is called from the several places in main USB code
; and activates the transfer which was previously allocated by
; ohci_alloc_transfer.
; ecx -> last descriptor for the transfer, ebx -> usb_pipe
proc ohci_insert_transfer
; 1. Advance the queue of transfer descriptors.
mov eax, [ecx+ohci_gtd.NextTD-ohci_gtd.SoftwarePart]
mov [ebx+ohci_pipe.TailP-ohci_pipe.SoftwarePart], eax
; 2. For control and bulk pipes, notify the controller that
; there is new work in control/bulk queue respectively.
ohci_notify_new_work:
mov edx, [ebx+usb_pipe.Controller]
mov edx, [edx+ohci_controller.MMIOBase-sizeof.ohci_controller]
cmp [ebx+usb_pipe.Type], CONTROL_PIPE
jz .control
cmp [ebx+usb_pipe.Type], BULK_PIPE
jnz .nothing
.bulk:
mov dword [edx+OhciCommandStatusReg], 4
jmp .nothing
.control:
mov dword [edx+OhciCommandStatusReg], 2
.nothing:
ret
endp
 
; This function is called from ohci_process_deferred when
; a new device has been reset and needs to be configured.
proc ohci_port_after_reset
; 1. Get the status.
; If reset has been failed (device disconnected during reset),
; continue to next device (if there is one).
xor eax, eax
xchg al, [esi+usb_controller.ResettingStatus]
test al, al
js usb_test_pending_port
; If the controller has disabled the port (e.g. overcurrent),
; continue to next device (if there is one).
movzx ecx, [esi+usb_controller.ResettingPort]
mov eax, [edi+OhciRhPortStatusReg+ecx*4]
test al, 2
jnz @f
DEBUGF 1,'K : USB port disabled after reset, status=%x\n',eax
jmp usb_test_pending_port
@@:
push ecx
; 2. Get LowSpeed bit to bit 0 of eax and call the worker procedure
; to notify the protocol layer about new OHCI device.
mov eax, [edi+OhciRhPortStatusReg+ecx*4]
DEBUGF 1,'K : port_after_reset [%d] status of port %d is %x\n',[timer_ticks],ecx,eax
shr eax, 9
call ohci_new_device
pop ecx
; 3. If something at the protocol layer has failed
; (no memory, no bus address), disable the port and stop the initialization.
test eax, eax
jnz .nothing
.disable_exit:
mov dword [edi+OhciRhPortStatusReg+ecx*4], 1
jmp usb_test_pending_port
.nothing:
ret
endp
 
; This procedure is called from uhci_port_init and from hub support code
; when a new device is connected and has been reset.
; It calls usb_new_device at the protocol layer with correct parameters.
; in: esi -> usb_controller, eax = speed;
; OHCI is USB1 device, so only low bit of eax (LowSpeed) is used.
proc ohci_new_device
; 1. Clear all bits of speed except bit 0.
and eax, 1
; 2. Store the speed for the protocol layer.
mov [esi+usb_controller.ResettingSpeed], al
; 3. Create pseudo-pipe in the stack.
; See ohci_init_pipe: only .Controller and .Flags fields are used.
shl eax, 13
push esi ; .Controller
mov ecx, esp
sub esp, 12 ; ignored fields
push eax ; .Flags
; 4. Notify the protocol layer.
call usb_new_device
; 5. Cleanup the stack after step 3 and return.
add esp, 20
ret
endp
 
; This procedure is called in the USB thread from usb_thread_proc,
; processes regular actions and those actions which can't be safely done
; from interrupt handler.
; Returns maximal time delta before the next call.
proc ohci_process_deferred
push ebx edi ; save used registers to be stdcall
; 1. Initialize the return value.
push -1
; 2. Process disconnect events.
call usb_disconnect_stage2
; 3. Check for connected devices.
; If there is a connected device which was connected less than
; USB_CONNECT_DELAY ticks ago, plan to wake up when the delay will be over.
; Otherwise, call ohci_new_port.
mov edi, [esi+ohci_controller.MMIOBase-sizeof.ohci_controller]
xor ecx, ecx
cmp [esi+usb_controller.NewConnected], ecx
jz .skip_newconnected
.portloop:
bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY
jge .connected
neg eax
cmp [esp], eax
jb .nextport
mov [esp], eax
jmp .nextport
.connected:
lock btr [esi+usb_controller.NewConnected], ecx
jnc .nextport
call ohci_new_port
.noconnect:
.nextport:
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop
.skip_newconnected:
; 4. Check for end of reset signalling. If so, call ohci_port_after_reset.
cmp [esi+usb_controller.ResettingStatus], 2
jnz .no_reset_recovery
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME
jge .reset_done
neg eax
cmp [esp], eax
jb .skip_roothub
mov [esp], eax
jmp .skip_roothub
.no_reset_recovery:
cmp [esi+usb_controller.ResettingStatus], 0
jz .skip_roothub
.reset_done:
call ohci_port_after_reset
.skip_roothub:
; 5. Finalize transfers processed by hardware.
; It is better to perform this step after processing disconnect events,
; although not strictly obligatory. This way, an active transfer aborted
; due to disconnect would be handled with more specific USB_STATUS_CLOSED,
; not USB_STATUS_NORESPONSE.
; Loop over all items in DoneList, call ohci_process_finalized_td for each.
xor ebx, ebx
xchg ebx, [esi+ohci_controller.DoneList-sizeof.ohci_controller]
.tdloop:
test ebx, ebx
jz .tddone
call ohci_process_finalized_td
jmp .tdloop
.tddone:
; 6. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 2 and 5 which could create new requests.
; 6a. Call the worker function from main USB code.
call usb_process_wait_lists
; 6b. If no new requests, skip the rest of this step.
test eax, eax
jz @f
; 6c. OHCI is not allowed to cache anything; we don't know what is
; processed right now, but we can be sure that the controller will not
; use any removed structure starting from the next frame.
; Schedule SOF event.
spin_lock_irq [esi+usb_controller.RemoveSpinlock]
mov eax, [esi+usb_controller.WaitPipeListAsync]
mov [esi+usb_controller.WaitPipeRequestAsync], eax
mov eax, [esi+usb_controller.WaitPipeListPeriodic]
mov [esi+usb_controller.WaitPipeRequestPeriodic], eax
; temporarily stop bulk and interrupt processing;
; this is required for handler of SOF event
and dword [edi+OhciControlReg], not 30h
; remember the frame number when processing has been stopped
; (needs to be done after stopping)
movzx eax, [esi+ohci_controller.FrameNumber-sizeof.ohci_controller]
mov [esi+usb_controller.StartWaitFrame], eax
; make sure that the next SOF will happen after the request
mov dword [edi+OhciInterruptStatusReg], 4
; enable interrupt on SOF
; Note: OhciInterruptEnableReg/OhciInterruptDisableReg have unusual semantics,
; so there should be 'mov' here, not 'or'
mov dword [edi+OhciInterruptEnableReg], 4
spin_unlock_irq [esi+usb_controller.RemoveSpinlock]
@@:
; 7. Restore the return value and return.
pop eax
pop edi ebx ; restore used registers to be stdcall
ret
endp
 
; Helper procedure for ohci_process_deferred. Processes one completed TD.
; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd.
proc ohci_process_finalized_td
; DEBUGF 1,'K : processing %x\n',ebx
; 1. Check whether the pipe has been closed, either due to API call or due to
; disconnect; if so, the callback will be called by usb_pipe_closed with
; correct status, so go to step 6 with ebx = 0 (do not free the TD).
mov edx, [ebx+usb_gtd.Pipe]
test [edx+usb_pipe.Flags], USB_FLAG_CLOSED
jz @f
lea eax, [ebx+ohci_gtd.NextTD-ohci_gtd.SoftwarePart]
xor ebx, ebx
jmp .next_td2
@@:
; 2. Remove the descriptor from the descriptors queue.
call usb_unlink_td
; 3. Get number of bytes that remain to be transferred.
; If CurBufPtr is zero, everything was transferred.
xor edx, edx
cmp [ebx+ohci_gtd.CurBufPtr-ohci_gtd.SoftwarePart], edx
jz .gotlen
; Otherwise, the remaining length is
; (BufEnd and 0xFFF) - (CurBufPtr and 0xFFF) + 1,
; plus 0x1000 if BufEnd and CurBufPtr are in different pages.
mov edx, [ebx+ohci_gtd.BufEnd-ohci_gtd.SoftwarePart]
mov eax, [ebx+ohci_gtd.CurBufPtr-ohci_gtd.SoftwarePart]
mov ecx, edx
and edx, 0xFFF
inc edx
xor ecx, eax
and ecx, -0x1000
jz @f
add edx, 0x1000
@@:
and eax, 0xFFF
sub edx, eax
.gotlen:
; The actual length is Length - (remaining length).
sub edx, [ebx+usb_gtd.Length]
neg edx
; 4. Check for error. If so, go to 7.
push ebx
mov eax, [ebx+ohci_gtd.Flags-ohci_gtd.SoftwarePart]
shr eax, 28
jnz .error
.notify:
; 5. Successful completion.
; 5a. Check whether this descriptor has an associated callback.
mov ecx, [ebx+usb_gtd.Callback]
test ecx, ecx
jz .ok_nocallback
; 5b. If so, call the callback.
stdcall_verify ecx, [ebx+usb_gtd.Pipe], eax, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .next_td
.ok_nocallback:
; 5c. Otherwise, add length of the current descriptor to the next descriptor.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.next_td:
; 6. Free the current descriptor and advance to the next item.
; If the current item is the last in the list,
; set DoneListEndPtr to pointer to DoneList.
cmp ebx, [esp]
jz @f
stdcall usb1_free_general_td, ebx
@@:
pop ebx
lea eax, [ebx+ohci_gtd.NextTD-ohci_gtd.SoftwarePart]
.next_td2:
push ebx
mov ebx, eax
lea edx, [esi+ohci_controller.DoneList-sizeof.ohci_controller]
xor ecx, ecx ; no next item
lock cmpxchg [esi+ohci_controller.DoneListEndPtr-sizeof.ohci_controller], edx
jz .last
; The current item is not the last.
; It is possible, although very rare, that ohci_irq has already advanced
; DoneListEndPtr, but not yet written NextTD. Wait until NextTD is nonzero.
@@:
mov ecx, [ebx]
test ecx, ecx
jz @b
.last:
pop ebx
; ecx = the next item
push ecx
; Free the current item, set ebx to the next item, continue to 5a.
test ebx, ebx
jz @f
stdcall usb1_free_general_td, ebx
@@:
pop ebx
ret
.error:
; 7. There was an error while processing this descriptor.
; The hardware has stopped processing the queue.
; 7a. Save status and length.
push eax
push edx
; DEBUGF 1,'K : TD failed:\n'
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-ohci_gtd.SoftwarePart],[ebx-ohci_gtd.SoftwarePart+4],[ebx-ohci_gtd.SoftwarePart+8],[ebx-ohci_gtd.SoftwarePart+12]
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-ohci_gtd.SoftwarePart+16],[ebx-ohci_gtd.SoftwarePart+20],[ebx-ohci_gtd.SoftwarePart+24],[ebx-ohci_gtd.SoftwarePart+28]
; mov eax, [ebx+usb_gtd.Pipe]
; DEBUGF 1,'K : pipe: %x %x %x %x\n',[eax-ohci_pipe.SoftwarePart],[eax-ohci_pipe.SoftwarePart+4],[eax-ohci_pipe.SoftwarePart+8],[eax-ohci_pipe.SoftwarePart+12]
; 7b. Traverse the list of descriptors looking for the final packet
; for this transfer.
; Free and unlink non-final descriptors, except the current one.
; Final descriptor will be freed in step 6.
call usb_is_final_packet
jnc .found_final
mov ebx, [ebx+usb_gtd.NextVirt]
virtual at esp
.length dd ?
.error_code dd ?
.current_item dd ?
end virtual
.look_final:
call usb_unlink_td
call usb_is_final_packet
jnc .found_final
push [ebx+usb_gtd.NextVirt]
stdcall usb1_free_general_td, ebx
pop ebx
jmp .look_final
.found_final:
; 7c. If error code is USB_STATUS_UNDERRUN and the last TD allows short packets,
; it is not an error.
; Note: all TDs except the last one in any transfer stage are marked
; as short-packet-is-error to stop controller from further processing
; of that stage; we need to restart processing from a TD following the last.
; After that, go to step 5 with eax = 0 (no error).
cmp dword [.error_code], USB_STATUS_UNDERRUN
jnz .no_underrun
test byte [ebx+ohci_gtd.Flags+2-ohci_gtd.SoftwarePart], 1 shl (18-16)
jz .no_underrun
and dword [.error_code], 0
mov ecx, [ebx+usb_gtd.Pipe]
mov edx, [ecx+ohci_pipe.HeadP-ohci_pipe.SoftwarePart]
and edx, 2
.advance_queue:
mov eax, [ebx+usb_gtd.NextVirt]
sub eax, ohci_gtd.SoftwarePart
call get_phys_addr
or eax, edx
mov [ecx+ohci_pipe.HeadP-ohci_pipe.SoftwarePart], eax
push ebx
mov ebx, ecx
call ohci_notify_new_work
pop ebx
pop edx eax
jmp .notify
; 7d. Abort the entire transfer.
; There are two cases: either there is only one transfer stage
; (everything except control transfers), then ebx points to the last TD and
; all previous TD were unlinked and dismissed (if possible),
; or there are several stages (a control transfer) and ebx points to the last
; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage,
; because Setup stage can not produce short packets); for Data stage, we need
; to unlink and free (if possible) one more TD and advance ebx to the next one.
.no_underrun:
cmp [ebx+usb_gtd.Callback], 0
jnz .halted
cmp ebx, [.current_item]
push [ebx+usb_gtd.NextVirt]
jz @f
stdcall usb1_free_general_td, ebx
@@:
pop ebx
call usb_unlink_td
.halted:
; 7e. For bulk/interrupt transfers we have no choice but halt the queue,
; the driver should intercede (through some API which is not written yet).
; Control pipes normally recover at the next SETUP transaction (first stage
; of any control transfer), so we hope on the best and just advance the queue
; to the next transfer. (According to the standard, "A control pipe may also
; support functional stall as well, but this is not recommended.").
; Advance the transfer queue to the next descriptor.
mov ecx, [ebx+usb_gtd.Pipe]
mov edx, [ecx+ohci_pipe.HeadP-ohci_pipe.SoftwarePart]
and edx, 2 ; keep toggleCarry bit
cmp [ecx+usb_pipe.Type], CONTROL_PIPE
jnz @f
inc edx ; set Halted bit
@@:
jmp .advance_queue
endp
 
; This procedure is called when a pipe is closing (either due to API call
; or due to disconnect); it unlinks the pipe from the corresponding list.
; esi -> usb_controller, ebx -> usb_pipe
proc ohci_unlink_pipe
cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE
jnz @f
mov eax, [ebx+ohci_pipe.Flags-ohci_pipe.SoftwarePart]
bt eax, 13
setc cl
bt eax, 11
setc ch
shr eax, 16
stdcall usb1_interrupt_list_unlink, eax, ecx
@@:
mov edx, [ebx+usb_pipe.NextVirt]
mov eax, [ebx+usb_pipe.PrevVirt]
mov [edx+usb_pipe.PrevVirt], eax
mov [eax+usb_pipe.NextVirt], edx
mov edx, [ebx+ohci_pipe.NextED-ohci_pipe.SoftwarePart]
mov [eax+ohci_pipe.NextED-ohci_pipe.SoftwarePart], edx
ret
endp
/kernel/branches/Kolibri-acpi/bus/usb/pipe.inc
0,0 → 1,813
; Functions for USB pipe manipulation: opening/closing, sending data etc.
;
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; USB pipe types
CONTROL_PIPE = 0
ISOCHRONOUS_PIPE = 1
BULK_PIPE = 2
INTERRUPT_PIPE = 3
 
; Status codes for transfer callbacks.
; Taken from OHCI as most verbose controller in this sense.
USB_STATUS_OK = 0 ; no error
USB_STATUS_CRC = 1 ; CRC error
USB_STATUS_BITSTUFF = 2 ; bit stuffing violation
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
USB_STATUS_STALL = 4 ; device returned STALL
USB_STATUS_NORESPONSE = 5 ; device not responding
USB_STATUS_PIDCHECK = 6 ; invalid PID check bits
USB_STATUS_WRONGPID = 7 ; unexpected PID value
USB_STATUS_OVERRUN = 8 ; too many data from endpoint
USB_STATUS_UNDERRUN = 9 ; too few data from endpoint
USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
USB_STATUS_CLOSED = 16 ; pipe closed
; either explicitly with USBClosePipe
; or implicitly due to device disconnect
 
; flags for usb_pipe.Flags
USB_FLAG_CLOSED = 1 ; pipe is closed, no new transfers
; pipe is closed, return error instead of submitting any new transfer
USB_FLAG_CAN_FREE = 2
; pipe is closed via explicit call to USBClosePipe, so it can be freed without
; any driver notification; if this flag is not set, then the pipe is closed due
; to device disconnect, so it must remain valid until return from disconnect
; callback provided by the driver
USB_FLAG_EXTRA_WAIT = 4
; The pipe was in wait list, while another event occured;
; when the first wait will be done, reinsert the pipe to wait list
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
 
; Pipe descriptor.
; * An USB pipe is described by two structures, for hardware and for software.
; * This is the software part. The hardware part is defined in a driver
; of the corresponding controller.
; * The hardware part is located immediately before usb_pipe,
; both are allocated at once by controller-specific code
; (it knows the total length, which depends on the hardware part).
struct usb_pipe
Controller dd ?
; Pointer to usb_controller structure corresponding to this pipe.
; Must be the first dword after hardware part, see *hci_new_device.
;
; Every endpoint is included into one of processing lists:
; * Bulk list contains all Bulk endpoints.
; * Control list contains all Control endpoints.
; * Several Periodic lists serve Interrupt endpoints with different interval.
; - There are N=2^n "leaf" periodic lists for N ms interval, one is processed
; in the frames 0,N,2N,..., another is processed in the frames
; 1,1+N,1+2N,... and so on. The hardware starts processing of periodic
; endpoints in every frame from the list identified by lower n bits of the
; frame number; the addresses of these N lists are written to the
; controller data area during the initialization.
; - We assume that n=5, N=32 to simplify the code and compact the data.
; OHCI works in this way. UHCI and EHCI actually have n=10, N=1024,
; but this is an overkill for interrupt endpoints; the large value of N is
; useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code
; initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value,
; giving essentially N=32.
; This restriction means that the actual maximum interval of polling any
; interrupt endpoint is 32ms, which seems to be a reasonable value.
; - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms
; interval and so on. Finally, there is one list for 1ms interval. Their
; addresses are not directly known to the controller.
; - The hardware serves endpoints following a physical link from the hardware
; part.
; - The hardware links are organized as follows. If the list item is not the
; last, it's hardware link points to the next item. The hardware link of
; the last item points to the first item of the "next" list.
; - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms
; is the k-th periodic list for interval M ms, M >= 1. In this scheme,
; if two "previous" lists are served in the frames k,k+2M,k+4M,...
; and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in
; the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want.
; - The links between Periodic, Control, Bulk lists and the processing of
; Isochronous endpoints are controller-specific.
; * The head of every processing list is a static entry which does not
; correspond to any real pipe. It is described by usb_static_ep
; structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus
; sizeof hardware part is 20h, the total number of lists is
; 32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page,
; leaving space for other data. This is another reason for 32ms limit.
; * Static endpoint descriptors are kept in *hci_controller structure.
; * All items in every processing list, including the static head, are
; organized in a double-linked list using .NextVirt and .PrevVirt fields.
; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items.
NextVirt dd ?
; Next endpoint in the processing list.
; See also PrevVirt field and the description before NextVirt field.
PrevVirt dd ?
; Previous endpoint in the processing list.
; See also NextVirt field and the description before NextVirt field.
;
; Every pipe has the associated transfer queue, that is, the double-linked
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
; endpoints this list consists of usb_gtd structures
; (GTD = General Transfer Descriptors), for Isochronous endpoints
; this list consists of usb_itd structures, which are not developed yet.
; The pipe needs to know only the last TD; the first TD can be
; obtained as [[pipe.LastTD].NextVirt].
LastTD dd ?
; Last TD in the transfer queue.
;
; All opened pipes corresponding to the same physical device are organized in
; the double-linked list using .NextSibling and .PrevSibling fields.
; The head of this list is kept in usb_device_data structure (OpenedPipeList).
; This list is used when the device is disconnected and all pipes for the
; device should be closed.
; Also, all pipes closed due to disconnect must remain valid at least until
; driver-provided disconnect function returns; all should-be-freed-but-not-now
; pipes for one device are organized in another double-linked list with
; the head in usb_device_data.ClosedPipeList; this list uses the same link
; fields, one pipe can never be in both lists.
NextSibling dd ?
; Next pipe for the physical device.
PrevSibling dd ?
; Previous pipe for the physical device.
;
; When hardware part of pipe is changed, some time is needed before further
; actions so that hardware reacts on this change. During that time,
; all changed pipes are organized in single-linked list with the head
; usb_controller.WaitPipeList* and link field NextWait.
; Currently there are two possible reasons to change:
; change of address/packet size in initial configuration,
; close of the pipe. They are distinguished by USB_FLAG_CLOSED.
NextWait dd ?
Lock MUTEX
; Mutex that guards operations with transfer queue for this pipe.
Type db ?
; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE.
Flags db ?
; Combination of flags, USB_FLAG_*.
rb 2 ; dword alignment
DeviceData dd ?
; Pointer to usb_device_data, common for all pipes for one device.
ends
 
; This structure describes the static head of every list of pipes.
struct usb_static_ep
; software fields
Bandwidth dd ?
; valid only for interrupt/isochronous USB1 lists
; The offsets of the following two fields must be the same in this structure
; and in usb_pipe.
NextVirt dd ?
PrevVirt dd ?
ends
 
; This structure represents one transfer descriptor
; ('g' stands for "general" as opposed to isochronous usb_itd).
; Note that one transfer can have several descriptors:
; a control transfer has three stages.
; Additionally, every controller has a limit on transfer length with
; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI),
; large transfers must be split into individual packets according to that limit.
struct usb_gtd
Callback dd ?
; Zero for intermediate descriptors, pointer to callback function
; for final descriptor. See the docs for description of the callback.
UserData dd ?
; Dword which is passed to Callback as is, not used by USB code itself.
; Two following fields organize all descriptors for one pipe in
; the linked list.
NextVirt dd ?
PrevVirt dd ?
Pipe dd ?
; Pointer to the parent usb_pipe.
Buffer dd ?
; Pointer to data for this descriptor.
Length dd ?
; Length of data for this descriptor.
ends
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
USB_STDCALL_VERIFY = 1
macro stdcall_verify [arg]
{
common
if USB_STDCALL_VERIFY
pushad
stdcall arg
call verify_regs
popad
else
stdcall arg
end if
}
 
; Initialization of usb_static_ep structure,
; called from controller-specific initialization; edi -> usb_static_ep
proc usb_init_static_endpoint
mov [edi+usb_static_ep.NextVirt], edi
mov [edi+usb_static_ep.PrevVirt], edi
ret
endp
 
; Part of API for drivers, see documentation for USBOpenPipe.
proc usb_open_pipe stdcall uses ebx esi edi,\
config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword
locals
targetsmask dd ? ; S-Mask for USB2
bandwidth dd ?
target dd ?
endl
; 1. Verify type of pipe: it must be one of *_PIPE constants.
; Isochronous pipes are not supported yet.
mov eax, [type]
cmp eax, INTERRUPT_PIPE
ja .badtype
cmp al, ISOCHRONOUS_PIPE
jnz .goodtype
.badtype:
dbgstr 'unsupported type of USB pipe'
jmp .return0
.goodtype:
; 2. Allocate memory for pipe and transfer queue.
; Empty transfer queue consists of one inactive TD.
mov ebx, [config_pipe]
mov esi, [ebx+usb_pipe.Controller]
mov edx, [esi+usb_controller.HardwareFunc]
call [edx+usb_hardware_func.AllocPipe]
test eax, eax
jz .nothing
mov edi, eax
mov edx, [esi+usb_controller.HardwareFunc]
call [edx+usb_hardware_func.AllocTD]
test eax, eax
jz .free_and_return0
; 3. Initialize transfer queue: pointer to transfer descriptor,
; pointers in transfer descriptor, queue lock.
mov [edi+usb_pipe.LastTD], eax
mov [eax+usb_gtd.NextVirt], eax
mov [eax+usb_gtd.PrevVirt], eax
mov [eax+usb_gtd.Pipe], edi
lea ecx, [edi+usb_pipe.Lock]
call mutex_init
; 4. Initialize software part of pipe structure, except device-related fields.
mov al, byte [type]
mov [edi+usb_pipe.Type], al
xor eax, eax
mov [edi+usb_pipe.Flags], al
mov [edi+usb_pipe.DeviceData], eax
mov [edi+usb_pipe.Controller], esi
or [edi+usb_pipe.NextWait], -1
; 5. Initialize device-related fields:
; for zero endpoint, set .NextSibling = .PrevSibling = this;
; for other endpoins, copy device data, take the lock guarding pipe list
; for the device and verify that disconnect processing has not yet started
; for the device. (Since disconnect processing also takes that lock,
; either it has completed or it will not start until we release the lock.)
; Note: usb_device_disconnected should not see the new pipe until
; initialization is complete, so that lock will be held during next steps
; (disconnect processing should either not see it at all, or see fully
; initialized pipe).
cmp [endpoint], eax
jz .zero_endpoint
mov ecx, [ebx+usb_pipe.DeviceData]
mov [edi+usb_pipe.DeviceData], ecx
call mutex_lock
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
jz .common
.fail:
; If disconnect processing has completed, unlock the mutex, free memory
; allocated in step 2 and return zero.
call mutex_unlock
mov edx, [esi+usb_controller.HardwareFunc]
stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD]
.free_and_return0:
mov edx, [esi+usb_controller.HardwareFunc]
stdcall [edx+usb_hardware_func.FreePipe], edi
.return0:
xor eax, eax
jmp .nothing
.zero_endpoint:
mov [edi+usb_pipe.NextSibling], edi
mov [edi+usb_pipe.PrevSibling], edi
.common:
; 6. Initialize hardware part of pipe structure.
; 6a. Acquire the corresponding mutex.
lea ecx, [esi+usb_controller.ControlLock]
cmp [type], BULK_PIPE
jb @f ; control pipe
lea ecx, [esi+usb_controller.BulkLock]
jz @f ; bulk pipe
lea ecx, [esi+usb_controller.PeriodicLock]
@@:
call mutex_lock
; 6b. Let the controller-specific code do its job.
push ecx
mov edx, [esi+usb_controller.HardwareFunc]
mov eax, [edi+usb_pipe.LastTD]
mov ecx, [config_pipe]
call [edx+usb_hardware_func.InitPipe]
pop ecx
; 6c. Release the mutex.
push eax
call mutex_unlock
pop eax
; 6d. If controller-specific code indicates failure,
; release the lock taken in step 5, free memory allocated in step 2
; and return zero.
test eax, eax
jz .fail
; 7. The pipe is initialized. If this is not the first pipe for the device,
; insert it to the tail of pipe list for the device,
; increment number of pipes,
; release the lock taken at step 5.
mov ecx, [edi+usb_pipe.DeviceData]
test ecx, ecx
jz @f
mov eax, [ebx+usb_pipe.PrevSibling]
mov [edi+usb_pipe.NextSibling], ebx
mov [edi+usb_pipe.PrevSibling], eax
mov [ebx+usb_pipe.PrevSibling], edi
mov [eax+usb_pipe.NextSibling], edi
inc [ecx+usb_device_data.NumPipes]
call mutex_unlock
@@:
; 8. Return pointer to usb_pipe.
mov eax, edi
.nothing:
ret
endp
 
; This procedure is called several times during initial device configuration,
; when usb_device_data structure is reallocated.
; It (re)initializes all pointers in usb_device_data.
; ebx -> usb_pipe
proc usb_reinit_pipe_list
push eax
; 1. (Re)initialize the lock guarding pipe list.
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_init
; 2. Initialize list of opened pipes: two entries, the head and ebx.
add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling
mov [ecx+usb_pipe.NextSibling], ebx
mov [ecx+usb_pipe.PrevSibling], ebx
mov [ebx+usb_pipe.NextSibling], ecx
mov [ebx+usb_pipe.PrevSibling], ecx
; 3. Initialize list of closed pipes: empty list, only the head is present.
add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList
mov [ecx+usb_pipe.NextSibling], ecx
mov [ecx+usb_pipe.PrevSibling], ecx
pop eax
ret
endp
 
; Part of API for drivers, see documentation for USBClosePipe.
proc usb_close_pipe
push ebx esi ; save used registers to be stdcall
virtual at esp
rd 2 ; saved registers
dd ? ; return address
.pipe dd ?
end virtual
; 1. Lock the pipe list for the device.
mov ebx, [.pipe]
mov esi, [ebx+usb_pipe.Controller]
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_lock
; 2. Set the flag "the driver has abandoned this pipe, free it at any time".
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
call mutex_unlock
; 3. Call the worker function.
call usb_close_pipe_nolock
; 4. Unlock the pipe list for the device.
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_unlock
; 5. Wakeup the USB thread so that it can proceed with releasing that pipe.
push edi
call usb_wakeup
pop edi
; 6. Return.
pop esi ebx ; restore used registers to be stdcall
retn 4
endp
 
; Worker function for pipe closing. Called by usb_close_pipe API and
; from disconnect processing.
; The lock guarding pipe list for the device should be held by the caller.
; ebx -> usb_pipe, esi -> usb_controller
proc usb_close_pipe_nolock
; 1. Set the flag "pipe is closed, ignore new transfers".
; If it was already set, do nothing.
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT
jc .closed
call mutex_unlock
; 2. Remove the pipe from the list of opened pipes.
mov eax, [ebx+usb_pipe.NextSibling]
mov edx, [ebx+usb_pipe.PrevSibling]
mov [eax+usb_pipe.PrevSibling], edx
mov [edx+usb_pipe.NextSibling], eax
; 3. Unlink the pipe from hardware structures.
; 3a. Acquire the corresponding lock.
lea edx, [esi+usb_controller.WaitPipeListAsync]
lea ecx, [esi+usb_controller.ControlLock]
cmp [ebx+usb_pipe.Type], BULK_PIPE
jb @f ; control pipe
lea ecx, [esi+usb_controller.BulkLock]
jz @f ; bulk pipe
add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync
lea ecx, [esi+usb_controller.PeriodicLock]
@@:
push edx
call mutex_lock
push ecx
; 3b. Let the controller-specific code do its job.
mov eax, [esi+usb_controller.HardwareFunc]
call [eax+usb_hardware_func.UnlinkPipe]
; 3c. Release the corresponding lock.
pop ecx
call mutex_unlock
; 4. Put the pipe into wait queue.
pop edx
cmp [ebx+usb_pipe.NextWait], -1
jz .insert_new
or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT
jmp .inserted
.insert_new:
mov eax, [edx]
mov [ebx+usb_pipe.NextWait], eax
mov [edx], ebx
.inserted:
; 5. Return.
ret
.closed:
call mutex_unlock
xor eax, eax
ret
endp
 
; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the
; corresponding wait list. It means that the hardware has fully forgot about it.
; ebx -> usb_pipe, esi -> usb_controller
proc usb_pipe_closed
push edi
mov edi, [esi+usb_controller.HardwareFunc]
; 1. Loop over all transfers, calling the driver with USB_STATUS_CLOSED
; and freeing all descriptors.
mov edx, [ebx+usb_pipe.LastTD]
test edx, edx
jz .no_transfer
mov edx, [edx+usb_gtd.NextVirt]
.transfer_loop:
cmp edx, [ebx+usb_pipe.LastTD]
jz .transfer_done
mov ecx, [edx+usb_gtd.Callback]
test ecx, ecx
jz .no_callback
push edx
stdcall_verify ecx, ebx, USB_STATUS_CLOSED, \
[edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData]
pop edx
.no_callback:
push [edx+usb_gtd.NextVirt]
stdcall [edi+usb_hardware_func.FreeTD], edx
pop edx
jmp .transfer_loop
.transfer_done:
stdcall [edi+usb_hardware_func.FreeTD], edx
.no_transfer:
; 2. Decrement number of pipes for the device.
; If this pipe is the last pipe, go to 5.
mov ecx, [ebx+usb_pipe.DeviceData]
call mutex_lock
dec [ecx+usb_device_data.NumPipes]
jz .last_pipe
call mutex_unlock
; 3. If the flag "the driver has abandoned this pipe" is set,
; free memory and return.
test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE
jz .nofree
stdcall [edi+usb_hardware_func.FreePipe], ebx
pop edi
ret
; 4. Otherwise, add it to the list of closed pipes and return.
.nofree:
add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
mov edx, [ecx+usb_pipe.PrevSibling]
mov [ebx+usb_pipe.NextSibling], ecx
mov [ebx+usb_pipe.PrevSibling], edx
mov [ecx+usb_pipe.PrevSibling], ebx
mov [edx+usb_pipe.NextSibling], ebx
pop edi
ret
.last_pipe:
; That was the last pipe for the device.
; 5. Notify device driver(s) about disconnect.
call mutex_unlock
movzx eax, [ecx+usb_device_data.NumInterfaces]
test eax, eax
jz .notify_done
add ecx, [ecx+usb_device_data.Interfaces]
.notify_loop:
mov edx, [ecx+usb_interface_data.DriverFunc]
test edx, edx
jz @f
mov edx, [edx+USBSRV.usb_func]
cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4
jb @f
mov edx, [edx+USBFUNC.device_disconnect]
test edx, edx
jz @f
push eax ecx
stdcall_verify edx, [ecx+usb_interface_data.DriverData]
pop ecx eax
@@:
add ecx, sizeof.usb_interface_data
dec eax
jnz .notify_loop
.notify_done:
; 6. Bus address, if assigned, can now be reused.
call [edi+usb_hardware_func.GetDeviceAddress]
test eax, eax
jz @f
bts [esi+usb_controller.ExistingAddresses], eax
@@:
dbgstr 'USB device disconnected'
; 7. All drivers have returned from disconnect callback,
; so all drivers should not use any device-related pipes.
; Free the remaining pipes.
mov eax, [ebx+usb_pipe.DeviceData]
add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
push eax
mov eax, [eax+usb_pipe.NextSibling]
.free_loop:
cmp eax, [esp]
jz .free_done
push [eax+usb_pipe.NextSibling]
stdcall [edi+usb_hardware_func.FreePipe], eax
pop eax
jmp .free_loop
.free_done:
stdcall [edi+usb_hardware_func.FreePipe], ebx
pop eax
; 8. Free the usb_device_data structure.
sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling
call free
; 9. Return.
.nothing:
pop edi
ret
endp
 
; Part of API for drivers, see documentation for USBNormalTransferAsync.
proc usb_normal_transfer_async stdcall uses ebx edi,\
pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
; 1. Sanity check: callback must be nonzero.
; (It is important for other parts of code.)
xor eax, eax
cmp [callback], eax
jz .nothing
; 2. Lock the transfer queue.
mov ebx, [pipe]
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
; 3. If the pipe has already been closed (presumably due to device disconnect),
; release the lock taken in step 2 and return zero.
xor eax, eax
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
jnz .unlock
; 4. Allocate and initialize TDs for the transfer.
mov edx, [ebx+usb_pipe.Controller]
mov edi, [edx+usb_controller.HardwareFunc]
stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0
; If failed, release the lock taken in step 2 and return zero.
test eax, eax
jz .unlock
; 5. Store callback and its parameters in the last descriptor for this transfer.
mov ecx, [eax+usb_gtd.PrevVirt]
mov edx, [callback]
mov [ecx+usb_gtd.Callback], edx
mov edx, [calldata]
mov [ecx+usb_gtd.UserData], edx
mov edx, [buffer]
mov [ecx+usb_gtd.Buffer], edx
; 6. Advance LastTD pointer and activate transfer.
push [ebx+usb_pipe.LastTD]
mov [ebx+usb_pipe.LastTD], eax
call [edi+usb_hardware_func.InsertTransfer]
pop eax
; 7. Release the lock taken in step 2 and
; return pointer to the first descriptor for the new transfer.
.unlock:
push eax
lea ecx, [ebx+usb_pipe.Lock]
call mutex_unlock
pop eax
.nothing:
ret
endp
 
; Part of API for drivers, see documentation for USBControlTransferAsync.
proc usb_control_async stdcall uses ebx edi,\
pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword
locals
last_td dd ?
endl
; 1. Sanity check: callback must be nonzero.
; (It is important for other parts of code.)
cmp [callback], 0
jz .return0
; 2. Lock the transfer queue.
mov ebx, [pipe]
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
; 3. If the pipe has already been closed (presumably due to device disconnect),
; release the lock taken in step 2 and return zero.
test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED
jnz .unlock_return0
; A control transfer contains two or three stages:
; Setup stage, optional Data stage, Status stage.
; 4. Allocate and initialize TDs for the Setup stage.
; Payload is 8 bytes from [config].
mov edx, [ebx+usb_pipe.Controller]
mov edi, [edx+usb_controller.HardwareFunc]
stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0
; short transfer is an error, direction is DATA0, token is SETUP
mov [last_td], eax
test eax, eax
jz .fail
; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero.
; Payload is [size] bytes from [buffer].
mov edx, [config]
mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT
cmp byte [edx], 0
jns @f
cmp [size], 0
jz @f
inc ecx ; token is IN
@@:
cmp [size], 0
jz .nodata
push ecx
stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx
pop ecx
test eax, eax
jz .fail
mov [last_td], eax
.nodata:
; 6. Allocate and initialize TDs for the Status stage.
; No payload.
xor ecx, 3 ; IN becomes OUT, OUT becomes IN
stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx
test eax, eax
jz .fail
; 7. Store callback and its parameters in the last descriptor for this transfer.
mov ecx, [eax+usb_gtd.PrevVirt]
mov edx, [callback]
mov [ecx+usb_gtd.Callback], edx
mov edx, [calldata]
mov [ecx+usb_gtd.UserData], edx
mov edx, [buffer]
mov [ecx+usb_gtd.Buffer], edx
; 8. Advance LastTD pointer and activate transfer.
push [ebx+usb_pipe.LastTD]
mov [ebx+usb_pipe.LastTD], eax
call [edi+usb_hardware_func.InsertTransfer]
; 9. Release the lock taken in step 2 and
; return pointer to the first descriptor for the new transfer.
lea ecx, [ebx+usb_pipe.Lock]
call mutex_unlock
pop eax
ret
.fail:
mov eax, [last_td]
test eax, eax
jz .unlock_return0
stdcall usb_undo_tds, [ebx+usb_pipe.LastTD]
.unlock_return0:
lea ecx, [ebx+usb_pipe.Lock]
call mutex_unlock
.return0:
xor eax, eax
ret
endp
 
; Initialize software part of usb_gtd. Called from controller-specific code
; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd,
; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] ->
; current (initializing) usb_gtd.
; Returns ecx = [.td].
proc usb_init_transfer
virtual at ebp-4
.Size dd ?
rd 2
.Buffer dd ?
dd ?
.Flags dd ?
.td dd ?
end virtual
mov [eax+usb_gtd.Pipe], ebx
mov ecx, [.td]
mov [eax+usb_gtd.PrevVirt], ecx
mov edx, [ecx+usb_gtd.NextVirt]
mov [ecx+usb_gtd.NextVirt], eax
mov [eax+usb_gtd.NextVirt], edx
mov [edx+usb_gtd.PrevVirt], eax
mov edx, [.Size]
mov [ecx+usb_gtd.Length], edx
xor edx, edx
mov [ecx+usb_gtd.Callback], edx
mov [ecx+usb_gtd.UserData], edx
ret
endp
 
; Free all TDs for the current transfer if something has failed
; during initialization (e.g. no memory for the next TD).
; Stdcall with one stack argument = first TD for the transfer
; and eax = last initialized TD for the transfer.
proc usb_undo_tds
push [eax+usb_gtd.NextVirt]
@@:
cmp eax, [esp+8]
jz @f
push [eax+usb_gtd.PrevVirt]
stdcall [edi+usb_hardware_func.FreeTD], eax
pop eax
jmp @b
@@:
pop ecx
mov [eax+usb_gtd.NextVirt], ecx
mov [ecx+usb_gtd.PrevVirt], eax
ret 4
endp
 
; Helper procedure for handling short packets in controller-specific code.
; Returns with CF cleared if this is the final packet in some stage:
; for control transfers that means one of Data and Status stages,
; for other transfers - the final packet in the only stage.
proc usb_is_final_packet
cmp [ebx+usb_gtd.Callback], 0
jnz .nothing
mov eax, [ebx+usb_gtd.NextVirt]
cmp [eax+usb_gtd.Callback], 0
jz .stc
mov eax, [ebx+usb_gtd.Pipe]
cmp [eax+usb_pipe.Type], CONTROL_PIPE
jz .nothing
.stc:
stc
.nothing:
ret
endp
 
; Helper procedure for controller-specific code:
; removes one TD from the transfer queue, ebx -> usb_gtd to remove.
proc usb_unlink_td
mov ecx, [ebx+usb_gtd.Pipe]
add ecx, usb_pipe.Lock
call mutex_lock
mov eax, [ebx+usb_gtd.PrevVirt]
mov edx, [ebx+usb_gtd.NextVirt]
mov [edx+usb_gtd.PrevVirt], eax
mov [eax+usb_gtd.NextVirt], edx
call mutex_unlock
ret
endp
 
if USB_STDCALL_VERIFY
proc verify_regs
virtual at esp
dd ? ; return address
.edi dd ?
.esi dd ?
.ebp dd ?
.esp dd ?
.ebx dd ?
.edx dd ?
.ecx dd ?
.eax dd ?
end virtual
cmp ebx, [.ebx]
jz @f
dbgstr 'ERROR!!! ebx changed'
@@:
cmp esi, [.esi]
jz @f
dbgstr 'ERROR!!! esi changed'
@@:
cmp edi, [.edi]
jz @f
dbgstr 'ERROR!!! edi changed'
@@:
cmp ebp, [.ebp]
jz @f
dbgstr 'ERROR!!! ebp changed'
@@:
ret
endp
end if
/kernel/branches/Kolibri-acpi/bus/usb/protocol.inc
0,0 → 1,926
; Implementation of the USB protocol for device enumeration.
; Manage a USB device when it becomes ready for USB commands:
; configure, enumerate, load the corresponding driver(s),
; pass device information to the driver.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; USB standard request codes
USB_GET_STATUS = 0
USB_CLEAR_FEATURE = 1
USB_SET_FEATURE = 3
USB_SET_ADDRESS = 5
USB_GET_DESCRIPTOR = 6
USB_SET_DESCRIPTOR = 7
USB_GET_CONFIGURATION = 8
USB_SET_CONFIGURATION = 9
USB_GET_INTERFACE = 10
USB_SET_INTERFACE = 11
USB_SYNCH_FRAME = 12
 
; USB standard descriptor types
USB_DEVICE_DESCR = 1
USB_CONFIG_DESCR = 2
USB_STRING_DESCR = 3
USB_INTERFACE_DESCR = 4
USB_ENDPOINT_DESCR = 5
USB_DEVICE_QUALIFIER_DESCR = 6
USB_OTHER_SPEED_CONFIG_DESCR = 7
USB_INTERFACE_POWER_DESCR = 8
 
; Possible speeds of USB devices
USB_SPEED_FS = 0 ; full-speed
USB_SPEED_LS = 1 ; low-speed
USB_SPEED_HS = 2 ; high-speed
 
; Compile-time setting. If set, the code will dump all descriptors as they are
; read to the debug board.
USB_DUMP_DESCRIPTORS = 1
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; USB descriptors. See USB specification for detailed explanations.
; First two bytes of every descriptor have the same meaning.
struct usb_descr
bLength db ?
; Size of this descriptor in bytes
bDescriptorType db ?
; One of USB_*_DESCR constants.
ends
 
; USB device descriptor
struct usb_device_descr usb_descr
bcdUSB dw ?
; USB Specification Release number in BCD, e.g. 110h = USB 1.1
bDeviceClass db ?
; USB Device Class Code
bDeviceSubClass db ?
; USB Device Subclass Code
bDeviceProtocol db ?
; USB Device Protocol Code
bMaxPacketSize0 db ?
; Maximum packet size for zero endpoint
idVendor dw ?
; Vendor ID
idProduct dw ?
; Product ID
bcdDevice dw ?
; Device release number in BCD
iManufacturer db ?
; Index of string descriptor describing manufacturer
iProduct db ?
; Index of string descriptor describing product
iSerialNumber db ?
; Index of string descriptor describing serial number
bNumConfigurations db ?
; Number of possible configurations
ends
 
; USB configuration descriptor
struct usb_config_descr usb_descr
wTotalLength dw ?
; Total length of data returned for this configuration
bNumInterfaces db ?
; Number of interfaces in this configuration
bConfigurationValue db ?
; Value for SET_CONFIGURATION control request
iConfiguration db ?
; Index of string descriptor describing this configuration
bmAttributes db ?
; Bit 6 is SelfPowered, bit 5 is RemoteWakeupSupported,
; bit 7 must be 1, other bits must be 0
bMaxPower db ?
; Maximum power consumption from the bus in 2mA units
ends
 
; USB interface descriptor
struct usb_interface_descr usb_descr
; The following two fields work in pair. Sometimes one interface can work
; in different modes; e.g. videostream from web-cameras requires different
; bandwidth depending on resolution/quality/compression settings.
; Each mode of each interface has its own descriptor with its own endpoints
; following; all descriptors for one interface have the same bInterfaceNumber,
; and different bAlternateSetting.
; By default, any interface operates in mode with bAlternateSetting = 0.
; Often this is the only mode. If there are another modes, the active mode
; is selected by SET_INTERFACE(bAlternateSetting) control request.
bInterfaceNumber db ?
bAlternateSetting db ?
bNumEndpoints db ?
; Number of endpoints used by this interface, excluding zero endpoint
bInterfaceClass db ?
; USB Interface Class Code
bInterfaceSubClass db ?
; USB Interface Subclass Code
bInterfaceProtocol db ?
; USB Interface Protocol Code
iInterface db ?
; Index of string descriptor describing this interface
ends
 
; USB endpoint descriptor
struct usb_endpoint_descr usb_descr
bEndpointAddress db ?
; Lower 4 bits form endpoint number,
; upper bit is 0 for OUT endpoints and 1 for IN endpoints,
; other bits must be zero
bmAttributes db ?
; Lower 2 bits form transfer type, one of *_PIPE,
; other bits must be zero for non-isochronous endpoints;
; refer to the USB specification for meaning in isochronous case
wMaxPacketSize dw ?
; Lower 11 bits form maximum packet size,
; next two bits specify the number of additional transactions per microframe
; for high-speed periodic endpoints, other bits must be zero.
bInterval db ?
; Interval for polling endpoint for data transfers.
; Isochronous and high-speed interrupt endpoints: poll every 2^(bInterval-1)
; (micro)frames
; Full/low-speed interrupt endpoints: poll every bInterval frames
; High-speed bulk/control OUT endpoints: maximum NAK rate
ends
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
; When a new device is ready to be configured, a controller-specific code
; calls usb_new_device.
; The sequence of further actions:
; * open pipe for the zero endpoint (usb_new_device);
; maximum packet size is not known yet, but it must be at least 8 bytes,
; so it is safe to send packets with <= 8 bytes
; * issue SET_ADDRESS control request (usb_new_device)
; * set the new device address in the pipe (usb_set_address_callback)
; * notify a controller-specific code that initialization of other ports
; can be started (usb_set_address_callback)
; * issue GET_DESCRIPTOR control request for first 8 bytes of device descriptor
; (usb_after_set_address)
; * first 8 bytes of device descriptor contain the true packet size for zero
; endpoint, so set the true packet size (usb_get_descr8_callback)
; * first 8 bytes of a descriptor contain the full size of this descriptor,
; issue GET_DESCRIPTOR control request for the full device descriptor
; (usb_after_set_endpoint_size)
; * issue GET_DESCRIPTOR control request for first 8 bytes of configuration
; descriptor (usb_get_descr_callback)
; * issue GET_DESCRIPTOR control request for full configuration descriptor
; (usb_know_length_callback)
; * issue SET_CONFIGURATION control request (usb_set_config_callback)
; * parse configuration descriptor, load the corresponding driver(s),
; pass the configuration descriptor to the driver and let the driver do
; the further work (usb_got_config_callback)
 
; This function is called from controller-specific part
; when a new device is ready to be configured.
; in: ecx -> pseudo-pipe, part of usb_pipe
; in: esi -> usb_controller
; in: [esi+usb_controller.ResettingHub] is the pointer to usb_hub for device,
; NULL if the device is connected to the root hub
; in: [esi+usb_controller.ResettingPort] is the port for the device, zero-based
; in: [esi+usb_controller.ResettingSpeed] is the speed of the device, one of
; USB_SPEED_xx.
; out: eax = 0 <=> failed, the caller should disable the port.
proc usb_new_device
push ebx edi ; save used registers to be stdcall
; 1. Allocate resources. Any device uses the following resources:
; - device address in the bus
; - memory for device data
; - pipe for zero endpoint
; If some allocation fails, we must undo our actions. Closing the pipe
; is a hard task, so we avoid it and open the pipe as the last resource.
; The order for other two allocations is quite arbitrary.
; 1a. Allocate a bus address.
push ecx
call usb_set_address_request
pop ecx
; 1b. If failed, just return zero.
test eax, eax
jz .nothing
; 1c. Allocate memory for device data.
; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR
; input and output, see usb_after_set_address. Later we will reallocate it
; to actual size needed for descriptors.
push sizeof.usb_device_data + 8
pop eax
push ecx
call malloc
pop ecx
; 1d. If failed, free the bus address and return zero.
test eax, eax
jz .nomemory
; 1e. Open pipe for endpoint zero.
; For now, we do not know the actual maximum packet size;
; for full-speed devices it can be any of 8, 16, 32, 64 bytes,
; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes.
; Thus, we must use some fake "maximum packet size" until the actual size
; will be known. However, the maximum packet size must be at least 8, and
; initial stages of the configuration process involves only packets of <= 8
; bytes, they will be transferred correctly as long as
; the fake "maximum packet size" is also at least 8.
; Thus, any number >= 8 is suitable for actual hardware.
; However, software emulation of EHCI in VirtualBox assumes that high-speed
; control transfers are those originating from pipes with max packet size = 64,
; even on early stages of the configuration process. This is incorrect,
; but we have no specific preferences, so let VirtualBox be happy and use 64
; as the fake "maximum packet size".
push eax
; We will need many zeroes.
; "push edi" is one byte, "push 0" is two bytes; save space, use edi.
xor edi, edi
stdcall usb_open_pipe, ecx, edi, 64, edi, edi
; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes.
xchg eax, ebx
pop eax
; 1f. If failed, free the memory, the bus address and return zero.
test ebx, ebx
jz .freememory
; 2. Store pointer to device data in the pipe structure.
mov [ebx+usb_pipe.DeviceData], eax
; 3. Init device data, using usb_controller.Resetting* variables.
mov [eax+usb_device_data.NumPipes], 1
mov [eax+usb_device_data.ConfigDataSize], edi
mov [eax+usb_device_data.Interfaces], edi
movzx ecx, [esi+usb_controller.ResettingPort]
; Note: the following write zeroes
; usb_device_data.DeviceDescrSize, usb_device_data.NumInterfaces,
; usb_device_data.Speed.
mov dword [eax+usb_device_data.Port], ecx
mov dl, [esi+usb_controller.ResettingSpeed]
mov [eax+usb_device_data.Speed], dl
mov edx, [esi+usb_controller.ResettingHub]
mov [eax+usb_device_data.Hub], edx
; 4. Store pointer to the config pipe in the hub data.
; Config pipe serves as device identifier.
; Root hubs use the array inside usb_controller structure,
; non-root hubs use the array immediately after usb_hub structure.
test edx, edx
jz .roothub
mov edx, [edx+usb_hub.ConnectedDevicesPtr]
mov [edx+ecx*4], ebx
jmp @f
.roothub:
mov [esi+usb_controller.DevicesByPort+ecx*4], ebx
@@:
call usb_reinit_pipe_list
; 5. Issue SET_ADDRESS control request, using buffer filled in step 1a.
; Use the return value from usb_control_async as our return value;
; if it is zero, then something has failed.
lea eax, [esi+usb_controller.SetAddressBuffer]
stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi
.nothing:
; 6. Return.
pop edi ebx ; restore used registers to be stdcall
ret
; Handlers of failures in steps 1b, 1d, 1f.
.freememory:
call free
jmp .freeaddr
.nomemory:
dbgstr 'No memory for device data'
.freeaddr:
mov ecx, dword [esi+usb_controller.SetAddressBuffer+2]
bts [esi+usb_controller.ExistingAddresses], ecx
xor eax, eax
jmp .nothing
endp
 
; Helper procedure for usb_new_device.
; Allocates a new USB address and fills usb_controller.SetAddressBuffer
; with data for SET_ADDRESS(allocated_address) request.
; out: eax = 0 <=> failed
; Destroys edi.
proc usb_set_address_request
; There are 128 bits, one for each possible address.
; Note: only the USB thread works with usb_controller.ExistingAddresses,
; so there is no need for synchronization.
; We must find a bit set to 1 and clear it.
; 1. Find the first dword which has a nonzero bit = which is nonzero.
mov ecx, 128/32
lea edi, [esi+usb_controller.ExistingAddresses]
xor eax, eax
repz scasd
; 2. If all dwords are zero, return an error.
jz .error
; 3. The dword at [edi-4] is nonzero. Find the lowest nonzero bit.
bsf eax, [edi-4]
; Now eax = bit number inside the dword at [edi-4].
; 4. Clear the bit.
btr [edi-4], eax
; 5. Generate the address by edi = memory address and eax = bit inside dword.
; Address = eax + 8 * (edi-4 - (esi+usb_controller.ExistingAddress)).
sub edi, esi
lea edi, [eax+(edi-4-usb_controller.ExistingAddresses)*8]
; 6. Store the allocated address in SetAddressBuffer and fill remaining fields.
; Note that usb_controller is zeroed at allocation, so only command byte needs
; to be filled.
mov byte [esi+usb_controller.SetAddressBuffer+1], USB_SET_ADDRESS
mov dword [esi+usb_controller.SetAddressBuffer+2], edi
; 7. Return non-zero value in eax.
inc eax
.nothing:
ret
.error:
dbgstr 'cannot allocate USB address'
xor eax, eax
jmp .nothing
endp
 
; This procedure is called by USB stack when SET_ADDRESS request initiated by
; usb_new_device is completed, either successfully or unsuccessfully.
; Note that USB stack uses esi = pointer to usb_controller.
proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
push ebx ; save ebx to be stdcall
; Load data to registers for further references.
mov ebx, [pipe]
mov ecx, dword [esi+usb_controller.SetAddressBuffer+2]
mov eax, [esi+usb_controller.HardwareFunc]
; 1. Check whether the device has accepted new address. If so, proceed to 2.
; Otherwise, go to 3.
cmp [status], 0
jnz .error
; 2. Address accepted.
; 2a. The controller-specific structure for the control pipe still uses
; zero address. Call the controller-specific function to change it to
; the actual address.
; Note that the hardware could cache the controller-specific structure,
; so setting the address could take some time until the cache is evicted.
; Thus, the call is asynchronous; meet us in usb_after_set_address when it will
; be safe to continue.
dbgstr 'address set in device'
call [eax+usb_hardware_func.SetDeviceAddress]
; 2b. If the port is in non-root hub, clear 'reset in progress' flag.
; In any case, proceed to 4.
mov eax, [esi+usb_controller.ResettingHub]
test eax, eax
jz .return
and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS
.return:
; 4. Address configuration done, we can proceed with other ports.
; Call the worker function for that.
call usb_test_pending_port
.nothing:
pop ebx ; restore ebx to be stdcall
ret
.error:
; 3. Device error: device not responding, disconnect etc.
DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status]
; 3a. The address has not been accepted. Mark it as free.
bts dword [esi+usb_controller.ExistingAddresses], ecx
; 3b. Disable the port with bad device.
; For the root hub, call the controller-specific function and go to 6.
; For non-root hubs, let the hub code do its work and return (the request
; could take some time, the hub code is responsible for proceeding).
cmp [esi+usb_controller.ResettingHub], 0
jz .roothub
mov eax, [esi+usb_controller.ResettingHub]
call usb_hub_disable_resetting_port
jmp .nothing
.roothub:
movzx ecx, [esi+usb_controller.ResettingPort]
call [eax+usb_hardware_func.PortDisable]
jmp .return
endp
 
; This procedure is called from usb_subscription_done when the hardware cache
; is cleared after request from usb_set_address_callback.
; in: ebx -> usb_pipe
proc usb_after_set_address
dbgstr 'address set for controller'
; Issue control transfer GET_DESCRIPTOR(DEVICE_DESCR) for first 8 bytes.
; Remember, we still do not know the actual packet size;
; 8-bytes-request is safe.
; usb_new_device has allocated 8 extra bytes besides sizeof.usb_device_data;
; use them for both input and output.
mov eax, [ebx+usb_pipe.DeviceData]
add eax, usb_device_data.DeviceDescriptor
mov dword [eax], \
80h + \ ; device-to-host, standard, device-wide
(USB_GET_DESCRIPTOR shl 8) + \ ; request
(0 shl 16) + \ ; descriptor index: there is only one
(USB_DEVICE_DESCR shl 24) ; descriptor type
mov dword [eax+4], 8 shl 16 ; data length
stdcall usb_control_async, ebx, eax, eax, 8, usb_get_descr8_callback, eax, 0
ret
endp
 
; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE_DESCR)
; request initiated by usb_after_set_address is completed, either successfully
; or unsuccessfully.
; Note that USB stack uses esi = pointer to usb_controller.
proc usb_get_descr8_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; mov eax, [buffer]
; DEBUGF 1,'K : descr8: l=%x; %x %x %x %x %x %x %x %x\n',[length],\
; [eax]:2,[eax+1]:2,[eax+2]:2,[eax+3]:2,[eax+4]:2,[eax+5]:2,[eax+6]:2,[eax+7]:2
push edi ebx ; save used registers to be stdcall
mov ebx, [pipe]
; 1. Check whether the operation was successful.
; If not, say something to the debug board and stop the initialization.
cmp [status], 0
jnz .error
; 2. Length of descriptor must be at least sizeof.usb_device_descr bytes.
; If not, say something to the debug board and stop the initialization.
mov eax, [ebx+usb_pipe.DeviceData]
cmp [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength], sizeof.usb_device_descr
jb .error
; 3. Now first 8 bytes of device descriptor are known;
; set DeviceDescrSize accordingly.
mov [eax+usb_device_data.DeviceDescrSize], 8
; 4. The controller-specific structure for the control pipe still uses
; the fake "maximum packet size". Call the controller-specific function to
; change it to the actual packet size from the device.
; Note that the hardware could cache the controller-specific structure,
; so changing it could take some time until the cache is evicted.
; Thus, the call is asynchronous; meet us in usb_after_set_endpoint_size
; when it will be safe to continue.
movzx ecx, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bMaxPacketSize0]
mov eax, [esi+usb_controller.HardwareFunc]
call [eax+usb_hardware_func.SetEndpointPacketSize]
.nothing:
; 5. Return.
pop ebx edi ; restore used registers to be stdcall
ret
.error:
dbgstr 'error with USB device descriptor'
jmp .nothing
endp
 
; This procedure is called from usb_subscription_done when the hardware cache
; is cleared after request from usb_get_descr8_callback.
; in: ebx -> usb_pipe
proc usb_after_set_endpoint_size
; 1. Reallocate memory for device data:
; add memory for now-known size of device descriptor and extra 8 bytes
; for further actions.
; 1a. Allocate new memory.
mov eax, [ebx+usb_pipe.DeviceData]
movzx eax, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength]
; save length for step 2
push eax
add eax, sizeof.usb_device_data + 8
; Note that malloc destroys ebx.
push ebx
call malloc
pop ebx
; 1b. If failed, say something to the debug board and stop the initialization.
test eax, eax
jz .nomemory
; 1c. Copy data from old memory to new memory and switch the pointer in usb_pipe.
push eax
push esi edi
mov esi, [ebx+usb_pipe.DeviceData]
mov [ebx+usb_pipe.DeviceData], eax
mov edi, eax
mov eax, esi
repeat sizeof.usb_device_data / 4
movsd
end repeat
pop edi esi
call usb_reinit_pipe_list
; 1d. Free the old memory.
; Note that free destroys ebx.
push ebx
call free
pop ebx
pop eax
; 2. Issue control transfer GET_DESCRIPTOR(DEVICE) for full descriptor.
; restore length saved in step 1a
pop edx
add eax, sizeof.usb_device_data
mov dword [eax], \
80h + \ ; device-to-host, standard, device-wide
(USB_GET_DESCRIPTOR shl 8) + \ ; request
(0 shl 16) + \ ; descriptor index: there is only one
(USB_DEVICE_DESCR shl 24) ; descriptor type
and dword [eax+4], 0
mov [eax+6], dl ; data length
stdcall usb_control_async, ebx, eax, eax, edx, usb_get_descr_callback, eax, 0
; 3. Return.
ret
.nomemory:
dbgstr 'No memory for device data'
ret
endp
 
; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE)
; request initiated by usb_after_set_endpoint_size is completed,
; either successfully or unsuccessfully.
proc usb_get_descr_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; Note: the prolog is the same as in usb_get_descr8_callback.
push edi ebx ; save used registers to be stdcall
; 1. Check whether the operation was successful.
; If not, say something to the debug board and stop the initialization.
cmp [status], 0
jnz usb_get_descr8_callback.error
; The full descriptor is known, dump it if specified by compile-time option.
if USB_DUMP_DESCRIPTORS
mov eax, [buffer]
mov ecx, [length]
sub ecx, 8
jbe .skipdebug
DEBUGF 1,'K : device descriptor:'
@@:
DEBUGF 1,' %x',[eax]:2
inc eax
dec ecx
jnz @b
DEBUGF 1,'\n'
.skipdebug:
end if
; 2. Check that bLength is the same as was in the previous request.
; If not, say something to the debug board and stop the initialization.
; It is important, because usb_after_set_endpoint_size has allocated memory
; according to the old bLength. Note that [length] for control transfers
; includes 8 bytes of setup packet, so data length = [length] - 8.
mov eax, [buffer]
movzx ecx, [eax+usb_device_descr.bLength]
add ecx, 8
cmp [length], ecx
jnz usb_get_descr8_callback.error
; Amuse the user if she is watching the debug board.
mov cl, [eax+usb_device_descr.bNumConfigurations]
DEBUGF 1,'K : found USB device with ID %x:%x, %d configuration(s)\n',\
[eax+usb_device_descr.idVendor]:4,\
[eax+usb_device_descr.idProduct]:4,\
cl
; 3. If there are no configurations, stop the initialization.
cmp [eax+usb_device_descr.bNumConfigurations], 0
jz .nothing
; 4. Copy length of device descriptor to device data structure.
movzx edx, [eax+usb_device_descr.bLength]
mov [eax+usb_device_data.DeviceDescrSize-usb_device_data.DeviceDescriptor], dl
; 5. Issue control transfer GET_DESCRIPTOR(CONFIGURATION). We do not know
; the full length of that descriptor, so start with first 8 bytes, they contain
; the full length.
; usb_after_set_endpoint_size has allocated 8 extra bytes after the
; device descriptor, use them for both input and output.
add eax, edx
mov dword [eax], \
80h + \ ; device-to-host, standard, device-wide
(USB_GET_DESCRIPTOR shl 8) + \ ; request
(0 shl 16) + \ ; descriptor index: there is only one
(USB_CONFIG_DESCR shl 24) ; descriptor type
mov dword [eax+4], 8 shl 16 ; data length
stdcall usb_control_async, [pipe], eax, eax, 8, usb_know_length_callback, eax, 0
.nothing:
; 6. Return.
pop ebx edi ; restore used registers to be stdcall
ret
endp
 
; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION)
; request initiated by usb_get_descr_callback is completed,
; either successfully or unsuccessfully.
proc usb_know_length_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
push ebx ; save used registers to be stdcall
; 1. Check whether the operation was successful.
; If not, say something to the debug board and stop the initialization.
cmp [status], 0
jnz .error
; 2. Get the total length of data associated with config descriptor and store
; it in device data structure. The total length must be at least
; sizeof.usb_config_descr bytes; if not, say something to the debug board and
; stop the initialization.
mov eax, [buffer]
mov edx, [pipe]
movzx ecx, [eax+usb_config_descr.wTotalLength]
mov eax, [edx+usb_pipe.DeviceData]
cmp ecx, sizeof.usb_config_descr
jb .error
mov [eax+usb_device_data.ConfigDataSize], ecx
; 3. Reallocate memory for device data:
; include usb_device_data structure, device descriptor,
; config descriptor with all associated data, and extra bytes
; sufficient for 8 bytes control packet and for one usb_interface_data struc.
; Align extra bytes to dword boundary.
if sizeof.usb_interface_data > 8
.extra_size = sizeof.usb_interface_data
else
.extra_size = 8
end if
; 3a. Allocate new memory.
movzx edx, [eax+usb_device_data.DeviceDescrSize]
lea eax, [ecx+edx+sizeof.usb_device_data+.extra_size+3]
and eax, not 3
push eax
call malloc
pop edx
; 3b. If failed, say something to the debug board and stop the initialization.
test eax, eax
jz .nomemory
; 3c. Copy data from old memory to new memory and switch the pointer in usb_pipe.
push eax
mov ebx, [pipe]
push esi edi
mov esi, [ebx+usb_pipe.DeviceData]
mov edi, eax
mov [ebx+usb_pipe.DeviceData], eax
mov eax, esi
movzx ecx, [esi+usb_device_data.DeviceDescrSize]
sub edx, .extra_size
mov [esi+usb_device_data.Interfaces], edx
add ecx, sizeof.usb_device_data + 8
mov edx, ecx
shr ecx, 2
and edx, 3
rep movsd
mov ecx, edx
rep movsb
pop edi esi
call usb_reinit_pipe_list
; 3d. Free old memory.
call free
pop eax
; 4. Issue control transfer GET_DESCRIPTOR(DEVICE) for full descriptor.
movzx ecx, [eax+usb_device_data.DeviceDescrSize]
mov edx, [eax+usb_device_data.ConfigDataSize]
lea eax, [eax+ecx+sizeof.usb_device_data]
mov dword [eax], \
80h + \ ; device-to-host, standard, device-wide
(USB_GET_DESCRIPTOR shl 8) + \ ; request
(0 shl 16) + \ ; descriptor index: there is only one
(USB_CONFIG_DESCR shl 24) ; descriptor type
and dword [eax+4], 0
mov word [eax+6], dx ; data length
stdcall usb_control_async, [pipe], eax, eax, edx, usb_set_config_callback, eax, 0
.nothing:
; 5. Return.
pop ebx ; restore used registers to be stdcall
ret
.error:
dbgstr 'error with USB configuration descriptor'
jmp .nothing
.nomemory:
dbgstr 'No memory for device data'
jmp .nothing
endp
 
; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION)
; request initiated by usb_know_length_callback is completed,
; either successfully or unsuccessfully.
proc usb_set_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
; Note that the prolog is the same as in usb_know_length_callback.
push ebx ; save used registers to be stdcall
; 1. Check whether the operation was successful.
; If not, say something to the debug board and stop the initialization.
xor ecx, ecx
mov ebx, [pipe]
cmp [status], ecx
jnz usb_know_length_callback.error
; The full descriptor is known, dump it if specified by compile-time option.
if USB_DUMP_DESCRIPTORS
mov eax, [buffer]
mov ecx, [length]
sub ecx, 8
jbe .skip_debug
DEBUGF 1,'K : config descriptor:'
@@:
DEBUGF 1,' %x',[eax]:2
inc eax
dec ecx
jnz @b
DEBUGF 1,'\n'
.skip_debug:
xor ecx, ecx
end if
; 2. Issue control transfer SET_CONFIGURATION to activate this configuration.
; Usually this is the only configuration.
; Use extra bytes allocated by usb_know_length_callback;
; offset from device data start is stored in Interfaces.
mov eax, [ebx+usb_pipe.DeviceData]
mov edx, [buffer]
add eax, [eax+usb_device_data.Interfaces]
mov dl, [edx+usb_config_descr.bConfigurationValue]
mov dword [eax], USB_SET_CONFIGURATION shl 8
mov dword [eax+4], ecx
mov byte [eax+2], dl
stdcall usb_control_async, [pipe], eax, ecx, ecx, usb_got_config_callback, [buffer], ecx
pop ebx ; restore used registers to be stdcall
ret
endp
 
; This procedure is called by USB stack when SET_CONFIGURATION
; request initiated by usb_set_config_callback is completed,
; either successfully or unsuccessfully.
; If successfully, the device is configured and ready to work,
; pass the device to the corresponding driver(s).
proc usb_got_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword
locals
InterfacesData dd ?
NumInterfaces dd ?
driver dd ?
endl
; 1. If there was an error, say something to the debug board and stop the
; initialization.
cmp [status], 0
jz @f
dbgstr 'USB error in SET_CONFIGURATION'
ret
@@:
push ebx edi ; save used registers to be stdcall
; 2. Sanity checks: the total length must be the same as before (because we
; have allocated memory assuming the old value), length of config descriptor
; must be at least sizeof.usb_config_descr (we use fields from it),
; there must be at least one interface.
mov ebx, [pipe]
mov ebx, [ebx+usb_pipe.DeviceData]
mov eax, [calldata]
mov edx, [ebx+usb_device_data.ConfigDataSize]
cmp [eax+usb_config_descr.wTotalLength], dx
jnz .invalid
cmp [eax+usb_config_descr.bLength], 9
jb .invalid
movzx edx, [eax+usb_config_descr.bNumInterfaces]
test edx, edx
jnz @f
.invalid:
dbgstr 'error: invalid configuration descriptor'
jmp .nothing
@@:
; 3. Store the number of interfaces in device data structure.
mov [ebx+usb_device_data.NumInterfaces], dl
; 4. If there is only one interface (which happens quite often),
; the memory allocated in usb_know_length_callback is sufficient.
; Otherwise (which also happens quite often), reallocate device data.
; 4a. Check whether there is only one interface. If so, skip this step.
cmp edx, 1
jz .has_memory
; 4b. Allocate new memory.
mov eax, [ebx+usb_device_data.Interfaces]
lea eax, [eax+edx*sizeof.usb_interface_data]
call malloc
; 4c. If failed, say something to the debug board and
; stop the initialization.
test eax, eax
jnz @f
dbgstr 'No memory for device data'
jmp .nothing
@@:
; 4d. Copy data from old memory to new memory and switch the pointer in usb_pipe.
push eax
push esi
mov ebx, [pipe]
mov edi, eax
mov esi, [ebx+usb_pipe.DeviceData]
mov [ebx+usb_pipe.DeviceData], eax
mov eax, esi
mov ecx, [esi+usb_device_data.Interfaces]
shr ecx, 2
rep movsd
pop esi
call usb_reinit_pipe_list
; 4e. Free old memory.
call free
pop ebx
.has_memory:
; 5. Initialize interfaces table: zero all contents.
mov edi, [ebx+usb_device_data.Interfaces]
add edi, ebx
mov [InterfacesData], edi
movzx ecx, [ebx+usb_device_data.NumInterfaces]
if sizeof.usb_interface_data <> 8
You have changed sizeof.usb_interface_data? Modify this place too.
end if
add ecx, ecx
xor eax, eax
rep stosd
; No interfaces are found yet.
mov [NumInterfaces], eax
; 6. Get the pointer to config descriptor data.
; Note: if there was reallocation, [buffer] is not valid anymore,
; so calculate value based on usb_device_data.
movzx eax, [ebx+usb_device_data.DeviceDescrSize]
lea eax, [eax+ebx+sizeof.usb_device_data]
mov [calldata], eax
mov ecx, [ebx+usb_device_data.ConfigDataSize]
; 7. Loop over all descriptors,
; scan for interface descriptors with bAlternateSetting = 0,
; load the corresponding driver, call its AddDevice function.
.descriptor_loop:
; While in loop: eax points to the current descriptor,
; ecx = number of bytes left, the iteration starts only if ecx is nonzero,
; edx = size of the current descriptor.
; 7a. The first byte is always accessible; it contains the length of
; the current descriptor. Validate that the length is at least 2 bytes,
; and the entire descriptor is readable (the length is at most number of
; bytes left).
movzx edx, [eax+usb_descr.bLength]
cmp edx, sizeof.usb_descr
jb .invalid
cmp ecx, edx
jb .invalid
; 7b. Check descriptor type. Ignore all non-INTERFACE descriptor.
cmp byte [eax+usb_descr.bDescriptorType], USB_INTERFACE_DESCR
jz .interface
.next_descriptor:
; 7c. Advance pointer, decrease length left, if there is still something left,
; continue the loop.
add eax, edx
sub ecx, edx
jnz .descriptor_loop
.done:
.nothing:
pop edi ebx ; restore used registers to be stdcall
ret
.interface:
; 7d. Validate the descriptor length.
cmp edx, sizeof.usb_interface_descr
jb .next_descriptor
; 7e. If bAlternateSetting is nonzero, this descriptor actually describes
; another mode of already known interface and belongs to the already loaded
; driver; amuse the user and continue to 7c.
cmp byte [eax+usb_interface_descr.bAlternateSetting], 0
jz @f
DEBUGF 1,'K : note: alternate setting with %x/%x/%x\n',\
[eax+usb_interface_descr.bInterfaceClass]:2,\
[eax+usb_interface_descr.bInterfaceSubClass]:2,\
[eax+usb_interface_descr.bInterfaceProtocol]:2
jmp .next_descriptor
@@:
; 7f. Check that the new interface does not overflow allocated table.
mov edx, [NumInterfaces]
inc dl
jz .invalid
cmp dl, [ebx+usb_device_data.NumInterfaces]
ja .invalid
; 7g. We have found a new interface. Advance bookkeeping vars.
mov [NumInterfaces], edx
add [InterfacesData], sizeof.usb_interface_data
; 7h. Save length left and pointer to the current interface descriptor.
push ecx eax
; Amuse the user if she is watching the debug board.
DEBUGF 1,'K : USB interface class/subclass/protocol = %x/%x/%x\n',\
[eax+usb_interface_descr.bInterfaceClass]:2,\
[eax+usb_interface_descr.bInterfaceSubClass]:2,\
[eax+usb_interface_descr.bInterfaceProtocol]:2
; 7i. Select the correct driver based on interface class.
; For hubs, go to 7j. Otherwise, go to 7k.
; Note: this should be rewritten as table-based lookup when more drivers will
; be available.
cmp byte [eax+usb_interface_descr.bInterfaceClass], 9
jz .found_hub
mov edx, usb_hid_name
cmp byte [eax+usb_interface_descr.bInterfaceClass], 3
jz .load_driver
mov edx, usb_print_name
cmp byte [eax+usb_interface_descr.bInterfaceClass], 7
jz .load_driver
mov edx, usb_stor_name
cmp byte [eax+usb_interface_descr.bInterfaceClass], 8
jz .load_driver
mov edx, usb_other_name
jmp .load_driver
.found_hub:
; 7j. Hubs are a part of USB stack, thus, integrated into the kernel.
; Use the pointer to hub callbacks and go to 7m.
mov eax, usb_hub_pseudosrv - USBSRV.usb_func
jmp .driver_loaded
.load_driver:
; 7k. Load the corresponding driver.
push ebx esi edi
stdcall get_service, edx
pop edi esi ebx
; 7l. If failed, say something to the debug board and go to 7p.
test eax, eax
jnz .driver_loaded
dbgstr 'failed to load class driver'
jmp .next_descriptor2
.driver_loaded:
; 7m. Call AddDevice function of the driver.
; Note that top of stack contains a pointer to the current interface,
; saved by step 7h.
mov [driver], eax
mov eax, [eax+USBSRV.usb_func]
pop edx
push edx
; Note: usb_hub_init assumes that edx points to usb_interface_descr,
; ecx = length rest; if you change the code, modify usb_hub_init also.
stdcall [eax+USBFUNC.add_device], [pipe], [calldata], edx
; 7n. If failed, say something to the debug board and go to 7p.
test eax, eax
jnz .store_data
dbgstr 'USB device initialization failed'
jmp .next_descriptor2
.store_data:
; 7o. Store the returned value and the driver handle to InterfacesData.
; Note that step 7g has already advanced InterfacesData.
mov edx, [InterfacesData]
mov [edx+usb_interface_data.DriverData-sizeof.usb_interface_data], eax
mov eax, [driver]
mov [edx+usb_interface_data.DriverFunc-sizeof.usb_interface_data], eax
.next_descriptor2:
; 7p. Restore registers saved in step 7h, get the descriptor length and
; continue to 7c.
pop eax ecx
movzx edx, byte [eax+usb_descr.bLength]
jmp .next_descriptor
endp
 
; Driver names, see step 7i of usb_got_config_callback.
iglobal
usb_hid_name db 'usbhid',0
usb_stor_name db 'usbstor',0
usb_print_name db 'usbprint',0
usb_other_name db 'usbother',0
endg
/kernel/branches/Kolibri-acpi/bus/usb/scheduler.inc
0,0 → 1,508
; Implementation of periodic transaction scheduler for USB.
; Bandwidth dedicated to periodic transactions is limited, so
; different pipes should be scheduled as uniformly as possible.
 
; USB1 scheduler.
; Algorithm is simple:
; when adding a pipe, optimize the following quantity:
; * for every millisecond, take all bandwidth scheduled to periodic transfers,
; * calculate maximum over all milliseconds,
; * select a variant which minimizes that maximum;
; when removing a pipe, do nothing (except for bookkeeping).
 
; sanity check: structures in UHCI and OHCI should be the same
if (sizeof.ohci_static_ep=sizeof.uhci_static_ep)&(ohci_static_ep.SoftwarePart=uhci_static_ep.SoftwarePart)&(ohci_static_ep.NextList=uhci_static_ep.NextList)
; Select a list for a new pipe.
; in: esi -> usb_controller, maxpacket, type, interval can be found in the stack
; in: ecx = 2 * maximal interval = total number of periodic lists + 1
; in: edx -> {u|o}hci_static_ep for the first list
; in: eax -> byte past {u|o}hci_static_ep for the last list in the first group
; out: edx -> usb_static_ep for the selected list or zero if failed
proc usb1_select_interrupt_list
; inherit some variables from usb_open_pipe
virtual at ebp-8
.bandwidth dd ?
.target dd ?
dd ?
dd ?
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
push ebx edi ; save used registers to be stdcall
push eax ; save eax for checks in step 3
; 1. Only intervals 2^k ms can be supported.
; The core specification says that the real interval should not be greater
; than the interval given by the endpoint descriptor, but can be less.
; Determine the actual interval as 2^k ms.
mov eax, ecx
; 1a. Set [.interval] to 1 if it was zero; leave it as is otherwise
cmp [.interval], 1
adc [.interval], 0
; 1b. Divide ecx by two while it is strictly greater than [.interval].
@@:
shr ecx, 1
cmp [.interval], ecx
jb @b
; ecx = the actual interval
;
; For example, let ecx = 8, eax = 64.
; The scheduler space is 32 milliseconds,
; we need to schedule something every 8 ms;
; there are 8 variants: schedule at times 0,8,16,24,
; schedule at times 1,9,17,25,..., schedule at times 7,15,23,31.
; Now concentrate: there are three nested loops,
; * the innermost loop calculates the total periodic bandwidth scheduled
; in the given millisecond,
; * the intermediate loop calculates the maximum over all milliseconds
; in the given variant, that is the quantity we're trying to minimize,
; * the outermost loop checks all variants.
; 2. Calculate offset between the first list and the first list for the
; selected interval, in bytes; save in the stack for step 4.
sub eax, ecx
sub eax, ecx
imul eax, sizeof.ohci_static_ep
push eax
imul ebx, ecx, sizeof.ohci_static_ep
; 3. Select the best variant.
; 3a. The outermost loop.
; Prepare for the loop: set the current optimal bandwidth to maximum
; possible value (so that any variant will pass the first comparison),
; calculate delta for the intermediate loop.
or [.bandwidth], -1
.varloop:
; 3b. The intermediate loop.
; Prepare for the loop: set the maximum to be calculated to zero,
; save counter of the outermost loop.
xor edi, edi
push edx
virtual at esp
.cur_variant dd ? ; step 3b
.result_delta dd ? ; step 2
.group1_limit dd ? ; function prolog
end virtual
.calc_max_bandwidth:
; 3c. The innermost loop. Sum over all lists.
xor eax, eax
push edx
.calc_bandwidth:
add eax, [edx+ohci_static_ep.SoftwarePart+usb_static_ep.Bandwidth]
mov edx, [edx+ohci_static_ep.NextList]
test edx, edx
jnz .calc_bandwidth
pop edx
; 3d. The intermediate loop continued: update maximum.
cmp eax, edi
jb @f
mov edi, eax
@@:
; 3e. The intermediate loop continued: advance counter.
add edx, ebx
cmp edx, [.group1_limit]
jb .calc_max_bandwidth
; 3e. The intermediate loop done: restore counter of the outermost loop.
pop edx
; 3f. The outermost loop continued: if the current variant is
; better (maybe not strictly) then the previous optimum, update
; the optimal bandwidth and resulting list.
cmp edi, [.bandwidth]
ja @f
mov [.bandwidth], edi
mov [.target], edx
@@:
; 3g. The outermost loop continued: advance counter.
add edx, sizeof.ohci_static_ep
dec ecx
jnz .varloop
; 4. Get the pointer to the best list.
pop edx ; restore value from step 2
pop eax ; purge stack var from prolog
add edx, [.target]
; 5. Calculate bandwidth for the new pipe.
mov eax, [.maxpacket] ; TODO: calculate real bandwidth
and eax, (1 shl 11) - 1
; 6. TODO: check that bandwidth for the new pipe plus old bandwidth
; still fits to maximum allowed by the core specification.
; 7. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
add edx, ohci_static_ep.SoftwarePart
add [edx+usb_static_ep.Bandwidth], eax
pop edi ebx ; restore used registers to be stdcall
ret
endp
; sanity check, part 2
else
.err select_interrupt_list must be different for UHCI and OHCI
end if
 
; Pipe is removing, update the corresponding lists.
; We do not reorder anything, so just update book-keeping variable
; in the list header.
proc usb1_interrupt_list_unlink
virtual at esp
dd ? ; return address
.maxpacket dd ?
.lowspeed db ?
.direction db ?
rb 2
end virtual
; find list header
mov edx, ebx
@@:
mov edx, [edx+usb_pipe.NextVirt]
cmp [edx+usb_pipe.Controller], esi
jnz @b
; subtract pipe bandwidth
; TODO: calculate real bandwidth
mov eax, [.maxpacket]
and eax, (1 shl 11) - 1
sub [edx+usb_static_ep.Bandwidth], eax
ret 8
endp
 
; USB2 scheduler.
; There are two parts: high-speed pipes and split-transaction pipes.
; Split-transaction scheduler is currently a stub.
; High-speed scheduler uses the same algorithm as USB1 scheduler:
; when adding a pipe, optimize the following quantity:
; * for every microframe, take all bandwidth scheduled to periodic transfers,
; * calculate maximum over all microframe,
; * select a variant which minimizes that maximum;
; when removing a pipe, do nothing (except for bookkeeping).
; in: esi -> usb_controller
; out: edx -> usb_static_ep, eax = S-Mask
proc ehci_select_hs_interrupt_list
; inherit some variables from usb_open_pipe
virtual at ebp-12
.targetsmask dd ?
.bandwidth dd ?
.target dd ?
dd ?
dd ?
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
; prolog, initialize local vars
or [.bandwidth], -1
or [.target], -1
or [.targetsmask], -1
push ebx edi ; save used registers to be stdcall
; 1. In EHCI, every list describes one millisecond = 8 microframes.
; Thus, there are two significantly different branches:
; for pipes with interval >= 8 microframes, advance to 2,
; for pipes which should be planned in every frame (one or more microframes),
; go to 9.
; Note: the actual interval for high-speed devices is 2^([.interval]-1),
; (the core specification forbids [.interval] == 0)
mov ecx, [.interval]
dec ecx
cmp ecx, 3
jb .every_frame
; 2. Determine the actual interval in milliseconds.
sub ecx, 3
cmp ecx, 5 ; maximum 32ms
jbe @f
push 5
pop ecx
@@:
; There are four nested loops,
; * Loop #4 (the innermost one) calculates the total periodic bandwidth
; scheduled in the given microframe of the given millisecond.
; * Loop #3 calculates the maximum over all milliseconds
; in the given variant, that is the quantity we're trying to minimize.
; * Loops #1 and #2 check all variants;
; loop #1 is responsible for the target millisecond,
; loop #2 is responsible for the microframe within millisecond.
; 3. Prepare for loops.
; ebx = number of iterations of loop #1
; [esp] = delta of counter for loop #3, in bytes
; [esp+4] = delta between the first group and the target group, in bytes
push 1
pop ebx
push sizeof.ehci_static_ep
pop edx
shl ebx, cl
shl edx, cl
mov eax, 64*sizeof.ehci_static_ep
sub eax, edx
sub eax, edx
push eax
push edx
; 4. Select the best variant.
; 4a. Loop #1: initialize counter = pointer to ehci_static_ep for
; the target millisecond in the first group.
lea edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller]
.varloop0:
; 4b. Loop #2: initialize counter = microframe within the target millisecond.
xor ecx, ecx
.varloop:
; 4c. Loop #3: save counter of loop #1,
; initialize counter with the value of loop #1 counter,
; initialize maximal bandwidth = zero.
xor edi, edi
push edx
virtual at esp
.saved_counter1 dd ? ; step 4c
.loop3_delta dd ? ; step 3
.target_delta dd ? ; step 3
end virtual
.calc_max_bandwidth:
; 4d. Loop #4: initialize counter with the value of loop #3 counter,
; initialize total bandwidth = zero.
xor eax, eax
push edx
.calc_bandwidth:
; 4e. Loop #4: add the bandwidth from the current list
; and advance to the next list, while there is one.
add ax, [edx+ehci_static_ep.Bandwidths+ecx*2]
mov edx, [edx+ehci_static_ep.NextList]
test edx, edx
jnz .calc_bandwidth
; 4f. Loop #4 end: restore counter of loop #3.
pop edx
; 4g. Loop #3: update maximal bandwidth.
cmp eax, edi
jb @f
mov edi, eax
@@:
; 4h. Loop #3: advance the counter and repeat while within the first group.
lea eax, [esi+ehci_controller.IntEDs+32*sizeof.ehci_static_ep-sizeof.ehci_controller]
add edx, [.loop3_delta]
cmp edx, eax
jb .calc_max_bandwidth
; 4i. Loop #3 end: restore counter of loop #1.
pop edx
; 4j. Loop #2: if the current variant is better (maybe not strictly)
; then the previous optimum, update the optimal bandwidth and the target.
cmp edi, [.bandwidth]
ja @f
mov [.bandwidth], edi
mov [.target], edx
push 1
pop eax
shl eax, cl
mov [.targetsmask], eax
@@:
; 4k. Loop #2: continue 8 times for every microframe.
inc ecx
cmp ecx, 8
jb .varloop
; 4l. Loop #1: advance counter and repeat ebx times,
; ebx was calculated in step 3.
add edx, sizeof.ehci_static_ep
dec ebx
jnz .varloop0
; 5. Get the pointer to the best list.
pop edx ; restore value from step 3
pop edx ; get delta calculated in step 3
add edx, [.target]
; 6. Calculate bandwidth for the new pipe.
; TODO1: calculate real bandwidth
mov eax, [.maxpacket]
mov ecx, eax
and eax, (1 shl 11) - 1
shr ecx, 11
inc ecx
and ecx, 3
imul eax, ecx
; 7. TODO2: check that bandwidth for the new pipe plus old bandwidth
; still fits to maximum allowed by the core specification
; current [.bandwidth] + new bandwidth <= limit;
; USB2 specification allows maximum 60000*80% bit times for periodic microframe
; 8. Convert {o|u}hci_static_ep to usb_static_ep, update bandwidth and return.
mov ecx, [.targetsmask]
add [edx+ehci_static_ep.Bandwidths+ecx*2], ax
add edx, ehci_static_ep.SoftwarePart
push 1
pop eax
shl eax, cl
pop edi ebx ; restore used registers to be stdcall
ret
.every_frame:
; The pipe should be scheduled every frame in two or more microframes.
; 9. Calculate maximal bandwidth for every microframe: three nested loops.
; 9a. The outermost loop: ebx = microframe to calculate.
xor ebx, ebx
.calc_all_bandwidths:
; 9b. The intermediate loop:
; edx = pointer to ehci_static_ep in the first group, [esp] = counter,
; edi = maximal bandwidth
lea edx, [esi+ehci_controller.IntEDs-sizeof.ehci_controller]
xor edi, edi
push 32
.calc_max_bandwidth2:
; 9c. The innermost loop: calculate bandwidth for the given microframe
; in the given frame.
xor eax, eax
push edx
.calc_bandwidth2:
add ax, [edx+ehci_static_ep.Bandwidths+ebx*2]
mov edx, [edx+ehci_static_ep.NextList]
test edx, edx
jnz .calc_bandwidth2
pop edx
; 9d. The intermediate loop continued: update maximal bandwidth.
cmp eax, edi
jb @f
mov edi, eax
@@:
add edx, sizeof.ehci_static_ep
dec dword [esp]
jnz .calc_max_bandwidth2
pop eax
; 9e. Push the calculated maximal bandwidth and continue the outermost loop.
push edi
inc ebx
cmp ebx, 8
jb .calc_all_bandwidths
virtual at esp
.bandwidth7 dd ?
.bandwidth6 dd ?
.bandwidth5 dd ?
.bandwidth4 dd ?
.bandwidth3 dd ?
.bandwidth2 dd ?
.bandwidth1 dd ?
.bandwidth0 dd ?
end virtual
; 10. Select the best variant.
; edx = S-Mask = bitmask of scheduled microframes
push 0x11
pop edx
cmp ecx, 1
ja @f
mov dl, 0x55
jz @f
mov dl, 0xFF
@@:
; try all variants edx, edx shl 1, edx shl 2, ...
; until they fit in the lower byte (8 microframes per frame)
.select_best_mframe:
xor edi, edi
mov ecx, edx
mov eax, esp
.calc_mframe:
add cl, cl
jnc @f
cmp edi, [eax]
jae @f
mov edi, [eax]
@@:
add eax, 4
test cl, cl
jnz .calc_mframe
cmp [.bandwidth], edi
jb @f
mov [.bandwidth], edi
mov [.targetsmask], edx
@@:
add dl, dl
jnc .select_best_mframe
; 11. Restore stack after step 9.
add esp, 8*4
; 12. Get the pointer to the target list (responsible for every microframe).
lea edx, [esi+ehci_controller.IntEDs.SoftwarePart+62*sizeof.ehci_static_ep-sizeof.ehci_controller]
; 13. TODO1: calculate real bandwidth.
mov eax, [.maxpacket]
mov ecx, eax
and eax, (1 shl 11) - 1
shr ecx, 11
inc ecx
and ecx, 3
imul eax, ecx
; 14. TODO2: check that current [.bandwidth] + new bandwidth <= limit;
; USB2 specification allows maximum 60000*80% bit times for periodic microframe.
; Update bandwidths including the new pipe.
mov ecx, [.targetsmask]
lea edi, [edx+ehci_static_ep.Bandwidths-ehci_static_ep.SoftwarePart]
.update_bandwidths:
shr ecx, 1
jnc @f
add [edi], ax
@@:
add edi, 2
test ecx, ecx
jnz .update_bandwidths
; 15. Return target list and target S-Mask.
mov eax, [.targetsmask]
pop edi ebx ; restore used registers to be stdcall
ret
endp
 
; Pipe is removing, update the corresponding lists.
; We do not reorder anything, so just update book-keeping variable
; in the list header.
proc ehci_hs_interrupt_list_unlink
; get target list
mov edx, [ebx+ehci_pipe.BaseList-ehci_pipe.SoftwarePart]
; TODO: calculate real bandwidth
movzx eax, word [ebx+ehci_pipe.Token-ehci_pipe.SoftwarePart+2]
mov ecx, [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart]
and eax, (1 shl 11) - 1
shr ecx, 30
imul eax, ecx
movzx ecx, byte [ebx+ehci_pipe.Flags-ehci_pipe.SoftwarePart]
add edx, ehci_static_ep.Bandwidths - ehci_static_ep.SoftwarePart
; update bandwidth
.dec_bandwidth:
shr ecx, 1
jnc @f
sub [edx], ax
@@:
add edx, 2
test ecx, ecx
jnz .dec_bandwidth
; return
ret
endp
 
uglobal
ehci_last_fs_alloc dd ?
endg
 
; This needs to be rewritten. Seriously.
; It schedules everything to the first microframe of some frame,
; frame is spinned out of thin air.
; This works while you have one keyboard and one mouse...
; maybe even ten keyboards and ten mice... but give any serious stress,
; and this would break.
proc ehci_select_fs_interrupt_list
virtual at ebp-12
.targetsmask dd ?
.bandwidth dd ?
.target dd ?
dd ?
dd ?
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
cmp [.interval], 1
adc [.interval], 0
mov ecx, 64
mov eax, ecx
@@:
shr ecx, 1
cmp [.interval], ecx
jb @b
sub eax, ecx
sub eax, ecx
dec ecx
and ecx, [ehci_last_fs_alloc]
inc [ehci_last_fs_alloc]
add eax, ecx
imul eax, sizeof.ehci_static_ep
lea edx, [esi+ehci_controller.IntEDs.SoftwarePart+eax-sizeof.ehci_controller]
mov ax, 1C01h
ret
endp
 
proc ehci_fs_interrupt_list_unlink
ret
endp
/kernel/branches/Kolibri-acpi/bus/usb/uhci.inc
0,0 → 1,1817
; Code for UHCI controllers.
; Note: it should be moved to an external driver,
; it was convenient to have this code compiled into the kernel during initial
; development, but there are no reasons to keep it here.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; UHCI register declarations
UhciCommandReg = 0
UhciStatusReg = 2
UhciInterruptReg = 4
UhciFrameNumberReg = 6
UhciBaseAddressReg = 8
UhciSOFModifyReg = 0Ch
UhciPort1StatusReg = 10h
; possible PIDs for USB data transfers
USB_PID_SETUP = 2Dh
USB_PID_IN = 69h
USB_PID_OUT = 0E1h
; UHCI does not support an interrupt on root hub status change. We must poll
; the controller periodically. This is the period in timer ticks (10ms).
; We use the value 100 ms: it is valid value for USB hub poll rate (1-255 ms),
; small enough to be responsible to connect events and large enough to not
; load CPU too often.
UHCI_POLL_INTERVAL = 100
; the following constant is an invalid encoding for length fields in
; uhci_gtd; it is used to check whether an inactive TD has been
; completed (actual length of the transfer is valid) or not processed at all
; (actual length of the transfer is UHCI_INVALID_LENGTH).
; Valid values are 0-4FFh and 7FFh. We use 700h as an invalid value.
UHCI_INVALID_LENGTH = 700h
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
 
; UHCI-specific part of a pipe descriptor.
; * The structure corresponds to the Queue Head aka QH from the UHCI
; specification with some additional fields.
; * The hardware uses first two fields (8 bytes). Next two fields are used for
; software book-keeping.
; * The hardware requires 16-bytes alignment of the hardware part.
; Since the allocator (usb_allocate_common) allocates memory sequentially
; from page start (aligned on 0x1000 bytes), size of the structure must be
; divisible by 16.
struct uhci_pipe
NextQH dd ?
; 1. First bit (bit 0) is Terminate bit. 1 = there is no next QH.
; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextQH points to QH.
; 3. Next two bits (bits 2-3) are reserved.
; 4. With masked 4 lower bits, this is the physical address of the next QH in
; the QH list.
; See also the description before NextVirt field of the usb_pipe
; structure. Additionally to that description, the following is specific for
; the UHCI controller:
; * n=10, N=1024. However, this number is quite large.
; * 1024 lists are used only for individual transfer descriptors for
; Isochronous endpoints. This means that the software can sleep up to 1024 ms
; before initiating the next portion of a large isochronous transfer, which
; is a sufficiently large value.
; * We use the 32ms upper limit for interrupt endpoint polling interval.
; This seems to be a reasonable value.
; * The "next" list for last Periodic list is the Control list.
; * The "next" list for Control list is Bulk list and the "next"
; list for Bulk list is Control list. This loop is used for bandwidth
; reclamation: the hardware traverses lists until end-of-frame.
HeadTD dd ?
; 1. First bit (bit 0) is Terminate bit. 1 = there is no TDs in this QH.
; 2. Next bit (bit 1) is QH/TD select bit. 1 = HeadTD points to QH.
; 3. Next two bits (bits 2-3) are reserved.
; 4. With masked 4 lower bits, this is the physical address of the first TD in
; the TD queue for this QH.
Token dd ?
; This field is a template for uhci_gtd.Token field in transfer
; descriptors. The meaning of individual bits is the same as for
; uhci_gtd.Token, except that PID bitfield is always
; USB_PID_SETUP/IN/OUT for control/in/out pipes,
; the MaximumLength bitfield encodes maximum packet size,
; the Reserved bit 20 is LowSpeedDevice bit.
ErrorTD dd ?
; Usually NULL. If nonzero, it is a pointer to descriptor which was error'd
; and should be freed sometime in the future (the hardware could still use it).
SoftwarePart rd sizeof.usb_pipe/4
; Common part for all controllers, described by usb_pipe structure.
ends
 
if sizeof.uhci_pipe mod 16
.err uhci_pipe must be 16-bytes aligned
end if
 
; This structure describes the static head of every list of pipes.
; The hardware requires 16-bytes alignment of this structure.
; All instances of this structure are located sequentially in uhci_controller,
; uhci_controller is page-aligned, so it is sufficient to make this structure
; 16-bytes aligned and verify that the first instance is 16-bytes aligned
; inside uhci_controller.
struct uhci_static_ep
NextQH dd ?
; Same as uhci_pipe.NextQH.
HeadTD dd ?
; Same as uhci_pipe.HeadTD.
NextList dd ?
; Virtual address of the next list.
dd ?
; Not used.
SoftwarePart rd sizeof.usb_static_ep/4
; Common part for all controllers, described by usb_static_ep structure.
dd ?
; Padding for 16-byte alignment.
ends
 
if sizeof.uhci_static_ep mod 16
.err uhci_static_ep must be 16-bytes aligned
end if
 
; UHCI-specific part of controller data.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 4096 bytes and corresponds to
; the Frame List from UHCI specification.
; * The hardware requires page-alignment of the hardware part, so
; the entire descriptor must be page-aligned.
; This structure is allocated with kernel_alloc (see usb_init_controller),
; this gives page-aligned data.
struct uhci_controller
; ------------------------------ hardware fields ------------------------------
FrameList rd 1024
; Entry n corresponds to the head of the frame list to be executed in
; the frames n,n+1024,n+2048,n+3096,...
; The first bit of each entry is Terminate bit, 1 = the frame is empty.
; The second bit of each entry is QH/TD select bit, 1 = the entry points to
; QH, 0 = to TD.
; With masked 2 lower bits, the entry is a physical address of the first QH/TD
; to be executed.
; ------------------------------ software fields ------------------------------
; Every list has the static head, which is an always empty QH.
; The following fields are static heads, one per list:
; 32+16+8+4+2+1 = 63 for Periodic lists, 1 for Control list and 1 for Bulk list.
IntEDs uhci_static_ep
rb 62 * sizeof.uhci_static_ep
ControlED uhci_static_ep
BulkED uhci_static_ep
IOBase dd ?
; Base port in I/O space for UHCI controller.
; UHCI register UhciXxx is addressed as in/out to IOBase + UhciXxx,
; see declarations in the beginning of this source.
DeferredActions dd ?
; Bitmask of bits from UhciStatusReg which need to be processed
; by uhci_process_deferred. Bit 0 = a transaction with IOC bit
; has completed. Bit 1 = a transaction has failed. Set by uhci_irq,
; cleared by uhci_process_deferred.
LastPollTime dd ?
; See the comment before UHCI_POLL_INTERVAL. This variable keeps the
; last time, in timer ticks, when the polling was done.
ends
 
if uhci_controller.IntEDs mod 16
.err Static endpoint descriptors must be 16-bytes aligned inside uhci_controller
end if
 
; UHCI general transfer descriptor.
; * The structure describes non-Isochronous data transfers
; for the UHCI controller.
; * The structure includes two parts, the hardware part and the software part.
; * The hardware part consists of first 16 bytes and corresponds to the
; Transfer Descriptor aka TD from UHCI specification.
; * The hardware requires 16-bytes alignment of the hardware part, so
; the entire descriptor must be 16-bytes aligned. Since the allocator
; (uhci_allocate_common) allocates memory sequentially from page start
; (aligned on 0x1000 bytes), size of the structure must be divisible by 16.
struct uhci_gtd
NextTD dd ?
; 1. First bit (bit 0) is Terminate bit. 1 = there is no next TD.
; 2. Next bit (bit 1) is QH/TD select bit. 1 = NextTD points to QH.
; This bit is always set to 0 in the implementation.
; 3. Next bit (bit 2) is Depth/Breadth select bit. 1 = the controller should
; proceed to the NextTD after this TD is complete. 0 = the controller
; should proceed to the next endpoint after this TD is complete.
; The implementation sets this bit to 0 for final stages of all transactions
; and to 1 for other stages.
; 4. Next bit (bit 3) is reserved and must be zero.
; 5. With masked 4 lower bits, this is the physical address of the next TD
; in the TD list.
ControlStatus dd ?
; 1. Lower 11 bits (bits 0-10) are ActLen. This is written by the controller
; at the conclusion of a USB transaction to indicate the actual number of
; bytes that were transferred minus 1.
; 2. Next 6 bits (bits 11-16) are reserved.
; 3. Next bit (bit 17) signals Bitstuff error.
; 4. Next bit (bit 18) signals CRC/Timeout error.
; 5. Next bit (bit 19) signals NAK receive.
; 6. Next bit (bit 20) signals Babble error.
; 7. Next bit (bit 21) signals Data Buffer error.
; 8. Next bit (bit 22) signals Stall error.
; 9. Next bit (bit 23) is Active field. 1 = this TD should be processed.
; 10. Next bit (bit 24) is InterruptOnComplete bit. 1 = the controller should
; issue an interrupt on completion of the frame in which this TD is
; executed.
; 11. Next bit (bit 25) is IsochronousSelect bit. 1 = this TD is isochronous.
; 12. Next bit (bit 26) is LowSpeedDevice bit. 1 = this TD is for low-speed.
; 13. Next two bits (bits 27-28) are ErrorCounter field. This field is
; decremented by the controller on every non-fatal error with this TD.
; Babble and Stall are considered fatal errors and immediately deactivate
; the TD without decrementing this field. 0 = no error limit,
; n = deactivate the TD after n errors.
; 14. Next bit (bit 29) is ShortPacketDetect bit. 1 = short packet is an error.
; Note: the specification defines this bit as input for the controller,
; but does not specify the value written by controller.
; Some controllers (e.g. Intel) keep the value, some controllers (e.g. VIA)
; set the value to whether a short packet was actually detected
; (or something like that).
; Thus, we duplicate this bit as bit 0 of OrigBufferInfo.
; 15. Upper two bits (bits 30-31) are reserved.
Token dd ?
; 1. Lower 8 bits (bits 0-7) are PID, one of USB_PID_*.
; 2. Next 7 bits (bits 8-14) are DeviceAddress field. This is the address of
; the target device on the USB bus.
; 3. Next 4 bits (bits 15-18) are Endpoint field. This is the target endpoint
; number.
; 4. Next bit (bit 19) is DataToggle bit. n = issue/expect DATAn token.
; 5. Next bit (bit 20) is reserved.
; 6. Upper 11 bits (bits 21-31) are MaximumLength field. This field specifies
; the maximum number of data bytes for the transfer minus 1 byte. Null data
; packet is encoded as 0x7FF, maximum possible non-null data packet is 1280
; bytes, encoded as 0x4FF.
Buffer dd ?
; Physical address of the data buffer for this TD.
OrigBufferInfo dd ?
; Usually NULL. If the original buffer crosses a page boundary, this is a
; pointer to the structure uhci_original_buffer for this request.
; bit 0: 1 = short packet is NOT allowed
; (before the TD is processed, it is the copy of bit 29 of ControlStatus;
; some controllers modify that bit, so we need a copy in a safe place)
SoftwarePart rd sizeof.usb_gtd/4
; Software part, common for all controllers.
ends
 
if sizeof.uhci_gtd mod 16
.err uhci_gtd must be 16-bytes aligned
end if
 
; UHCI requires that the entire transfer buffer should be on one page.
; If the actual buffer crosses page boundary, uhci_alloc_packet
; allocates additional memory for buffer for hardware.
; This structure describes correspondence between two buffers.
struct uhci_original_buffer
OrigBuffer dd ?
UsedBuffer dd ?
ends
 
; Description of UHCI-specific data and functions for
; controller-independent code.
; Implements the structure usb_hardware_func from hccommon.inc for UHCI.
iglobal
align 4
uhci_hardware_func:
dd 'UHCI'
dd sizeof.uhci_controller
dd uhci_init
dd uhci_process_deferred
dd uhci_set_device_address
dd uhci_get_device_address
dd uhci_port_disable
dd uhci_new_port.reset
dd uhci_set_endpoint_packet_size
dd usb1_allocate_endpoint
dd uhci_free_pipe
dd uhci_init_pipe
dd uhci_unlink_pipe
dd usb1_allocate_general_td
dd uhci_free_td
dd uhci_alloc_transfer
dd uhci_insert_transfer
dd uhci_new_device
endg
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
; Controller-specific initialization function.
; Called from usb_init_controller. Initializes the hardware and
; UHCI-specific parts of software structures.
; eax = pointer to uhci_controller to be initialized
; [ebp-4] = pcidevice
proc uhci_init
; inherit some variables from the parent (usb_init_controller)
.devfn equ ebp - 4
.bus equ ebp - 3
; 1. Store pointer to uhci_controller for further use.
push eax
mov edi, eax
mov esi, eax
; 2. Initialize uhci_controller.FrameList.
; Note that FrameList is located in the beginning of uhci_controller,
; so esi and edi now point to uhci_controller.FrameList.
; First 32 entries of FrameList contain physical addresses
; of first 32 Periodic static heads, further entries duplicate these.
; See the description of structures for full info.
; Note that all static heads fit in one page, so one call to
; get_phys_addr is sufficient.
if (uhci_controller.IntEDs / 0x1000) <> (uhci_controller.BulkED / 0x1000)
.err assertion failed
end if
; 2a. Get physical address of first static head.
; Note that 1) it is located in the beginning of a page
; and 2) all other static heads fit in the same page,
; so one call to get_phys_addr without correction of lower 12 bits
; is sufficient.
if (uhci_controller.IntEDs mod 0x1000) <> 0
.err assertion failed
end if
add eax, uhci_controller.IntEDs
call get_phys_addr
; 2b. Fill first 32 entries.
inc eax
inc eax ; set QH bit for uhci_pipe.NextQH
push 32
pop ecx
mov edx, ecx
@@:
stosd
add eax, sizeof.uhci_static_ep
loop @b
; 2c. Fill the rest entries.
mov ecx, 1024 - 32
rep movsd
; 3. Initialize static heads uhci_controller.*ED.
; Use the loop over groups: first group consists of first 32 Periodic
; descriptors, next group consists of next 16 Periodic descriptors,
; ..., last group consists of the last Periodic descriptor.
; 3a. Prepare for the loop.
; make esi point to the second group, other registers are already set.
add esi, 32*4 + 32*sizeof.uhci_static_ep
; 3b. Loop over groups. On every iteration:
; edx = size of group, edi = pointer to the current group,
; esi = pointer to the next group, eax = physical address of the next group.
.init_static_eds:
; 3c. Get the size of next group.
shr edx, 1
; 3d. Exit the loop if there is no next group.
jz .init_static_eds_done
; 3e. Initialize the first half of the current group.
; Advance edi to the second half.
push eax esi
call uhci_init_static_ep_group
pop esi eax
; 3f. Initialize the second half of the current group
; with the same values.
; Advance edi to the next group, esi/eax to the next of the next group.
call uhci_init_static_ep_group
jmp .init_static_eds
.init_static_eds_done:
; 3g. Initialize the last static head.
xor esi, esi
call uhci_init_static_endpoint
; 3i. Initialize the head of Control list.
add eax, sizeof.uhci_static_ep
call uhci_init_static_endpoint
; 3j. Initialize the head of Bulk list.
sub eax, sizeof.uhci_static_ep
call uhci_init_static_endpoint
; 4. Get I/O base address and size from PCI bus.
; 4a. Read&save PCI command state.
mov bh, [.devfn]
mov ch, [.bus]
mov cl, 1
mov eax, ecx
mov bl, 4
call pci_read_reg
push eax
; 4b. Disable IO access.
and al, not 1
push ecx
xchg eax, ecx
call pci_write_reg
pop ecx
; 4c. Read&save IO base address.
mov eax, ecx
mov bl, 20h
call pci_read_reg
and al, not 3
xchg eax, edi
; now edi = IO base
; 4d. Write 0xffff to IO base address.
push ecx
xchg eax, ecx
or ecx, -1
call pci_write_reg
pop ecx
; 4e. Read IO base address.
mov eax, ecx
call pci_read_reg
and al, not 3
cwde
not eax
inc eax
xchg eax, esi
; now esi = IO size
; 4f. Restore IO base address.
xchg eax, ecx
mov ecx, edi
push eax
call pci_write_reg
pop eax
; 4g. Restore PCI command state and enable io & bus master access.
pop ecx
or ecx, 5
mov bl, 4
push eax
call pci_write_reg
pop eax
; 5. Reset the controller.
; 5e. Host reset.
mov edx, edi
mov ax, 2
out dx, ax
; 5f. Wait up to 10ms.
push 10
pop ecx
@@:
push esi
push 1
pop esi
call delay_ms
pop esi
in ax, dx
test al, 2
loopnz @b
jz @f
dbgstr 'UHCI controller reset timeout'
jmp .fail
@@:
if 0
; emergency variant for tests - always wait 10 ms
; wait 10 ms
push esi
push 10
pop esi
call delay_ms
pop esi
; clear reset signal
xor eax, eax
out dx, ax
end if
.resetok:
; 6. Get number of ports & disable all ports.
add esi, edi
lea edx, [edi+UhciPort1StatusReg]
.scanports:
cmp edx, esi
jae .doneports
in ax, dx
cmp ax, 0xFFFF
jz .doneports
test al, al
jns .doneports
xor eax, eax
out dx, ax
inc edx
inc edx
jmp .scanports
.doneports:
lea esi, [edx-UhciPort1StatusReg]
sub esi, edi
shr esi, 1 ; esi = number of ports
jnz @f
dbgstr 'error: no ports on UHCI controller'
jmp .fail
@@:
; 7. Setup the rest of uhci_controller.
xchg esi, [esp] ; restore the pointer to uhci_controller from the step 1
add esi, sizeof.uhci_controller
pop [esi+usb_controller.NumPorts]
DEBUGF 1,'K : UHCI controller at %x:%x with %d ports initialized\n',[.bus]:2,[.devfn]:2,[esi+usb_controller.NumPorts]
mov [esi+uhci_controller.IOBase-sizeof.uhci_controller], edi
mov eax, [timer_ticks]
mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax
; 8. Hook interrupt.
mov ah, [.bus]
mov al, 0
mov bh, [.devfn]
mov bl, 3Ch
call pci_read_reg
; al = IRQ
; DEBUGF 1,'K : UHCI %x: io=%x, irq=%x\n',esi,edi,al
movzx eax, al
stdcall attach_int_handler, eax, uhci_irq, esi
; 9. Setup controller registers.
xor eax, eax
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
; 9a. UhciStatusReg := 3Fh: clear all status bits
; (for this register 1 clears the corresponding bit, 0 does not change it).
inc edx
inc edx ; UhciStatusReg == 2
mov al, 3Fh
out dx, ax
; 9b. UhciInterruptReg := 0Dh.
inc edx
inc edx ; UhciInterruptReg == 4
mov al, 0Dh
out dx, ax
; 9c. UhciFrameNumberReg := 0.
inc edx
inc edx ; UhciFrameNumberReg == 6
mov al, 0
out dx, ax
; 9d. UhciBaseAddressReg := physical address of uhci_controller.
inc edx
inc edx ; UhciBaseAddressReg == 8
lea eax, [esi-sizeof.uhci_controller]
call get_phys_addr
out dx, eax
; 9e. UhciCommandReg := Run + Configured + (MaxPacket is 64 bytes)
sub edx, UhciBaseAddressReg ; UhciCommandReg == 0
mov ax, 0C1h ; Run, Configured, MaxPacket = 64b
out dx, ax
; 10. Do initial scan of existing devices.
call uhci_poll_roothub
; 11. Return pointer to usb_controller.
xchg eax, esi
ret
.fail:
; On error, pop the pointer saved at step 1 and return zero.
; Note that the main code branch restores the stack at step 7 and never fails
; after step 7.
pop ecx
xor eax, eax
ret
endp
 
; Controller-specific pre-initialization function: take ownership from BIOS.
; UHCI has no mechanism to ask the owner politely to release ownership,
; so do it in inpolite way, preventing controller from any SMI activity.
proc uhci_kickoff_bios
; 1. Get the I/O address.
mov ah, [esi+PCIDEV.bus]
mov al, 1
mov bh, [esi+PCIDEV.devfn]
mov bl, 20h
call pci_read_reg
and eax, 0xFFFC
xchg eax, edx
; 2. Stop the controller and disable all interrupts.
in ax, dx
and al, not 1
out dx, ax
add edx, UhciInterruptReg
xor eax, eax
out dx, ax
; 3. Disable all bits for SMI routing, clear SMI routing status,
; enable master interrupt bit.
mov ah, [esi+PCIDEV.bus]
mov al, 1
mov bl, 0xC0
mov ecx, 0AF00h
call pci_write_reg
ret
endp
 
; Helper procedure for step 3 of uhci_init.
; Initializes the static head of one list.
; eax = physical address of the "next" list, esi = pointer to the "next" list,
; edi = pointer to head to initialize.
; Advances edi to the next head, keeps eax/esi.
proc uhci_init_static_endpoint
mov [edi+uhci_static_ep.NextQH], eax
mov byte [edi+uhci_static_ep.HeadTD], 1
mov [edi+uhci_static_ep.NextList], esi
add edi, uhci_static_ep.SoftwarePart
call usb_init_static_endpoint
add edi, sizeof.uhci_static_ep - uhci_static_ep.SoftwarePart
ret
endp
 
; Helper procedure for step 3 of uhci_init, see comments there.
; Initializes one half of group of static heads.
; edx = size of the next group = half of size of the group,
; edi = pointer to the group, eax = physical address of the next group,
; esi = pointer to the next group.
; Advances eax, esi, edi to next group, keeps edx.
proc uhci_init_static_ep_group
push edx
@@:
call uhci_init_static_endpoint
add eax, sizeof.uhci_static_ep
add esi, sizeof.uhci_static_ep
dec edx
jnz @b
pop edx
ret
endp
 
; IRQ handler for UHCI controllers.
uhci_irq.noint:
; Not our interrupt: restore esi and return zero.
pop esi
xor eax, eax
ret
proc uhci_irq
push esi ; save used register to be cdecl
virtual at esp
dd ? ; saved esi
dd ? ; return address
.controller dd ?
end virtual
mov esi, [.controller]
; 1. Read UhciStatusReg.
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
inc edx
inc edx ; UhciStatusReg == 2
in ax, dx
; 2. Test whether it is our interrupt; if so, at least one status bit is set.
test al, 0x1F
jz .noint
; 3. Clear all status bits.
out dx, ax
; 4. Sanity check.
test al, 0x3C
jz @f
DEBUGF 1,'K : something terrible happened with UHCI (%x)\n',al
@@:
; 5. We can't do too much from an interrupt handler, e.g. we can't take
; any mutex locks since our code could be called when another code holds the
; lock and has no chance to release it. Thus, only inform the processing thread
; that it should scan the queue and wake it if needed.
lock or byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al
push ebx
xor ebx, ebx
inc ebx
call usb_wakeup_if_needed
pop ebx
; 6. This is our interrupt; return 1.
mov al, 1
pop esi ; restore used register to be stdcall
ret
endp
 
; This procedure is called in the USB thread from usb_thread_proc,
; processes regular actions and those actions which can't be safely done
; from interrupt handler.
; Returns maximal time delta before the next call.
proc uhci_process_deferred
push ebx edi ; save used registers to be stdcall
; 1. Initialize the return value.
push -1
; 2. Poll the root hub every UHCI_POLL_INTERVAL ticks.
; Also force polling if some transaction has completed with errors;
; the error can be caused by disconnect, try to detect it.
test byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], 2
jnz .force_poll
mov eax, [timer_ticks]
sub eax, [esi+uhci_controller.LastPollTime-sizeof.uhci_controller]
sub eax, UHCI_POLL_INTERVAL
jl .nopoll
.force_poll:
mov eax, [timer_ticks]
mov [esi+uhci_controller.LastPollTime-sizeof.uhci_controller], eax
call uhci_poll_roothub
mov eax, -UHCI_POLL_INTERVAL
.nopoll:
neg eax
cmp [esp], eax
jb @f
mov [esp], eax
@@:
; 3. Process wait lists.
; 3a. Test whether there is a wait request.
mov eax, [esi+usb_controller.WaitPipeRequestAsync]
cmp eax, [esi+usb_controller.ReadyPipeHeadAsync]
jnz .check_removed
mov eax, [esi+usb_controller.WaitPipeRequestPeriodic]
cmp eax, [esi+usb_controller.ReadyPipeHeadPeriodic]
jz @f
.check_removed:
; 3b. Yep. Find frame and compare it with the saved one.
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
add edx, UhciFrameNumberReg
in ax, dx
cmp word [esi+usb_controller.StartWaitFrame], ax
jnz .removed
; 3c. The same frame; wake up in 0.01 sec.
mov dword [esp], 1
jmp @f
.removed:
; 3d. The frame is changed, old contents is guaranteed to be forgotten.
mov eax, [esi+usb_controller.WaitPipeRequestAsync]
mov [esi+usb_controller.ReadyPipeHeadAsync], eax
mov eax, [esi+usb_controller.WaitPipeRequestPeriodic]
mov [esi+usb_controller.ReadyPipeHeadPeriodic], eax
@@:
; 4. Process disconnect events. This should be done after step 2
; (which includes the first stage of disconnect processing).
call usb_disconnect_stage2
; 5. Test whether USB_CONNECT_DELAY for a connected device is over.
; Call uhci_new_port for all such devices.
xor ecx, ecx
cmp [esi+usb_controller.NewConnected], ecx
jz .skip_newconnected
.portloop:
bt [esi+usb_controller.NewConnected], ecx
jnc .noconnect
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ConnectedTime+ecx*4]
sub eax, USB_CONNECT_DELAY
jge .connected
neg eax
cmp [esp], eax
jb .nextport
mov [esp], eax
jmp .nextport
.connected:
btr [esi+usb_controller.NewConnected], ecx
call uhci_new_port
.noconnect:
.nextport:
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop
.skip_newconnected:
; 6. Test for processed packets.
; This should be done after step 4, so transfers which were failed due
; to disconnect are marked with the exact reason, not just
; 'device not responding'.
xor eax, eax
xchg byte [esi+uhci_controller.DeferredActions-sizeof.uhci_controller], al
test al, 3
jz .noioc
call uhci_process_updated_schedule
.noioc:
; 7. Test whether reset signalling has been started. If so,
; either should be stopped now (if time is over) or schedule wakeup (otherwise).
; This should be done after step 6, because a completed SET_ADDRESS command
; could result in reset of a new port.
.test_reset:
; 7a. Test whether reset signalling is active.
cmp [esi+usb_controller.ResettingStatus], 1
jnz .no_reset_in_progress
; 7b. Yep. Test whether it should be stopped.
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_TIME
jge .reset_done
; 7c. Not yet, but initiate wakeup in -eax ticks and exit this step.
neg eax
cmp [esp], eax
jb .skip_reset
mov [esp], eax
jmp .skip_reset
.reset_done:
; 7d. Yep, call the worker function and proceed to 7e.
call uhci_port_reset_done
.no_reset_in_progress:
; 7e. Test whether reset process is done, either successful or failed.
cmp [esi+usb_controller.ResettingStatus], 0
jz .skip_reset
; 7f. Yep. Test whether it should be stopped.
mov eax, [timer_ticks]
sub eax, [esi+usb_controller.ResetTime]
sub eax, USB_RESET_RECOVERY_TIME
jge .reset_recovery_done
; 7g. Not yet, but initiate wakeup in -eax ticks and exit this step.
neg eax
cmp [esp], eax
jb .skip_reset
mov [esp], eax
jmp .skip_reset
.reset_recovery_done:
; 7h. Yep, call the worker function. This could initiate another reset,
; so return to the beginning of this step.
call uhci_port_init
jmp .test_reset
.skip_reset:
; 8. Process wait-done notifications, test for new wait requests.
; Note: that must be done after steps 4 and 6 which could create new requests.
; 8a. Call the worker function.
call usb_process_wait_lists
; 8b. If no new requests, skip the rest of this step.
test eax, eax
jz @f
; 8c. UHCI is not allowed to cache anything; we don't know what is
; processed right now, but we can be sure that the controller will not
; use any removed structure starting from the next frame.
; Request removal of everything disconnected until now,
; schedule wakeup in 0.01 sec.
mov eax, [esi+usb_controller.WaitPipeListAsync]
mov [esi+usb_controller.WaitPipeRequestAsync], eax
mov eax, [esi+usb_controller.WaitPipeListPeriodic]
mov [esi+usb_controller.WaitPipeRequestPeriodic], eax
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
add edx, UhciFrameNumberReg
in ax, dx
mov word [esi+usb_controller.StartWaitFrame], ax
mov dword [esp], 1
@@:
; 9. Return the value from the top of stack.
pop eax
pop edi ebx ; restore used registers to be stdcall.
ret
endp
 
; This procedure is called in the USB thread from uhci_process_deferred
; when UHCI IRQ handler has signalled that new IOC-packet was processed.
; It scans all lists for completed packets and calls uhci_process_finalized_td
; for those packets.
; in: esi -> usb_controller
proc uhci_process_updated_schedule
; Important note: we cannot hold the list lock during callbacks,
; because callbacks sometimes open and/or close pipes and thus acquire/release
; the corresponding lock itself.
; Fortunately, pipes can be finally freed only by another step of
; uhci_process_deferred, so all pipes existing at the start of this function
; will be valid while this function is running. Some pipes can be removed
; from the corresponding list, some pipes can be inserted; insert/remove
; functions guarantee that traversing one list yields all pipes that were in
; that list at the beginning of the traversing (possibly with some new pipes,
; possibly without some new pipes, that doesn't matter).
; 1. Process all Periodic lists.
lea edi, [esi+uhci_controller.IntEDs.SoftwarePart-sizeof.uhci_controller]
lea ebx, [esi+uhci_controller.IntEDs.SoftwarePart+63*sizeof.uhci_static_ep-sizeof.uhci_controller]
@@:
call uhci_process_updated_list
cmp edi, ebx
jnz @b
; 2. Process the Control list.
call uhci_process_updated_list
; 3. Process the Bulk list.
call uhci_process_updated_list
; 4. Return.
ret
endp
 
; This procedure is called from uhci_process_updated_schedule,
; see comments there.
; It processes one list, esi -> usb_controller, edi -> usb_static_ep,
; and advances edi to the next head.
proc uhci_process_updated_list
push ebx ; save used register to be stdcall
; 1. Perform the external loop over all pipes.
mov ebx, [edi+usb_static_ep.NextVirt]
.loop:
cmp ebx, edi
jz .done
; store pointer to the next pipe in the stack
push [ebx+usb_static_ep.NextVirt]
; 2. For every pipe, perform the internal loop over all descriptors.
; All descriptors are organized in the queue; we process items from the start
; of the queue until a) the last descriptor (not the part of the queue itself)
; or b) an active (not yet processed by the hardware) descriptor is reached.
lea ecx, [ebx+usb_pipe.Lock]
call mutex_lock
mov ebx, [ebx+usb_pipe.LastTD]
push ebx
mov ebx, [ebx+usb_gtd.NextVirt]
.tdloop:
; 3. For every descriptor, test active flag and check for end-of-queue;
; if either of conditions holds, exit from the internal loop.
cmp ebx, [esp]
jz .tddone
mov eax, [ebx+uhci_gtd.ControlStatus-uhci_gtd.SoftwarePart]
test eax, 1 shl 23 ; active?
jnz .tddone
; Release the queue lock while processing one descriptor:
; callback function could (and often would) schedule another transfer.
push ecx
call mutex_unlock
call uhci_process_finalized_td
pop ecx
call mutex_lock
jmp .tdloop
.tddone:
call mutex_unlock
pop ebx
; End of internal loop, restore pointer to the next pipe
; and continue the external loop.
pop ebx
jmp .loop
.done:
pop ebx ; restore used register to be stdcall
add edi, sizeof.uhci_static_ep
ret
endp
 
; This procedure is called from uhci_process_updated_list, which is itself
; called from uhci_process_updated_schedule, see comments there.
; It processes one completed descriptor.
; in: esi -> usb_controller, ebx -> usb_gtd, out: ebx -> next usb_gtd.
proc uhci_process_finalized_td
; 1. Remove this descriptor from the list of descriptors for this pipe.
call usb_unlink_td
; DEBUGF 1,'K : finalized TD:\n'
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8]
; DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8]
; 2. If this is IN transfer into special buffer, copy the data
; to target location.
mov edx, [ebx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart]
and edx, not 1 ; clear lsb (used for another goal)
jz .nocopy
cmp byte [ebx+uhci_gtd.Token-uhci_gtd.SoftwarePart], USB_PID_IN
jnz .nocopy
; Note: we assume that pointer to buffer is valid in the memory space of
; the USB thread. This means that buffer must reside in kernel memory
; (shared by all processes).
push esi edi
mov esi, [edx+uhci_original_buffer.UsedBuffer]
mov edi, [edx+uhci_original_buffer.OrigBuffer]
mov ecx, [ebx+uhci_gtd.ControlStatus-uhci_gtd.SoftwarePart]
inc ecx
and ecx, 7FFh
mov edx, ecx
shr ecx, 2
and edx, 3
rep movsd
mov ecx, edx
rep movsb
pop edi esi
.nocopy:
; 3. Calculate actual number of bytes transferred.
; 3a. Read the state.
mov eax, [ebx+uhci_gtd.ControlStatus-uhci_gtd.SoftwarePart]
mov ecx, [ebx+uhci_gtd.Token-uhci_gtd.SoftwarePart]
; 3b. Get number of bytes processed.
lea edx, [eax+1]
and edx, 7FFh
; 3c. Subtract number of bytes in this packet.
add ecx, 1 shl 21
shr ecx, 21
sub edx, ecx
; 3d. Add total length transferred so far.
add edx, [ebx+usb_gtd.Length]
; Actions on error and on success are slightly different.
; 4. Test for error. On error, proceed to step 5, otherwise go to step 6
; with ecx = 0 (no error).
; USB transaction error is always considered as such.
; If short packets are not allowed, UHCI controllers do not set an error bit,
; but stop (clear Active bit and do not advance) the queue.
; Short packet is considered as an error if the packet is actually short
; (actual length is less than maximal one) and the code creating the packet
; requested that behaviour (so bit 0 of OrigBufferInfo is set; this could be
; because the caller disallowed short packets or because the packet is not
; the last one in the corresponding transfer).
xor ecx, ecx
test eax, 1 shl 22
jnz .error
test byte [ebx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart], 1
jz .notify
cmp edx, [ebx+usb_gtd.Length]
jz .notify
.error:
; 5. There was an error while processing this packet.
; The hardware has stopped processing the queue.
DEBUGF 1,'K : TD failed:\n'
if uhci_gtd.SoftwarePart <> 20
.err modify offsets for debug output
end if
DEBUGF 1,'K : %x %x %x %x\n',[ebx-20],[ebx-16],[ebx-12],[ebx-8]
DEBUGF 1,'K : %x %x %x %x\n',[ebx-4],[ebx],[ebx+4],[ebx+8]
; 5a. Save the status and length.
push edx
push eax
mov eax, [ebx+usb_gtd.Pipe]
DEBUGF 1,'K : pipe: %x %x\n',[eax+0-uhci_pipe.SoftwarePart],[eax+4-uhci_pipe.SoftwarePart]
; 5b. Store the current TD as an error packet.
; If an error packet is already stored for this pipe,
; it is definitely not used already, so free the old packet.
mov eax, [eax+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart]
test eax, eax
jz @f
stdcall uhci_free_td, eax
@@:
mov eax, [ebx+usb_gtd.Pipe]
mov [eax+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart], ebx
; 5c. Traverse the list of descriptors looking for the final packet
; for this transfer.
; Free and unlink non-final descriptors, except the current one.
; Final descriptor will be freed in step 7.
call usb_is_final_packet
jnc .found_final
mov ebx, [ebx+usb_gtd.NextVirt]
.look_final:
call usb_unlink_td
call usb_is_final_packet
jnc .found_final
push [ebx+usb_gtd.NextVirt]
stdcall uhci_free_td, ebx
pop ebx
jmp .look_final
.found_final:
; 5d. Restore the status saved in 5a and transform it to the error code.
pop eax ; error code
shr eax, 16
; Notes:
; * any USB transaction error results in Stalled bit; if it is not set,
; but we are here, it must be due to short packet;
; * babble is considered a fatal USB transaction error,
; other errors just lead to retrying the transaction;
; if babble is detected, return the corresponding error;
; * if several non-fatal errors have occured during transaction retries,
; all corresponding bits are set. In this case, return some error code,
; the order is quite arbitrary.
push USB_STATUS_UNDERRUN
pop ecx
test al, 1 shl (22-16) ; not Stalled?
jz .know_error
mov cl, USB_STATUS_OVERRUN
test al, 1 shl (20-16) ; Babble detected?
jnz .know_error
mov cl, USB_STATUS_BITSTUFF
test al, 1 shl (17-16) ; Bitstuff error?
jnz .know_error
mov cl, USB_STATUS_NORESPONSE
test al, 1 shl (18-16) ; CRC/TimeOut error?
jnz .know_error
mov cl, USB_STATUS_BUFOVERRUN
test al, 1 shl (21-16) ; Data Buffer error?
jnz .know_error
mov cl, USB_STATUS_STALL
.know_error:
; 5e. If error code is USB_STATUS_UNDERRUN
; and the last TD allows short packets, it is not an error.
; Note: all TDs except the last one in any transfer stage are marked
; as short-packet-is-error to stop controller from further processing
; of that stage; we need to restart processing from a TD following the last.
; After that, go to step 6 with ecx = 0 (no error).
cmp ecx, USB_STATUS_UNDERRUN
jnz @f
test byte [ebx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart], 1
jnz @f
; The controller has stopped this queue on the error packet.
; Update uhci_pipe.HeadTD to point to the next packet in the queue.
call uhci_fix_toggle
xor ecx, ecx
.control:
mov eax, [ebx+uhci_gtd.NextTD-uhci_gtd.SoftwarePart]
and al, not 0xF
mov edx, [ebx+usb_gtd.Pipe]
mov [edx+uhci_pipe.HeadTD-uhci_pipe.SoftwarePart], eax
pop edx ; length
jmp .notify
@@:
; 5f. Abort the entire transfer.
; There are two cases: either there is only one transfer stage
; (everything except control transfers), then ebx points to the last TD and
; all previous TD were unlinked and dismissed (if possible),
; or there are several stages (a control transfer) and ebx points to the last
; TD of Data or Status stage (usb_is_final_packet does not stop in Setup stage,
; because Setup stage can not produce short packets); for Data stage, we need
; to unlink and free (if possible) one more TD and advance ebx to the next one.
cmp [ebx+usb_gtd.Callback], 0
jnz .normal
; We cannot free ErrorTD yet, it could still be used by the hardware.
push ecx
mov eax, [ebx+usb_gtd.Pipe]
push [ebx+usb_gtd.NextVirt]
cmp ebx, [eax+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart]
jz @f
stdcall uhci_free_td, ebx
@@:
pop ebx
call usb_unlink_td
pop ecx
.normal:
; 5g. For bulk/interrupt transfers we have no choice but halt the queue,
; the driver should intercede (through some API which is not written yet).
; Control pipes normally recover at the next SETUP transaction (first stage
; of any control transfer), so we hope on the best and just advance the queue
; to the next transfer. (According to the standard, "A control pipe may also
; support functional stall as well, but this is not recommended.").
mov edx, [ebx+usb_gtd.Pipe]
cmp [edx+usb_pipe.Type], CONTROL_PIPE
jz .control
; Bulk/interrupt transfer; halt the queue.
mov eax, [ebx+uhci_gtd.NextTD-uhci_gtd.SoftwarePart]
and al, not 0xF
inc eax ; set Halted bit
mov [edx+uhci_pipe.HeadTD-uhci_pipe.SoftwarePart], eax
pop edx ; restore length saved in step 5a
.notify:
; 6. Either the descriptor in ebx was processed without errors,
; or all necessary error actions were taken and ebx points to the last
; related descriptor.
; 6a. Test whether it is the last packet in the transfer
; <=> it has an associated callback.
mov eax, [ebx+usb_gtd.Callback]
test eax, eax
jz .nocallback
; 6b. It has an associated callback; call it with corresponding parameters.
stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \
[ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData]
jmp .callback
.nocallback:
; 6c. It is an intermediate packet. Add its length to the length
; in the following packet.
mov eax, [ebx+usb_gtd.NextVirt]
add [eax+usb_gtd.Length], edx
.callback:
; 7. Free the current descriptor (if allowed) and return the next one.
; 7a. Save pointer to the next descriptor.
push [ebx+usb_gtd.NextVirt]
; 7b. Free the descriptor, unless it is saved as ErrorTD.
mov eax, [ebx+usb_gtd.Pipe]
cmp [eax+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart], ebx
jz @f
stdcall uhci_free_td, ebx
@@:
; 7c. Restore pointer to the next descriptor and return.
pop ebx
ret
endp
 
; Helper procedure for restarting transfer queue.
; When transfers are queued, their toggle bit is filled assuming that
; everything will go without errors. On error, some packets needs to be
; skipped, so toggle bits may become incorrect.
; This procedure fixes toggle bits.
; in: ebx -> last packet to be skipped, ErrorTD -> last processed packet
proc uhci_fix_toggle
; 1. Nothing to do for control pipes: in that case,
; toggle bits for different transfer stages are independent.
mov ecx, [ebx+usb_gtd.Pipe]
cmp [ecx+usb_pipe.Type], CONTROL_PIPE
jz .nothing
; 2. The hardware expects next packet with toggle = (ErrorTD.toggle xor 1),
; the current value in next packet is (ebx.toggle xor 1).
; Nothing to do if ErrorTD.toggle == ebx.toggle.
mov eax, [ecx+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart]
mov eax, [eax+uhci_gtd.Token-uhci_gtd.SoftwarePart]
xor eax, [ebx+uhci_gtd.Token-uhci_gtd.SoftwarePart]
test eax, 1 shl 19
jz .nothing
; 3. Lock the transfer queue.
add ecx, usb_pipe.Lock
call mutex_lock
; 4. Flip the toggle bit in all packets from ebx.NextVirt to ecx.LastTD
; (inclusive).
mov eax, [ebx+usb_gtd.NextVirt]
.loop:
xor byte [eax+uhci_gtd.Token-uhci_gtd.SoftwarePart+2], 1 shl (19-16)
cmp eax, [ecx+usb_pipe.LastTD-usb_pipe.Lock]
mov eax, [eax+usb_gtd.NextVirt]
jnz .loop
; 5. Flip the toggle bit in uhci_pipe structure.
xor byte [ecx+uhci_pipe.Token-uhci_pipe.SoftwarePart-usb_pipe.Lock+2], 1 shl (19-16)
or dword [ecx+uhci_pipe.Token-uhci_pipe.SoftwarePart-usb_pipe.Lock], eax
; 6. Unlock the transfer queue.
call mutex_unlock
.nothing:
ret
endp
 
; This procedure is called in the USB thread from uhci_process_deferred
; every UHCI_POLL_INTERVAL ticks. It polls the controller for
; connect/disconnect events.
; in: esi -> usb_controller
proc uhci_poll_roothub
push ebx ; save used register to be stdcall
; 1. Prepare for the loop for every port.
xor ecx, ecx
.portloop:
; 2. Some implementations of UHCI set ConnectStatusChange bit in a response to
; PortReset. Thus, we must ignore this change for port which is resetting.
cmp cl, [esi+usb_controller.ResettingPort]
jz .nextport
; 3. Read port status.
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx
; 4. If no change bits are set, continue to the next port.
test al, 0Ah
jz .nextport
; 5. Clear change bits and read the status again.
; (It is possible, although quite unlikely, that some event occurs between
; the first read and the clearing, invalidating the old status. If an event
; occurs after the clearing, we will not miss it, looking in the next scan.
out dx, ax
mov ebx, eax
in ax, dx
; 6. Process connect change notifications.
; Note: if connect status has changed, ignore enable status change;
; it is normal to disable a port at disconnect event.
; Some controllers set enable status change bit, some don't.
test bl, 2
jz .noconnectchange
DEBUGF 1,'K : [%d] UHCI %x connect status changed, %x/%x\n',[timer_ticks],esi,bx,ax
; yep. Regardless of the current status, note disconnect event;
; if there is something connected, store the connect time and note connect event.
; In any way, do not process
bts [esi+usb_controller.NewDisconnected], ecx
test al, 1
jz .disconnect
mov eax, [timer_ticks]
mov [esi+usb_controller.ConnectedTime+ecx*4], eax
bts [esi+usb_controller.NewConnected], ecx
jmp .nextport
.disconnect:
btr [esi+usb_controller.NewConnected], ecx
jmp .nextport
.noconnectchange:
; 7. Process enable change notifications.
; Note: that needs work.
test bl, 8
jz .nextport
test al, 4
jnz .nextport
dbgstr 'Port disabled'
.nextport:
; 8. Continue the loop for every port.
inc ecx
cmp ecx, [esi+usb_controller.NumPorts]
jb .portloop
pop ebx ; restore used register to be stdcall
ret
endp
 
; This procedure is called from uhci_process_deferred when
; a new device was connected at least USB_CONNECT_DELAY ticks
; and therefore is ready to be configured.
; in: esi -> usb_controller, ecx = port (zero-based)
proc uhci_new_port
; test whether we are configuring another port
; if so, postpone configuring and return
bts [esi+usb_controller.PendingPorts], ecx
cmp [esi+usb_controller.ResettingPort], -1
jnz .nothing
btr [esi+usb_controller.PendingPorts], ecx
; fall through to uhci_new_port.reset
 
; This function is called from uhci_new_port and uhci_test_pending_port.
; It starts reset signalling for the port. Note that in USB first stages
; of configuration can not be done for several ports in parallel.
.reset:
; 1. Store information about resetting hub (roothub) and port.
and [esi+usb_controller.ResettingHub], 0
mov [esi+usb_controller.ResettingPort], cl
; 2. Initiate reset signalling.
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx
or ah, 2
out dx, ax
; 3. Store the current time and set status to 1 = reset signalling active.
mov eax, [timer_ticks]
mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 1
.nothing:
ret
endp
 
; This procedure is called from uhci_process_deferred when
; reset signalling for a port needs to be finished.
proc uhci_port_reset_done
; 1. Stop reset signalling.
movzx ecx, [esi+usb_controller.ResettingPort]
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx
DEBUGF 1,'K : [%d] UHCI %x status %x/',[timer_ticks],esi,ax
and ah, not 2
out dx, ax
; 2. Status bits in UHCI are invalid during reset signalling.
; Wait a millisecond while status bits become valid again.
push esi
push 1
pop esi
call delay_ms
pop esi
; 3. ConnectStatus bit is zero during reset and becomes 1 during step 2;
; some controllers interpret this as a (fake) connect event.
; Enable port and clear status change notification.
in ax, dx
DEBUGF 1,'%x\n',ax
or al, 6 ; enable port, clear status change
out dx, ax
; 4. Store the current time and set status to 2 = reset recovery active.
mov eax, [timer_ticks]
DEBUGF 1,'K : reset done at %d\n',[timer_ticks]
mov [esi+usb_controller.ResetTime], eax
mov [esi+usb_controller.ResettingStatus], 2
ret
endp
 
; This procedure is called from uhci_process_deferred when
; a new device has been reset, recovered after reset and
; needs to be configured.
; in: esi -> usb_controller
proc uhci_port_init
; 1. Read port status.
mov [esi+usb_controller.ResettingStatus], 0
movzx ecx, [esi+usb_controller.ResettingPort]
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+ecx*2+UhciPort1StatusReg]
in ax, dx
DEBUGF 1,'K : [%d] UHCI %x status %x\n',[timer_ticks],esi,ax
; 2. If the device has been disconnected, stop the initialization.
test al, 1
jnz @f
dbgstr 'USB port disabled after reset'
jmp usb_test_pending_port
@@:
; 3. Copy LowSpeed bit to bit 0 of eax and call the worker procedure
; to notify the protocol layer about new UHCI device.
push edx
mov al, ah
call uhci_new_device
pop edx
test eax, eax
jnz .nothing
; 4. If something at the protocol layer has failed
; (no memory, no bus address), disable the port and stop the initialization.
.disable_exit:
in ax, dx
and al, not 4
out dx, ax ; disable the port
jmp usb_test_pending_port
.nothing:
ret
endp
 
; This procedure is called from uhci_port_init and from hub support code
; when a new device is connected and has been reset.
; It calls usb_new_device at the protocol layer with correct parameters.
; in: esi -> usb_controller, eax = speed;
; UHCI is USB1 device, so only low bit of eax (LowSpeed) is used.
proc uhci_new_device
; 1. Clear all bits of speed except bit 0.
and eax, 1
; 2. Store the speed for the protocol layer.
mov [esi+usb_controller.ResettingSpeed], al
; 3. Create pseudo-pipe in the stack.
; See uhci_init_pipe: only .Controller and .Token fields are used.
push esi ; fill .Controller field
mov ecx, esp
shl eax, 20 ; bit 20 = LowSpeedDevice
push eax ; ignored (ErrorTD)
push eax ; .Token field: DeviceAddress is zero, bit 20 = LowSpeedDevice
; 4. Notify the protocol layer.
call usb_new_device
; 5. Cleanup the stack after step 3 and return.
add esp, 12
ret
endp
 
; This procedure is called from usb_set_address_callback
; and stores USB device address in the uhci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
proc uhci_set_device_address
mov byte [ebx+uhci_pipe.Token+1-uhci_pipe.SoftwarePart], cl
call usb_subscription_done
ret
endp
 
; This procedure returns USB device address from the uhci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = endpoint address
proc uhci_get_device_address
mov al, byte [ebx+uhci_pipe.Token+1-uhci_pipe.SoftwarePart]
and eax, 7Fh
ret
endp
 
; This procedure is called from usb_set_address_callback
; if the device does not accept SET_ADDRESS command and needs
; to be disabled at the port level.
; in: esi -> usb_controller, ecx = port (zero-based)
proc uhci_port_disable
mov edx, [esi+uhci_controller.IOBase-sizeof.uhci_controller]
lea edx, [edx+UhciPort1StatusReg+ecx*2]
in ax, dx
and al, not 4
out dx, ax
ret
endp
 
; This procedure is called from usb_get_descr8_callback when
; the packet size for zero endpoint becomes known and
; stores the packet size in uhci_pipe structure.
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
proc uhci_set_endpoint_packet_size
dec ecx
shl ecx, 21
and [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart], (1 shl 21) - 1
or [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart], ecx
; uhci_pipe.Token field is purely for software bookkeeping and does not affect
; the hardware; thus, we can continue initialization immediately.
call usb_subscription_done
ret
endp
 
; This procedure is called from API usb_open_pipe and processes
; the controller-specific part of this API. See docs.
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
proc uhci_init_pipe
; inherit some variables from the parent usb_open_pipe
virtual at ebp+8
.config_pipe dd ?
.endpoint dd ?
.maxpacket dd ?
.type dd ?
.interval dd ?
end virtual
; 1. Initialize ErrorTD to zero.
and [edi+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart], 0
; 2. Initialize HeadTD to the physical address of the first TD.
push eax ; store pointer to the first TD for step ?
sub eax, uhci_gtd.SoftwarePart
call get_phys_addr
mov [edi+uhci_pipe.HeadTD-uhci_pipe.SoftwarePart], eax
; 3. Initialize Token field:
; take DeviceAddress and LowSpeedDevice from the parent pipe,
; take Endpoint and MaximumLength fields from API arguments,
; set PID depending on pipe type and provided pipe direction,
; set DataToggle to zero.
mov eax, [ecx+uhci_pipe.Token-uhci_pipe.SoftwarePart]
and eax, 0x107F00 ; keep DeviceAddress and LowSpeedDevice
mov edx, [.endpoint]
and edx, 15
shl edx, 15
or eax, edx
mov edx, [.maxpacket]
dec edx
shl edx, 21
or eax, edx
mov al, USB_PID_SETUP
cmp [.type], CONTROL_PIPE
jz @f
mov al, USB_PID_OUT
test byte [.endpoint], 80h
jz @f
mov al, USB_PID_IN
@@:
mov [edi+uhci_pipe.Token-uhci_pipe.SoftwarePart], eax
; 4. Initialize the first TD:
; copy Token from uhci_pipe.Token zeroing reserved bit 20,
; set ControlStatus for future transfers, bit make it inactive,
; set bit 0 in NextTD = "no next TD".
pop edx ; restore pointer saved in step 2
mov [edx+uhci_gtd.Token-uhci_gtd.SoftwarePart], eax
and byte [edx+uhci_gtd.Token+2-uhci_gtd.SoftwarePart], not (1 shl (20-16))
and eax, 1 shl 20
shl eax, 6
or eax, UHCI_INVALID_LENGTH + (3 shl 27)
; not processed, inactive, allow 3 errors
mov [edx+uhci_gtd.ControlStatus-uhci_gtd.SoftwarePart], eax
mov [edx+uhci_gtd.NextTD-uhci_gtd.SoftwarePart], 1
; 5. Select the corresponding list and insert to the list.
; 5a. Use Control list for control pipes, Bulk list for bulk pipes.
lea edx, [esi+uhci_controller.ControlED.SoftwarePart-sizeof.uhci_controller]
cmp [.type], BULK_PIPE
jb .insert ; control pipe
lea edx, [esi+uhci_controller.BulkED.SoftwarePart-sizeof.uhci_controller]
jz .insert ; bulk pipe
.interrupt_pipe:
; 5b. For interrupt pipes, let the scheduler select the appropriate list
; based on the current bandwidth distribution and the requested bandwidth.
; This could fail if the requested bandwidth is not available;
; if so, return an error.
lea edx, [esi + uhci_controller.IntEDs - sizeof.uhci_controller]
lea eax, [esi + uhci_controller.IntEDs + 32*sizeof.uhci_static_ep - sizeof.uhci_controller]
push 64
pop ecx
call usb1_select_interrupt_list
test edx, edx
jz .return0
.insert:
; Insert to the head of the corresponding list.
; Note: inserting to the head guarantees that the list traverse in
; uhci_process_updated_schedule, once started, will not interact with new pipes.
; However, we still need to ensure that links in the new pipe (edi.NextVirt)
; are initialized before links to the new pipe (edx.NextVirt).
; 5c. Insert in the list of virtual addresses.
mov ecx, [edx+usb_pipe.NextVirt]
mov [edi+usb_pipe.NextVirt], ecx
mov [edi+usb_pipe.PrevVirt], edx
mov [ecx+usb_pipe.PrevVirt], edi
mov [edx+usb_pipe.NextVirt], edi
; 5d. Insert in the hardware list: copy previous NextQH to the new pipe,
; store the physical address of the new pipe to previous NextQH.
mov ecx, [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart]
mov [edi+uhci_pipe.NextQH-uhci_pipe.SoftwarePart], ecx
lea eax, [edi-uhci_pipe.SoftwarePart]
call get_phys_addr
inc eax
inc eax
mov [edx+uhci_static_ep.NextQH-uhci_static_ep.SoftwarePart], eax
; 6. Return with nonzero eax.
ret
.return0:
xor eax, eax
ret
endp
 
; This procedure is called when a pipe is closing (either due to API call
; or due to disconnect); it unlinks a pipe from the corresponding list.
if uhci_static_ep.SoftwarePart <> uhci_pipe.SoftwarePart
.err uhci_unlink_pipe assumes that uhci_static_ep.SoftwarePart == uhci_pipe.SoftwarePart
end if
proc uhci_unlink_pipe
cmp [ebx+usb_pipe.Type], INTERRUPT_PIPE
jnz @f
mov eax, [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart]
cmp al, USB_PID_IN
setz ch
bt eax, 20
setc cl
add eax, 1 shl 21
shr eax, 21
stdcall usb1_interrupt_list_unlink, eax, ecx
@@:
; Note: we need to ensure that NextVirt field of the pipe is not modified;
; this procedure can be called while uhci_process_updated_schedule processes
; the same pipe, and it needs a correct NextVirt field to continue.
mov edx, [ebx+usb_pipe.NextVirt]
mov eax, [ebx+usb_pipe.PrevVirt]
mov [edx+usb_pipe.PrevVirt], eax
mov [eax+usb_pipe.NextVirt], edx
; Note: eax could be either usb_pipe or usb_static_ep;
; fortunately, NextQH and SoftwarePart have same offsets in both.
mov edx, [ebx+uhci_pipe.NextQH-uhci_pipe.SoftwarePart]
mov [eax+uhci_pipe.NextQH-uhci_pipe.SoftwarePart], edx
ret
endp
 
; Free memory associated with pipe.
; For UHCI, this includes usb_pipe structure and ErrorTD, if present.
proc uhci_free_pipe
mov eax, [esp+4]
mov eax, [eax+uhci_pipe.ErrorTD-uhci_pipe.SoftwarePart]
test eax, eax
jz @f
stdcall uhci_free_td, eax
@@:
jmp usb1_free_endpoint
endp
 
; This procedure is called from the several places in main USB code
; and allocates required packets for the given transfer stage.
; ebx = pipe, other parameters are passed through the stack
proc uhci_alloc_transfer stdcall uses edi, buffer:dword, size:dword, flags:dword, td:dword, direction:dword
locals
token dd ?
origTD dd ?
packetSize dd ? ; must be the last variable, see usb_init_transfer
endl
; 1. [td] will be the first packet in the transfer.
; Save it to allow unrolling if something will fail.
mov eax, [td]
mov [origTD], eax
; In UHCI one TD describes one packet, transfers should be split into parts
; with size <= endpoint max packet size.
; 2. Get the maximum packet size for endpoint from uhci_pipe.Token
; and generate Token field for TDs.
mov edi, [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart]
mov eax, edi
shr edi, 21
inc edi
; zero packet size (it will be set for every packet individually),
; zero reserved bit 20,
and eax, (1 shl 20) - 1
mov [packetSize], edi
; set the correct PID if it is different from the pipe-wide PID
; (Data and Status stages of control transfers),
mov ecx, [direction]
and ecx, 3
jz @f
mov al, USB_PID_OUT
dec ecx
jz @f
mov al, USB_PID_IN
@@:
; set the toggle bit for control transfers,
mov ecx, [direction]
test cl, 1 shl 3
jz @f
and ecx, 1 shl 2
and eax, not (1 shl 19)
shl ecx, 19-2
or eax, ecx
@@:
; store the resulting Token in the stack variable.
mov [token], eax
; 3. While the remaining data cannot fit in one packet,
; allocate full packets (of maximal possible size).
.fullpackets:
cmp [size], edi
jbe .lastpacket
call uhci_alloc_packet
test eax, eax
jz .fail
mov [td], eax
add [buffer], edi
sub [size], edi
jmp .fullpackets
.lastpacket:
; 4. The remaining data can fit in one packet;
; allocate the last packet with size = size of remaining data.
mov eax, [size]
mov [packetSize], eax
call uhci_alloc_packet
test eax, eax
jz .fail
; 5. Clear 'short packets are not allowed' bit for the last packet,
; if the caller requested this.
; Note: even if the caller says that short transfers are ok,
; all packets except the last one are marked as 'must be complete':
; if one of them will be short, the software intervention is needed
; to skip remaining packets; uhci_process_finalized_td will handle this
; transparently to the caller.
test [flags], 1
jz @f
and byte [ecx+uhci_gtd.ControlStatus+3-uhci_gtd.SoftwarePart], not (1 shl (29-24))
and byte [ecx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart], not 1
@@:
; 6. Update toggle bit in uhci_pipe structure from current value of [token].
mov edx, [token]
xor edx, [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart]
and edx, 1 shl 19
xor [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart], edx
.nothing:
ret
.fail:
mov edi, uhci_hardware_func
mov eax, [td]
stdcall usb_undo_tds, [origTD]
xor eax, eax
jmp .nothing
endp
 
; Helper procedure for uhci_alloc_transfer. Allocates one packet.
proc uhci_alloc_packet
; inherit some variables from the parent uhci_alloc_transfer
virtual at ebp-12
.token dd ?
.origTD dd ?
.packetSize dd ?
rd 2
.buffer dd ?
.transferSize dd ?
.Flags dd ?
.td dd ?
.direction dd ?
end virtual
; 1. In UHCI all data for one packet must be on the same page.
; Thus, if the given buffer splits page boundary, we need a temporary buffer
; and code that transfers data between the given buffer and the temporary one.
; 1a. There is no buffer for zero-length packets.
xor eax, eax
cmp [.packetSize], eax
jz .notempbuf
; 1b. A temporary buffer is not required if the first and the last bytes
; of the given buffer are the same except lower 12 bits.
mov edx, [.buffer]
add edx, [.packetSize]
dec edx
xor edx, [.buffer]
test edx, -0x1000
jz .notempbuf
; 1c. We need a temporary buffer. Allocate [packetSize]*2 bytes, so that
; there must be [packetSize] bytes on one page,
; plus space for a header uhci_original_buffer.
push ebx
mov eax, [.packetSize]
add eax, eax
add eax, sizeof.uhci_original_buffer
call malloc
pop ebx
; 1d. If failed, return zero.
test eax, eax
jz .nothing
; 1e. Test whether [.packetSize] bytes starting from
; eax + sizeof.uhci_original_buffer are in the same page.
; If so, use eax + sizeof.uhci_original_buffer as a temporary buffer.
; Otherwise, use the beginning of the next page as a temporary buffer
; (since we have overallocated, sufficient space must remain).
lea ecx, [eax+sizeof.uhci_original_buffer]
mov edx, ecx
add edx, [.packetSize]
dec edx
xor edx, ecx
test edx, -0x1000
jz @f
mov ecx, eax
or ecx, 0xFFF
inc ecx
@@:
mov [eax+uhci_original_buffer.UsedBuffer], ecx
mov ecx, [.buffer]
mov [eax+uhci_original_buffer.OrigBuffer], ecx
; 1f. For SETUP and OUT packets, copy data from the given buffer
; to the temporary buffer now. For IN packets, data go in other direction
; when the transaction completes.
cmp byte [.token], USB_PID_IN
jz .nocopy
push esi edi
mov esi, ecx
mov edi, [eax+uhci_original_buffer.UsedBuffer]
mov ecx, [.packetSize]
mov edx, ecx
shr ecx, 2
and edx, 3
rep movsd
mov ecx, edx
rep movsb
pop edi esi
.nocopy:
.notempbuf:
; 2. Allocate the next TD.
push eax
call usb1_allocate_general_td
pop edx
; If failed, free the temporary buffer (if it was allocated) and return zero.
test eax, eax
jz .fail
; 3. Initialize controller-independent parts of both TDs.
push edx
call usb_init_transfer
; 4. Initialize the next TD:
; mark it as last one (this will be changed when further packets will be
; allocated), copy Token field from uhci_pipe.Token zeroing bit 20,
; generate ControlStatus field, mark as Active
; (for last descriptor, this will be changed by uhci_insert_transfer).
mov [eax+uhci_gtd.NextTD-uhci_gtd.SoftwarePart], 1 ; no next TD
mov edx, [ebx+uhci_pipe.Token-uhci_pipe.SoftwarePart]
mov [eax+uhci_gtd.Token-uhci_gtd.SoftwarePart], edx
and byte [eax+uhci_gtd.Token+2-uhci_gtd.SoftwarePart], not (1 shl (20-16))
and edx, 1 shl 20
shl edx, 6
or edx, UHCI_INVALID_LENGTH + (1 shl 23) + (3 shl 27)
; not processed, active, allow 3 errors
mov [eax+uhci_gtd.ControlStatus-uhci_gtd.SoftwarePart], edx
; 5. Initialize remaining fields of the current TD.
; 5a. Store pointer to the buffer allocated in step 1 (or zero).
pop [ecx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart]
; 5b. Store physical address of the next TD.
push eax
sub eax, uhci_gtd.SoftwarePart
call get_phys_addr
; use Depth traversal unless this is the first TD in the transfer stage;
; uhci_insert_transfer will set Depth traversal for the first TD and clear
; it in the last TD
cmp ecx, [ebx+usb_pipe.LastTD]
jz @f
or eax, 4
@@:
mov [ecx+uhci_gtd.NextTD-uhci_gtd.SoftwarePart], eax
; 5c. Store physical address of the buffer: zero if no data present,
; the temporary buffer if it was allocated, the given buffer otherwise.
xor eax, eax
cmp [.packetSize], eax
jz .hasphysbuf
mov eax, [.buffer]
mov edx, [ecx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart]
test edx, edx
jz @f
mov eax, [edx+uhci_original_buffer.UsedBuffer]
@@:
call get_phys_addr
.hasphysbuf:
mov [ecx+uhci_gtd.Buffer-uhci_gtd.SoftwarePart], eax
; 5d. For IN transfers, disallow short packets.
; This will be overridden, if needed, by uhci_alloc_transfer.
mov eax, [.token]
mov edx, [.packetSize]
dec edx
cmp al, USB_PID_IN
jnz @f
or byte [ecx+uhci_gtd.ControlStatus+3-uhci_gtd.SoftwarePart], 1 shl (29-24) ; disallow short packets
or byte [ecx+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart], 1
@@:
; 5e. Get Token field: combine [.token] with [.packetSize].
shl edx, 21
or edx, eax
mov [ecx+uhci_gtd.Token-uhci_gtd.SoftwarePart], edx
; 6. Flip toggle bit in [.token].
xor eax, 1 shl 19
mov [.token], eax
; 7. Return pointer to the next TD.
pop eax
.nothing:
ret
.fail:
xchg eax, edx
call free
xor eax, eax
ret
endp
 
; This procedure is called from the several places in main USB code
; and activates the transfer which was previously allocated by
; uhci_alloc_transfer.
; ecx -> last descriptor for the transfer, ebx -> usb_pipe
proc uhci_insert_transfer
; DEBUGF 1,'K : uhci_insert_transfer: eax=%x, ecx=%x, [esp+4]=%x\n',eax,ecx,[esp+4]
and byte [eax+uhci_gtd.ControlStatus+2-uhci_gtd.SoftwarePart], not (1 shl (23-16)) ; clear Active bit
or byte [ecx+uhci_gtd.ControlStatus+3-uhci_gtd.SoftwarePart], 1 shl (24-24) ; set InterruptOnComplete bit
mov eax, [esp+4]
or byte [eax+uhci_gtd.ControlStatus+2-uhci_gtd.SoftwarePart], 1 shl (23-16) ; set Active bit
or byte [eax+uhci_gtd.NextTD-uhci_gtd.SoftwarePart], 4 ; set Depth bit
ret
endp
 
; Free all memory associated with one TD.
; For UHCI, this includes memory for uhci_gtd itself
; and the temporary buffer, if present.
proc uhci_free_td
mov eax, [esp+4]
mov eax, [eax+uhci_gtd.OrigBufferInfo-uhci_gtd.SoftwarePart]
and eax, not 1
jz .nobuf
push ebx
call free
pop ebx
.nobuf:
sub dword [esp+4], uhci_gtd.SoftwarePart
jmp usb_free_common
endp
/kernel/branches/Kolibri-acpi/bus/usb
Property changes:
Added: tsvn:logminsize
+5
\ No newline at end of property
/kernel/branches/Kolibri-acpi/const.inc
181,132 → 181,7
 
OS_BASE equ 0x80000000
 
window_data equ (OS_BASE+0x0001000)
 
CURRENT_TASK equ (OS_BASE+0x0003000)
TASK_COUNT equ (OS_BASE+0x0003004)
TASK_BASE equ (OS_BASE+0x0003010)
TASK_DATA equ (OS_BASE+0x0003020)
TASK_EVENT equ (OS_BASE+0x0003020)
 
mouseunder equ (OS_BASE+0x0006900)
CDDataBuf equ (OS_BASE+0x0007000)
FLOPPY_BUFF equ (OS_BASE+0x0008000)
ACTIVE_PROC_STACK equ (OS_BASE+0x000A400) ;unused
idts equ (OS_BASE+0x000B100)
WIN_STACK equ (OS_BASE+0x000C000)
WIN_POS equ (OS_BASE+0x000C400)
FDD_BUFF equ (OS_BASE+0x000D000)
 
;unused ? only one reference
ENABLE_TASKSWITCH equ (OS_BASE+0x000E000)
 
PUTPIXEL equ (OS_BASE+0x000E020)
GETPIXEL equ (OS_BASE+0x000E024)
 
;unused ? only one reference
BANK_SWITCH equ (OS_BASE+0x000E030)
 
;unused ? store mousepointer
MOUSE_PICTURE equ (OS_BASE+0x000F200)
 
;MOUSE_VISIBLE equ (OS_BASE+0x000F204)
WIN_TEMP_XY equ (OS_BASE+0x000F300)
KEY_COUNT equ (OS_BASE+0x000F400)
KEY_BUFF equ (OS_BASE+0x000F401)
 
BTN_COUNT equ (OS_BASE+0x000F500)
BTN_BUFF equ (OS_BASE+0x000F501)
 
;CPU_FREQ equ (OS_BASE+0x000F600)
 
;unused ? no active references
;MOUSE_PORT equ (OS_BASE+0x000F604)
 
;unused
PS2_CHUNK equ (OS_BASE+0x000FB00)
 
MOUSE_SCROLL_H equ (OS_BASE+0x000FB08)
MOUSE_X equ (OS_BASE+0x000FB0A)
MOUSE_Y equ (OS_BASE+0x000FB0C)
MOUSE_SCROLL_V equ (OS_BASE+0x000FB0E)
 
MOUSE_COLOR_MEM equ (OS_BASE+0x000FB10)
COLOR_TEMP equ (OS_BASE+0x000FB30)
BTN_DOWN equ (OS_BASE+0x000FB40)
MOUSE_DOWN equ (OS_BASE+0x000FB44)
X_UNDER equ (OS_BASE+0x000FB4A)
Y_UNDER equ (OS_BASE+0x000FB4C)
ScreenBPP equ (OS_BASE+0x000FBF1)
 
;unused ? only one reference
MOUSE_BUFF_COUNT equ (OS_BASE+0x000FCFF)
 
Screen_Max_X equ (OS_BASE+0x000FE00)
Screen_Max_Y equ (OS_BASE+0x000FE04)
BytesPerScanLine equ (OS_BASE+0x000FE08)
SCR_MODE equ (OS_BASE+0x000FE0C)
 
LFBAddress equ (OS_BASE+0x000FE80)
BTN_ADDR equ (OS_BASE+0x000FE88)
MEM_AMOUNT equ (OS_BASE+0x000FE8C)
 
SYS_SHUTDOWN equ (OS_BASE+0x000FF00)
TASK_ACTIVATE equ (OS_BASE+0x000FF01)
 
REDRAW_BACKGROUND equ (OS_BASE+0x000FFF0)
 
BANK_RW equ (OS_BASE+0x000FFF2)
MOUSE_BACKGROUND equ (OS_BASE+0x000FFF4)
DONT_DRAW_MOUSE equ (OS_BASE+0x000FFF5)
DONT_SWITCH equ (OS_BASE+0x000FFFF)
 
TMP_STACK_TOP equ 0x006CC00
 
sys_pgdir equ (OS_BASE+0x006F000)
 
DRIVE_DATA equ (OS_BASE+0x0070000)
 
SLOT_BASE equ (OS_BASE+0x0080000)
 
;unused
TMP_BUFF equ (OS_BASE+0x0090000)
 
VGABasePtr equ (OS_BASE+0x00A0000)
 
RAMDISK equ (OS_BASE+0x0100000)
RAMDISK_FAT equ (OS_BASE+0x0280000)
FLOPPY_FAT equ (OS_BASE+0x0282000)
 
CLEAN_ZONE equ 0x284000
IDE_DMA equ 0x284000
 
BgrAuxTable equ (OS_BASE+0x0298000)
; unused?
SB16Buffer equ (OS_BASE+0x02A0000)
SB16_Status equ (OS_BASE+0x02B0000)
 
BUTTON_INFO equ (OS_BASE+0x02B3FEE)
 
BPSLine_calc_area equ (OS_BASE+0x02C4000)
d_width_calc_area equ (OS_BASE+0x02CA000)
 
RESERVED_PORTS equ (OS_BASE+0x02D0000)
BOOT_VAR equ (OS_BASE+0x02E0000)
 
stack_data_start equ (OS_BASE+0x02F0000)
eth_data_start equ (OS_BASE+0x02F0000)
stack_data equ (OS_BASE+0x02F4000)
stack_data_end equ (OS_BASE+0x030ffff)
resendQ equ (OS_BASE+0x0310000)
 
skin_data equ (OS_BASE+0x0318000)
draw_data equ (OS_BASE+0x0320000)
 
BgrDrawMode equ (OS_BASE+0x0323FF4)
BgrDataWidth equ (OS_BASE+0x0323FF8)
BgrDataHeight equ (OS_BASE+0x0323FFC)
 
sys_pgmap equ (OS_BASE+0x0324000)
 
UPPER_KERNEL_PAGES equ (OS_BASE+0x0400000)
331,7 → 206,7
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)
368,7 → 243,6
BOOT_VESA_MODE equ 0x9008 ;word vesa video mode
BOOT_X_RES equ 0x900A ;word X res
BOOT_Y_RES equ 0x900C ;word Y res
;;BOOT_MOUSE_PORT equ 0x9010 ;byte mouse port - not used
BOOT_BANK_SW equ 0x9014 ;dword Vesa 1.2 pm bank switch
BOOT_LFB equ 0x9018 ;dword Vesa 2.0 LFB address
BOOT_MTRR equ 0x901C ;byte 0 or 1 : enable MTRR graphics acceleration
400,7 → 274,8
EVENT_IPC equ 0x00000040
EVENT_NETWORK equ 0x00000080
EVENT_DEBUG equ 0x00000100
EVENT_EXTENDED equ 0x00000200
EVENT_NETWORK2 equ 0x00000200
EVENT_EXTENDED equ 0x00000400
 
EV_INTR equ 1
 
634,6 → 509,17
srv_proc_ex dd ? ;+0x2C ;kernel mode service handler
ends
 
struct USBSRV
srv SRV
usb_func dd ?
ends
 
struct USBFUNC
strucsize dd ?
add_device dd ?
device_disconnect dd ?
ends
 
DRV_ENTRY equ 1
DRV_EXIT equ -1
 
688,4 → 574,5
list LHEAD
handler dd ? ;handler roututine
data dd ? ;user-specific data
num_ints dd ? ;how many times handled
ends
/kernel/branches/Kolibri-acpi/core/conf_lib-sp.inc
1,11 → 1,11
; 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
/kernel/branches/Kolibri-acpi/core/conf_lib.inc
72,54 → 72,7
udev_midibase db 'midibase',0
udev_midibase_def db '0x320',0
endg
;set up netvork configuration
proc set_network_conf
locals
par db 30 dup(?)
endl
pushad
 
;[net]
;active
lea eax, [par]
invoke ini.get_int, conf_fname, unet, unet_active, 0
or eax, eax
jz .do_not_set_net
mov eax, [stack_config]
and eax, 0xFFFFFF80
add eax, 3
mov [stack_config], eax
call ash_eth_enable
 
;addr
lea eax, [par]
push eax
invoke ini.get_str, conf_fname, unet, unet_addr, eax, 30, unet_def
pop eax
stdcall do_inet_adr, eax
mov [stack_ip], eax
 
;mask
lea eax, [par]
push eax
invoke ini.get_str, conf_fname, unet, unet_mask, eax, 30, unet_def
pop eax
stdcall do_inet_adr, eax
mov [subnet_mask], eax
 
;gate
lea eax, [par]
push eax
invoke ini.get_str, conf_fname, unet, unet_gate, eax, 30, unet_def
pop eax
stdcall do_inet_adr, eax
mov [gateway_ip], eax
.do_not_set_net:
popad
ret
 
 
endp
iglobal
if lang eq sp
include 'core/conf_lib-sp.inc'
164,7 → 117,7
proc strtoint_dec stdcall,strs
pushad
xor edx, edx
; ¯®¨áª ª®­æ 
; поиск конца
mov esi, [strs]
@@:
lodsb
180,7 → 133,7
dec ebx
or ebx, ebx
jz @f
imul ecx, ecx, 10; ¯®à冷ª
imul ecx, ecx, 10; порядок
jmp @b
@@:
 
/kernel/branches/Kolibri-acpi/core/dll.inc
142,7 → 142,11
cmp [edi+SRV.size], sizeof.SRV
jne .fail
 
stdcall [edi+SRV.srv_proc], esi
; stdcall [edi+SRV.srv_proc], esi
mov eax, [edi+SRV.srv_proc]
test eax, eax
jz .fail
stdcall eax, esi
ret
.fail:
xor eax, eax
174,7 → 178,11
cmp [eax+SRV.size], sizeof.SRV
jne .fail
 
stdcall [eax+SRV.srv_proc], ecx
; stdcall [eax+SRV.srv_proc], ecx
mov eax, [eax+SRV.srv_proc]
test eax, eax
jz .fail
stdcall eax, ecx
ret
.fail:
or eax, -1
213,9 → 221,31
ret
endp
 
align 4
proc reg_service stdcall, name:dword, handler:dword
reg_service:
xor eax, eax
mov ecx, [esp+8]
jecxz .nothing
push sizeof.SRV
push ecx
pushd [esp+12]
call reg_service_ex
.nothing:
ret 8
 
reg_usb_driver:
push sizeof.USBSRV
pushd [esp+12]
pushd [esp+12]
call reg_service_ex
test eax, eax
jz .nothing
mov ecx, [esp+12]
mov [eax+USBSRV.usb_func], ecx
.nothing:
ret 12
 
proc reg_service_ex stdcall, name:dword, handler:dword, srvsize:dword
 
push ebx
 
xor eax, eax
223,10 → 253,10
cmp [name], eax
je .fail
 
cmp [handler], eax
je .fail
; cmp [handler], eax
; je .fail
 
mov eax, sizeof.SRV
mov eax, [srvsize]
call malloc
test eax, eax
jz .fail
/kernel/branches/Kolibri-acpi/core/exports.inc
95,7 → 95,18
szTimerHS db 'TimerHS',0
szCancelTimerHS db 'CancelTimerHS',0
 
szRegUSBDriver db 'RegUSBDriver',0
szUSBOpenPipe db 'USBOpenPipe',0
szUSBClosePipe db 'USBClosePipe',0
szUSBNormalTransferAsync db 'USBNormalTransferAsync',0
szUSBControlTransferAsync db 'USBControlTransferAsync',0
 
szNetRegDev db 'NetRegDev',0
szNetUnRegDev db 'NetUnRegDev',0
szNetPtrToNum db 'NetPtrToNum',0
szNetLinkChanged db 'NetLinkChanged',0
szEth_input db 'Eth_input',0
 
align 16
kernel_export:
dd szRegService , reg_service
180,6 → 191,18
dd szTimerHS , timer_hs
dd szCancelTimerHS , cancel_timer_hs
 
dd szRegUSBDriver , reg_usb_driver
dd szUSBOpenPipe , usb_open_pipe
dd szUSBClosePipe , usb_close_pipe
dd szUSBNormalTransferAsync, usb_normal_transfer_async
dd szUSBControlTransferAsync, usb_control_async
 
dd szNetRegDev , NET_add_device
dd szNetUnRegDev , NET_remove_device
dd szNetPtrToNum , NET_ptr_to_num
dd szNetLinkChanged , NET_link_changed
dd szEth_input , ETH_input
 
exp_lfb:
dd szLFBAddress , 0
dd 0 ;terminator, must be zero
/kernel/branches/Kolibri-acpi/core/fpu.inc
179,5 → 179,5
iret
 
iglobal
fpu_owner dd 0
fpu_owner dd 2
endg
/kernel/branches/Kolibri-acpi/core/irq.inc
66,8 → 66,7
test edx, edx
jz .err
 
pushfd
cli
spin_lock_irqsave IrqsList
 
;allocate handler
 
84,6 → 83,7
mov eax, [user_data]
mov [ecx+IRQH.handler], edx
mov [ecx+IRQH.data], eax
and [ecx+IRQH.num_ints], 0
 
lea edx, [irqh_tab+ebx*8]
list_add_tail ecx, edx ;clobber eax
90,7 → 90,7
stdcall enable_irq, ebx
 
.fail:
popfd
spin_unlock_irqrestore IrqsList
.err:
pop ebx
mov eax, [.irqh]
188,7 → 188,7
 
push [ebx+IRQH.data]
call [ebx+IRQH.handler]
add esp, 4
pop ecx
 
pop esi
pop edi
197,6 → 197,7
test eax, eax
jz .next
 
inc [ebx+IRQH.num_ints]
btr [irq_active_set], ebp
jmp .next
 
204,9 → 205,70
btr [irq_active_set], ebp
jnc .exit
 
; There is at least one configuration with one device which generates IRQ
; that is not the same as it should be according to PCI config space.
; For that device, the handler is registered at wrong IRQ.
; As a workaround, when nobody acknowledges the generated IRQ,
; try to ask all other registered handlers; if some handler acknowledges
; the IRQ this time, relink it to the current IRQ list.
; To make this more reliable, for every handler keep number of times
; that it has acknowledged an IRQ, and assume that handlers with at least one
; acknowledged IRQ are registered properly.
; Note: this still isn't 100% correct, because two IRQs can fire simultaneously,
; the better way would be to find the correct IRQ, but I don't know how to do
; this in that case.
; Also, [fdc_irq_func], [irq14_func], [irq15_func] could process interrupt
; but do not return whether they did it, so just ignore IRQs 6, 14, 15.
cmp ebp, 6
jz .fail
cmp ebp, 14
jz .fail
cmp ebp, 15
jz .fail
push ebp
xor ebp, ebp
.try_other_irqs:
cmp ebp, [esp]
jz .try_next_irq
cmp ebp, 1
jz .try_next_irq
cmp ebp, 12
jz .try_next_irq
lea esi, [irqh_tab+ebp*8]
mov ebx, esi
.try_next_handler:
mov ebx, [ebx+IRQH.list.next]
cmp ebx, esi
je .try_next_irq
cmp [ebx+IRQH.num_ints], 0
jne .try_next_handler
; keyboard handler acknowledges everything
push [ebx+IRQH.data]
call [ebx+IRQH.handler]
pop ecx
test eax, eax
jz .try_next_handler
 
.found_in_wrong_list:
DEBUGF 1,'K : warning: relinking handler from IRQ%d to IRQ%d\n',\
ebp, [esp]
pop ebp
spin_lock_irqsave IrqsList
list_del ebx
lea edx, [irqh_tab+ebp*8]
list_add_tail ebx, edx
spin_unlock_irqrestore IrqsList
jmp .exit
 
.try_next_irq:
inc ebp
cmp ebp, 16
jb .try_other_irqs
pop ebp
 
.fail:
inc [irq_failed+ebp*4]
.exit:
mov [check_idle_semaphore], 5
 
mov ecx, ebp
call irq_eoi
/kernel/branches/Kolibri-acpi/core/memory.inc
631,21 → 631,21
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,8 → 661,8
 
.user_space:
test eax, PG_MAP
jnz .err_access ;Ñòðàíèöà ïðèñóòñòâóåò
;Îøèáêà äîñòóïà ?
jnz .err_access ;Страница присутствует
;Ошибка доступа ?
 
shr ebx, 12
mov ecx, ebx
669,13 → 669,13
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
 
.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
1136,11 → 1136,6
mov eax, [dst_slot]
shl eax, 8
or [eax+SLOT_BASE+0xA8], dword 0x40
cmp dword [check_idle_semaphore], 20
jge .ipc_no_cis
 
mov dword [check_idle_semaphore], 5
.ipc_no_cis:
push 0
jmp .ret
.no_pid:
/kernel/branches/Kolibri-acpi/core/sched.inc
29,8 → 29,8
.nocounter:
xor ecx, ecx ; send End Of Interrupt signal
call irq_eoi
btr dword[DONT_SWITCH], 0
jc .return
; btr dword[DONT_SWITCH], 0
; jc .return
call find_next_task
jz .return ; if there is only one running process
call do_change_task
61,7 → 61,7
call find_next_task
jz .return ; the same task -> skip switch
@@:
mov byte[DONT_SWITCH], 1
; mov byte[DONT_SWITCH], 1
call do_change_task
.return:
popad
89,9 → 89,6
ret
align 4
updatecputimes:
xor eax, eax
xchg eax, [idleuse]
mov [idleusesec], eax
mov ecx, [TASK_COUNT]
mov edi, TASK_DATA
.newupdate:
102,58 → 99,8
loop .newupdate
ret
 
align 4
find_next_task:
;info:
; Find next task to execute
;retval:
; ebx = address of the APPDATA for the selected task (slot-base)
; esi = previous slot-base ([current_slot] at the begin)
; edi = address of the TASKDATA for the selected task
; ZF = 1 if the task is the same
;warning:
; [CURRENT_TASK] = bh , [TASK_BASE] = edi -- as result
; [current_slot] is not set to new value (ebx)!!!
;scratched: eax,ecx
call update_counters ; edi := [TASK_BASE]
Mov esi, ebx, [current_slot]
.loop:
cmp bh, [TASK_COUNT]
jb @f
xor bh, bh
mov edi, CURRENT_TASK
@@:
inc bh ; ebx += APPDATA.size
add edi, 0x20; edi += TASKDATA.size
mov al, [edi+TASKDATA.state]
test al, al
jz .found ; state == 0
cmp al, 5
jne .loop ; state == 1,2,3,4,9
; state == 5
pushad ; more freedom for [APPDATA.wait_test]
call [ebx+APPDATA.wait_test]
mov [esp+28], eax
popad
or eax, eax
jnz @f
; testing for timeout
mov ecx, [timer_ticks]
sub ecx, [ebx+APPDATA.wait_begin]
cmp ecx, [ebx+APPDATA.wait_timeout]
jb .loop
@@:
mov [ebx+APPDATA.wait_param], eax ; retval for wait
mov [edi+TASKDATA.state], 0
.found:
mov [CURRENT_TASK], bh
mov [TASK_BASE], edi
rdtsc ;call _rdtsc
mov [edi+TASKDATA.counter_add], eax; for next using update_counters
cmp ebx, esi ;esi - previous slot-base
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:
305,6 → 252,143
 
purge MUTEX_WAITER
 
MAX_PRIORITY = 0 ; highest, used for kernel tasks
USER_PRIORITY = 1 ; default
IDLE_PRIORITY = 2 ; lowest, only IDLE thread goes here
NR_SCHED_QUEUES = 3 ; MUST equal IDLE_PRIORYTY + 1
 
uglobal
; [scheduler_current + i*4] = zero if there are no threads with priority i,
; pointer to APPDATA of the current thread with priority i otherwise.
align 4
scheduler_current rd NR_SCHED_QUEUES
endg
 
; Add the given thread to the given priority list for the scheduler.
; in: edx -> APPDATA, ecx = priority
proc scheduler_add_thread
; 1. Acquire the lock.
spin_lock_irqsave SchedulerLock
; 2. Store the priority in APPDATA structure.
mov [edx+APPDATA.priority], ecx
; 3. There are two different cases: the given list is empty or not empty.
; In first case, go to 6. Otherwise, advance to 4.
mov eax, [scheduler_current+ecx*4]
test eax, eax
jz .new_list
; 4. Insert the new item immediately before the current item.
mov ecx, [eax+APPDATA.in_schedule.prev]
mov [edx+APPDATA.in_schedule.next], eax
mov [edx+APPDATA.in_schedule.prev], ecx
mov [eax+APPDATA.in_schedule.prev], edx
mov [ecx+APPDATA.in_schedule.next], edx
; 5. Release the lock and return.
spin_unlock_irqrestore SchedulerLock
ret
.new_list:
; 6. Initialize the list with one item and make it the current item.
mov [edx+APPDATA.in_schedule.next], edx
mov [edx+APPDATA.in_schedule.prev], edx
mov [scheduler_current+ecx*4], edx
; 7. Release the lock and return.
spin_unlock_irqrestore SchedulerLock
ret
endp
 
; Remove the given thread from the corresponding priority list for the scheduler.
; in: edx -> APPDATA
proc scheduler_remove_thread
; 1. Acquire the lock.
spin_lock_irqsave SchedulerLock
; 2. Remove the item from the corresponding list.
mov eax, [edx+APPDATA.in_schedule.next]
mov ecx, [edx+APPDATA.in_schedule.prev]
mov [eax+APPDATA.in_schedule.prev], ecx
mov [ecx+APPDATA.in_schedule.next], eax
; 3. If the given thread is the current item in the list,
; advance the current item.
; 3a. Check whether the given thread is the current item;
; if no, skip the rest of this step.
mov ecx, [edx+APPDATA.priority]
cmp [scheduler_current+ecx*4], edx
jnz .return
; 3b. Set the current item to eax; step 2 has set eax = next item.
mov [scheduler_current+ecx*4], eax
; 3c. If there were only one item in the list, zero the current item.
cmp eax, edx
jnz .return
mov [scheduler_current+ecx*4], 0
.return:
; 4. Release the lock and return.
spin_unlock_irqrestore SchedulerLock
ret
endp
 
;info:
; Find next task to execute
;retval:
; ebx = address of the APPDATA for the selected task (slot-base)
; edi = address of the TASKDATA for the selected task
; ZF = 1 if the task is the same
;warning:
; [CURRENT_TASK] = bh , [TASK_BASE] = edi -- as result
; [current_slot] is not set to new value (ebx)!!!
;scratched: eax,ecx
proc find_next_task
call update_counters
spin_lock_irqsave SchedulerLock
xor ecx, ecx
.priority_loop:
mov ebx, [scheduler_current+ecx*4]
test ebx, ebx
jz .priority_next
.task_loop:
mov ebx, [ebx+APPDATA.in_schedule.next]
mov edi, ebx
shr edi, 3
add edi, CURRENT_TASK - (SLOT_BASE shr 3)
mov al, [edi+TASKDATA.state]
test al, al
jz .task_found ; state == 0
cmp al, 5
jne .task_next ; state == 1,2,3,4,9
; state == 5
pushad ; more freedom for [APPDATA.wait_test]
call [ebx+APPDATA.wait_test]
mov [esp+28], eax
popad
or eax, eax
jnz @f
; testing for timeout
mov eax, [timer_ticks]
sub eax, [ebx+APPDATA.wait_begin]
cmp eax, [ebx+APPDATA.wait_timeout]
jb .task_next
xor eax, eax
@@:
mov [ebx+APPDATA.wait_param], eax ; retval for wait
mov [edi+TASKDATA.state], 0
.task_found:
mov [scheduler_current+ecx*4], ebx
spin_unlock_irqrestore SchedulerLock
.found:
mov [CURRENT_TASK], bh
mov [TASK_BASE], edi
rdtsc ;call _rdtsc
mov [edi+TASKDATA.counter_add], eax; for next using update_counters
cmp ebx, [current_slot]
ret
.task_next:
cmp ebx, [scheduler_current+ecx*4]
jnz .task_loop
.priority_next:
inc ecx
cmp ecx, NR_SCHED_QUEUES
jb .priority_loop
hlt
jmp $-1
endp
 
if 0
 
struc TIMER
316,13 → 400,6
}
 
 
MAX_PROIRITY 0 ; highest, used for kernel tasks
MAX_USER_PRIORITY 0 ; highest priority for user processes
USER_PRIORITY 7 ; default (should correspond to nice 0)
MIN_USER_PRIORITY 14 ; minimum priority for user processes
IDLE_PRIORITY 15 ; lowest, only IDLE process goes here
NR_SCHED_QUEUES 16 ; MUST equal IDLE_PRIORYTY + 1
 
uglobal
rdy_head rd 16
endg
/kernel/branches/Kolibri-acpi/core/sys32-sp.inc
1,4 → 1,4
; 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
/kernel/branches/Kolibri-acpi/core/sys32.inc
61,7 → 61,7
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
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
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
159,6 → 159,7
call show_error_parameters ;; only ONE using, inline ???
;mov edx, [TASK_BASE]
mov [edx + TASKDATA.state], byte 4 ; terminate
call wakeup_osloop
jmp change_task ; stack - here it does not matter at all, SEE: core/shed.inc
.debug:
; we are debugged process, notify debugger and suspend ourself
261,8 → 262,10
 
 
align 4
set_application_table_status:
push eax
lock_application_table:
push eax ecx edx
mov ecx, application_table_mutex
call mutex_lock
 
mov eax, [CURRENT_TASK]
shl eax, 5
269,37 → 272,30
add eax, CURRENT_TASK+TASKDATA.pid
mov eax, [eax]
 
mov [application_table_status], eax
mov [application_table_owner], eax
 
pop eax
pop edx ecx eax
 
ret
 
align 4
clear_application_table_status:
push eax
unlock_application_table:
push eax ecx edx
 
mov eax, [CURRENT_TASK]
shl eax, 5
add eax, CURRENT_TASK+TASKDATA.pid
mov eax, [eax]
mov [application_table_owner], 0
mov ecx, application_table_mutex
call mutex_unlock
 
cmp eax, [application_table_status]
jne apptsl1
xor eax, eax
mov [application_table_status], eax
apptsl1:
pop edx ecx eax
 
pop eax
 
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:
338,17 → 334,11
mov [CURRENT_TASK+esi+TASKDATA.state], 9
ret
@@:
lea edx, [SLOT_BASE+esi]
call scheduler_remove_thread
;mov esi,process_terminating
;call sys_msg_board_str
@@:
cli
cmp [application_table_status], 0
je term9
sti
call change_task
jmp @b
term9:
call set_application_table_status
call lock_application_table
 
; if the process is in V86 mode...
mov eax, [.slot]
391,11 → 381,11
stdcall destroy_app_space, [SLOT_BASE+eax+APPDATA.dir_table], [SLOT_BASE+eax+APPDATA.dlls_list_ptr]
 
mov esi, [.slot]
cmp [fpu_owner], esi ; if user fpu last -> fpu user = 1
cmp [fpu_owner], esi ; if user fpu last -> fpu user = 2
jne @F
 
mov [fpu_owner], 1
mov eax, [256+SLOT_BASE+APPDATA.fpu_state]
mov [fpu_owner], 2
mov eax, [256*2+SLOT_BASE+APPDATA.fpu_state]
clts
bt [cpu_caps], CAPS_SSE
jnc .no_SSE
685,10 → 675,7
xor esi, esi
call redrawscreen
 
mov [MOUSE_BACKGROUND], byte 0; no mouse background
mov [DONT_DRAW_MOUSE], byte 0; draw mouse
 
and [application_table_status], 0
call unlock_application_table
;mov esi,process_terminated
;call sys_msg_board_str
add esp, 4
695,17 → 682,6
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
/kernel/branches/Kolibri-acpi/core/syscall.inc
29,7 → 29,7
 
align 32
sysenter_entry:
; Íàñòðàèâàåì ñòåê
; Настраиваем стек
mov esp, [ss:tss._esp0]
sti
push ebp ; save app esp + 4
47,7 → 47,7
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
112,11 → 112,11
 
align 4
servetable:
dd socket ; 53-Socket interface
dd 0
dd 0
dd 0
dd 0
dd 0
dd file_system ; 58-Common file system interface
dd 0
dd 0
182,8 → 182,8
dd sys_apm ; 49-Advanced Power Management (APM)
dd syscall_set_window_shape ; 50-Window shape & scale
dd syscall_threads ; 51-Threads
dd stack_driver_stat ; 52-Stack driver status
dd cross_order ; 53-Socket interface
dd undefined_syscall ; 52-Stack driver status
dd undefined_syscall ; 53-Socket interface
dd undefined_syscall ; 54-reserved
dd sound_interface ; 55-Sound interface
dd undefined_syscall ; 56-reserved
204,9 → 204,9
dd syscall_window_settings ; 71-Window settings
dd sys_sendwindowmsg ; 72-Send window message
dd blit_32 ; 73-blitter;
dd undefined_syscall ; 74-reserved for new stack
dd undefined_syscall ; 75-reserved for new stack
dd undefined_syscall ; 76-reserved for new stack
dd sys_network ; 74-reserved for new stack
dd sys_socket ; 75-reserved for new stack
dd sys_protocols ; 76-reserved for new stack
times 255 - ( ($-servetable2) /4 ) dd undefined_syscall
dd sys_end ; -1-end application
 
/kernel/branches/Kolibri-acpi/core/taskman.inc
90,7 → 90,7
stdcall set_cursor, [def_cursor_clock]
mov [handle], eax
mov [redrawmouse_unconditional], 1
call __sys_draw_pointer
call wakeup_osloop
popad
@@:
mov [flags], edx
152,20 → 152,8
test eax, eax
jz .err_hdr
 
.wait_lock:
cmp [application_table_status], 0
je .get_lock
call change_task
jmp .wait_lock
call lock_application_table
 
.get_lock:
mov eax, 1
xchg eax, [application_table_status]
test eax, eax
jnz .wait_lock
 
call set_application_table_status
 
call get_new_process_place
test eax, eax
mov esi, -0x20 ; too many processes
246,9 → 234,8
mov eax, [save_cr3]
call set_cr3
 
xor ebx, ebx
mov [application_table_status], ebx;unlock application_table_status mutex
mov eax, [process_number];set result
call unlock_application_table
 
jmp .final
 
259,8 → 246,7
.err_hdr:
stdcall kernel_free, [file_base]
.err_file:
xor eax, eax
mov [application_table_status], eax
call unlock_application_table
mov eax, esi
.final:
cmp [SCR_MODE], word 0x13
268,7 → 254,7
pushad
stdcall set_cursor, [handle]
mov [redrawmouse_unconditional], 1
call __sys_draw_pointer
call wakeup_osloop
popad
@@:
ret
550,7 → 536,7
 
xor edx, edx
push edx
mov eax, 0x2
mov eax, 0x1
mov ebx, [pg_dir]
.loop:
;eax = current slot of process
898,20 → 884,8
mov [app_path], eax
;mov esi,new_process_loading
;call sys_msg_board_str
.wait_lock:
cmp [application_table_status], 0
je .get_lock
call change_task
jmp .wait_lock
call lock_application_table
 
.get_lock:
mov eax, 1
xchg eax, [application_table_status]
test eax, eax
jnz .wait_lock
 
call set_application_table_status
 
call get_new_process_place
test eax, eax
jz .failed
967,14 → 941,13
 
;mov esi,new_process_running
;call sys_msg_board_str ;output information about succefull startup
xor eax, eax
mov [application_table_status], eax ;unlock application_table_status mutex
mov eax, [process_number] ;set result
call unlock_application_table
ret
.failed:
xor eax, eax
.failed1:
mov [application_table_status], eax
call unlock_application_table
dec eax ;-1
ret
endp
1148,6 → 1121,7
mov eax, [esi+0x08] ;app_eip
mov [ebx+REG_EIP], eax;app_entry
mov [ebx+REG_CS], dword app_code
mov ecx, USER_PRIORITY
mov eax, [CURRENT_TASK]
shl eax, 8 ; created by kernel?
cmp [SLOT_BASE+eax+APPDATA.dir_table], sys_pgdir - OS_BASE
1155,6 → 1129,7
cmp [app_path], 0 ; it is a thread?
jnz @f
mov [ebx+REG_CS], dword os_code ; kernel thread
mov ecx, MAX_PRIORITY
@@:
mov [ebx+REG_EFLAGS], dword EFL_IOPL1+EFL_IF
 
1162,20 → 1137,22
mov [ebx+REG_APP_ESP], eax;app_stack
mov [ebx+REG_SS], dword app_data
 
lea ecx, [ebx+REG_RET]
lea edx, [ebx+REG_RET]
mov ebx, [slot]
shl ebx, 5
mov [ebx*8+SLOT_BASE+APPDATA.saved_esp], ecx
mov [ebx*8+SLOT_BASE+APPDATA.saved_esp], edx
 
xor ecx, ecx; process state - running
xor edx, edx; process state - running
; set if debuggee
test byte [flags], 1
jz .no_debug
inc ecx ; process state - suspended
inc edx ; process state - suspended
mov eax, [CURRENT_TASK]
mov [SLOT_BASE+ebx*8+APPDATA.debugger_slot], eax
.no_debug:
mov [CURRENT_TASK+ebx+TASKDATA.state], cl
mov [CURRENT_TASK+ebx+TASKDATA.state], dl
lea edx, [SLOT_BASE+ebx*8]
call scheduler_add_thread
;mov esi,new_process_running
;call sys_msg_board_str ;output information about succefull startup
ret
/kernel/branches/Kolibri-acpi/core/timers.inc
203,3 → 203,28
call unlock_timer_list
; 4. Return.
ret
 
; This is a simplified version of check_timers that does not call anything,
; just checks whether check_timers should do something.
proc check_timers_has_work?
pushf
cli
mov eax, [timer_list+TIMER.Next]
.loop:
cmp eax, timer_list
jz .done_nowork
mov edx, [timer_ticks]
sub edx, [eax+TIMER.Time]
jns .done_haswork
mov eax, [eax+TIMER.Next]
jmp .loop
.done_nowork:
popf
xor eax, eax
ret
.done_haswork:
popf
xor eax, eax
inc eax
ret
endp
/kernel/branches/Kolibri-acpi/core/v86.inc
178,7 → 178,7
; 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
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]
/kernel/branches/Kolibri-acpi/data16.inc
13,9 → 13,9
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:
/kernel/branches/Kolibri-acpi/data32.inc
49,43 → 49,43
 
 
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 '®¯ë⪠ ¨­¨æ¨ «¨§ æ¨¨ APIC',0
boot_enableirq db '‚ª«îç¨âì ¯à¥à뢠­¨ï 2, 6, 13, 14, 15',0
boot_enablint_ide db ' §à¥è¥­¨¥ ¯à¥à뢠­¨© ¢ ª®­â஫«¥à¥ IDE',0
boot_detectfloppy db '®¨áª floppy ¤¨áª®¢®¤®¢',0
boot_detecthdcd db '®¨áª ¦¥áâª¨å ¤¨áª®¢ ¨ ATAPI ¯à¨¢®¤®¢',0
boot_getcache db '®«ã祭¨¥ ¯ ¬ï⨠¤«ï ªíè ',0
boot_detectpart db '®¨áª à §¤¥«®¢ ­  ¤¨áª®¢ëå ãáâனá⢠å',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 '®¨áª ãáâனáâ¢',0
boot_timer db '“áâ ­®¢ª  â ©¬¥à ',0
boot_irqs db '¥à¥®¯à¥¤¥«¥­¨¥ IRQ',0
boot_setmouse db '“áâ ­®¢ª  ¬ëè¨',0
boot_windefs db '“áâ ­®¢ª  ­ áâ஥ª ®ª®­ ¯® 㬮«ç ­¨î',0
boot_bgr db '“áâ ­®¢ª  ä®­ ',0
boot_resirqports db '¥§¥à¢¨à®¢ ­¨¥ 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'
145,6 → 145,7
szHwMouse db 'ATI2D',0
szPS2MDriver db 'PS2MOUSE',0
;szCOM_MDriver db 'COM_MOUSE',0
szVidintel db 'vidintel',0
szUSB db 'USB',0
szAtiHW db '/rd/1/drivers/ati2d.drv',0
 
158,7 → 159,7
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
332,8 → 333,9
mem_used_list rd 64*2
mem_hash_cnt rd 64
 
MEM_AMOUNT rd 1
 
cpu_freq rq 1
 
heap_mutex MUTEX
heap_size rd 1
heap_free rd 1
363,6 → 365,35
_WinMapAddress rd 1
_WinMapSize rd 1
 
Screen_Max_X rd 1
Screen_Max_Y rd 1
 
LFBAddress rd 1
BytesPerScanLine rd 1
SCR_MODE rw 2
 
PUTPIXEL rd 1
GETPIXEL rd 1
 
BgrDrawMode rd 1
BgrDataWidth rd 1
BgrDataHeight rd 1
REDRAW_BACKGROUND rb 4
 
MOUSE_PICTURE rd 1
 
MOUSE_SCROLL_H rw 1
MOUSE_X: rw 1
MOUSE_Y: rw 1
MOUSE_SCROLL_V rw 1
 
X_UNDER rw 1
Y_UNDER rw 1
COLOR_TEMP rd 1
MOUSE_COLOR_MEM rd 1
 
BTN_DOWN: rb 4
 
def_cursor rd 1
def_cursor_clock rd 1
current_cursor rd 1
394,9 → 425,22
 
current_slot rd 1
 
BTN_ADDR rd 1
BTN_COUNT rb 4
BTN_BUFF rd 255
 
KEY_COUNT rb 4
KEY_BUFF rb 128
 
mouseunder rd 16*24
 
SYS_SHUTDOWN rb 4
 
 
; status
hd1_status rd 1 ; 0 - free : other - pid
application_table_status rd 1 ; 0 - free : other - pid
application_table_owner rd 1 ; 0 - free : other - pid
application_table_mutex MUTEX
 
; device addresses
mididp rd 1
501,4 → 545,30
BiosDiskCaches rb 80h*(cache_ide1-cache_ide0)
BiosDiskPartitions rd 80h
 
align 4096
 
SLOT_BASE: rb 64*1024
DRIVE_DATA: rb 64*1024
RESERVED_PORTS: rb 64*1024
FLOPPY_BUFF: rb 16*1024
BUTTON_INFO: rb 16*1024
BgrAuxTable: rb 32*1024
skin_data: rb 32*1024
 
;IDE_DMA: rb 32*1024
 
window_data: rb 8192
CURRENT_TASK: rb 8192
draw_data: rb 4096
WIN_STACK: rb 0x400
WIN_POS: rb 0x800
 
CDDataBuf: rb 4096
 
idts rq 0x41
 
RAMDISK_FAT: rb 2856*2 +16
 
FLOPPY_FAT: rb 2856*2 +16
 
IncludeUGlobals
/kernel/branches/Kolibri-acpi/data32sp.inc
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
/kernel/branches/Kolibri-acpi/detect/biosdisk.inc
7,6 → 7,7
 
; Detect all BIOS hard drives.
; diamond, 2008
; Do not include USB mass storages. CleverMouse, 2013
 
xor cx, cx
mov es, cx
24,21 → 25,40
test ah, ah
jz bddc
inc cx
; We are going to call int 13h/func 48h, Extended get drive parameters.
; The latest version of the EDD specification is 3.0.
; There are two slightly incompatible variants for version 3.0;
; original one from Phoenix in 1998, see e.g.
; http://www.t10.org/t13/technical/d98120r0.pdf, and T13 draft,
; http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf
; T13 draft addresses more possible buses, so it gives additional 8 bytes
; for device path.
; Most BIOSes follow Phoenix, but T13 version is also known to be used
; (e.g. systems based on AMD Geode).
; Fortunately, there is an in/out length field, so
; it is easy to tell what variant was selected by the BIOS:
; Phoenix-3.0 has 42h bytes, T13-3.0 has 4Ah bytes.
; Note that 2.0 has 1Eh bytes, 1.1 has 1Ah bytes; both variants of 3.0 have
; the same structure for first 1Eh bytes, compatible with previous versions.
; Note also that difference between Phoenix-3.0 and T13-3.0 starts near the
; end of the structure, so the current code doesn't even need to distinguish.
; It needs, however, give at least 4Ah bytes as input and expect that BIOS
; could return 42h bytes as output while still giving all the information.
mov ah, 48h
push ds
push es
pop ds
mov si, 0xA000
mov word [si], 1Eh
mov word [si], 4Ah
mov ah, 48h
int 13h
pop ds
jc bddc2
inc byte [es:0x907F]
cmp word [es:si], 1Eh
jb bddl
jb .noide
cmp word [es:si+1Ah], 0xFFFF
jz bddl
jz .noide
inc byte [es:0x907F]
mov al, dl
stosb
push ds
61,7 → 81,15
stosw
pop ds
jmp bddc2
bddl:
.noide:
cmp word [es:si], 42h
jb .nousb
cmp word [es:si+28h], 'US'
jnz .nousb
cmp byte [es:si+2Ah], 'B'
jz bddc2
.nousb:
inc byte [es:0x907F]
mov al, dl
stosb
xor ax, ax
/kernel/branches/Kolibri-acpi/detect/dev_fd.inc
9,9 → 9,9
 
 
;***************************************************
; ïðåäâàðèòåëüíàÿ î÷èñòêà îáëàñòè òàáëèöû
; ïîèñê è çàíåñåíèå â òàáëèöó ïðèâîäîâ FDD
; àâòîð Mario79
; предварительная очистка области таблицы
; поиск и занесение в таблицу приводов FDD
; автор Mario79
;***************************************************
xor eax, eax
mov edi, DRIVE_DATA
/kernel/branches/Kolibri-acpi/detect/dev_hdcd.inc
9,13 → 9,13
 
 
;******************************************************
; ïîèñê ïðèâîäîâ HDD è CD
; àâòîð èñõîäíîãî òåêñòà Êóëàêîâ Âëàäèìèð Ãåííàäüåâè÷.
; àäàïòàöèÿ è äîðàáîòêà Mario79
; поиск приводов HDD и CD
; автор исходного текста Кулаков Владимир Геннадьевич.
; адаптация и доработка Mario79
;******************************************************
 
;****************************************************
;* ÏÎÈÑÊ HDD è CD *
;* ПОИСК HDD и CD *
;****************************************************
FindHDD:
mov [ChannelNumber], 1
71,54 → 71,54
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
 
 
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
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
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
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
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
ret
@@Err4_2:
mov [DevErrorCode], 4
; Çàïèñàòü êîä îøèáêè
; Записать код ошибки
ret
 
EndFindHDD:
/kernel/branches/Kolibri-acpi/detect/sear_par.inc
9,9 → 9,9
 
 
;****************************************************
; ïîèñê ëîãè÷åñêèõ äèñêîâ íà îáíàðóæåííûõ HDD
; è çàíåñåíèå äàííûõ â îáëàñòü òàáëèöû
; àâòîð Mario79
; поиск логических дисков на обнаруженных HDD
; и занесение данных в область таблицы
; автор Mario79
;****************************************************
mov [transfer_adress], DRIVE_DATA+0xa
search_partitions_ide0:
/kernel/branches/Kolibri-acpi/docs/drivers_api.txt
13,12 → 13,14
more DiskMediaChanged, then optionally DiskDel. The driver must not call
two functions in parallel, including two calls to DiskMediaChanged.
 
void* stdcall DiskAdd(DISKFUNC* functions, const char* name, void* userdata,
int flags); ; The pointer 'functions' must be valid at least until the disk
will be deleted ; (until DISKFUNC.close is called). ; The pointer 'name' can
be invalid after this function returns. ; It should point to ASCIIZ-string
without leading '/' in latin lowercase and ; digits, like 'usbhd0'. ; The
value 'userdata' is any pointer-sized data, passed as is to all ; callbacks.
void* DiskAdd(DISKFUNC* functions, const char* name, void* userdata, int flags);
; The pointer 'functions' must be valid at least until the disk will be deleted
; (until DISKFUNC.close is called).
; The pointer 'name' can be invalid after this function returns.
; It should point to ASCIIZ-string without leading '/' in latin lowercase and
; digits, like 'usbhd0'.
; The value 'userdata' is any pointer-sized data, passed as is to all
; callbacks.
DISK_NO_INSERT_NOTIFICATION = 1
; The bitfield 'flags' has currently only one bit defined. If it is set, the
; driver will never call DiskMediaChanged(hDisk, true), so the kernel must scan
27,13 → 29,13
{
.strucsize dd ?
.close dd ?
; void stdcall (*close)(void* userdata);
; void close(void* userdata);
; Optional.
; The last function that is called for the given disk. The kernel calls it when
; the kernel has finished all operations with the disk and it is safe to free
; all driver-specific data identified by 'userdata'.
.closemedia dd ?
; void stdcall (*closemedia)(void* userdata);
; void closemedia(void* userdata);
; Optional.
; The kernel calls this function when it finished all processing with the
; current media. If media is removed, the driver should decline all requests
41,25 → 43,25
; until this function is called. If media is removed, a new call to
; DiskMediaChanged(hDisk, true) is not allowed until this function is called.
.querymedia dd ?
; int stdcall (*querymedia)(void* userdata, DISKMEDIAINFO* info);
; int querymedia(void* userdata, DISKMEDIAINFO* info);
; return value: 0 = success, otherwise = error
.read dd ?
; int stdcall (*read)(void* userdata, void* buffer, __int64 startsector,
; int read(void* userdata, void* buffer, __int64 startsector,
; int* numsectors);
; return value: 0 = success, otherwise = error
.write dd ?
; int stdcall (*write)(void* userdata, const void* buffer, __int64 startsector,
; int write(void* userdata, const void* buffer, __int64 startsector,
; int* numsectors);
; Optional.
; return value: 0 = success, otherwise = error
.flush dd ?
; int stdcall (*flush)(void* userdata);
; int flush(void* userdata);
; Optional.
; Flushes the hardware cache, if it exists. Note that a driver should not
; implement a software cache for read/write, since they are called from the
; kernel cache manager.
.adjust_cache_size dd ?
; unsigned int stdcall (*adjust_cache_size)(unsigned int suggested_size);
; unsigned int adjust_cache_size(unsigned int suggested_size);
; Optional.
; Returns the cache size for this device in bytes. 0 = disable cache.
}
/kernel/branches/Kolibri-acpi/docs/loader_doc.txt
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/
 
 
/kernel/branches/Kolibri-acpi/docs/sysfuncr.txt
5,2667 → 5,2667
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
‘ˆ‘’…Œ›… ”“Š–ˆˆ Ž…€–ˆŽŽ‰ ‘ˆ‘’…Œ› Kolibri 0.7.7.0
СИСТЕМНЫЕ ФУНКЦИИ ОПЕРАЦИОННОЙ СИСТЕМЫ Kolibri 0.7.7.0
 
®¬¥à ä㭪樨 ¯®¬¥é ¥âáï ¢ ॣ¨áâà eax.
‚맮¢ á¨á⥬­®© ä㭪樨 ®áãé¥á⢫ï¥âáï ª®¬ ­¤®© "int 0x40".
‚ᥠॣ¨áâàë, ªà®¬¥ ® 㪠§ ­­ëå ¢ ¢®§¢à é ¥¬®¬ §­ ç¥­¨¨,
¢ª«îç ï ॣ¨áâà ä« £®¢ eflags, á®åà ­ïîâáï.
Номер функции помещается в регистр eax.
Вызов системной функции осуществляется командой "int 0x40".
Все регистры, кроме явно указанных в возвращаемом значении,
включая регистр флагов eflags, сохраняются.
 
 
======================================================================
============== ”ã­ªæ¨ï 0 - ®¯à¥¤¥«¨âì ¨ ­ à¨á®¢ âì ®ª­®. =============
============== Функция 0 - определить и нарисовать окно. =============
======================================================================
Ž¯à¥¤¥«ï¥â ®ª­® ¯à¨«®¦¥­¨ï. ¨áã¥â à ¬ªã ®ª­ , § £®«®¢®ª ¨ à ¡®çãî
®¡« áâì. „«ï ®ª®­ ᮠ᪨­®¬ ®¯à¥¤¥«ï¥â áâ ­¤ àâ­ë¥ ª­®¯ª¨ § ªàëâ¨ï ¨
¬¨­¨¬¨§ æ¨¨.
 à ¬¥âàë:
* eax = 0 - ­®¬¥à ä㭪樨
* ebx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ x]
* ecx = [ª®®à¤¨­ â  ¯® ®á¨ y]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = 0xXYRRGGBB, £¤¥:
* Y = áâ¨«ì ®ª­ :
* Y=0 - ⨯ I - ®ª­® 䨪á¨à®¢ ­­ëå à §¬¥à®¢
* Y=1 - ⮫쪮 ®¯à¥¤¥«¨âì ®¡« áâì ®ª­ , ­¨ç¥£® ­¥ à¨á®¢ âì
* Y=2 - ⨯ II - ®ª­® ¨§¬¥­ï¥¬ëå à §¬¥à®¢
* Y=3 - ®ª­® ᮠ᪨­®¬
* Y=4 - ®ª­® ᮠ᪨­®¬ 䨪á¨à®¢ ­­ëå à §¬¥à®¢
* ®áâ «ì­ë¥ ¢®§¬®¦­ë¥ §­ ç¥­¨ï (®â 5 ¤® 15) § à¥§¥à¢¨à®¢ ­ë,
¢ë§®¢ ä㭪樨 á â ª¨¬¨ Y ¨£­®à¨àã¥âáï
* RR, GG, BB = ᮮ⢥âá⢥­­® ªà á­ ï, §¥«¥­ ï, ᨭïï
á®áâ ¢«ïî騥 æ¢¥â  à ¡®ç¥© ®¡« á⨠®ª­ 
(¨£­®à¨àã¥âáï ¤«ï á⨫ï Y=2)
* X = DCBA (¡¨âë)
* A = 1 - ã ®ª­  ¥áâì § £®«®¢®ª; ¤«ï á⨫¥© Y=3,4  ¤à¥á áâப¨
§ £®«®¢ª  § ¤ ñâáï ¢ edi, ¤«ï ¯à®ç¨å á⨫¥©
¨á¯®«ì§ã¥âáï ¯®¤äã­ªæ¨ï 1 ä㭪樨 71
* B = 1 - ª®®à¤¨­ âë ¢á¥å £à ä¨ç¥áª¨å ¯à¨¬¨â¨¢®¢ § ¤ îâáï
®â­®á¨â¥«ì­® ª«¨¥­â᪮© ®¡« á⨠®ª­ 
* C = 1 - ­¥ § ªà è¨¢ âì à ¡®çãî ®¡« áâì ¯à¨ ®âà¨á®¢ª¥ ®ª­ 
* D = 0 - ­®à¬ «ì­ ï § «¨¢ª  à ¡®ç¥© ®¡« áâ¨, 1 - £à ¤¨¥­â­ ï
‘«¥¤ãî騥 ¯ à ¬¥âàë ¯à¥¤­ §­ ç¥­ë ¤«ï ®ª®­ ⨯  I ¨ II ¨
¨£­®à¨àãîâáï ¤«ï á⨫¥© Y=1,3:
* esi = 0xXYRRGGBB - 梥⠧ £®«®¢ª 
* RR, GG, BB ®¯à¥¤¥«ïîâ á ¬ 梥â
* Y=0 - ®¡ëç­®¥ ®ª­®, Y=1 - ­¥¯¥à¥¬¥é ¥¬®¥ ®ª­®
* X ®¯à¥¤¥«ï¥â £à ¤¨¥­â § £®«®¢ª : X=0 - ­¥â £à ¤¨¥­â ,
X=8 - ®¡ëç­ë© £à ¤¨¥­â,
¤«ï ®ª®­ ⨯  II X=4 - ­¥£ â¨¢­ë© £à ¤¨¥­â
* ¯à®ç¨¥ §­ ç¥­¨ï X ¨ Y § à¥§¥à¢¨à®¢ ­ë
* edi = 0x00RRGGBB - 梥â à ¬ª¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ®«®¦¥­¨¥ ¨ à §¬¥àë ®ª­  ãáâ ­ ¢«¨¢ îâáï ¯à¨ ¯¥à¢®¬ ¢ë§®¢¥
í⮩ ä㭪樨 ¨ ¨£­®à¨àãîâáï ¯à¨ ¯®á«¥¤ãîé¨å; ¤«ï ¨§¬¥­¥­¨ï
¯®«®¦¥­¨ï ¨/¨«¨ à §¬¥à®¢ 㦥 ᮧ¤ ­­®£® ®ª­  ¨á¯®«ì§ã©â¥
67-î äã­ªæ¨î.
* „«ï ®ª®­ á⨫¥© Y=3,4 á § £®«®¢ª®¬ (A=1) áâப  § £®«®¢ª 
ãáâ ­ ¢«¨¢ ¥âáï ¯à¨ ¯¥à¢®¬ ¢ë§®¢¥ í⮩ ä㭪樨 ¨ ¨£­®à¨àã¥âáï ¯à¨
¯®á«¥¤ãîé¨å (â®ç­¥¥ £®¢®àï, ¨£­®à¨àã¥âáï ¯®á«¥ ¢ë§®¢ 
¯®¤ä㭪樨 2 ä㭪樨 12 - ª®­æ  ¯¥à¥à¨á®¢ª¨);
¤«ï ¨§¬¥­¥­¨ï áâப¨ § £®«®¢ª  㦥 ᮧ¤ ­­®£® ®ª­  ¨á¯®«ì§ã©â¥
¯®¤äã­ªæ¨î 1 ä㭪樨 71.
* …᫨ ¨á¯®«ì§®¢ âì ®ª­  ᮮ⢥âáâ¢ãîé¨å á⨫¥©, â® ¯®«®¦¥­¨¥
¨/¨«¨ à §¬¥àë ®ª­  ¬®£ãâ ¬¥­ïâìáï ¯®«ì§®¢ â¥«¥¬.
’¥ªã騥 ¯®«®¦¥­¨¥ ¨ à §¬¥àë ¬®£ãâ ¡ëâì ¯®«ãç¥­ë ¢ë§®¢®¬ ä㭪樨 9.
* Žª­® ¤®«¦­® 㬥é âìáï ­  íªà ­¥. …᫨ ¯¥à¥¤ ­­ë¥ ª®®à¤¨­ âë
¨ à §¬¥àë ­¥ 㤮¢«¥â¢®àïîâ í⮬ã ãá«®¢¨î, ⮠ᮮ⢥âáâ¢ãîé ï
ª®®à¤¨­ â  (¨«¨, ¢®§¬®¦­®, ®¡¥) áç¨â ¥âáï ­ã«¥¬,   ¥á«¨ ¨ íâ®
­¥ ¯®¬®£ ¥â, ⮠ᮮ⢥âáâ¢ãî騩 à §¬¥à (¨«¨, ¢®§¬®¦­®, ®¡ )
ãáâ ­ ¢«¨¢ ¥âáï ¢ à §¬¥à íªà ­ .
Определяет окно приложения. Рисует рамку окна, заголовок и рабочую
область. Для окон со скином определяет стандартные кнопки закрытия и
минимизации.
Параметры:
* eax = 0 - номер функции
* ebx = [координата по оси x]*65536 + [размер по оси x]
* ecx = [координата по оси y]*65536 + [размер по оси y]
* edx = 0xXYRRGGBB, где:
* Y = стиль окна:
* Y=0 - тип I - окно фиксированных размеров
* Y=1 - только определить область окна, ничего не рисовать
* Y=2 - тип II - окно изменяемых размеров
* Y=3 - окно со скином
* Y=4 - окно со скином фиксированных размеров
* остальные возможные значения (от 5 до 15) зарезервированы,
вызов функции с такими Y игнорируется
* RR, GG, BB = соответственно красная, зеленая, синяя
составляющие цвета рабочей области окна
(игнорируется для стиля Y=2)
* X = DCBA (биты)
* A = 1 - у окна есть заголовок; для стилей Y=3,4 адрес строки
заголовка задаётся в edi, для прочих стилей
используется подфункция 1 функции 71
* B = 1 - координаты всех графических примитивов задаются
относительно клиентской области окна
* C = 1 - не закрашивать рабочую область при отрисовке окна
* D = 0 - нормальная заливка рабочей области, 1 - градиентная
Следующие параметры предназначены для окон типа I и II и
игнорируются для стилей Y=1,3:
* esi = 0xXYRRGGBB - цвет заголовка
* RR, GG, BB определяют сам цвет
* Y=0 - обычное окно, Y=1 - неперемещаемое окно
* X определяет градиент заголовка: X=0 - нет градиента,
X=8 - обычный градиент,
для окон типа II X=4 - негативный градиент
* прочие значения X и Y зарезервированы
* edi = 0x00RRGGBB - цвет рамки
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Положение и размеры окна устанавливаются при первом вызове
этой функции и игнорируются при последующих; для изменения
положения и/или размеров уже созданного окна используйте
67-ю функцию.
* Для окон стилей Y=3,4 с заголовком (A=1) строка заголовка
устанавливается при первом вызове этой функции и игнорируется при
последующих (точнее говоря, игнорируется после вызова
подфункции 2 функции 12 - конца перерисовки);
для изменения строки заголовка уже созданного окна используйте
подфункцию 1 функции 71.
* Если использовать окна соответствующих стилей, то положение
и/или размеры окна могут меняться пользователем.
Текущие положение и размеры могут быть получены вызовом функции 9.
* Окно должно умещаться на экране. Если переданные координаты
и размеры не удовлетворяют этому условию, то соответствующая
координата (или, возможно, обе) считается нулем, а если и это
не помогает, то соответствующий размер (или, возможно, оба)
устанавливается в размер экрана.
„ «¥¥ ®¡®§­ ç¨¬ xpos,ypos,xsize,ysize - §­ ç¥­¨ï, ¯¥à¥¤ ¢ ¥¬ë¥
¢ ebx,ecx. Š®®à¤¨­ âë ¯à¨¢®¤ïâáï ®â­®á¨â¥«ì­® «¥¢®£® ¢¥àå­¥£®
㣫  ®ª­ , ª®â®àë©, â ª¨¬ ®¡à §®¬, § ¤ ¥âáï ª ª (0,0), ª®®à¤¨­ âë
¯à ¢®£® ­¨¦­¥£® 㣫  áãâì (xsize,ysize).
*  §¬¥àë ®ª­  ¯®­¨¬ îâáï ¢ á¬ëá«¥ ª®®à¤¨­ â ¯à ¢®£® ­¨¦­¥£® 㣫 .
â® ¦¥ ®â­®á¨âáï ¨ ª® ¢á¥¬ ®áâ «ì­ë¬ äã­ªæ¨ï¬.
â® ®§­ ç ¥â, ç⮠ॠ«ì­ë¥ à §¬¥àë ­  1 ¯¨ªá¥«ì ¡®«ìè¥.
* ‚¨¤ ®ª­  ⨯  I:
* à¨áã¥âáï ¢­¥è­ïï à ¬ª  梥â , 㪠§ ­­®£® ¢ edi,
è¨à¨­®© 1 ¯¨ªá¥«ì
* à¨áã¥âáï § £®«®¢®ª - ¯àאַ㣮«ì­¨ª á «¥¢ë¬ ¢¥àå­¨¬ 㣫®¬ (1,1)
¨ ¯à ¢ë¬ ­¨¦­¨¬ (xsize-1,min(25,ysize)) 梥â , 㪠§ ­­®£® ¢ esi
(á ãç¥â®¬ £à ¤¨¥­â )
* ¥á«¨ ysize>=26, â® § ªà è¨¢ ¥âáï à ¡®ç ï ®¡« áâì ®ª­  -
¯àאַ㣮«ì­¨ª á «¥¢ë¬ ¢¥àå­¨¬ 㣫®¬ (1,21) ¨ ¯à ¢ë¬ ­¨¦­¨¬
(xsize-1,ysize-1) (à §¬¥à ¬¨ (xsize-1)*(ysize-21)) - 梥⮬,
㪠§ ­­ë¬ ¢ edx (á ãç¥â®¬ £à ¤¨¥­â )
* ¥á«¨ A=1 ¨ áâப  § £®«®¢ª  ãáâ ­®¢«¥­  ¯®¤ä㭪樥© 1
ä㭪樨 71, â® ®­  ¢ë¢®¤¨âáï ¢ ᮮ⢥âáâ¢ãî饬 ¬¥á⥠§ £®«®¢ª 
* ‚¨¤ ®ª­  á⨫ï Y=1:
* ¯®«­®áâìî ®¯à¥¤¥«ï¥âáï ¯à¨«®¦¥­¨¥¬
* ‚¨¤ ®ª­  ⨯  II:
* à¨áã¥âáï ¢­¥è­ïï à ¬ª  è¨à¨­®© 1 ¯¨ªá¥«ì "§ â¥­ñ­­®£®" 梥â 
edi (¢á¥ á®áâ ¢«ïî騥 æ¢¥â  ã¬¥­ìè îâáï ¢ ¤¢  à § )
* à¨áã¥âáï ¯à®¬¥¦ãâ®ç­ ï à ¬ª  è¨à¨­®© 3 ¯¨ªá¥«ï æ¢¥â  edi
* à¨áã¥âáï ¢­ãâ७­ïï à ¬ª  è¨à¨­®© 1 ¯¨ªá¥«ì
"§ â¥­ñ­­®£®" æ¢¥â  edi
* à¨áã¥âáï § £®«®¢®ª - ¯àאַ㣮«ì­¨ª á «¥¢ë¬ ¢¥àå­¨¬ 㣫®¬ (4,4)
¨ ¯à ¢ë¬ ­¨¦­¨¬ (xsize-4,min(20,ysize)) 梥â , 㪠§ ­­®£® ¢ esi
(á ãç¥â®¬ £à ¤¨¥­â )
* ¥á«¨ ysize>=26, â® § ªà è¨¢ ¥âáï à ¡®ç ï ®¡« áâì ®ª­  -
¯àאַ㣮«ì­¨ª á «¥¢ë¬ ¢¥àå­¨¬ 㣫®¬ (5,20) ¨ ¯à ¢ë¬ ­¨¦­¨¬
(xsize-5,ysize-5) - 梥⮬, 㪠§ ­­ë¬ ¢ edx (á ãç¥â®¬ £à ¤¨¥­â )
* ¥á«¨ A=1 ¨ áâப  § £®«®¢ª  ãáâ ­®¢«¥­  ¯®¤ä㭪樥© 1
ä㭪樨 71, â® ®­  ¢ë¢®¤¨âáï ¢ ᮮ⢥âáâ¢ãî饬 ¬¥á⥠§ £®«®¢ª 
* ‚¨¤ ®ª­  ᮠ᪨­®¬:
* à¨áã¥âáï ¢­¥è­ïï à ¬ª  è¨à¨­®© 1 ¯¨ªá¥«ì
æ¢¥â  'outer' ¨§ ᪨­ 
* à¨áã¥âáï ¯à®¬¥¦ãâ®ç­ ï à ¬ª  è¨à¨­®© 3 ¯¨ªá¥«ï
æ¢¥â  'frame' ¨§ ᪨­ 
* à¨áã¥âáï ¢­ãâ७­ïï à ¬ª  è¨à¨­®© 1 ¯¨ªá¥«ì
æ¢¥â  'inner' ¨§ ᪨­ 
* à¨áã¥âáï § £®«®¢®ª (¯® ª à⨭ª ¬ ¨§ ᪨­ ) ¢ ¯àאַ㣮«ì­¨ª¥
Далее обозначим xpos,ypos,xsize,ysize - значения, передаваемые
в ebx,ecx. Координаты приводятся относительно левого верхнего
угла окна, который, таким образом, задается как (0,0), координаты
правого нижнего угла суть (xsize,ysize).
* Размеры окна понимаются в смысле координат правого нижнего угла.
Это же относится и ко всем остальным функциям.
Это означает, что реальные размеры на 1 пиксель больше.
* Вид окна типа I:
* рисуется внешняя рамка цвета, указанного в edi,
шириной 1 пиксель
* рисуется заголовок - прямоугольник с левым верхним углом (1,1)
и правым нижним (xsize-1,min(25,ysize)) цвета, указанного в esi
(с учетом градиента)
* если ysize>=26, то закрашивается рабочая область окна -
прямоугольник с левым верхним углом (1,21) и правым нижним
(xsize-1,ysize-1) (размерами (xsize-1)*(ysize-21)) - цветом,
указанным в edx (с учетом градиента)
* если A=1 и строка заголовка установлена подфункцией 1
функции 71, то она выводится в соответствующем месте заголовка
* Вид окна стиля Y=1:
* полностью определяется приложением
* Вид окна типа II:
* рисуется внешняя рамка шириной 1 пиксель "затенённого" цвета
edi (все составляющие цвета уменьшаются в два раза)
* рисуется промежуточная рамка шириной 3 пикселя цвета edi
* рисуется внутренняя рамка шириной 1 пиксель
"затенённого" цвета edi
* рисуется заголовок - прямоугольник с левым верхним углом (4,4)
и правым нижним (xsize-4,min(20,ysize)) цвета, указанного в esi
(с учетом градиента)
* если ysize>=26, то закрашивается рабочая область окна -
прямоугольник с левым верхним углом (5,20) и правым нижним
(xsize-5,ysize-5) - цветом, указанным в edx (с учетом градиента)
* если A=1 и строка заголовка установлена подфункцией 1
функции 71, то она выводится в соответствующем месте заголовка
* Вид окна со скином:
* рисуется внешняя рамка шириной 1 пиксель
цвета 'outer' из скина
* рисуется промежуточная рамка шириной 3 пикселя
цвета 'frame' из скина
* рисуется внутренняя рамка шириной 1 пиксель
цвета 'inner' из скина
* рисуется заголовок (по картинкам из скина) в прямоугольнике
(0,0) - (xsize,_skinh-1)
* ¥á«¨ ysize>=26, â® § ªà è¨¢ ¥âáï à ¡®ç ï ®¡« áâì ®ª­  -
¯àאַ㣮«ì­¨ª á «¥¢ë¬ ¢¥àå­¨¬ 㣫®¬ (5,_skinh) ¨ ¯à ¢ë¬ ­¨¦­¨¬
(xsize-5,ysize-5) - 梥⮬, 㪠§ ­­ë¬ ¢ edx (á ãç¥â®¬ £à ¤¨¥­â )
* ®¯à¥¤¥«ïîâáï ¤¢¥ áâ ­¤ àâ­ë¥ ª­®¯ª¨: § ªàëâ¨ï ¨ ¬¨­¨¬¨§ æ¨¨
(ᬮâਠäã­ªæ¨î 8)
* ¥á«¨ A=1 ¨ ¢ edi (­¥­ã«¥¢®©) 㪠§ â¥«ì ­  áâப㠧 £®«®¢ª ,
â® ®­  ¢ë¢®¤¨âáï ¢ § £®«®¢ª¥ ¢ ¬¥áâ¥, ®¯à¥¤¥«ï¥¬®¬ ᪨­®¬
* ‡­ ç¥­¨¥ ¯¥à¥¬¥­­®© _skinh ¤®áâ㯭® ª ª १ã«ìâ â ¢ë§®¢ 
¯®¤ä㭪樨 4 ä㭪樨 48
* если ysize>=26, то закрашивается рабочая область окна -
прямоугольник с левым верхним углом (5,_skinh) и правым нижним
(xsize-5,ysize-5) - цветом, указанным в edx (с учетом градиента)
* определяются две стандартные кнопки: закрытия и минимизации
(смотри функцию 8)
* если A=1 и в edi (ненулевой) указатель на строку заголовка,
то она выводится в заголовке в месте, определяемом скином
* Значение переменной _skinh доступно как результат вызова
подфункции 4 функции 48
 
======================================================================
================= ”ã­ªæ¨ï 1 - ¯®áâ ¢¨âì â®çªã ¢ ®ª­¥. ================
================= Функция 1 - поставить точку в окне. ================
======================================================================
 à ¬¥âàë:
* eax = 1 - ­®¬¥à ä㭪樨
* ebx = x-ª®®à¤¨­ â  (®â­®á¨â¥«ì­® ®ª­ )
* ecx = y-ª®®à¤¨­ â  (®â­®á¨â¥«ì­® ®ª­ )
* edx = 0x00RRGGBB - 梥â â®çª¨
edx = 0x01xxxxxx - ¨­¢¥àâ¨à®¢ âì 梥â â®çª¨
(¬« ¤è¨¥ 24 ¡¨â  ¨£­®à¨àãîâáï)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
Параметры:
* eax = 1 - номер функции
* ebx = x-координата (относительно окна)
* ecx = y-координата (относительно окна)
* edx = 0x00RRGGBB - цвет точки
edx = 0x01xxxxxx - инвертировать цвет точки
(младшие 24 бита игнорируются)
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
============== ”ã­ªæ¨ï 2 - ¯®«ãç¨âì ª®¤ ­ ¦ â®© ª« ¢¨è¨. =============
============== Функция 2 - получить код нажатой клавиши. =============
======================================================================
‡ ¡¨à ¥â ª®¤ ­ ¦ â®© ª« ¢¨è¨ ¨§ ¡ãä¥à .
 à ¬¥âàë:
* eax = 2 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ¡ãä¥à ¯ãáâ, ¢®§¢à é ¥âáï eax=1
* ¥á«¨ ¡ãä¥à ­¥¯ãáâ, â® ¢®§¢à é ¥âáï al=0, ah=ª®¤ ­ ¦ â®© ª« ¢¨è¨,
áâ à襥 á«®¢® ॣ¨áâà  eax ®¡­ã«¥­®
* ¥á«¨ ¥áâì "£®àïç ï ª« ¢¨è ", â® ¢®§¢à é ¥âáï
al=2, ah=᪠­ª®¤ ­ ¦ â®© ª« ¢¨è¨ (0 ¤«ï ã¯à ¢«ïîé¨å ª« ¢¨è),
áâ à襥 á«®¢® ॣ¨áâà  eax ᮤ¥à¦¨â á®áâ®ï­¨¥ ã¯à ¢«ïîé¨å ª« ¢¨è
¢ ¬®¬¥­â ­ ¦ â¨ï £®àï祩 ª« ¢¨è¨
‡ ¬¥ç ­¨ï:
* ‘ãé¥áâ¢ã¥â ®¡é¥á¨á⥬­ë© ¡ãä¥à ­ ¦ âëå ª« ¢¨è à §¬¥à®¬ 120 ¡ ©â,
®à£ ­¨§®¢ ­­ë© ª ª ®ç¥à¥¤ì.
* ‘ãé¥áâ¢ã¥â ¥éñ ®¤¨­ ®¡é¥á¨á⥬­ë© ¡ãä¥à ­  120 "£®àïç¨å ª« ¢¨è".
* à¨ ¢ë§®¢¥ í⮩ ä㭪樨 ¯à¨«®¦¥­¨¥¬ á ­¥ ªâ¨¢­ë¬ ®ª­®¬
áç¨â ¥âáï, çâ® ¡ãä¥à ­ ¦ âëå ª« ¢¨è ¯ãáâ.
* ® 㬮«ç ­¨î íâ  äã­ªæ¨ï ¢®§¢à é ¥â ASCII-ª®¤ë; ¯¥à¥ª«îç¨âìáï ­ 
०¨¬ ᪠­ª®¤®¢ (¨ ­ § ¤) ¬®¦­® á ¨á¯®«ì§®¢ ­¨¥¬ ä㭪樨 66.
Ž¤­ ª®, £®àï稥 ª« ¢¨è¨ ¢á¥£¤  ¢®§¢à é îâáï ª ª ᪠­ª®¤ë.
* “§­ âì, ª ª¨¥ ª®¬¡¨­ æ¨¨ ª« ¢¨è ᮮ⢥âáâ¢ãîâ ª ª¨¬ ª®¤ ¬, ¬®¦­®,
§ ¯ãá⨢ ¯à¨«®¦¥­¨ï keyascii ¨ scancode.
* ‘ª ­ª®¤ë ¢®§¢à é îâáï ­¥¯®á।á⢥­­® ª« ¢¨ âãன ¨ 䨪á¨à®¢ ­ë;
ASCII-ª®¤ë ¯®«ãç îâáï á ¨á¯®«ì§®¢ ­¨¥¬ â ¡«¨æ ¯à¥®¡à §®¢ ­¨ï,
ª®â®àë¥ ¬®¦­® ãáâ ­®¢¨âì ¯®¤ä㭪樥© 2 ä㭪樨 21 ¨ ¯à®ç¨â âì
¯®¤ä㭪樥© 2 ä㭪樨 26.
* Š ª á«¥¤á⢨¥, ASCII-ª®¤ë ãç¨â뢠îâ ⥪ãéãî à áª« ¤ªã ª« ¢¨ âãàë
(rus/en) ¢ ®â«¨ç¨¥ ®â ᪠­ª®¤®¢.
* ®áâ㯠¥â ¨­ä®à¬ æ¨ï ⮫쪮 ® â¥å £®àïç¨å ª« ¢¨è å, ª®â®àë¥ ¡ë«¨
®¯à¥¤¥«¥­ë í⨬ ¯®â®ª®¬ ¯®¤ä㭪樥© 4 ä㭪樨 66.
Забирает код нажатой клавиши из буфера.
Параметры:
* eax = 2 - номер функции
Возвращаемое значение:
* если буфер пуст, возвращается eax=1
* если буфер непуст, то возвращается al=0, ah=код нажатой клавиши,
старшее слово регистра eax обнулено
* если есть "горячая клавиша", то возвращается
al=2, ah=сканкод нажатой клавиши (0 для управляющих клавиш),
старшее слово регистра eax содержит состояние управляющих клавиш
в момент нажатия горячей клавиши
Замечания:
* Существует общесистемный буфер нажатых клавиш размером 120 байт,
организованный как очередь.
* Существует ещё один общесистемный буфер на 120 "горячих клавиш".
* При вызове этой функции приложением с неактивным окном
считается, что буфер нажатых клавиш пуст.
* По умолчанию эта функция возвращает ASCII-коды; переключиться на
режим сканкодов (и назад) можно с использованием функции 66.
Однако, горячие клавиши всегда возвращаются как сканкоды.
* Узнать, какие комбинации клавиш соответствуют каким кодам, можно,
запустив приложения keyascii и scancode.
* Сканкоды возвращаются непосредственно клавиатурой и фиксированы;
ASCII-коды получаются с использованием таблиц преобразования,
которые можно установить подфункцией 2 функции 21 и прочитать
подфункцией 2 функции 26.
* Как следствие, ASCII-коды учитывают текущую раскладку клавиатуры
(rus/en) в отличие от сканкодов.
* Поступает информация только о тех горячих клавишах, которые были
определены этим потоком подфункцией 4 функции 66.
 
======================================================================
================ ”ã­ªæ¨ï 3 - ¯®«ãç¨âì á¨á⥬­®¥ ¢à¥¬ï. ===============
================ Функция 3 - получить системное время. ===============
======================================================================
 à ¬¥âàë:
* eax = 3 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0x00SSMMHH, £¤¥ HH:MM:SS = ç áë:¬¨­ãâë:ᥪ㭤ë
* ª ¦¤ë© í«¥¬¥­â ¢®§¢à é ¥âáï ª ª BCD-ç¨á«®, ­ ¯à¨¬¥à,
¤«ï ¢à¥¬¥­¨ 23:59:59 १ã«ìâ â ¡ã¤¥â 0x00595923
‡ ¬¥ç ­¨ï:
* ‘¬®âਠ⠪¦¥ ¯®¤äã­ªæ¨î 9 ä㭪樨 26 - ¯®«ã祭¨¥ ¢à¥¬¥­¨
á ¬®¬¥­â  § ¯ã᪠ á¨á⥬ë; ®­  ¢® ¬­®£¨å á«ãç ïå 㤮¡­¥¥,
¯®áª®«ìªã ¢®§¢à é ¥â ¯à®áâ® DWORD-§­ ç¥­¨¥ áç¥â稪  ¢à¥¬¥­¨.
* ‘¨á⥬­®¥ ¢à¥¬ï ¬®¦­® ãáâ ­®¢¨âì ä㭪樥© 22.
Параметры:
* eax = 3 - номер функции
Возвращаемое значение:
* eax = 0x00SSMMHH, где HH:MM:SS = часы:минуты:секунды
* каждый элемент возвращается как BCD-число, например,
для времени 23:59:59 результат будет 0x00595923
Замечания:
* Смотри также подфункцию 9 функции 26 - получение времени
с момента запуска системы; она во многих случаях удобнее,
поскольку возвращает просто DWORD-значение счетчика времени.
* Системное время можно установить функцией 22.
 
======================================================================
============== ”ã­ªæ¨ï 4 - ¢ë¢¥á⨠áâபã ⥪áâ  ¢ ®ª­®. =============
============== Функция 4 - вывести строку текста в окно. =============
======================================================================
 à ¬¥âàë:
* eax = 4 - ­®¬¥à ä㭪樨
* ebx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
* ecx = 0xXYRRGGBB, £¤¥
* RR, GG, BB § ¤ îâ 梥â ⥪áâ 
* X=ABnn (¡¨âë):
* nn § ¤ ¥â ¨á¯®«ì§ã¥¬ë© èà¨äâ: 0=á¨á⥬­ë© ¬®­®è¨à¨­­ë©,
1=á¨á⥬­ë© èà¨äâ ¯¥à¥¬¥­­®© è¨à¨­ë
* A=0 - ¢ë¢®¤¨âì esi ᨬ¢®«®¢, A=1 - ¢ë¢®¤¨âì ASCIIZ-áâபã
* B=1 - § ªà è¨¢ âì ä®­ 梥⮬ edi
* Y=Cnnn (¡¨âë):
* C=1 ¯¥à¥­ ¯à ¢¨âì ¢ë¢®¤ ¢ ®¡« áâì ¯®«ì§®¢ â¥«ï, § ¤ ­® ¢ edi
* nnn - ­¥ ¨á¯®«ì§ã¥âáï ¢ ⥪ã饬 ¢¨¤¥, ¤®«¦­® ¡ëâì 0 (zero)
* edx = 㪠§ â¥«ì ­  ­ ç «® áâப¨
* esi = ¤«ï A=0 ¤«¨­  áâப¨, ¤®«¦­  ¡ëâì ­¥ ¡®«ìè¥ 255;
¤«ï A=1 ¨£­®à¨àã¥âáï
* edi = 梥⠤«ï § ªà áª¨ ä®­ , ¥á«¨ B=1
* edi = 㪠§ â¥«ì ­  ®¡« áâì ¯®«ì§®¢ â¥«ï, ¥á«¨ C=1
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ¥à¢ë© á¨á⥬­ë© èà¨äâ áç¨â뢠¥âáï ¯à¨ § £à㧪¥ ¨§ ä ©«  char.mt,
¢â®à®© - ¨§ char2.mt.
* Ž¡  èà¨äâ  ¨¬¥îâ ¢ëá®âã 9 ¯¨ªá¥«¥©, è¨à¨­  ¬®­®è¨à¨­­®£® èà¨äâ 
à ¢­  6 ¯¨ªá¥«¥©.
* C=1, £«ã¡¨­  â®çª¨ = 32 ¡¨â , ®¡« áâì ¯®«ì§®¢ â¥«ï ¢ë£«ï¤¨â â ª:
Параметры:
* eax = 4 - номер функции
* ebx = [координата по оси x]*65536 + [координата по оси y]
* ecx = 0xXYRRGGBB, где
* RR, GG, BB задают цвет текста
* X=ABnn (биты):
* nn задает используемый шрифт: 0=системный моноширинный,
1=системный шрифт переменной ширины
* A=0 - выводить esi символов, A=1 - выводить ASCIIZ-строку
* B=1 - закрашивать фон цветом edi
* Y=Cnnn (биты):
* C=1 перенаправить вывод в область пользователя, задано в edi
* nnn - не используется в текущем виде, должно быть 0 (zero)
* edx = указатель на начало строки
* esi = для A=0 длина строки, должна быть не больше 255;
для A=1 игнорируется
* edi = цвет для закраски фона, если B=1
* edi = указатель на область пользователя, если C=1
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Первый системный шрифт считывается при загрузке из файла char.mt,
второй - из char2.mt.
* Оба шрифта имеют высоту 9 пикселей, ширина моноширинного шрифта
равна 6 пикселей.
* C=1, глубина точки = 32 бита, область пользователя выглядит так:
dword Xsize
dword Ysize
®áâ â®ª ®¡« á⨠= Xsize * Y size * 4
* ¥«ì§ï ®¤­®¢à¥¬¥­­® ¨á¯®«ì§®¢ âì B=1 ¨ C=1, ¯®áª®«ìªã ¢ ®¡®¨å
á«ãç ïå ¨á¯®«ì§®¢ ­ ॣ¨áâà edi ¤«ï à §­ëå 楫¥©.
остаток области = Xsize * Y size * 4
* Нельзя одновременно использовать B=1 и C=1, поскольку в обоих
случаях использован регистр edi для разных целей.
======================================================================
========================= ”ã­ªæ¨ï 5 - ¯ ã§ . =========================
========================= Функция 5 - пауза. =========================
======================================================================
‡ ¤¥à¦¨¢ ¥â ¢ë¯®«­¥­¨¥ ¯à®£à ¬¬ë ­  § ¤ ­­®¥ ¢à¥¬ï.
 à ¬¥âàë:
* eax = 5 - ­®¬¥à ä㭪樨
* ebx = ¢à¥¬ï ¢ á®âëå ¤®«ïå ᥪ㭤ë
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ¥à¥¤ ç  ebx=0 ­¥ ¯¥à¥¤ ¥â ã¯à ¢«¥­¨¥ á«¥¤ãî饬㠯à®æ¥ááã ¨
¢®®¡é¥ ­¥ ¯à®¨§¢®¤¨â ­¨ª ª¨å ¤¥©á⢨©. …᫨ ¤¥©á⢨⥫쭮
âॡã¥âáï ¯¥à¥¤ âì ã¯à ¢«¥­¨¥ á«¥¤ãî饬㠯à®æ¥ááã
(§ ª®­ç¨âì ⥪ã騩 ª¢ ­â ¢à¥¬¥­¨), ¨á¯®«ì§ã©â¥ ¯®¤äã­ªæ¨î 1
ä㭪樨 68.
Задерживает выполнение программы на заданное время.
Параметры:
* eax = 5 - номер функции
* ebx = время в сотых долях секунды
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Передача ebx=0 не передает управление следующему процессу и
вообще не производит никаких действий. Если действительно
требуется передать управление следующему процессу
(закончить текущий квант времени), используйте подфункцию 1
функции 68.
 
======================================================================
=============== ”ã­ªæ¨ï 6 - ¯à®ç¨â âì ä ©« á à ¬¤¨áª . ===============
=============== Функция 6 - прочитать файл с рамдиска. ===============
======================================================================
 à ¬¥âàë:
* eax = 6 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨¬ï ä ©« 
* ecx = ­®¬¥à áâ à⮢®£® ¡«®ª , áç¨â ï á 1;
ecx=0 - ç¨â âì á ­ ç «  ä ©«  (â® ¦¥ á ¬®¥, çâ® ¨ ecx=1)
* edx = ç¨á«® ¡«®ª®¢ ¤«ï ç⥭¨ï;
edx=0 - ç¨â âì ®¤¨­ ¡«®ª (â® ¦¥ á ¬®¥, çâ® ¨ edx=1)
* esi = 㪠§ â¥«ì ­  ®¡« áâì ¯ ¬ïâ¨, ªã¤  ¡ã¤ãâ § ¯¨á ­ë ¤ ­­ë¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¤«¨­  ä ©«  ¢ ¡ ©â å, ¥á«¨ ä ©« ãᯥ譮 ¯à®ç¨â ­
* eax = -1, ¥á«¨ ä ©« ­¥ ­ ©¤¥­
‡ ¬¥ç ­¨ï:
* „ ­­ ï äã­ªæ¨ï ï¥âáï ãáâ à¥¢è¥©; äã­ªæ¨ï 70
¯®§¢®«ï¥â ¢ë¯®«­ïâì ⥠¦¥ ¤¥©á⢨ï á à áè¨à¥­­ë¬¨ ¢®§¬®¦­®áâﬨ.
* «®ª = 512 ¡ ©â.
* „«ï ç⥭¨ï ¢á¥£® ä ©«  ¬®¦­® 㪠§ âì § ¢¥¤®¬® ¡®«ì讥 §­ ç¥­¨¥
¢ edx, ­ ¯à¨¬¥à, edx = -1; ­® ¢ í⮬ á«ãç ¥ ¡ã¤ì⥠£®â®¢ë ª ⮬ã,
çâ® ¯à®£à ¬¬  "㯠¤¥â", ¥á«¨ ä ©« ®ª ¦¥âáï ᫨誮¬ ¡®«ì訬
¨ "­¥ ¢«¥§¥â" ¢ ¯ ¬ïâì ¯à®£à ¬¬ë.
* ˆ¬ï ä ©«  ¤®«¦­® ¡ëâì «¨¡® ¢ ä®à¬ â¥ 8+3 ᨬ¢®«®¢
(¯¥à¢ë¥ 8 ᨬ¢®«®¢ - ᮡá⢥­­® ¨¬ï, ¯®á«¥¤­¨¥ 3 - à áè¨à¥­¨¥,
ª®à®âª¨¥ ¨¬¥­  ¨ à áè¨à¥­¨ï ¤®¯®«­ïîâáï ¯à®¡¥« ¬¨),
«¨¡® ¢ ä®à¬ â¥ 8.3 ᨬ¢®«®¢ "FILE.EXT"/"FILE.EX "
(¨¬ï ­¥ ¡®«¥¥ 8 ᨬ¢®«®¢, â®çª , à áè¨à¥­¨¥ 3 ᨬ¢®« ,
¤®¯®«­¥­­®¥ ¯à¨ ­¥®¡å®¤¨¬®á⨠¯à®¡¥« ¬¨).
ˆ¬ï ä ©«  ¤®«¦­® ¡ëâì § ¯¨á ­® § £« ¢­ë¬¨ ¡ãª¢ ¬¨.
‡ ¢¥àè î騩 ᨬ¢®« á ª®¤®¬ 0 ­¥ ­ã¦¥­ (­¥ ASCIIZ-áâப ).
* â  äã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥â ¯ ¯ª¨ ­  à ¬¤¨áª¥.
Параметры:
* eax = 6 - номер функции
* ebx = указатель на имя файла
* ecx = номер стартового блока, считая с 1;
ecx=0 - читать с начала файла (то же самое, что и ecx=1)
* edx = число блоков для чтения;
edx=0 - читать один блок (то же самое, что и edx=1)
* esi = указатель на область памяти, куда будут записаны данные
Возвращаемое значение:
* eax = длина файла в байтах, если файл успешно прочитан
* eax = -1, если файл не найден
Замечания:
* Данная функция является устаревшей; функция 70
позволяет выполнять те же действия с расширенными возможностями.
* Блок = 512 байт.
* Для чтения всего файла можно указать заведомо большое значение
в edx, например, edx = -1; но в этом случае будьте готовы к тому,
что программа "упадет", если файл окажется слишком большим
и "не влезет" в память программы.
* Имя файла должно быть либо в формате 8+3 символов
(первые 8 символов - собственно имя, последние 3 - расширение,
короткие имена и расширения дополняются пробелами),
либо в формате 8.3 символов "FILE.EXT"/"FILE.EX "
(имя не более 8 символов, точка, расширение 3 символа,
дополненное при необходимости пробелами).
Имя файла должно быть записано заглавными буквами.
Завершающий символ с кодом 0 не нужен (не ASCIIZ-строка).
* Эта функция не поддерживает папки на рамдиске.
 
======================================================================
=============== ”ã­ªæ¨ï 7 - ¢ë¢¥á⨠¨§®¡à ¦¥­¨¥ ¢ ®ª­®. ==============
=============== Функция 7 - вывести изображение в окно. ==============
======================================================================
 à ¬¥âàë:
* eax = 7 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨§®¡à ¦¥­¨¥ ¢ ä®à¬ â¥ BBGGRRBBGGRR...
* ecx = [à §¬¥à ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Š®®à¤¨­ âë ¨§®¡à ¦¥­¨ï - íâ® ª®®à¤¨­ âë ¢¥àå­¥£® «¥¢®£® 㣫 
¨§®¡à ¦¥­¨ï ®â­®á¨â¥«ì­® ®ª­ .
*  §¬¥à ¨§®¡à ¦¥­¨ï ¢ ¡ ©â å ¥áâì 3*xsize*ysize.
Параметры:
* eax = 7 - номер функции
* ebx = указатель на изображение в формате BBGGRRBBGGRR...
* ecx = [размер по оси x]*65536 + [размер по оси y]
* edx = [координата по оси x]*65536 + [координата по оси y]
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Координаты изображения - это координаты верхнего левого угла
изображения относительно окна.
* Размер изображения в байтах есть 3*xsize*ysize.
 
======================================================================
=============== ”ã­ªæ¨ï 8 - ®¯à¥¤¥«¨âì/㤠«¨âì ª­®¯ªã. ===============
=============== Функция 8 - определить/удалить кнопку. ===============
======================================================================
 à ¬¥âàë ¤«ï ®¯à¥¤¥«¥­¨ï ª­®¯ª¨:
* eax = 8 - ­®¬¥à ä㭪樨
* ebx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ x]
* ecx = [ª®®à¤¨­ â  ¯® ®á¨ y]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = 0xXYnnnnnn, £¤¥:
* nnnnnn = ¨¤¥­â¨ä¨ª â®à ª­®¯ª¨
* áâ à訩 (31-©) ¡¨â edx á¡à®è¥­
* ¥á«¨ 30-© ¡¨â edx ãáâ ­®¢«¥­ - ­¥ ¯à®à¨á®¢ë¢ âì ª­®¯ªã
* ¥á«¨ 29-© ¡¨â edx ãáâ ­®¢«¥­ - ­¥ à¨á®¢ âì à ¬ªã
¯à¨ ­ ¦ â¨¨ ­  ª­®¯ªã
* esi = 0x00RRGGBB - 梥⠪­®¯ª¨
 à ¬¥âàë ¤«ï 㤠«¥­¨ï ª­®¯ª¨:
* eax = 8 - ­®¬¥à ä㭪樨
* edx = 0x80nnnnnn, £¤¥ nnnnnn - ¨¤¥­â¨ä¨ª â®à ª­®¯ª¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
*  §¬¥àë ª­®¯ª¨ ¤®«¦­ë ¡ëâì ¡®«ìè¥ 0 ¨ ¬¥­ìè¥ 0x8000.
* „«ï ®ª®­ ᮠ᪨­®¬ ¯à¨ ®¯à¥¤¥«¥­¨¨ ®ª­  (¢ë§®¢¥ 0-© ä㭪樨)
ᮧ¤ îâáï ¤¢¥ áâ ­¤ àâ­ë¥ ª­®¯ª¨ - § ªàëâ¨ï ®ª­ 
á ¨¤¥­â¨ä¨ª â®à®¬ 1 ¨ ¬¨­¨¬¨§ æ¨¨ ®ª­  á ¨¤¥­â¨ä¨ª â®à®¬ 0xffff.
* ‘®§¤ ­¨¥ ¤¢ãå ª­®¯®ª á ®¤¨­ ª®¢ë¬¨ ¨¤¥­â¨ä¨ª â®à ¬¨
¢¯®«­¥ ¤®¯ãá⨬®.
* Š­®¯ª  á ¨¤¥­â¨ä¨ª â®à®¬ 0xffff ¯à¨ ­ ¦ â¨¨ ¨­â¥à¯à¥â¨àã¥âáï
á¨á⥬®© ª ª ª­®¯ª  ¬¨­¨¬¨§ æ¨¨, á¨á⥬  ®¡à ¡ â뢠¥â â ª®¥
­ ¦ â¨¥ á ¬®áâ®ï⥫쭮, ­¥ ®¡à é ïáì ª ¯à¨«®¦¥­¨î.
‚ ®áâ «ì­®¬ íâ® ®¡ëç­ ï ª­®¯ª .
* Ž¡é¥¥ ª®«¨ç¥á⢮ ª­®¯®ª ¤«ï ¢á¥å ¯à¨«®¦¥­¨© ®£à ­¨ç¥­®
ç¨á«®¬ 4095.
Параметры для определения кнопки:
* eax = 8 - номер функции
* ebx = [координата по оси x]*65536 + [размер по оси x]
* ecx = [координата по оси y]*65536 + [размер по оси y]
* edx = 0xXYnnnnnn, где:
* nnnnnn = идентификатор кнопки
* старший (31-й) бит edx сброшен
* если 30-й бит edx установлен - не прорисовывать кнопку
* если 29-й бит edx установлен - не рисовать рамку
при нажатии на кнопку
* esi = 0x00RRGGBB - цвет кнопки
Параметры для удаления кнопки:
* eax = 8 - номер функции
* edx = 0x80nnnnnn, где nnnnnn - идентификатор кнопки
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Размеры кнопки должны быть больше 0 и меньше 0x8000.
* Для окон со скином при определении окна (вызове 0-й функции)
создаются две стандартные кнопки - закрытия окна
с идентификатором 1 и минимизации окна с идентификатором 0xffff.
* Создание двух кнопок с одинаковыми идентификаторами
вполне допустимо.
* Кнопка с идентификатором 0xffff при нажатии интерпретируется
системой как кнопка минимизации, система обрабатывает такое
нажатие самостоятельно, не обращаясь к приложению.
В остальном это обычная кнопка.
* Общее количество кнопок для всех приложений ограничено
числом 4095.
 
======================================================================
============= ”ã­ªæ¨ï 9 - ¨­ä®à¬ æ¨ï ® ¯®â®ª¥ ¢ë¯®«­¥­¨ï. ============
============= Функция 9 - информация о потоке выполнения. ============
======================================================================
 à ¬¥âàë:
* eax = 9 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¡ãä¥à à §¬¥à  1 Š¡
* ecx = ­®¬¥à á«®â  ¯®â®ª 
ecx = -1 - ¯®«ãç¨âì ¨­ä®à¬ æ¨î ® ⥪ã饬 ¯®â®ª¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¬ ªá¨¬ «ì­ë© ­®¬¥à á«®â  ¯®â®ª 
* ¡ãä¥à, ­  ª®â®àë© ãª §ë¢ ¥â ebx, ᮤ¥à¦¨â á«¥¤ãîéãî ¨­ä®à¬ æ¨î:
* +0: dword: ¨á¯®«ì§®¢ ­¨¥ ¯à®æ¥áá®à  (᪮«ìª® ⠪⮢ ¢ ᥪ㭤ã
ã室¨â ­  ¨á¯®«­¥­¨¥ ¨¬¥­­® í⮣® ¯®â®ª )
* +4: word: ¯®§¨æ¨ï ®ª­  ¯®â®ª  ¢ ®ª®­­®¬ áâíª¥
* +6: word: (­¥ ¨¬¥¥â ®â­®è¥­¨ï ª § ¯à®è¥­­®¬ã ¯®â®ªã)
­®¬¥à á«®â  ¯®â®ª , ®ª­® ª®â®à®£® ­ å®¤¨âáï ¢ ®ª®­­®¬ áâíª¥
¢ ¯®§¨æ¨¨ ecx
* +8: word: § à¥§¥à¢¨à®¢ ­®
* +10 = +0xA: 11 ¡ ©â: ¨¬ï ¯à®æ¥áá 
(¨¬ï § ¯ã饭­®£® ä ©«  - ¨á¯®«­ï¥¬ë© ä ©« ¡¥§ à áè¨à¥­¨ï)
* +21 = +0x15: byte: § à¥§¥à¢¨à®¢ ­®, íâ®â ¡ ©â ­¥ ¨§¬¥­ï¥âáï
* +22 = +0x16: dword:  ¤à¥á ¯à®æ¥áá  ¢ ¯ ¬ïâ¨
* +26 = +0x1A: dword: à §¬¥à ¨á¯®«ì§ã¥¬®© ¯ ¬ï⨠- 1
* +30 = +0x1E: dword: ¨¤¥­â¨ä¨ª â®à (PID/TID)
* +34 = +0x22: dword: ª®®à¤¨­ â  ®ª­  ¯®â®ª  ¯® ®á¨ x
* +38 = +0x26: dword: ª®®à¤¨­ â  ®ª­  ¯®â®ª  ¯® ®á¨ y
* +42 = +0x2A: dword: à §¬¥à ®ª­  ¯®â®ª  ¯® ®á¨ x
* +46 = +0x2E: dword: à §¬¥à ®ª­  ¯®â®ª  ¯® ®á¨ y
* +50 = +0x32: word: á®áâ®ï­¨¥ á«®â  ¯®â®ª :
* 0 = ¯®â®ª ¢ë¯®«­ï¥âáï
* 1 = ¯®â®ª ¯à¨®áâ ­®¢«¥­
* 2 = ¯®â®ª ¯à¨®áâ ­®¢«¥­ ¢ ¬®¬¥­â ®¦¨¤ ­¨ï ᮡëâ¨ï
* 3 = ¯®â®ª § ¢¥àè ¥âáï ¢ १ã«ìâ â¥ ¢ë§®¢  ä㭪樨 -1 ¨«¨
­ á¨«ìá⢥­­® ª ª á«¥¤á⢨¥ ¢ë§®¢  ¯®¤ä㭪樨 2 ä㭪樨 18
¨«¨ § ¢¥à襭¨ï à ¡®âë á¨á⥬ë
* 4 = ¯®â®ª § ¢¥àè ¥âáï ¢ १ã«ìâ â¥ ¨áª«î祭¨ï
* 5 = ¯®â®ª ®¦¨¤ ¥â ᮡëâ¨ï
* 9 = § ¯à®è¥­­ë© á«®â ᢮¡®¤¥­, ¢áï ®áâ «ì­ ï ¨­ä®à¬ æ¨ï ®
᫮⥠­¥ ¨¬¥¥â á¬ëá« 
* +52 = +0x34: word: § à¥§¥à¢¨à®¢ ­®, íâ® á«®¢® ­¥ ¨§¬¥­ï¥âáï
* +54 = +0x36: dword: ª®®à¤¨­ â  ­ ç «  ª«¨¥­â᪮© ®¡« áâ¨
¯® ®á¨ x
* +58 = +0x3A: dword: ª®®à¤¨­ â  ­ ç «  ª«¨¥­â᪮© ®¡« áâ¨
¯® ®á¨ y
* +62 = +0x3E: dword: è¨à¨­  ª«¨¥­â᪮© ®¡« áâ¨
* +66 = +0x42: dword: ¢ëá®â  ª«¨¥­â᪮© ®¡« áâ¨
* +70 = +0x46: byte: á®áâ®ï­¨¥ ®ª­  - ¡¨â®¢®¥ ¯®«¥
* ¡¨â 0 (¬ áª  1): ®ª­® ¬ ªá¨¬¨§¨à®¢ ­®
* ¡¨â 1 (¬ áª  2): ®ª­® ¬¨­¨¬¨§¨à®¢ ­® ¢ ¯ ­¥«ì § ¤ ç
* ¡¨â 2 (¬ áª  4): ®ª­® á¢ñà­ãâ® ¢ § £®«®¢®ª
* +71 = +0x47: dword: ¬ áª  ᮡë⨩
‡ ¬¥ç ­¨ï:
* ‘«®âë ­ã¬¥àãîâáï á 1.
* ‚®§¢à é ¥¬®¥ §­ ç¥­¨¥ ­¥ ¥áâì ®¡é¥¥ ç¨á«® ¯®â®ª®¢, ¯®áª®«ìªã
¡ë¢ îâ ᢮¡®¤­ë¥ á«®âë.
* à¨ ᮧ¤ ­¨¨ ¯à®æ¥áá   ¢â®¬ â¨ç¥áª¨ ᮧ¤ ¥âáï ¯®â®ª ¢ë¯®«­¥­¨ï.
* ”ã­ªæ¨ï ¢ë¤ ¥â ¨­ä®à¬ æ¨î ® ¯®â®ª¥. Š ¦¤ë© ¯à®æ¥áá ¨¬¥¥â
å®âï ¡ë ®¤¨­ ¯®â®ª. Ž¤¨­ ¯à®æ¥áá ¬®¦¥â ᮧ¤ âì ­¥áª®«ìª® ¯®â®ª®¢,
¢ í⮬ á«ãç ¥ ª ¦¤ë© ¯®â®ª ¯®«ãç ¥â ᢮© á«®â, ¯à¨ç¥¬ ¯®«ï
+10, +22, +26 ¢ íâ¨å á«®â å ᮢ¯ ¤ îâ.
„«ï ¯à¨«®¦¥­¨© ­¥ áãé¥áâ¢ã¥â ®¡é¥£® ᯮᮡ  ®¯à¥¤¥«¨âì,
¯à¨­ ¤«¥¦ â «¨ ¤¢  ¯®â®ª  ®¤­®¬ã ¯à®æ¥ááã.
* €ªâ¨¢­®¥ ®ª­® - ®ª­®, ­ å®¤ï饥áï ­  ¢¥à設¥ ®ª®­­®£® áâíª ,
®­® ¯®«ãç ¥â á®®¡é¥­¨ï ® ¢¢®¤¥ á ª« ¢¨ âãàë. „«ï ­¥£® ¯®§¨æ¨ï ¢
®ª®­­®¬ áâíª¥ ᮢ¯ ¤ ¥â á ¢®§¢à é ¥¬ë¬ §­ ç¥­¨¥¬.
* ‘«®â 1 ᮮ⢥âáâ¢ã¥â ᯥ樠«ì­®¬ã ¯®â®ªã ®¯¥à æ¨®­­®© á¨á⥬ë,
¤«ï ª®â®à®£®:
* ®ª­® ­ å®¤¨âáï ¢­¨§ã ®ª®­­®£® áâíª , ¯®«ï +4 ¨ +6 ᮤ¥à¦ â
§­ ç¥­¨¥ 1
* ¨¬ï ¯à®æ¥áá  - "OS/IDLE" (¤®¯®«­¥­­®¥ ¯à®¡¥« ¬¨)
*  ¤à¥á ¯à®æ¥áá  ¢ ¯ ¬ï⨠ࠢ¥­ 0, à §¬¥à ¨á¯®«ì§ã¥¬®© ¯ ¬ïâ¨
Параметры:
* eax = 9 - номер функции
* ebx = указатель на буфер размера 1 Кб
* ecx = номер слота потока
ecx = -1 - получить информацию о текущем потоке
Возвращаемое значение:
* eax = максимальный номер слота потока
* буфер, на который указывает ebx, содержит следующую информацию:
* +0: dword: использование процессора (сколько тактов в секунду
уходит на исполнение именно этого потока)
* +4: word: позиция окна потока в оконном стэке
* +6: word: (не имеет отношения к запрошенному потоку)
номер слота потока, окно которого находится в оконном стэке
в позиции ecx
* +8: word: зарезервировано
* +10 = +0xA: 11 байт: имя процесса
(имя запущенного файла - исполняемый файл без расширения)
* +21 = +0x15: byte: зарезервировано, этот байт не изменяется
* +22 = +0x16: dword: адрес процесса в памяти
* +26 = +0x1A: dword: размер используемой памяти - 1
* +30 = +0x1E: dword: идентификатор (PID/TID)
* +34 = +0x22: dword: координата окна потока по оси x
* +38 = +0x26: dword: координата окна потока по оси y
* +42 = +0x2A: dword: размер окна потока по оси x
* +46 = +0x2E: dword: размер окна потока по оси y
* +50 = +0x32: word: состояние слота потока:
* 0 = поток выполняется
* 1 = поток приостановлен
* 2 = поток приостановлен в момент ожидания события
* 3 = поток завершается в результате вызова функции -1 или
насильственно как следствие вызова подфункции 2 функции 18
или завершения работы системы
* 4 = поток завершается в результате исключения
* 5 = поток ожидает события
* 9 = запрошенный слот свободен, вся остальная информация о
слоте не имеет смысла
* +52 = +0x34: word: зарезервировано, это слово не изменяется
* +54 = +0x36: dword: координата начала клиентской области
по оси x
* +58 = +0x3A: dword: координата начала клиентской области
по оси y
* +62 = +0x3E: dword: ширина клиентской области
* +66 = +0x42: dword: высота клиентской области
* +70 = +0x46: byte: состояние окна - битовое поле
* бит 0 (маска 1): окно максимизировано
* бит 1 (маска 2): окно минимизировано в панель задач
* бит 2 (маска 4): окно свёрнуто в заголовок
* +71 = +0x47: dword: маска событий
Замечания:
* Слоты нумеруются с 1.
* Возвращаемое значение не есть общее число потоков, поскольку
бывают свободные слоты.
* При создании процесса автоматически создается поток выполнения.
* Функция выдает информацию о потоке. Каждый процесс имеет
хотя бы один поток. Один процесс может создать несколько потоков,
в этом случае каждый поток получает свой слот, причем поля
+10, +22, +26 в этих слотах совпадают.
Для приложений не существует общего способа определить,
принадлежат ли два потока одному процессу.
* Активное окно - окно, находящееся на вершине оконного стэка,
оно получает сообщения о вводе с клавиатуры. Для него позиция в
оконном стэке совпадает с возвращаемым значением.
* Слот 1 соответствует специальному потоку операционной системы,
для которого:
* окно находится внизу оконного стэка, поля +4 и +6 содержат
значение 1
* имя процесса - "OS/IDLE" (дополненное пробелами)
* адрес процесса в памяти равен 0, размер используемой памяти
16 Mb (0x1000000)
* PID=1
* ª®®à¤¨­ âë ¨ à §¬¥àë ®ª­ , à ¢­® ª ª ¨ ª«¨¥­â᪮© ®¡« áâ¨,
ãá«®¢­® ¯®« £ îâáï à ¢­ë¬¨ 0
* á®áâ®ï­¨¥ á«®â  - ¢á¥£¤  0 (¢ë¯®«­ï¥âáï)
* ¢à¥¬ï ¢ë¯®«­¥­¨ï ᪫ ¤ë¢ ¥âáï ¨§ ¢à¥¬¥­¨, ã室ï饣® ­ 
ᮡá⢥­­® à ¡®âã, ¨ ¢à¥¬¥­¨ ¯à®áâ®ï ¢ ®¦¨¤ ­¨¨ ¯à¥à뢠­¨ï
(ª®â®à®¥ ¬®¦­® ¯®«ãç¨âì ¢ë§®¢®¬ ¯®¤ä㭪樨 4 ä㭪樨 18).
*  ç¨­ ï á® á«®â  2, à §¬¥é îâáï ®¡ëç­ë¥ ¯à¨«®¦¥­¨ï.
* Ž¡ëç­ë¥ ¯à¨«®¦¥­¨ï à §¬¥é îâáï ¢ ¯ ¬ï⨠¯®  ¤à¥áã 0
(ª®­áâ ­â  ï¤à  std_application_base_address).
 «®¦¥­¨ï ­¥ ¯à®¨á室¨â, ¯®áª®«ìªã ã ª ¦¤®£® ¯à®æ¥áá  á¢®ï
â ¡«¨æ  áâà ­¨æ.
* à¨ ᮧ¤ ­¨¨ ¯®â®ª  ¥¬ã ­ §­ ç îâáï ᫮⠢ á¨á⥬­®© â ¡«¨æ¥ ¨
¨¤¥­â¨ä¨ª â®à (Process/Thread IDentifier = PID/TID), ª®â®àë¥ ¤«ï
§ ¤ ­­®£® ¯®â®ª  ­¥ ¨§¬¥­ïîâáï á® ¢à¥¬¥­¥¬.
®á«¥ § ¢¥à襭¨ï ¯®â®ª  ¥£® ᫮⠬®¦¥â ¡ëâì § ­®¢® ¨á¯®«ì§®¢ ­
¤«ï ¤à㣮£® ¯®â®ª . ˆ¤¥­â¨ä¨ª â®à ¯®â®ª  ­¥ ¬®¦¥â ¡ëâì ­ §­ ç¥­
¤à㣮¬ã ¯®â®ªã ¤ ¦¥ ¯®á«¥ § ¢¥à襭¨ï ¯¥à¢®£®.
 §­ ç ¥¬ë¥ ­®¢ë¬ ¯®â®ª ¬ ¨¤¥­â¨ä¨ª â®àë ¬®­®â®­­® à áâãâ.
* …᫨ ¯®â®ª ¥é¥ ­¥ ®¯à¥¤¥«¨« ᢮¥ ®ª­® ¢ë§®¢®¬ ä㭪樨 0, â®
¯®«®¦¥­¨¥ ¨ à §¬¥àë í⮣® ®ª­  ¯®« £ îâáï ­ã«ï¬¨.
* Š®®à¤¨­ âë ª«¨¥­â᪮© ®¡« á⨠®ª­  ¡¥àãâáï ®â­®á¨â¥«ì­® ®ª­ .
* ‚ ¤ ­­ë© ¬®¬¥­â ¨á¯®«ì§ã¥âáï ⮫쪮 ç áâì ¡ãä¥à  à §¬¥à®¬
71 = 0x47 ¡ ©â . ’¥¬ ­¥ ¬¥­¥¥ ४®¬¥­¤ã¥âáï ¨á¯®«ì§®¢ âì ¡ãä¥à
à §¬¥à®¬ 1 Š¡ ¤«ï ¡ã¤ã饩 ᮢ¬¥á⨬®áâ¨, ¢ ¡ã¤ã饬 ¬®£ãâ ¡ëâì
¤®¡ ¢«¥­ë ­¥ª®â®àë¥ ¯®«ï.
* координаты и размеры окна, равно как и клиентской области,
условно полагаются равными 0
* состояние слота - всегда 0 (выполняется)
* время выполнения складывается из времени, уходящего на
собственно работу, и времени простоя в ожидании прерывания
(которое можно получить вызовом подфункции 4 функции 18).
* Начиная со слота 2, размещаются обычные приложения.
* Обычные приложения размещаются в памяти по адресу 0
(константа ядра std_application_base_address).
Наложения не происходит, поскольку у каждого процесса своя
таблица страниц.
* При создании потока ему назначаются слот в системной таблице и
идентификатор (Process/Thread IDentifier = PID/TID), которые для
заданного потока не изменяются со временем.
После завершения потока его слот может быть заново использован
для другого потока. Идентификатор потока не может быть назначен
другому потоку даже после завершения первого.
Назначаемые новым потокам идентификаторы монотонно растут.
* Если поток еще не определил свое окно вызовом функции 0, то
положение и размеры этого окна полагаются нулями.
* Координаты клиентской области окна берутся относительно окна.
* В данный момент используется только часть буфера размером
71 = 0x47 байта. Тем не менее рекомендуется использовать буфер
размером 1 Кб для будущей совместимости, в будущем могут быть
добавлены некоторые поля.
 
======================================================================
==================== ”ã­ªæ¨ï 10 - ®¦¨¤ âì ᮡëâ¨ï. ===================
==================== Функция 10 - ожидать события. ===================
======================================================================
…᫨ ®ç¥à¥¤ì á®®¡é¥­¨© ¯ãáâ , â® ¦¤¥â ¯®ï¢«¥­¨ï á®®¡é¥­¨ï ¢ ®ç¥à¥¤¨.
‚ â ª®¬ á®áâ®ï­¨¨ ¯®â®ª ­¥ ¯®«ã砥⠯à®æ¥áá®à­®£® ¢à¥¬¥­¨.
‡ â¥¬ áç¨â뢠¥â á®®¡é¥­¨¥ ¨§ ®ç¥à¥¤¨.
Если очередь сообщений пуста, то ждет появления сообщения в очереди.
В таком состоянии поток не получает процессорного времени.
Затем считывает сообщение из очереди.
 
 à ¬¥âàë:
* eax = 10 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ᮡë⨥ (ᬮâਠᯨ᮪ ᮡë⨩)
‡ ¬¥ç ­¨ï:
* “ç¨â뢠îâáï ⮫쪮 ⥠ᮡëâ¨ï, ª®â®àë¥ ¢å®¤ïâ ¢ ¬ áªã,
ãáâ ­ ¢«¨¢ ¥¬ãî ä㭪樥© 40. ® 㬮«ç ­¨î í⮠ᮡëâ¨ï
¯¥à¥à¨á®¢ª¨, ­ ¦ â¨ï ­  ª« ¢¨è¨ ¨ ­  ª­®¯ª¨.
* „«ï ¯à®¢¥àª¨, ¥áâì «¨ á®®¡é¥­¨¥ ¢ ®ç¥à¥¤¨, ¨á¯®«ì§ã©â¥ äã­ªæ¨î 11.
—â®¡ë ¦¤ âì ­¥ ¡®«¥¥ ®¯à¥¤¥«¥­­®£® ¢à¥¬¥­¨, ¨á¯®«ì§ã©â¥
äã­ªæ¨î 23.
Параметры:
* eax = 10 - номер функции
Возвращаемое значение:
* eax = событие (смотри список событий)
Замечания:
* Учитываются только те события, которые входят в маску,
устанавливаемую функцией 40. По умолчанию это события
перерисовки, нажатия на клавиши и на кнопки.
* Для проверки, есть ли сообщение в очереди, используйте функцию 11.
Чтобы ждать не более определенного времени, используйте
функцию 23.
 
======================================================================
======= ”ã­ªæ¨ï 11 - ¯à®¢¥à¨âì, ¥áâì «¨ ᮡë⨥, ¡¥§ ®¦¨¤ ­¨ï. =======
======= Функция 11 - проверить, есть ли событие, без ожидания. =======
======================================================================
…᫨ ¢ ®ç¥à¥¤¨ á®®¡é¥­¨© ¥áâì ª ª®¥-⮠ᮡë⨥, â® áç¨â뢠¥â ¨
¢®§¢à é ¥â ¥£®. …᫨ ®ç¥à¥¤ì ¯ãáâ , ¢®§¢à é ¥â ­ã«ì.
 à ¬¥âàë:
* eax = 11 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ®ç¥à¥¤ì á®®¡é¥­¨© ¯ãáâ 
* ¨­ ç¥ eax = ᮡë⨥ (ᬮâਠᯨ᮪ ᮡë⨩)
‡ ¬¥ç ­¨ï:
* “ç¨â뢠îâáï ⮫쪮 ⥠ᮡëâ¨ï, ª®â®àë¥ ¢å®¤ïâ ¢ ¬ áªã,
ãáâ ­ ¢«¨¢ ¥¬ãî ä㭪樥© 40. ® 㬮«ç ­¨î í⮠ᮡëâ¨ï
¯¥à¥à¨á®¢ª¨, ­ ¦ â¨ï ­  ª« ¢¨è¨ ¨ ­  ª­®¯ª¨.
* „«ï ®¦¨¤ ­¨ï ¯®ï¢«¥­¨ï ᮡëâ¨ï ¢ ®ç¥à¥¤¨, ¨á¯®«ì§ã©â¥ äã­ªæ¨î 10.
—â®¡ë ¦¤ âì ­¥ ¡®«¥¥ ®¯à¥¤¥«¥­­®£® ¢à¥¬¥­¨, ¨á¯®«ì§ã©â¥
äã­ªæ¨î 23.
Если в очереди сообщений есть какое-то событие, то считывает и
возвращает его. Если очередь пуста, возвращает нуль.
Параметры:
* eax = 11 - номер функции
Возвращаемое значение:
* eax = 0 - очередь сообщений пуста
* иначе eax = событие (смотри список событий)
Замечания:
* Учитываются только те события, которые входят в маску,
устанавливаемую функцией 40. По умолчанию это события
перерисовки, нажатия на клавиши и на кнопки.
* Для ожидания появления события в очереди, используйте функцию 10.
Чтобы ждать не более определенного времени, используйте
функцию 23.
 
======================================================================
=========== ”ã­ªæ¨ï 12 - ­ ç âì/§ ª®­ç¨âì ¯¥à¥à¨á®¢ªã ®ª­ . ==========
=========== Функция 12 - начать/закончить перерисовку окна. ==========
======================================================================
 
-------------- ®¤äã­ªæ¨ï 1 - ­ ç âì ¯¥à¥à¨á®¢ªã ®ª­ . ---------------
 à ¬¥âàë:
* eax = 12 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
-------------- Подфункция 1 - начать перерисовку окна. ---------------
Параметры:
* eax = 12 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
 
------------- ®¤äã­ªæ¨ï 2 - § ª®­ç¨âì ¯¥à¥à¨á®¢ªã ®ª­ . -------------
 à ¬¥âàë:
* eax = 12 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­ ç «  ¯¥à¥à¨á®¢ª¨ 㤠«ï¥â ¢á¥ ®¯à¥¤¥«ñ­­ë¥
ä㭪樥© 8 ª­®¯ª¨, ¨å á«¥¤ã¥â ®¯à¥¤¥«¨âì ¯®¢â®à­®.
------------- Подфункция 2 - закончить перерисовку окна. -------------
Параметры:
* eax = 12 - номер функции
* ebx = 2 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Функция начала перерисовки удаляет все определённые
функцией 8 кнопки, их следует определить повторно.
 
======================================================================
============ ”ã­ªæ¨ï 13 - ­ à¨á®¢ âì ¯àאַ㣮«ì­¨ª ¢ ®ª­¥. ===========
============ Функция 13 - нарисовать прямоугольник в окне. ===========
======================================================================
 à ¬¥âàë:
* eax = 13 - ­®¬¥à ä㭪樨
* ebx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ x]
* ecx = [ª®®à¤¨­ â  ¯® ®á¨ y]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = 梥â 0xRRGGBB ¨«¨ 0x80RRGGBB ¤«ï £à ¤¨¥­â­®© § «¨¢ª¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ®¤ ª®®à¤¨­ â ¬¨ ¯®­¨¬ îâáï ª®®à¤¨­ âë «¥¢®£® ¢¥àå­¥£® 㣫 
¯àאַ㣮«ì­¨ª  ®â­®á¨â¥«ì­® ®ª­ .
Параметры:
* eax = 13 - номер функции
* ebx = [координата по оси x]*65536 + [размер по оси x]
* ecx = [координата по оси y]*65536 + [размер по оси y]
* edx = цвет 0xRRGGBB или 0x80RRGGBB для градиентной заливки
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Под координатами понимаются координаты левого верхнего угла
прямоугольника относительно окна.
 
======================================================================
================ ”ã­ªæ¨ï 14 - ¯®«ãç¨âì à §¬¥àë íªà ­ . ===============
================ Функция 14 - получить размеры экрана. ===============
======================================================================
 à ¬¥âàë:
* eax = 14 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = [xsize]*65536 + [ysize], £¤¥
* xsize = x-ª®®à¤¨­ â  ¯à ¢®£® ­¨¦­¥£® 㣫  íªà ­  =
à §¬¥à ¯® £®à¨§®­â «¨ - 1
* ysize = y-ª®®à¤¨­ â  ¯à ¢®£® ­¨¦­¥£® 㣫  íªà ­  =
à §¬¥à ¯® ¢¥à⨪ «¨ - 1
‡ ¬¥ç ­¨ï:
* ‘¬®âਠ⠪¦¥ ¯®¤äã­ªæ¨î 5 ä㭪樨 48 - ¯®«ãç¨âì à §¬¥àë à ¡®ç¥©
®¡« á⨠íªà ­ .
Параметры:
* eax = 14 - номер функции
Возвращаемое значение:
* eax = [xsize]*65536 + [ysize], где
* xsize = x-координата правого нижнего угла экрана =
размер по горизонтали - 1
* ysize = y-координата правого нижнего угла экрана =
размер по вертикали - 1
Замечания:
* Смотри также подфункцию 5 функции 48 - получить размеры рабочей
области экрана.
 
======================================================================
= ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì à §¬¥à ä®­®¢®£® ¨§®¡à ¦¥­¨ï. =
= Функция 15, подфункция 1 - установить размер фонового изображения. =
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = è¨à¨­  ¨§®¡à ¦¥­¨ï
* edx = ¢ëá®â  ¨§®¡à ¦¥­¨ï
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ‚맮¢ ä㭪樨 ®¡ï§ â¥«¥­ ¯¥à¥¤ ¢ë§®¢®¬ ¯®¤ä㭪権 2 ¨ 5.
* „«ï ®¡­®¢«¥­¨ï íªà ­  (¯®á«¥ § ¢¥à襭¨ï á¥à¨¨ ª®¬ ­¤, à ¡®â îé¨å á
ä®­®¬) ¢ë§ë¢ ©â¥ ¯®¤äã­ªæ¨î 3 ¯¥à¥à¨á®¢ª¨ ä®­ .
* …áâì ¯ à­ ï äã­ªæ¨ï ¯®«ã祭¨ï à §¬¥à®¢ ä®­®¢®£® ¨§®¡à ¦¥­¨ï -
¯®¤äã­ªæ¨ï 1 ä㭪樨 39.
Параметры:
* eax = 15 - номер функции
* ebx = 1 - номер подфункции
* ecx = ширина изображения
* edx = высота изображения
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Вызов функции обязателен перед вызовом подфункций 2 и 5.
* Для обновления экрана (после завершения серии команд, работающих с
фоном) вызывайте подфункцию 3 перерисовки фона.
* Есть парная функция получения размеров фонового изображения -
подфункция 1 функции 39.
 
======================================================================
= ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 2 - ¯®áâ ¢¨âì â®çªã ­  ä®­®¢®¬ ¨§®¡à ¦¥­¨¨. =
= Функция 15, подфункция 2 - поставить точку на фоновом изображении. =
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ᬥ饭¨¥
* edx = 梥â â®çª¨ 0xRRGGBB
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ‘¬¥é¥­¨¥ ¤«ï â®çª¨ á ª®®à¤¨­ â ¬¨ (x,y) ¢ëç¨á«ï¥âáï ª ª
Параметры:
* eax = 15 - номер функции
* ebx = 2 - номер подфункции
* ecx = смещение
* edx = цвет точки 0xRRGGBB
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Смещение для точки с координатами (x,y) вычисляется как
(x+y*xsize)*3.
* …᫨ 㪠§ ­­®¥ ᬥ饭¨¥ ¯à¥¢ëè ¥â ãáâ ­®¢«¥­­ë© ¯®¤ä㭪樥© 1
à §¬¥à, ¢ë§®¢ ¨£­®à¨àã¥âáï.
* „«ï ®¡­®¢«¥­¨ï íªà ­  (¯®á«¥ § ¢¥à襭¨ï á¥à¨¨ ª®¬ ­¤, à ¡®â îé¨å á
ä®­®¬) ¢ë§ë¢ ©â¥ ¯®¤äã­ªæ¨î 3 ¯¥à¥à¨á®¢ª¨ ä®­ .
* …áâì ¯ à­ ï äã­ªæ¨ï ¯®«ã祭¨ï â®çª¨ á ä®­®¢®£® ¨§®¡à ¦¥­¨ï -
¯®¤äã­ªæ¨ï 2 ä㭪樨 39.
* Если указанное смещение превышает установленный подфункцией 1
размер, вызов игнорируется.
* Для обновления экрана (после завершения серии команд, работающих с
фоном) вызывайте подфункцию 3 перерисовки фона.
* Есть парная функция получения точки с фонового изображения -
подфункция 2 функции 39.
 
======================================================================
============ ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 3 - ¯¥à¥à¨á®¢ âì ä®­. ============
============ Функция 15, подфункция 3 - перерисовать фон. ============
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
Параметры:
* eax = 15 - номер функции
* ebx = 3 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
===== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 4 - ãáâ ­®¢¨âì ०¨¬ ®âà¨á®¢ª¨ ä®­ . ====
===== Функция 15, подфункция 4 - установить режим отрисовки фона. ====
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ०¨¬ ®âà¨á®¢ª¨:
* 1 = § ¬®áâ¨âì
* 2 = à áâï­ãâì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* „«ï ®¡­®¢«¥­¨ï íªà ­  (¯®á«¥ § ¢¥à襭¨ï á¥à¨¨ ª®¬ ­¤, à ¡®â îé¨å á
ä®­®¬) ¢ë§ë¢ ©â¥ ¯®¤äã­ªæ¨î 3 ¯¥à¥à¨á®¢ª¨ ä®­ .
* …áâì ¯ à­ ï ª®¬ ­¤  ¯®«ã祭¨ï ०¨¬  ®âà¨á®¢ª¨ ä®­  -
¯®¤äã­ªæ¨ï 4 ä㭪樨 39.
Параметры:
* eax = 15 - номер функции
* ebx = 4 - номер подфункции
* ecx = режим отрисовки:
* 1 = замостить
* 2 = растянуть
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Для обновления экрана (после завершения серии команд, работающих с
фоном) вызывайте подфункцию 3 перерисовки фона.
* Есть парная команда получения режима отрисовки фона -
подфункция 4 функции 39.
 
======================================================================
===== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 5 - ¯®¬¥áâ¨âì ¡«®ª ¯¨ªá¥«¥© ­  ä®­. =====
===== Функция 15, подфункция 5 - поместить блок пикселей на фон. =====
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¤ ­­ë¥ ¢ ä®à¬ â¥ BBGGRRBBGGRR...
* edx = ᬥ饭¨¥ ¢ ¤ ­­ëå ä®­®¢®£® ¨§®¡à ¦¥­¨ï
* esi = à §¬¥à ¤ ­­ëå ¢ ¡ ©â å = 3 * ç¨á«® ¯¨ªá¥«¥©
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* à®¢¥àª¨ ª®à४⭮á⨠ᬥ饭¨ï ¨ à §¬¥à  ­¥ ¯à®¨§¢®¤¨âáï.
* –¢¥â ª ¦¤®£® ¯¨ªá¥«ï åà ­¨âáï ª ª 3-¡ ©â­ ï ¢¥«¨ç¨­  BBGGRR.
* ¨ªá¥«¨ ä®­®¢®£® ¨§®¡à ¦¥­¨ï § ¯¨á뢠îâáï ¯®á«¥¤®¢ â¥«ì­®
á«¥¢  ­ ¯à ¢®, ᢥàåã ¢­¨§.
* ‘¬¥é¥­¨¥ ¯¨ªá¥«ï á ª®®à¤¨­ â ¬¨ (x,y) ¥áâì (x+y*xsize)*3.
* „«ï ®¡­®¢«¥­¨ï íªà ­  (¯®á«¥ § ¢¥à襭¨ï á¥à¨¨ ª®¬ ­¤, à ¡®â îé¨å á
ä®­®¬) ¢ë§ë¢ ©â¥ ¯®¤äã­ªæ¨î 3 ¯¥à¥à¨á®¢ª¨ ä®­ .
Параметры:
* eax = 15 - номер функции
* ebx = 5 - номер подфункции
* ecx = указатель на данные в формате BBGGRRBBGGRR...
* edx = смещение в данных фонового изображения
* esi = размер данных в байтах = 3 * число пикселей
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Проверки корректности смещения и размера не производится.
* Цвет каждого пикселя хранится как 3-байтная величина BBGGRR.
* Пиксели фонового изображения записываются последовательно
слева направо, сверху вниз.
* Смещение пикселя с координатами (x,y) есть (x+y*xsize)*3.
* Для обновления экрана (после завершения серии команд, работающих с
фоном) вызывайте подфункцию 3 перерисовки фона.
 
======================================================================
====================== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 6 ======================
==== ‘¯à®¥æ¨à®¢ âì ¤ ­­ë¥ ä®­  ­   ¤à¥á­®¥ ¯à®áâà ­á⢮ ¯à®æ¥áá . ====
====================== Функция 15, подфункция 6 ======================
==== Спроецировать данные фона на адресное пространство процесса. ====
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 㪠§ â¥«ì ­  ¤ ­­ë¥ ä®­ , 0 ¯à¨ ®è¨¡ª¥
‡ ¬¥ç ­¨ï:
* ‘¯à®¥æ¨à®¢ ­­ë¥ ¤ ­­ë¥ ¤®áâã¯­ë ­  ç⥭¨¥ ¨ § ¯¨áì.
*  §¬¥à ¤ ­­ëå ä®­  à ¢¥­ 3*xsize*ysize. ˆ§¬¥­¥­¨¥ à §¬¥à®¢ ä®­ 
¡«®ª¨àã¥âáï ­  ¢à¥¬ï à ¡®âë á á¯à®¥æ¨à®¢ ­­ë¬¨ ¤ ­­ë¬¨.
* –¢¥â ª ¦¤®£® ¯¨ªá¥«ï åà ­¨âáï ª ª 3-¡ ©â®¢ ï ¢¥«¨ç¨­  BBGGRR.
* ¨ªá¥«¨ ä®­®¢®£® ¨§®¡à ¦¥­¨ï § ¯¨á뢠îâáï ¯®á«¥¤®¢ â¥«ì­®
á«¥¢  ­ ¯à ¢®, ᢥàåã ¢­¨§.
Параметры:
* eax = 15 - номер функции
* ebx = 6 - номер подфункции
Возвращаемое значение:
* eax = указатель на данные фона, 0 при ошибке
Замечания:
* Спроецированные данные доступны на чтение и запись.
* Размер данных фона равен 3*xsize*ysize. Изменение размеров фона
блокируется на время работы с спроецированными данными.
* Цвет каждого пикселя хранится как 3-байтовая величина BBGGRR.
* Пиксели фонового изображения записываются последовательно
слева направо, сверху вниз.
 
======================================================================
====================== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 7 ======================
=== ‡ ªàëâì ¯à®¥ªæ¨î ¤ ­­ëå ä®­  ­   ¤à¥á­®¥ ¯à®áâà ­á⢮ ¯à®æ¥áá . ==
====================== Функция 15, подфункция 7 ======================
=== Закрыть проекцию данных фона на адресное пространство процесса. ==
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¤ ­­ë¥ ä®­ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 1 ¯à¨ ãᯥå¥, 0 ¯à¨ ®è¨¡ª¥
Параметры:
* eax = 15 - номер функции
* ebx = 7 - номер подфункции
* ecx = указатель на данные фона
Возвращаемое значение:
* eax = 1 при успехе, 0 при ошибке
 
======================================================================
====================== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 8 ======================
=========== ®«ãç¨âì ª®®à¤¨­ âë ¯®á«¥¤­¥© ®âà¨á®¢ª¨ ä®­ . ============
====================== Функция 15, подфункция 8 ======================
=========== Получить координаты последней отрисовки фона. ============
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 15 - номер функции
* ebx = 8 - номер подфункции
Возвращаемое значение:
* eax = [left]*65536 + [right]
* ebx = [top]*65536 + [bottom]
‡ ¬¥ç ­¨ï:
* (left,top) - ª®®à¤¨­ âë «¥¢®£® ¢¥àå­¥£® 㣫 ,
(right,bottom) - ª®®à¤¨­ âë ¯à ¢®£® ­¨¦­¥£®.
* „«ï ¯®«ã祭¨ï ¡®«¥¥ ¤®á⮢¥à­ëå ᢥ¤¥­¨©, ­¥®¡å®¤¨¬® ¢ë§¢ âì
äã­ªæ¨î áࠧ㠯®á«¥ ¯®«ã祭¨ï ᮡëâ¨ï:
5 = § ¢¥à訫 áì ¯¥à¥à¨á®¢ª  ä®­  à ¡®ç¥£® á⮫ 
Замечания:
* (left,top) - координаты левого верхнего угла,
(right,bottom) - координаты правого нижнего.
* Для получения более достоверных сведений, необходимо вызвать
функцию сразу после получения события:
5 = завершилась перерисовка фона рабочего стола
 
======================================================================
====================== ”ã­ªæ¨ï 15, ¯®¤äã­ªæ¨ï 9 ======================
=============== ¥à¥à¨á®¢ âì ¯àאַ㣮«ì­ãî ç áâì ä®­ . ===============
====================== Функция 15, подфункция 9 ======================
=============== Перерисовать прямоугольную часть фона. ===============
======================================================================
 à ¬¥âàë:
* eax = 15 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
Параметры:
* eax = 15 - номер функции
* ebx = 9 - номер подфункции
* ecx = [left]*65536 + [right]
* edx = [top]*65536 + [bottom]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* (left,top) - ª®®à¤¨­ âë «¥¢®£® ¢¥àå­¥£® 㣫 ,
(right,bottom) - ª®®à¤¨­ âë ¯à ¢®£® ­¨¦­¥£®.
* …᫨ ¯ à ¬¥âàë ãáâ ­®¢«¥­ë ­¥ª®à४⭮ - ä®­ ­¥ ¯¥à¥à¨á®¢ë¢ ¥âáï.
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* (left,top) - координаты левого верхнего угла,
(right,bottom) - координаты правого нижнего.
* Если параметры установлены некорректно - фон не перерисовывается.
 
======================================================================
============= ”ã­ªæ¨ï 16 - á®åà ­¨âì à ¬¤¨áª ­  ¤¨áª¥âã. =============
============= Функция 16 - сохранить рамдиск на дискету. =============
======================================================================
 à ¬¥âàë:
* eax = 16 - ­®¬¥à ä㭪樨
* ebx = 1 ¨«¨ ebx = 2 - ­  ª ªãî ¤¨áª¥âã á®åà ­ïâì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ®è¨¡ª 
Параметры:
* eax = 16 - номер функции
* ebx = 1 или ebx = 2 - на какую дискету сохранять
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - ошибка
 
======================================================================
============== ”ã­ªæ¨ï 17 - ¯®«ãç¨âì ª®¤ ­ ¦ â®© ª­®¯ª¨. =============
============== Функция 17 - получить код нажатой кнопки. =============
======================================================================
‡ ¡¨à ¥â ª®¤ ­ ¦ â®© ª­®¯ª¨ ¨§ ¡ãä¥à .
 à ¬¥âàë:
* eax = 17 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ¡ãä¥à ¯ãáâ, ¢®§¢à é ¥âáï eax=1
* ¥á«¨ ¡ãä¥à ­¥¯ãáâ:
* áâ à訥 24 ¡¨â  eax ᮤ¥à¦ â ¨¤¥­â¨ä¨ª â®à ª­®¯ª¨
(¢ ç áâ­®áâ¨, ¢ ah ®ª §ë¢ ¥âáï ¬« ¤è¨© ¡ ©â ¨¤¥­â¨ä¨ª â®à ;
¥á«¨ ¢á¥ ª­®¯ª¨ ¨¬¥îâ ¨¤¥­â¨ä¨ª â®à, ¬¥­ì訩 256,
â® ¤«ï à §«¨ç¥­¨ï ¤®áâ â®ç­® ah)
* al = 0 - ª­®¯ª  ¡ë«  ­ ¦ â  «¥¢®© ª­®¯ª®© ¬ëè¨
* al = ¡¨â, ᮮ⢥âáâ¢ãî騩 ­ ¦ ¢è¥© ª­®¯ª¥ ¬ëè¨, ¥á«¨ ­¥ «¥¢®©
‡ ¬¥ç ­¨ï:
* "ãä¥à" åà ­¨â ⮫쪮 ®¤­ã ª­®¯ªã, ¯à¨ ­ ¦ â¨¨ ­®¢®© ª­®¯ª¨
¨­ä®à¬ æ¨ï ® áâ à®© â¥àï¥âáï.
* à¨ ¢ë§®¢¥ í⮩ ä㭪樨 ¯à¨«®¦¥­¨¥¬ á ­¥ ªâ¨¢­ë¬ ®ª­®¬
¢®§¢à é ¥âáï ®â¢¥â "¡ãä¥à ¯ãáâ".
* ‚®§¢à é ¥¬®¥ §­ ç¥­¨¥ al ᮮ⢥âáâ¢ã¥â á®áâ®ï­¨î ª­®¯®ª ¬ëè¨
¢ ä®à¬ â¥ ¯®¤ä㭪樨 2 ä㭪樨 37 ¢ ¬®¬¥­â ­ ç «  ­ ¦ â¨ï
­  ª­®¯ªã, §  ¨áª«î祭¨¥¬ ¬« ¤è¥£® ¡¨â  (ᮮ⢥âáâ¢ãî饣® «¥¢®©
ª­®¯ª¥ ¬ëè¨), ª®â®àë© á¡à á뢠¥âáï.
Забирает код нажатой кнопки из буфера.
Параметры:
* eax = 17 - номер функции
Возвращаемое значение:
* если буфер пуст, возвращается eax=1
* если буфер непуст:
* старшие 24 бита eax содержат идентификатор кнопки
(в частности, в ah оказывается младший байт идентификатора;
если все кнопки имеют идентификатор, меньший 256,
то для различения достаточно ah)
* al = 0 - кнопка была нажата левой кнопкой мыши
* al = бит, соответствующий нажавшей кнопке мыши, если не левой
Замечания:
* "Буфер" хранит только одну кнопку, при нажатии новой кнопки
информация о старой теряется.
* При вызове этой функции приложением с неактивным окном
возвращается ответ "буфер пуст".
* Возвращаемое значение al соответствует состоянию кнопок мыши
в формате подфункции 2 функции 37 в момент начала нажатия
на кнопку, за исключением младшего бита (соответствующего левой
кнопке мыши), который сбрасывается.
======================================================================
= ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 1 - ᤥ« âì á ¬ë¬ ­¨¦­¨¬ ®ª­® ¯®â®ª . =======
= Функция 18, подфункция 1 - сделать самым нижним окно потока. =======
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à á«®â  ¯®â®ª 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
Параметры:
* eax = 18 - номер функции
* ebx = 1 - номер подфункции
* ecx = номер слота потока
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
==== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 2 - § ¢¥àè¨âì ¯à®æ¥áá/¯®â®ª ¯® á«®âã. ====
==== Функция 18, подфункция 2 - завершить процесс/поток по слоту. ====
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à á«®â  ¯à®æ¥áá /¯®â®ª 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ¥«ì§ï § ¢¥àè¨âì ¯®â®ª ®¯¥à æ¨®­­®© á¨á⥬ë OS/IDLE (­®¬¥à á«®â 
1), ¬®¦­® § ¢¥àè¨âì «î¡®© ®¡ëç­ë© ¯®â®ª/¯à®æ¥áá.
* ‘¬®âਠ⠪¦¥ ¯®¤äã­ªæ¨î 18 - § ¢¥à襭¨¥
¯à®æ¥áá /¯®â®ª  á § ¤ ­­ë¬ ¨¤¥­â¨ä¨ª â®à®¬.
Параметры:
* eax = 18 - номер функции
* ebx = 2 - номер подфункции
* ecx = номер слота процесса/потока
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Нельзя завершить поток операционной системы OS/IDLE (номер слота
1), можно завершить любой обычный поток/процесс.
* Смотри также подфункцию 18 - завершение
процесса/потока с заданным идентификатором.
 
======================================================================
= ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 3 - ᤥ« âì  ªâ¨¢­ë¬ ®ª­® § ¤ ­­®£® ¯®â®ª . =
= Функция 18, подфункция 3 - сделать активным окно заданного потока. =
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à á«®â  ¯®â®ª 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* à¨ 㪠§ ­¨¨ ª®à४⭮£®, ­® ­¥áãé¥áâ¢ãî饣® á«®â   ªâ¨¢¨§¨àã¥âáï
ª ª®¥-â® ®ª­®.
* “§­ âì, ª ª®¥ ®ª­® ï¥âáï  ªâ¨¢­ë¬, ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 7.
Параметры:
* eax = 18 - номер функции
* ebx = 3 - номер подфункции
* ecx = номер слота потока
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* При указании корректного, но несуществующего слота активизируется
какое-то окно.
* Узнать, какое окно является активным, можно вызовом подфункции 7.
 
======================================================================
”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 4 - ¯®«ãç¨âì áçñâ稪 ¯ãáâëå ⠪⮢ ¢ ᥪ㭤ã.
Функция 18, подфункция 4 - получить счётчик пустых тактов в секунду.
======================================================================
®¤ ¯ãáâ묨 ⠪⠬¨ ¯®­¨¬ ¥âáï ¢à¥¬ï, ¢ ª®â®à®¥ ¯à®æ¥áá®à ¯à®áâ ¨¢ ¥â
¢ ®¦¨¤ ­¨¨ ¯à¥à뢠­¨ï (¢ ¨­áâàãªæ¨¨ hlt).
Под пустыми тактами понимается время, в которое процессор простаивает
в ожидании прерывания (в инструкции hlt).
 
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = §­ ç¥­¨¥ áçñâ稪  ¯ãáâëå ⠪⮢ ¢ ᥪ㭤ã
Параметры:
* eax = 18 - номер функции
* ebx = 4 - номер подфункции
Возвращаемое значение:
* eax = значение счётчика пустых тактов в секунду
 
======================================================================
======== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 5 - ¯®«ãç¨âì ⠪⮢ãî ç áâ®âã. =======
======== Функция 18, подфункция 5 - получить тактовую частоту. =======
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ⠪⮢ ï ç áâ®â  (¯® ¬®¤ã«î 2^32 ⠪⮢ = 4ƒƒæ)
Параметры:
* eax = 18 - номер функции
* ebx = 5 - номер подфункции
Возвращаемое значение:
* eax = тактовая частота (по модулю 2^32 тактов = 4ГГц)
 
======================================================================
”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 6 - á®åà ­¨âì à ¬¤¨áª ¢ ä ©« ­  ¦ñá⪮¬ ¤¨áª¥.
Функция 18, подфункция 6 - сохранить рамдиск в файл на жёстком диске.
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  áâபã á ¯®«­ë¬ ¨¬¥­¥¬ ä ©« 
(­ ¯à¨¬¥à, "/hd0/1/kolibri/kolibri.img")
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* ¨­ ç¥ eax = ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
‡ ¬¥ç ­¨ï:
* ‚ᥠ¯ ¯ª¨ ¢ 㪠§ ­­®¬ ¯ã⨠¤®«¦­ë áãé¥á⢮¢ âì, ¨­ ç¥ ¢¥à­ñâáï
§­ ç¥­¨¥ 5, "ä ©« ­¥ ­ ©¤¥­".
Параметры:
* eax = 18 - номер функции
* ebx = 6 - номер подфункции
* ecx = указатель на строку с полным именем файла
(например, "/hd0/1/kolibri/kolibri.img")
Возвращаемое значение:
* eax = 0 - успешно
* иначе eax = код ошибки файловой системы
Замечания:
* Все папки в указанном пути должны существовать, иначе вернётся
значение 5, "файл не найден".
 
======================================================================
====== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 7 - ¯®«ãç¨âì ­®¬¥à  ªâ¨¢­®£® ®ª­ . =====
====== Функция 18, подфункция 7 - получить номер активного окна. =====
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ­®¬¥à  ªâ¨¢­®£® ®ª­  (­®¬¥à á«®â  ¯®â®ª , ®ª­® ª®â®à®£®
 ªâ¨¢­®)
‡ ¬¥ç ­¨ï:
* €ªâ¨¢­®¥ ®ª­® ­ å®¤¨âáï ¢¢¥àåã ®ª®­­®£® áâíª  ¨ ¯®«ãç ¥â
á®®¡é¥­¨ï ®¡® ¢áñ¬ ¢¢®¤¥ á ª« ¢¨ âãàë.
* ‘¤¥« âì ®ª­®  ªâ¨¢­ë¬ ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 3.
Параметры:
* eax = 18 - номер функции
* ebx = 7 - номер подфункции
Возвращаемое значение:
* eax = номер активного окна (номер слота потока, окно которого
активно)
Замечания:
* Активное окно находится вверху оконного стэка и получает
сообщения обо всём вводе с клавиатуры.
* Сделать окно активным можно вызовом подфункции 3.
 
======================================================================
==== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 8 - ®âª«îç¨âì/à §à¥è¨âì §¢ãª ᯨª¥à . ====
==== Функция 18, подфункция 8 - отключить/разрешить звук спикера. ====
======================================================================
à¨ ®âª«îçñ­­®¬ §¢ãª¥ ¢ë§®¢ë ¯®¤ä㭪樨 55 ä㭪樨 55 ¨£­®à¨àãîâáï.
à¨ ¢ª«îçñ­­®¬ - ­ ¯à ¢«ïîâáï ­  ¢áâ஥­­ë© ᯨª¥à.
При отключённом звуке вызовы подфункции 55 функции 55 игнорируются.
При включённом - направляются на встроенный спикер.
 
--------------- ®¤¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì á®áâ®ï­¨¥. ----------------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 1 - ­®¬¥à ¯®¤¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - §¢ãª ᯨª¥à  à §à¥èñ­; 1 - § ¯à¥éñ­
--------------- Подподфункция 1 - получить состояние. ----------------
Параметры:
* eax = 18 - номер функции
* ebx = 8 - номер подфункции
* ecx = 1 - номер подподфункции
Возвращаемое значение:
* eax = 0 - звук спикера разрешён; 1 - запрещён
 
-------------- ®¤¯®¤äã­ªæ¨ï 2 - ¯¥à¥ª«îç¨âì á®áâ®ï­¨¥. --------------
¥à¥ª«îç ¥â á®áâ®ï­¨ï à §à¥è¥­¨ï/§ ¯à¥é¥­¨ï.
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 2 - ­®¬¥à ¯®¤¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
-------------- Подподфункция 2 - переключить состояние. --------------
Переключает состояния разрешения/запрещения.
Параметры:
* eax = 18 - номер функции
* ebx = 8 - номер подфункции
* ecx = 2 - номер подподфункции
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
= ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 9 - § ¢¥à襭¨¥ à ¡®âë á¨á⥬ë á ¯ à ¬¥â஬. =
= Функция 18, подфункция 9 - завершение работы системы с параметром. =
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¯ à ¬¥âà:
* 2 = ¢ëª«îç¨âì ª®¬¯ìîâ¥à
* 3 = ¯¥à¥§ £à㧨âì ª®¬¯ìîâ¥à
* 4 = ¯¥à¥§ ¯ãáâ¨âì ï¤à® ¨§ ä ©«  kernel.mnt ­  à ¬¤¨áª¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¯à¨ ­¥¢¥à­®¬ ecx ॣ¨áâàë ­¥ ¬¥­ïîâáï (â.¥. eax=18)
* ¯à¨ ¯à ¢¨«ì­®¬ ¢ë§®¢¥ ¢á¥£¤  ¢®§¢à é ¥âáï ¯à¨§­ ª ãá¯¥å  eax=0
‡ ¬¥ç ­¨ï:
* ¥ á«¥¤ã¥â ¯®« £ âìáï ­  ¢®§¢à é ¥¬®¥ §­ ç¥­¨¥ ¯à¨ ­¥¢¥à­®¬
¢ë§®¢¥, ®­® ¬®¦¥â ¨§¬¥­¨âìáï ¢ ¯®á«¥¤ãîé¨å ¢¥àá¨ïå ï¤à .
Параметры:
* eax = 18 - номер функции
* ebx = 9 - номер подфункции
* ecx = параметр:
* 2 = выключить компьютер
* 3 = перезагрузить компьютер
* 4 = перезапустить ядро из файла kernel.mnt на рамдиске
Возвращаемое значение:
* при неверном ecx регистры не меняются (т.е. eax=18)
* при правильном вызове всегда возвращается признак успеха eax=0
Замечания:
* Не следует полагаться на возвращаемое значение при неверном
вызове, оно может измениться в последующих версиях ядра.
 
======================================================================
======== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 10 - ᢥà­ãâì ®ª­® ¯à¨«®¦¥­¨ï. =======
======== Функция 18, подфункция 10 - свернуть окно приложения. =======
======================================================================
‘¢®à ç¨¢ ¥â ᮡá⢥­­®¥ ®ª­®.
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 10 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Œ¨­¨¬¨§¨à®¢ ­­®¥ ®ª­® á â®çª¨ §à¥­¨ï ä㭪樨 9 á®åà ­ï¥â ¯®«®¦¥­¨¥
¨ à §¬¥àë.
* ‚®ááâ ­®¢«¥­¨¥ ®ª­  ¯à¨«®¦¥­¨ï ¯à®¨á室¨â ¯à¨  ªâ¨¢¨§¨à®¢ ­¨¨
¯®¤ä㭪樥© 3.
* Ž¡ëç­® ­¥â ­¥®¡å®¤¨¬®á⨠® ᢮à ç¨¢ âì/à §¢®à ç¨¢ âì ᢮ñ ®ª­®:
᢮à ç¨¢ ­¨¥ ®ª­  ®áãé¥á⢫ï¥âáï á¨á⥬®© ¯à¨ ­ ¦ â¨¨ ­  ª­®¯ªã
¬¨­¨¬¨§ æ¨¨ (ª®â®à ï ¤«ï ®ª®­ ᮠ᪨­®¬ ®¯à¥¤¥«ï¥âáï  ¢â®¬ â¨ç¥áª¨
ä㭪樥© 0, ¤«ï ®ª®­ ¡¥§ ᪨­  ¥ñ ¬®¦­® ®¯à¥¤¥«¨âì ä㭪樥© 8),
¢®ááâ ­®¢«¥­¨¥ - ¯à¨«®¦¥­¨¥¬ @panel.
Сворачивает собственное окно.
Параметры:
* eax = 18 - номер функции
* ebx = 10 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Минимизированное окно с точки зрения функции 9 сохраняет положение
и размеры.
* Восстановление окна приложения происходит при активизировании
подфункцией 3.
* Обычно нет необходимости явно сворачивать/разворачивать своё окно:
сворачивание окна осуществляется системой при нажатии на кнопку
минимизации (которая для окон со скином определяется автоматически
функцией 0, для окон без скина её можно определить функцией 8),
восстановление - приложением @panel.
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 11 =====================
============= ®«ãç¨âì ¨­ä®à¬ æ¨î ® ¤¨áª®¢®© ¯®¤á¨á⥬¥. =============
====================== Функция 18, подфункция 11 =====================
============= Получить информацию о дисковой подсистеме. =============
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ⨯ â ¡«¨æë:
* 1 = ª®à®âª ï ¢¥àá¨ï, 10 ¡ ©â
* 2 = ¯®«­ ï ¢¥àá¨ï, 65536 ¡ ©â
* edx = 㪠§ â¥«ì ­  ¡ãä¥à (¢ ¯à¨«®¦¥­¨¨) ¤«ï â ¡«¨æë
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
”®à¬ â â ¡«¨æë: ª®à®âª ï ¢¥àá¨ï:
* +0: byte: ¨­ä®à¬ æ¨ï ® ƒŒ„ (¤¨áª®¢®¤ å ¤«ï ¤¨áª¥â), AAAABBBB,
£¤¥ AAAA § ¤ ñâ ⨯ ¯¥à¢®£® ¤¨áª®¢®¤ , BBBB - ¢â®à®£® ᮣ« á­®
á«¥¤ãî饬ã ᯨáªã:
* 0 = ­¥â ¤¨áª®¢®¤ 
Параметры:
* eax = 18 - номер функции
* ebx = 11 - номер подфункции
* ecx = тип таблицы:
* 1 = короткая версия, 10 байт
* 2 = полная версия, 65536 байт
* edx = указатель на буфер (в приложении) для таблицы
Возвращаемое значение:
* функция не возвращает значения
Формат таблицы: короткая версия:
* +0: byte: информация о НГМД (дисководах для дискет), AAAABBBB,
где AAAA задаёт тип первого дисковода, BBBB - второго согласно
следующему списку:
* 0 = нет дисковода
* 1 = 360Kb, 5.25''
* 2 = 1.2Mb, 5.25''
* 3 = 720Kb, 3.5''
* 4 = 1.44Mb, 3.5''
* 5 = 2.88Mb, 3.5'' (â ª¨¥ ¤¨áª¥âë ᥩç á 㦥 ­¥ ¨á¯®«ì§ãîâáï)
 ¯à¨¬¥à, ¤«ï áâ ­¤ àâ­®© ª®­ä¨£ãà æ¨¨ ¨§ ®¤­®£® 1.44-¤¨áª®¢®¤ 
§¤¥áì ¡ã¤¥â 40h,   ¤«ï á«ãç ï 1.2Mb ­  A: ¨ 1.44Mb ­  B:
§­ ç¥­¨¥ ®ª §ë¢ ¥âáï 24h.
* +1: byte: ¨­ä®à¬ æ¨ï ® ¦ñáâª¨å ¤¨áª å ¨ CD-¯à¨¢®¤ å, AABBCCDD,
£¤¥ AA ᮮ⢥âáâ¢ã¥â ª®­â஫«¥àã IDE0, ..., DD - IDE3:
* 0 = ãáâனá⢮ ®âáãâáâ¢ã¥â
* 1 = ¦ñá⪨© ¤¨áª
* 2 = CD-¯à¨¢®¤
 ¯à¨¬¥à, ¢ á«ãç ¥ HD ­  IDE0 ¨ CD ­  IDE2 §¤¥áì ¡ã¤¥â 48h.
* +2: 4 db: ç¨á«® ­ ©¤¥­­ëå à §¤¥«®¢ ­  ¦ñáâª¨å ¤¨áª å á
ᮮ⢥âá⢥­­® IDE0,...,IDE3.
à¨ ®âáãâá⢨¨ ¦ñá⪮£® ¤¨áª  ­  IDEx ᮮ⢥âáâ¢ãî騩 ¡ ©â
­ã«¥¢®©, ¯à¨ ­ «¨ç¨¨ ¯®ª §ë¢ ¥â ç¨á«® à á¯®§­ ­­ëå à §¤¥«®¢,
ª®â®àëå ¬®¦¥â ¨ ­¥ ¡ëâì (¥á«¨ ­®á¨â¥«ì ­¥ ®âä®à¬ â¨à®¢ ­ ¨«¨
¥á«¨ ä ©«®¢ ï á¨á⥬  ­¥ ¯®¤¤¥à¦¨¢ ¥âáï). ‚ ⥪ã饩 ¢¥àᨨ ï¤à 
¤«ï ¦ñáâª¨å ¤¨áª®¢ ¯®¤¤¥à¦¨¢ îâáï ⮫쪮 FAT16, FAT32 ¨ NTFS.
* +6: 4 db: § à¥§¥à¢¨à®¢ ­®
”®à¬ â â ¡«¨æë: ¯®«­ ï ¢¥àá¨ï:
* +0: 10 db: â ª¨¥ ¦¥, ª ª ¨ ¢ ª®à®âª®© ¢¥àᨨ
* +10: 100 db: ¤ ­­ë¥ ¤«ï ¯¥à¢®£® à §¤¥« 
* +110: 100 db: ¤ ­­ë¥ ¤«ï ¢â®à®£® à §¤¥« 
* 5 = 2.88Mb, 3.5'' (такие дискеты сейчас уже не используются)
Например, для стандартной конфигурации из одного 1.44-дисковода
здесь будет 40h, а для случая 1.2Mb на A: и 1.44Mb на B:
значение оказывается 24h.
* +1: byte: информация о жёстких дисках и CD-приводах, AABBCCDD,
где AA соответствует контроллеру IDE0, ..., DD - IDE3:
* 0 = устройство отсутствует
* 1 = жёсткий диск
* 2 = CD-привод
Например, в случае HD на IDE0 и CD на IDE2 здесь будет 48h.
* +2: 4 db: число найденных разделов на жёстких дисках с
соответственно IDE0,...,IDE3.
При отсутствии жёсткого диска на IDEx соответствующий байт
нулевой, при наличии показывает число распознанных разделов,
которых может и не быть (если носитель не отформатирован или
если файловая система не поддерживается). В текущей версии ядра
для жёстких дисков поддерживаются только FAT16, FAT32 и NTFS.
* +6: 4 db: зарезервировано
Формат таблицы: полная версия:
* +0: 10 db: такие же, как и в короткой версии
* +10: 100 db: данные для первого раздела
* +110: 100 db: данные для второго раздела
* ...
* +10+100*(n-1): 100 db: ¤ ­­ë¥ ¤«ï ¯®á«¥¤­¥£® à §¤¥« 
 §¤¥«ë à á¯®«®¦¥­ë ¢ á«¥¤ãî饬 ¯®à浪¥: á­ ç «  ¯®á«¥¤®¢ â¥«ì­® ¢á¥
à á¯®§­ ­­ë¥ à §¤¥«ë ­  HD ­  IDE0 (¥á«¨ ¥áâì),
§ â¥¬ ­  HD ­  IDE1 (¥á«¨ ¥áâì) ¨ â.¤. ¤® IDE3.
”®à¬ â ¨­ä®à¬ æ¨¨ ® à §¤¥«¥:
* +0: dword: ­ ç «ì­ë© 䨧¨ç¥áª¨© ᥪâ®à à §¤¥« 
* +4: dword: ¯®á«¥¤­¨© 䨧¨ç¥áª¨© ᥪâ®à à §¤¥« 
(¯à¨­ ¤«¥¦¨â à §¤¥«ã)
* +8: byte: ⨯ ä ©«®¢®© á¨á⥬ë:
* +10+100*(n-1): 100 db: данные для последнего раздела
Разделы расположены в следующем порядке: сначала последовательно все
распознанные разделы на HD на IDE0 (если есть),
затем на HD на IDE1 (если есть) и т.д. до IDE3.
Формат информации о разделе:
* +0: dword: начальный физический сектор раздела
* +4: dword: последний физический сектор раздела
(принадлежит разделу)
* +8: byte: тип файловой системы:
16=FAT16, 32=FAT32, 1=NTFS
* ä®à¬ â ¤ «ì­¥©è¨å ¤ ­­ëå § ¢¨á¨â ®â ä ©«®¢®© á¨á⥬ë,
¬®¦¥â ¬¥­ïâìáï á ¨§¬¥­¥­¨ï¬¨ ¢ ï¤à¥ ¨ ¯®í⮬㠭¥ ®¯¨á뢠¥âáï
‡ ¬¥ç ­¨ï:
* Š®à®âª ï â ¡«¨æ  ¬®¦¥â ¡ëâì ¨á¯®«ì§®¢ ­  ¤«ï ¯®«ã祭¨ï ¨­ä®à¬ æ¨¨
®¡ ¨¬¥îé¨åáï ãáâனá⢠å.
* формат дальнейших данных зависит от файловой системы,
может меняться с изменениями в ядре и поэтому не описывается
Замечания:
* Короткая таблица может быть использована для получения информации
об имеющихся устройствах.
 
======================================================================
========== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 13 - ¯®«ãç¨âì ¢¥àá¨î ï¤à . =========
========== Функция 18, подфункция 13 - получить версию ядра. =========
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à (­¥ ¬¥­¥¥ 16 ¡ ©â), ªã¤  ¡ã¤¥â ¯®¬¥é¥­ 
¨­ä®à¬ æ¨ï
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‘âàãªâãà  ¡ãä¥à :
db a,b,c,d ¤«ï ¢¥àᨨ a.b.c.d
db 0: § à¥§¥à¢¨à®¢ ­®
dd REV - ­®¬¥à svn-ॢ¨§¨¨ ï¤à 
„«ï ï¤à  Kolibri 0.7.7.0+:
Параметры:
* eax = 18 - номер функции
* ebx = 13 - номер подфункции
* ecx = указатель на буфер (не менее 16 байт), куда будет помещена
информация
Возвращаемое значение:
* функция не возвращает значения
Структура буфера:
db a,b,c,d для версии a.b.c.d
db 0: зарезервировано
dd REV - номер svn-ревизии ядра
Для ядра Kolibri 0.7.7.0+:
db 0,7,7,0
db 0
dd 1675
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 14 =====================
======= Ž¦¨¤ âì ­ ç «  ®¡à â­®£® 室  «ãç  à §¢ñà⪨ ¬®­¨â®à . =======
====================== Функция 18, подфункция 14 =====================
======= Ожидать начала обратного хода луча развёртки монитора. =======
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 14 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 ª ª ¯à¨§­ ª ãᯥå 
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ¯à¥¤­ §­ ç¥­  ¨áª«îç¨â¥«ì­® ¤«ï  ªâ¨¢­ëå
¢ë᮪®¯à®¨§¢®¤¨â¥«ì­ëå £à ä¨ç¥áª¨å ¯à¨«®¦¥­¨©; ¨á¯®«ì§ã¥âáï ¤«ï
¯« ¢­®£® ¢ë¢®¤  £à ä¨ª¨.
Параметры:
* eax = 18 - номер функции
* ebx = 14 - номер подфункции
Возвращаемое значение:
* eax = 0 как признак успеха
Замечания:
* Функция предназначена исключительно для активных
высокопроизводительных графических приложений; используется для
плавного вывода графики.
 
======================================================================
== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 15 - ¯®¬¥áâ¨âì ªãàá®à ¬ëè¨ ¢ 業âà íªà ­ . =
== Функция 18, подфункция 15 - поместить курсор мыши в центр экрана. =
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 15 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 ª ª ¯à¨§­ ª ãᯥå 
Параметры:
* eax = 18 - номер функции
* ebx = 15 - номер подфункции
Возвращаемое значение:
* eax = 0 как признак успеха
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 16 =====================
============ ®«ãç¨âì à §¬¥à ᢮¡®¤­®© ®¯¥à â¨¢­®© ¯ ¬ïâ¨. ===========
====================== Функция 18, подфункция 16 =====================
============ Получить размер свободной оперативной памяти. ===========
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 16 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = à §¬¥à ᢮¡®¤­®© ¯ ¬ï⨠¢ ª¨«®¡ ©â å
Параметры:
* eax = 18 - номер функции
* ebx = 16 - номер подфункции
Возвращаемое значение:
* eax = размер свободной памяти в килобайтах
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 17 =====================
============ ®«ãç¨âì à §¬¥à ¨¬¥î饩áï ®¯¥à â¨¢­®© ¯ ¬ïâ¨. ===========
====================== Функция 18, подфункция 17 =====================
============ Получить размер имеющейся оперативной памяти. ===========
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 17 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ®¡é¨© à §¬¥à ¨¬¥î饩áï ¯ ¬ï⨠¢ ª¨«®¡ ©â å
Параметры:
* eax = 18 - номер функции
* ebx = 17 - номер подфункции
Возвращаемое значение:
* eax = общий размер имеющейся памяти в килобайтах
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 18 =====================
============= ‡ ¢¥àè¨âì ¯à®æ¥áá/¯®â®ª ¯® ¨¤¥­â¨ä¨ª â®àã. =============
====================== Функция 18, подфункция 18 =====================
============= Завершить процесс/поток по идентификатору. =============
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 18 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à ¯à®æ¥áá /¯®â®ª  (PID/TID)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = -1 - ®è¨¡ª  (¯à®æ¥áá ­¥ ­ ©¤¥­ ¨«¨ ï¥âáï á¨á⥬­ë¬)
‡ ¬¥ç ­¨ï:
* ¥«ì§ï § ¢¥àè¨âì ¯®â®ª ®¯¥à æ¨®­­®© á¨á⥬ë OS/IDLE (­®¬¥à á«®â 
1), ¬®¦­® § ¢¥àè¨âì «î¡®© ®¡ëç­ë© ¯®â®ª/¯à®æ¥áá.
* ‘¬®âਠ⠪¦¥ ¯®¤äã­ªæ¨î 2 - § ¢¥à襭¨¥
¯à®æ¥áá /¯®â®ª  ¯® § ¤ ­­®¬ã á«®âã.
Параметры:
* eax = 18 - номер функции
* ebx = 18 - номер подфункции
* ecx = идентификатор процесса/потока (PID/TID)
Возвращаемое значение:
* eax = 0 - успешно
* eax = -1 - ошибка (процесс не найден или является системным)
Замечания:
* Нельзя завершить поток операционной системы OS/IDLE (номер слота
1), можно завершить любой обычный поток/процесс.
* Смотри также подфункцию 2 - завершение
процесса/потока по заданному слоту.
 
======================================================================
=== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 19 - ¯®«ãç¨âì/ãáâ ­®¢¨âì ­ áâனª¨ ¬ëè¨. ==
=== Функция 18, подфункция 19 - получить/установить настройки мыши. ==
======================================================================
 
------------- ®¤¯®¤äã­ªæ¨ï 0 - ¯®«ãç¨âì ᪮à®áâì ¬ëè¨. --------------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0 - ­®¬¥à ¯®¤¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ⥪ãé ï ᪮à®áâì ¬ëè¨
------------- Подподфункция 0 - получить скорость мыши. --------------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 0 - номер подподфункции
Возвращаемое значение:
* eax = текущая скорость мыши
 
------------ ®¤¯®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì ᪮à®áâì ¬ëè¨. -------------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 1 - ­®¬¥à ¯®¤¯®¤ä㭪樨
* edx = ­®¢®¥ §­ ç¥­¨¥ ᪮à®áâ¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
------------ Подподфункция 1 - установить скорость мыши. -------------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 1 - номер подподфункции
* edx = новое значение скорости
Возвращаемое значение:
* функция не возвращает значения
 
------------- ®¤¯®¤äã­ªæ¨ï 2 - ¯®«ãç¨âì § ¤¥à¦ªã ¬ëè¨. --------------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 2 - ­®¬¥à ¯®¤¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ⥪ãé ï § ¤¥à¦ª  ¬ëè¨
------------- Подподфункция 2 - получить задержку мыши. --------------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 2 - номер подподфункции
Возвращаемое значение:
* eax = текущая задержка мыши
 
------------ ®¤¯®¤äã­ªæ¨ï 3 - ãáâ ­®¢¨âì § ¤¥à¦ªã ¬ëè¨. -------------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 3 - ­®¬¥à ¯®¤¯®¤ä㭪樨
* edx = ­®¢®¥ §­ ç¥­¨¥ § ¤¥à¦ª¨ ¬ëè¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
------------ Подподфункция 3 - установить задержку мыши. -------------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 3 - номер подподфункции
* edx = новое значение задержки мыши
Возвращаемое значение:
* функция не возвращает значения
 
-------- ®¤¯®¤äã­ªæ¨ï 4 - ãáâ ­®¢¨âì ¯®«®¦¥­¨¥ ªãàá®à  ¬ëè¨. --------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 4 - ­®¬¥à ¯®¤¯®¤ä㭪樨
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
-------- Подподфункция 4 - установить положение курсора мыши. --------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 4 - номер подподфункции
* edx = [координата по оси x]*65536 + [координата по оси y]
Возвращаемое значение:
* функция не возвращает значения
 
------- ®¤¯®¤äã­ªæ¨ï 5 - ᨬ㫨஢ âì á®áâ®ï­¨¥ ª« ¢¨è ¬ëè¨. --------
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 5 - ­®¬¥à ¯®¤¯®¤ä㭪樨
* edx = ¨­ä®à¬ æ¨ï ® í¬ã«¨à㥬®¬ á®áâ®ï­¨¨ ª­®¯®ª ¬ëè¨:
(ᮮ⢥âáâ¢ã¥â ¢®§¢à é ¥¬®¬ã §­ ç¥­¨î ¯®¤ä㭪樨 2 ä㭪樨 37)
* ¡¨â 0 ãáâ ­®¢«¥­ = «¥¢ ï ª­®¯ª  ­ ¦ â 
* ¡¨â 1 ãáâ ­®¢«¥­ = ¯à ¢ ï ª­®¯ª  ­ ¦ â 
* ¡¨â 2 ãáâ ­®¢«¥­ = á।­ïï ª­®¯ª  ­ ¦ â 
* ¡¨â 3 ãáâ ­®¢«¥­ = 4-ï ª­®¯ª  ­ ¦ â 
* ¡¨â 4 ãáâ ­®¢«¥­ = 5-ï ª­®¯ª  ­ ¦ â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ¥ª®¬¥­¤ã¥¬ ï ᪮à®áâì ¬ëè¨ (¢ ¯®¤¯®¤ä㭪樨 1) ®â 1 ¤® 9.
“áâ ­ ¢«¨¢ ¥¬ ï ¢¥«¨ç¨­  ­¥ ¯à®¢¥àï¥âáï ª®¤®¬ ï¤à , ¯®í⮬ã
¨á¯®«ì§ã©â¥ ®áâ®à®¦­®, ¯à¨ ­¥ª®à४⭮¬ §­ ç¥­¨¨ ªãàá®à ¬®¦¥â
"§ ¬ñ৭ãâì". ‘ª®à®áâì ¬ëè¨ ¬®¦­® ॣ㫨஢ âì ¢ ¯à¨«®¦¥­¨¨ SETUP.
* ¥ª®¬¥­¤ã¥¬ ï ¢¥«¨ç¨­  § ¤¥à¦ª¨ (¢ ¯®¤¯®¤ä㭪樨 3) = 10.
Œ¥­ì訥 §­ ç¥­¨ï ­¥ ®¡à ¡ â뢠îâáï COM-¬ëè ¬¨. à¨ ®ç¥­ì ¡®«ìè¨å
§­ ç¥­¨ïå ­¥¢®§¬®¦­® ¯¥à¥¤¢¨¦¥­¨¥ ¬ëè¨ ­  1 ¯¨ªá¥«ì ¨ ªãàá®à ¡ã¤¥â
¯à룠âì ­  ¢¥«¨ç¨­ã ãáâ ­®¢«¥­­®© ᪮à®á⨠(¯®¤¯®¤äã­ªæ¨ï 1).
“áâ ­ ¢«¨¢ ¥¬ ï ¢¥«¨ç¨­  ­¥ ¯à®¢¥àï¥âáï ª®¤®¬ ï¤à .
‚¥«¨ç¨­ã § ¤¥à¦ª¨ ¬®¦­® ¬¥­ïâì ¢ ¯à¨«®¦¥­¨¨ SETUP.
* ®¤¯®¤äã­ªæ¨ï 4 ­¥ ¯à®¢¥àï¥â ¯¥à¥¤ ­­®¥ §­ ç¥­¨¥. ¥à¥¤ ¢ë§®¢®¬
­¥®¡å®¤¨¬® 㧭 âì ⥪ã饥 à §à¥è¥­¨¥ íªà ­  (¯®¤ä㭪樥© 14)
¨ ¯à®¢¥à¨âì, çâ® ãáâ ­ ¢«¨¢ ¥¬®¥ ¯®«®¦¥­¨¥ ­¥ ¢ë室¨â §  ¯à¥¤¥«ë
íªà ­ .
------- Подподфункция 5 - симулировать состояние клавиш мыши. --------
Параметры:
* eax = 18 - номер функции
* ebx = 19 - номер подфункции
* ecx = 5 - номер подподфункции
* edx = информация о эмулируемом состоянии кнопок мыши:
(соответствует возвращаемому значению подфункции 2 функции 37)
* бит 0 установлен = левая кнопка нажата
* бит 1 установлен = правая кнопка нажата
* бит 2 установлен = средняя кнопка нажата
* бит 3 установлен = 4-я кнопка нажата
* бит 4 установлен = 5-я кнопка нажата
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Рекомендуемая скорость мыши (в подподфункции 1) от 1 до 9.
Устанавливаемая величина не проверяется кодом ядра, поэтому
используйте осторожно, при некорректном значении курсор может
"замёрзнуть". Скорость мыши можно регулировать в приложении SETUP.
* Рекомендуемая величина задержки (в подподфункции 3) = 10.
Меньшие значения не обрабатываются COM-мышами. При очень больших
значениях невозможно передвижение мыши на 1 пиксель и курсор будет
прыгать на величину установленной скорости (подподфункция 1).
Устанавливаемая величина не проверяется кодом ядра.
Величину задержки можно менять в приложении SETUP.
* Подподфункция 4 не проверяет переданное значение. Перед вызовом
необходимо узнать текущее разрешение экрана (подфункцией 14)
и проверить, что устанавливаемое положение не выходит за пределы
экрана.
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 20 =====================
============= ®«ãç¨âì ¨­ä®à¬ æ¨î ®¡ ®¯¥à â¨¢­®© ¯ ¬ïâ¨. =============
====================== Функция 18, подфункция 20 =====================
============= Получить информацию об оперативной памяти. =============
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 20 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï ¨­ä®à¬ æ¨¨ (36 ¡ ©â)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ®¡é¨© à §¬¥à ¨¬¥î饩áï ®¯¥à â¨¢­®© ¯ ¬ï⨠¢ ¡ ©â å
¨«¨ -1 ¢ á«ãç ¥ ®è¨¡ª¨
* ¡ãä¥à, ­  ª®â®àë© ãª §ë¢ ¥â ecx, ᮤ¥à¦¨â á«¥¤ãîéãî ¨­ä®à¬ æ¨î:
* +0: dword: ®¡é¨© à §¬¥à ¨¬¥î饩áï ®¯¥à â¨¢­®© ¯ ¬ï⨠¢ áâà ­¨æ å
* +4: dword: à §¬¥à ᢮¡®¤­®© ®¯¥à â¨¢­®© ¯ ¬ï⨠¢ áâà ­¨æ å
* +8: dword: ç¨á«® áâà ­¨ç­ëå ®è¨¡®ª (¨áª«î祭¨© #PF)
¢ ¯à¨«®¦¥­¨ïå
* +12: dword: à §¬¥à ªãç¨ ï¤à  ¢ ¡ ©â å
* +16: dword: à §¬¥à ᢮¡®¤­®© ¯ ¬ï⨠¢ ªãç¥ ï¤à  ¢ ¡ ©â å
* +20: dword: ®¡é¥¥ ª®«¨ç¥á⢮ ¡«®ª®¢ ¯ ¬ï⨠¢ ªãç¥ ï¤à 
* +24: dword: ª®«¨ç¥á⢮ ᢮¡®¤­ëå ¡«®ª®¢ ¯ ¬ï⨠¢ ªãç¥ ï¤à 
* +28: dword: à §¬¥à ­ ¨¡®«ì襣® ᢮¡®¤­®£® ¡«®ª  ¢ ªãç¥ ï¤à 
(§ à¥§¥à¢¨à®¢ ­®)
* +32: dword: à §¬¥à ­ ¨¡®«ì襣® ¢ë¤¥«¥­­®£® ¡«®ª  ¢ ªãç¥ ï¤à 
(§ à¥§¥à¢¨à®¢ ­®)
Параметры:
* eax = 18 - номер функции
* ebx = 20 - номер подфункции
* ecx = указатель на буфер для информации (36 байт)
Возвращаемое значение:
* eax = общий размер имеющейся оперативной памяти в байтах
или -1 в случае ошибки
* буфер, на который указывает ecx, содержит следующую информацию:
* +0: dword: общий размер имеющейся оперативной памяти в страницах
* +4: dword: размер свободной оперативной памяти в страницах
* +8: dword: число страничных ошибок (исключений #PF)
в приложениях
* +12: dword: размер кучи ядра в байтах
* +16: dword: размер свободной памяти в куче ядра в байтах
* +20: dword: общее количество блоков памяти в куче ядра
* +24: dword: количество свободных блоков памяти в куче ядра
* +28: dword: размер наибольшего свободного блока в куче ядра
(зарезервировано)
* +32: dword: размер наибольшего выделенного блока в куче ядра
(зарезервировано)
 
======================================================================
====================== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 21 =====================
======= ®«ãç¨âì ­®¬¥à á«®â  ¯à®æ¥áá /¯®â®ª  ¯® ¨¤¥­â¨ä¨ª â®àã. ======
====================== Функция 18, подфункция 21 =====================
======= Получить номер слота процесса/потока по идентификатору. ======
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 21 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à ¯à®æ¥áá /¯®â®ª  (PID/TID)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ®è¨¡ª  (­¥¢¥à­ë© ¨¤¥­â¨ä¨ª â®à)
* ¨­ ç¥ eax = ­®¬¥à á«®â 
Параметры:
* eax = 18 - номер функции
* ebx = 21 - номер подфункции
* ecx = идентификатор процесса/потока (PID/TID)
Возвращаемое значение:
* eax = 0 - ошибка (неверный идентификатор)
* иначе eax = номер слота
 
======================================================================
”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 22 - ®¯¥à æ¨¨ á ®ª­®¬ ¤à㣮£® ¯à®æ¥áá /¯®â®ª .
Функция 18, подфункция 22 - операции с окном другого процесса/потока.
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 22 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ⨯ ®¯¥à æ¨¨:
* 0 = ¬¨­¨¬¨§ æ¨ï ®ª­ , ¯®â®ª § ¤ ­ ­®¬¥à®¬ á«®â 
* 1 = ¬¨­¨¬¨§ æ¨ï ®ª­ , ¯®â®ª § ¤ ­ ¨¤¥­â¨ä¨ª â®à®¬
* 2 = ¢®ááâ ­®¢«¥­¨¥ ®ª­ , ¯®â®ª § ¤ ­ ­®¬¥à®¬ á«®â 
* 3 = ¢®ááâ ­®¢«¥­¨¥ ®ª­ , ¯®â®ª § ¤ ­ ¨¤¥­â¨ä¨ª â®à®¬
* edx = ¯ à ¬¥âà ®¯¥à æ¨¨ (­®¬¥à á«®â  ¨«¨ PID/TID)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = -1 - ®è¨¡ª  (­¥¯à ¢¨«ì­ë© ¯ à ¬¥âà)
‡ ¬¥ç ­¨ï:
* ®â®ª ¬®¦¥â ᢥà­ãâì ᢮ñ ®ª­® ¢ë§®¢®¬ ¯®¤ä㭪樨 10.
* ‚®ááâ ­®¢«¥­¨¥ ®ª­  á ®¤­®¢à¥¬¥­­®©  ªâ¨¢¨§ æ¨¥© ®áãé¥á⢫ï¥âáï
¯®¤ä㭪樨 3 (¯à¨­¨¬ î饩 ­®¬¥à á«®â ).
Параметры:
* eax = 18 - номер функции
* ebx = 22 - номер подфункции
* ecx = тип операции:
* 0 = минимизация окна, поток задан номером слота
* 1 = минимизация окна, поток задан идентификатором
* 2 = восстановление окна, поток задан номером слота
* 3 = восстановление окна, поток задан идентификатором
* edx = параметр операции (номер слота или PID/TID)
Возвращаемое значение:
* eax = 0 - успешно
* eax = -1 - ошибка (неправильный параметр)
Замечания:
* Поток может свернуть своё окно вызовом подфункции 10.
* Восстановление окна с одновременной активизацией осуществляется
подфункции 3 (принимающей номер слота).
 
======================================================================
======= ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 23 - ¬¨­¨¬¨§¨à®¢ âì ¢á¥ ®ª­ . =========
======= Функция 18, подфункция 23 - минимизировать все окна. =========
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 23 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ¢á¥ ®ª­  ¡ë«¨ ¬¨­¨¬¨§¨à®¢ ­ë ¤® ¢ë§®¢  ä㭪樨
* eax = N - ª®«¨ç¥á⢮ ®ª®­ ᢥà­ãâëå ä㭪樥©
‡ ¬¥ç ­¨ï:
* Žª­  ᯥæ. ¯®â®ª®¢ (¨¬ï ­ ç¨­ ¥âáï á ᨬ¢®«  @) ­¥ ᢮à ç¨¢ îâáï.
Параметры:
* eax = 18 - номер функции
* ebx = 23 - номер подфункции
Возвращаемое значение:
* eax = 0 - все окна были минимизированы до вызова функции
* eax = N - количество окон свернутых функцией
Замечания:
* Окна спец. потоков (имя начинается с символа @) не сворачиваются.
 
======================================================================
===== ”ã­ªæ¨ï 18, ¯®¤äã­ªæ¨ï 24 - ãáâ ­®¢¨âì ¯à¥¤¥«ë ®âà¨á®¢ª¨. ======
===== Функция 18, подфункция 24 - установить пределы отрисовки. ======
======================================================================
 à ¬¥âàë:
* eax = 18 - ­®¬¥à ä㭪樨
* ebx = 24 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¢ë© à §¬¥à ¯® X
* edx = ­®¢ë© à §¬¥à ¯® Y
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­¥ ¬¥­ï¥â 䨧¨ç¥áª¨© à §¬¥à ¢¨¤¥®à¥¦¨¬ . Ž­  ¯à¥¤­ §­ ç¥­ 
¤«ï ­¥áâ ­¤ àâ­ëå ¤¨á¯«¥¥¢, ®â®¡à ¦ îé¨å ¨§®¡à ¦¥­¨¥ ç áâ¨ç­®.
*  §¬¥àë 㪠§ë¢ ¥¬ë¥ ¢ ä㭪樨 ­¥ ¤®«¦­ë ¯à¥¢ëè âì à §¬¥àë ⥪ã饣®
¢¨¤¥®à¥¦¨¬ , ¨­ ç¥ äã­ªæ¨ï ­¨ç¥£® ­¥ ¨§¬¥­¨â.
Параметры:
* eax = 18 - номер функции
* ebx = 24 - номер подфункции
* ecx = новый размер по X
* edx = новый размер по Y
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Функция не меняет физический размер видеорежима. Она предназначена
для нестандартных дисплеев, отображающих изображение частично.
* Размеры указываемые в функции не должны превышать размеры текущего
видеорежима, иначе функция ничего не изменит.
 
======================================================================
==================== ”ã­ªæ¨ï 20 - ¨­â¥à䥩á MIDI. ====================
==================== Функция 20 - интерфейс MIDI. ====================
======================================================================
 
------------------------ ®¤äã­ªæ¨ï 1 - á¡à®á ------------------------
 à ¬¥âàë:
* eax = 20 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
------------------------ Подфункция 1 - сброс ------------------------
Параметры:
* eax = 20 - номер функции
* ebx = 1 - номер подфункции
 
-------------------- ®¤äã­ªæ¨ï 2 - ¢ë¢¥á⨠¡ ©â ---------------------
 à ¬¥âàë:
* eax = 20 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* cl = ¡ ©â ¤«ï ¢ë¢®¤ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥ (®¤¨­ ª®¢® ¤«ï ®¡¥¨å ¯®¤ä㭪権):
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥ ®¯à¥¤¥«ñ­ ¡ §®¢ë© ¯®àâ
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ®¯à¥¤¥«ñ­ ¡ §®¢ë© ¯®à⠢맮¢®¬
¯®¤ä㭪樨 1 ä㭪樨 21.
-------------------- Подфункция 2 - вывести байт ---------------------
Параметры:
* eax = 20 - номер функции
* ebx = 2 - номер подфункции
* cl = байт для вывода
Возвращаемое значение (одинаково для обеих подфункций):
* eax = 0 - успешно
* eax = 1 - не определён базовый порт
Замечания:
* Предварительно должен быть определён базовый порт вызовом
подфункции 1 функции 21.
 
======================================================================
==== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì ¡ §®¢ë© ¯®àâ MPU MIDI. ====
==== Функция 21, подфункция 1 - установить базовый порт MPU MIDI. ====
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à ¡ §®¢®£® ¯®àâ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = -1 - ®è¨¡®ç­ë© ­®¬¥à ¯®àâ 
‡ ¬¥ç ­¨ï:
* ®¬¥à ¯®àâ  ¤®«¦¥­ 㤮¢«¥â¢®àïâì ãá«®¢¨ï¬ 0x100<=ecx<=0xFFFF.
* “áâ ­®¢ª  ¡ §ë ­ã¦­  ¤«ï à ¡®âë ä㭪樨 20.
* ®«ãç¨âì ãáâ ­®¢«¥­­ë© ¡ §®¢ë© ¯®àâ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 1 ä㭪樨 26.
Параметры:
* eax = 21 - номер функции
* ebx = 1 - номер подфункции
* ecx = номер базового порта
Возвращаемое значение:
* eax = 0 - успешно
* eax = -1 - ошибочный номер порта
Замечания:
* Номер порта должен удовлетворять условиям 0x100<=ecx<=0xFFFF.
* Установка базы нужна для работы функции 20.
* Получить установленный базовый порт можно вызовом
подфункции 1 функции 26.
 
======================================================================
===== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 2 - ãáâ ­®¢¨âì à áª« ¤ªã ª« ¢¨ âãàë. ====
===== Функция 21, подфункция 2 - установить раскладку клавиатуры. ====
======================================================================
 áª« ¤ª  ª« ¢¨ âãàë ¨á¯®«ì§ã¥âáï ¤«ï ¯à¥®¡à §®¢ ­¨ï ᪠­ª®¤®¢,
¯®áâ㯠îé¨å ®â ª« ¢¨ âãàë, ¢ ASCII-ª®¤ë, áç¨â뢠¥¬ë¥ ä㭪樥© 2.
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ª ªãî à áª« ¤ªã ãáâ ­ ¢«¨¢ âì:
* 1 = ­®à¬ «ì­ãî
* 2 = à áª« ¤ªã ¯à¨ ­ ¦ â®¬ Shift
* 3 = à áª« ¤ªã ¯à¨ ­ ¦ â®¬ Alt
* edx = 㪠§ â¥«ì ­  à áª« ¤ªã - â ¡«¨æã ¤«¨­®© 128 ¡ ©â
ˆ«¨:
Раскладка клавиатуры используется для преобразования сканкодов,
поступающих от клавиатуры, в ASCII-коды, считываемые функцией 2.
Параметры:
* eax = 21 - номер функции
* ebx = 2 - номер подфункции
* ecx = какую раскладку устанавливать:
* 1 = нормальную
* 2 = раскладку при нажатом Shift
* 3 = раскладку при нажатом Alt
* edx = указатель на раскладку - таблицу длиной 128 байт
Или:
* ecx = 9
* dx = ¨¤¥­â¨ä¨ª â®à áâà ­ë (1=eng, 2=fi, 3=ger, 4=rus)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ¯ à ¬¥âà § ¤ ­ ­¥¢¥à­®
‡ ¬¥ç ­¨ï:
* …᫨ ­ ¦ â Alt, â® ¨á¯®«ì§ã¥âáï à áª« ¤ª  á Alt;
¥á«¨ ­¥ ­ ¦ â Alt, ­® ­ ¦ â Shift, â®
¨á¯®«ì§ã¥âáï à áª« ¤ª  á Shift;
¥á«¨ ­¥ ­ ¦ âë Alt ¨ Shift, ­® ­ ¦ â Ctrl, â® ¨á¯®«ì§ã¥âáï
­®à¬ «ì­ ï à áª« ¤ª , ¯®á«¥ 祣® ¨§ ª®¤  ¢ëç¨â ¥âáï 0x60;
¥á«¨ ­¥ ­ ¦ â  ­¨ ®¤­  ¨§ ã¯à ¢«ïîé¨å ª« ¢¨è, â® ¨á¯®«ì§ã¥âáï
­®à¬ «ì­ ï à áª« ¤ª .
* ®«ãç¨âì à áª« ¤ª¨ ¨ ¨¤¥­â¨ä¨ª â®à áâà ­ë ¬®¦­® á ¯®¬®éìî
¯®¤ä㭪樨 2 ä㭪樨 26.
* ˆ¤¥­â¨ä¨ª â®à áâà ­ë - £«®¡ «ì­ ï á¨á⥬­ ï ¯¥à¥¬¥­­ ï, ª®â®à ï
á ¬¨¬ ï¤à®¬ ­¥ ¨á¯®«ì§ã¥âáï; ®¤­ ª® ¯à¨«®¦¥­¨¥ @panel ®â®¡à ¦ ¥â
ᮮ⢥âáâ¢ãîéãî ⥪ã饩 áâà ­¥ ¨ª®­ªã.
* à¨«®¦¥­¨¥ @panel ¯¥à¥ª«îç ¥â à áª« ¤ª¨ ¯® § ¯à®áã ¯®«ì§®¢ â¥«ï.
* dx = идентификатор страны (1=eng, 2=fi, 3=ger, 4=rus)
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - параметр задан неверно
Замечания:
* Если нажат Alt, то используется раскладка с Alt;
если не нажат Alt, но нажат Shift, то
используется раскладка с Shift;
если не нажаты Alt и Shift, но нажат Ctrl, то используется
нормальная раскладка, после чего из кода вычитается 0x60;
если не нажата ни одна из управляющих клавиш, то используется
нормальная раскладка.
* Получить раскладки и идентификатор страны можно с помощью
подфункции 2 функции 26.
* Идентификатор страны - глобальная системная переменная, которая
самим ядром не используется; однако приложение @panel отображает
соответствующую текущей стране иконку.
* Приложение @panel переключает раскладки по запросу пользователя.
 
======================================================================
=========== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 3 - ãáâ ­®¢¨âì ¡ §ã CD. ===========
=========== Функция 21, подфункция 3 - установить базу CD. ===========
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¡ §  CD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 21 - номер функции
* ebx = 3 - номер подфункции
* ecx = база CD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
*  §  CD ¨á¯®«ì§ã¥âáï ä㭪樥© 24.
* ®«ãç¨âì ãáâ ­®¢«¥­­ãî ¡ §ã CD ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 3 ä㭪樨 26.
Замечания:
* База CD используется функцией 24.
* Получить установленную базу CD можно вызовом
подфункции 3 функции 26.
 
======================================================================
========= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 5 - ãáâ ­®¢¨âì ï§ëª á¨á⥬ë. ========
========= Функция 21, подфункция 5 - установить язык системы. ========
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ï§ëª á¨á⥬ë (1=eng, 2=fi, 3=ger, 4=rus)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 21 - номер функции
* ebx = 5 - номер подфункции
* ecx = язык системы (1=eng, 2=fi, 3=ger, 4=rus)
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
* Ÿ§ëª á¨á⥬ë - £«®¡ «ì­ ï á¨á⥬­ ï ¯¥à¥¬¥­­ ï, ­¨ª ª
­¥ ¨á¯®«ì§ã¥¬ ï á ¬¨¬ ï¤à®¬, ®¤­ ª® ¯à¨«®¦¥­¨¥ @panel à¨áã¥â
ᮮ⢥âáâ¢ãîéãî ¨ª®­ªã.
* à®¢¥à®ª ­  ª®à४⭮áâì ­¥ ¤¥« ¥âáï, ¯®áª®«ìªã ï¤à® íâã
¯¥à¥¬¥­­ãî ­¥ ¨á¯®«ì§ã¥â.
* ®«ãç¨âì ï§ëª á¨áâ¥¬ë ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 5 ä㭪樨 26.
Замечания:
* Язык системы - глобальная системная переменная, никак
не используемая самим ядром, однако приложение @panel рисует
соответствующую иконку.
* Проверок на корректность не делается, поскольку ядро эту
переменную не использует.
* Получить язык системы можно вызовом подфункции 5 функции 26.
 
======================================================================
=========== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 7 - ãáâ ­®¢¨âì ¡ §ã HD. ===========
=========== Функция 21, подфункция 7 - установить базу HD. ===========
======================================================================
 §  HD ­ã¦­  ¤«ï ®¯à¥¤¥«¥­¨ï, ­  ª ª®© ¦ñá⪨© ¤¨áª ¯¨á âì, ¯à¨
¨á¯®«ì§®¢ ­¨¨ ãáâ à¥¢è¥£® ᨭ⠪á¨á  /HD ¢ ãáâ à¥¢è¥© ä㭪樨 58;
¯à¨ ¨á¯®«ì§®¢ ­¨¨ ᮢ६¥­­®£® ᨭ⠪á¨á  /HD0,/HD1,/HD2,/HD3
¡ §  ãáâ ­ ¢«¨¢ ¥âáï  ¢â®¬ â¨ç¥áª¨.
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¡ §  HD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
База HD нужна для определения, на какой жёсткий диск писать, при
использовании устаревшего синтаксиса /HD в устаревшей функции 58;
при использовании современного синтаксиса /HD0,/HD1,/HD2,/HD3
база устанавливается автоматически.
Параметры:
* eax = 21 - номер функции
* ebx = 7 - номер подфункции
* ecx = база HD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
* ‹î¡®¥ ¯à¨«®¦¥­¨¥ ¢ «î¡®© ¬®¬¥­â ¢à¥¬¥­¨ ¬®¦¥â ¨§¬¥­¨âì ¡ §ã.
* ¥ á«¥¤ã¥â ¨§¬¥­ïâì ¡ §ã, ª®£¤  ª ª®¥-­¨¡ã¤ì ¯à¨«®¦¥­¨¥ à ¡®â ¥â
á ¦ñá⪨¬ ¤¨áª®¬. …᫨ ­¥ å®â¨â¥ £«îª®¢ á¨á⥬ë.
* ®«ãç¨âì ãáâ ­®¢«¥­­ãî ¡ §ã ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 7 ä㭪樨 26.
* ‘«¥¤ã¥â â ª¦¥ ®¯à¥¤¥«¨âì ¨á¯®«ì§ã¥¬ë© à §¤¥« ¦ñá⪮£® ¤¨áª 
¯®¤ä㭪樥© 8.
Замечания:
* Любое приложение в любой момент времени может изменить базу.
* Не следует изменять базу, когда какое-нибудь приложение работает
с жёстким диском. Если не хотите глюков системы.
* Получить установленную базу можно вызовом подфункции 7 функции 26.
* Следует также определить используемый раздел жёсткого диска
подфункцией 8.
 
======================================================================
========== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 8 - ãáâ ­®¢¨âì à §¤¥« HD. ==========
========== Функция 21, подфункция 8 - установить раздел HD. ==========
======================================================================
 §¤¥« HD ­ã¦¥­ ¤«ï ®¯à¥¤¥«¥­¨ï, ­  ª ª®© à §¤¥« ¦ñá⪮£® ¤¨áª 
¯¨á âì, ¯à¨ ¨á¯®«ì§®¢ ­¨¨ ãáâ à¥¢è¥£® ᨭ⠪á¨á  /HD ¢ ãáâ à¥¢è¥©
ä㭪樨 58; ¯à¨ ¨á¯®«ì§®¢ ­¨¨ ᮢ६¥­­®£® ᨭ⠪á¨á 
/HD0,/HD1,/HD2,/HD3 ¡ §  ¨ à §¤¥« ãáâ ­ ¢«¨¢ îâáï  ¢â®¬ â¨ç¥áª¨.
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = à §¤¥« HD (áç¨â ï á 1)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Раздел HD нужен для определения, на какой раздел жёсткого диска
писать, при использовании устаревшего синтаксиса /HD в устаревшей
функции 58; при использовании современного синтаксиса
/HD0,/HD1,/HD2,/HD3 база и раздел устанавливаются автоматически.
Параметры:
* eax = 21 - номер функции
* ebx = 8 - номер подфункции
* ecx = раздел HD (считая с 1)
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
* ‹î¡®¥ ¯à¨«®¦¥­¨¥ ¢ «î¡®© ¬®¬¥­â ¢à¥¬¥­¨ ¬®¦¥â ¨§¬¥­¨âì à §¤¥«.
* ¥ á«¥¤ã¥â ¨§¬¥­ïâì à §¤¥«, ª®£¤  ª ª®¥-­¨¡ã¤ì ¯à¨«®¦¥­¨¥ à ¡®â ¥â
á ¦ñá⪨¬ ¤¨áª®¬. …᫨ ­¥ å®â¨â¥ £«îª®¢ á¨á⥬ë.
* ®«ãç¨âì ãáâ ­®¢«¥­­ë© à §¤¥« ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 8
ä㭪樨 26.
* à®¢¥à®ª ­  ª®à४⭮áâì ­¥ ¤¥« ¥âáï.
* “§­ âì ç¨á«® à §¤¥«®¢ ­  ¦ñá⪮¬ ¤¨áª¥ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 11 ä㭪樨 18.
* ‘«¥¤ã¥â â ª¦¥ ®¯à¥¤¥«¨âì ¨á¯®«ì§ã¥¬ãî ¡ §ã ¦ñá⪮£® ¤¨áª 
¯®¤ä㭪樥© 7.
Замечания:
* Любое приложение в любой момент времени может изменить раздел.
* Не следует изменять раздел, когда какое-нибудь приложение работает
с жёстким диском. Если не хотите глюков системы.
* Получить установленный раздел можно вызовом подфункции 8
функции 26.
* Проверок на корректность не делается.
* Узнать число разделов на жёстком диске можно вызовом
подфункции 11 функции 18.
* Следует также определить используемую базу жёсткого диска
подфункцией 7.
 
======================================================================
====================== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 11 =====================
===========  §à¥è¨âì/§ ¯à¥â¨âì ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª HD. ==========
====================== Функция 21, подфункция 11 =====================
=========== Разрешить/запретить низкоуровневый доступ к HD. ==========
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0/1 - § ¯à¥â¨âì/à §à¥è¨âì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 21 - номер функции
* ebx = 11 - номер подфункции
* ecx = 0/1 - запретить/разрешить
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
* ˆá¯®«ì§ã¥âáï ¯à¨ LBA-ç⥭¨¨ (¯®¤äã­ªæ¨ï 8 ä㭪樨 58).
* ’¥ªãé ï ॠ«¨§ æ¨ï ¨á¯®«ì§ã¥â ⮫쪮 ¬« ¤è¨© ¡¨â ecx.
* ®«ãç¨âì ⥪ã饥 á®áâ®ï­¨¥ ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 11 ä㭪樨 26.
Замечания:
* Используется при LBA-чтении (подфункция 8 функции 58).
* Текущая реализация использует только младший бит ecx.
* Получить текущее состояние можно вызовом подфункции 11 функции 26.
 
======================================================================
====================== ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 12 =====================
==========  §à¥è¨âì/§ ¯à¥â¨âì ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI. ==========
====================== Функция 21, подфункция 12 =====================
========== Разрешить/запретить низкоуровневый доступ к PCI. ==========
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 12 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0/1 - § ¯à¥â¨âì/à §à¥è¨âì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 21 - номер функции
* ebx = 12 - номер подфункции
* ecx = 0/1 - запретить/разрешить
Возвращаемое значение:
* eax = 0
‡ ¬¥ç ­¨ï:
* ˆá¯®«ì§ã¥âáï ¯à¨ à ¡®â¥ á 設®© PCI (äã­ªæ¨ï 62).
* ’¥ªãé ï ॠ«¨§ æ¨ï ¨á¯®«ì§ã¥â ⮫쪮 ¬« ¤è¨© ¡¨â ecx.
* ®«ãç¨âì ⥪ã饥 á®áâ®ï­¨¥ ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 12 ä㭪樨 26.
Замечания:
* Используется при работе с шиной PCI (функция 62).
* Текущая реализация использует только младший бит ecx.
* Получить текущее состояние можно вызовом подфункции 12 функции 26.
 
======================================================================
============= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 13, ¯®¤¯®¤äã­ªæ¨ï 1 =============
==== ˆ­¨æ¨ «¨§¨à®¢ âì + ¯®«ãç¨âì ¨­ä®à¬ æ¨î ® ¤à ©¢¥à¥ vmode.mdr. ====
============= Функция 21, подфункция 13, подподфункция 1 =============
==== Инициализировать + получить информацию о драйвере vmode.mdr. ====
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 1 - ­®¬¥à ä㭪樨 ¤à ©¢¥à 
* edx = 㪠§ â¥«ì ­  ¡ãä¥à à §¬¥à  512 ¡ ©â
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ¤à ©¢¥à ­¥ § £à㦥­ (­¨ª®£¤  ­¥ ¡ë¢ ¥â ¢ ⥪ã饩 ॠ«¨§ æ¨¨):
Параметры:
* eax = 21 - номер функции
* ebx = 13 - номер подфункции
* ecx = 1 - номер функции драйвера
* edx = указатель на буфер размера 512 байт
Возвращаемое значение:
* если драйвер не загружен (никогда не бывает в текущей реализации):
* eax = -1
* ebx, ecx à §àãè îâáï
* ¥á«¨ ¤à ©¢¥à § £à㦥­:
* eax = 'MDAZ' (¢ á⨫¥ fasm' , â.¥. 'M' - ¬« ¤è¨© ¡ ©â,
'Z' - áâ à訩) - ᨣ­ âãà 
* ebx = ⥪ãé ï ç áâ®â  à §¢ñà⪨ (¢ ƒæ)
* ecx à §àãè ¥âáï
* ¡ãä¥à, ­  ª®â®àë© ãª §ë¢ ¥â edx, § ¯®«­¥­
”®à¬ â ¡ãä¥à :
* +0: 32*byte: ¨¬ï ¤à ©¢¥à , "Trans VideoDriver" (¡¥§ ª ¢ë祪,
¤®¯®«­¥­® ¯à®¡¥« ¬¨)
* +32 = +0x20: dword: ¢¥àá¨ï ¤à ©¢¥à  (¢¥àá¨ï x.y ª®¤¨àã¥âáï ª ª
y*65536+x), ¤«ï ⥪ã饩 ॠ«¨§ æ¨¨ 1 (1.0)
* +36 = +0x24: 7*dword: § à¥§¥à¢¨à®¢ ­® (0 ¢ ⥪ã饩 ॠ«¨§ æ¨¨)
* +64 = +0x40: 32*word: ᯨ᮪ ¯®¤¤¥à¦¨¢ ¥¬ëå ¢¨¤¥®à¥¦¨¬®¢ (ª ¦¤®¥
á«®¢® - ­®¬¥à ¢¨¤¥®à¥¦¨¬ , ¯®á«¥ ᮡá⢥­­® ᯨ᪠ ¨¤ã⠭㫨)
* +128 = +0x80: 32*(5*word): ᯨ᮪ ¯®¤¤¥à¦¨¢ ¥¬ëå ç áâ®â à §¢ñà⮪
¤«ï ¢¨¤¥®à¥¦¨¬®¢: ¤«ï ª ¦¤®£® ¢¨¤¥®à¥¦¨¬ , 㪠§ ­­®£® ¢ ¯à¥¤ë¤ã饬
¯®«¥, 㪠§ ­® ¤® 5 ¯®¤¤¥à¦¨¢ ¥¬ëå ç áâ®â
(¢ ­¥¨á¯®«ì§ã¥¬ëå ¯®§¨æ¨ïå § ¯¨á ­ë ­ã«¨)
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ¨­¨æ¨ «¨§¨àã¥â ¤à ©¢¥à (¥á«¨ ®­ ¥éñ ­¥ ¨­¨æ¨ «¨§¨à®¢ ­)
¨ ¤®«¦­  ¢ë§ë¢ âìáï ¯¥à¢®©, ¯¥à¥¤ ®áâ «ì­ë¬¨ (¨­ ç¥ ®­¨ ¡ã¤ãâ
¢®§¢à é âì -1, ­¨ç¥£® ­¥ ¤¥« ï).
* ‚ ⥪ã饩 ॠ«¨§ æ¨¨ ¯®¤¤¥à¦¨¢ ¥âáï ⮫쪮 ®¤­  ç áâ®â  à §¢ñà⪨
­  ¢¨¤¥®à¥¦¨¬.
* ebx, ecx разрушаются
* если драйвер загружен:
* eax = 'MDAZ' (в стиле fasm'а, т.е. 'M' - младший байт,
'Z' - старший) - сигнатура
* ebx = текущая частота развёртки (в Гц)
* ecx разрушается
* буфер, на который указывает edx, заполнен
Формат буфера:
* +0: 32*byte: имя драйвера, "Trans VideoDriver" (без кавычек,
дополнено пробелами)
* +32 = +0x20: dword: версия драйвера (версия x.y кодируется как
y*65536+x), для текущей реализации 1 (1.0)
* +36 = +0x24: 7*dword: зарезервировано (0 в текущей реализации)
* +64 = +0x40: 32*word: список поддерживаемых видеорежимов (каждое
слово - номер видеорежима, после собственно списка идут нули)
* +128 = +0x80: 32*(5*word): список поддерживаемых частот развёрток
для видеорежимов: для каждого видеорежима, указанного в предыдущем
поле, указано до 5 поддерживаемых частот
(в неиспользуемых позициях записаны нули)
Замечания:
* Функция инициализирует драйвер (если он ещё не инициализирован)
и должна вызываться первой, перед остальными (иначе они будут
возвращать -1, ничего не делая).
* В текущей реализации поддерживается только одна частота развёртки
на видеорежим.
 
======================================================================
============= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 13, ¯®¤¯®¤äã­ªæ¨ï 2 =============
============= ®«ãç¨âì ¨­ä®à¬ æ¨î ® ⥪ã饬 ¢¨¤¥®à¥¦¨¬¥. =============
============= Функция 21, подфункция 13, подподфункция 2 =============
============= Получить информацию о текущем видеорежиме. =============
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 2 - ­®¬¥à ä㭪樨 ¤à ©¢¥à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤à ©¢¥à ­¥ § £à㦥­ ¨«¨ ­¥ ¨­¨æ¨ «¨§¨à®¢ ­;
ebx,ecx à §àãè îâáï
* eax = [è¨à¨­ ]*65536 + [¢ëá®â ]
* ebx = ç áâ®â  ¢¥à⨪ «ì­®© à §¢ñà⪨ (¢ ƒæ)
* ecx = ­®¬¥à ⥪ã饣® ¢¨¤¥®à¥¦¨¬ 
‡ ¬¥ç ­¨ï:
* „à ©¢¥à ¯à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ¨­¨æ¨ «¨§¨à®¢ ­ ¢ë§®¢®¬
ä㭪樨 ¤à ©¢¥à  1.
* …᫨ ­ã¦­ë ⮫쪮 à §¬¥àë íªà ­ , 楫¥á®®¡à §­¥© ¨á¯®«ì§®¢ âì
äã­ªæ¨î 14 á ãçñ⮬ ⮣®, çâ® ®­  ¢®§¢à é ¥â à §¬¥àë ­  1 ¬¥­ìè¥.
Параметры:
* eax = 21 - номер функции
* ebx = 13 - номер подфункции
* ecx = 2 - номер функции драйвера
Возвращаемое значение:
* eax = -1 - драйвер не загружен или не инициализирован;
ebx,ecx разрушаются
* eax = [ширина]*65536 + [высота]
* ebx = частота вертикальной развёртки (в Гц)
* ecx = номер текущего видеорежима
Замечания:
* Драйвер предварительно должен быть инициализирован вызовом
функции драйвера 1.
* Если нужны только размеры экрана, целесообразней использовать
функцию 14 с учётом того, что она возвращает размеры на 1 меньше.
 
======================================================================
= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 13, ¯®¤¯®¤äã­ªæ¨ï 3 - ãáâ ­®¢¨âì ¢¨¤¥®à¥¦¨¬.
= Функция 21, подфункция 13, подподфункция 3 - установить видеорежим.
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 3 - ­®¬¥à ä㭪樨 ¤à ©¢¥à 
* edx = [ç áâ®â  à §¢ñà⪨]*65536 + [­®¬¥à ¢¨¤¥®à¥¦¨¬ ]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤à ©¢¥à ­¥ § £à㦥­, ­¥ ¨­¨æ¨ «¨§¨à®¢ ­ ¨«¨
¯à®¨§®è«  ®è¨¡ª 
* eax = 0 - ãᯥ譮
* ebx, ecx à §àãè îâáï
‡ ¬¥ç ­¨ï:
* „à ©¢¥à ¯à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ¨­¨æ¨ «¨§¨à®¢ ­ ¢ë§®¢®¬
ä㭪樨 ¤à ©¢¥à  1.
* ®¬¥à ¢¨¤¥®à¥¦¨¬  ¨ ç áâ®â  ¤®«¦­ë ¡ëâì ¢ â ¡«¨æ¥, ¢®§¢à é ¥¬®©
ä㭪樥© ¤à ©¢¥à  1.
Параметры:
* eax = 21 - номер функции
* ebx = 13 - номер подфункции
* ecx = 3 - номер функции драйвера
* edx = [частота развёртки]*65536 + [номер видеорежима]
Возвращаемое значение:
* eax = -1 - драйвер не загружен, не инициализирован или
произошла ошибка
* eax = 0 - успешно
* ebx, ecx разрушаются
Замечания:
* Драйвер предварительно должен быть инициализирован вызовом
функции драйвера 1.
* Номер видеорежима и частота должны быть в таблице, возвращаемой
функцией драйвера 1.
 
======================================================================
============= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 13, ¯®¤¯®¤äã­ªæ¨ï 4 =============
================= ‚¥à­ãâìáï ª ­ ç «ì­®¬ã ¢¨¤¥®à¥¦¨¬ã. ================
============= Функция 21, подфункция 13, подподфункция 4 =============
================= Вернуться к начальному видеорежиму. ================
======================================================================
‚®§¢à é ¥â íªà ­ ¢ ¢¨¤¥®à¥¦¨¬, ãáâ ­®¢«¥­­ë© ¯à¨ § £à㧪¥ á¨á⥬ë.
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 4 - ­®¬¥à ä㭪樨 ¤à ©¢¥à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤à ©¢¥à ­¥ § £à㦥­ ¨«¨ ­¥ ¨­¨æ¨ «¨§¨à®¢ ­
* eax = 0 - ãᯥ譮
* ebx, ecx à §àãè îâáï
‡ ¬¥ç ­¨ï:
* „à ©¢¥à ¯à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ¨­¨æ¨ «¨§¨à®¢ ­ ¢ë§®¢®¬
ä㭪樨 ¤à ©¢¥à  1.
Возвращает экран в видеорежим, установленный при загрузке системы.
Параметры:
* eax = 21 - номер функции
* ebx = 13 - номер подфункции
* ecx = 4 - номер функции драйвера
Возвращаемое значение:
* eax = -1 - драйвер не загружен или не инициализирован
* eax = 0 - успешно
* ebx, ecx разрушаются
Замечания:
* Драйвер предварительно должен быть инициализирован вызовом
функции драйвера 1.
 
======================================================================
============= ”ã­ªæ¨ï 21, ¯®¤äã­ªæ¨ï 13, ¯®¤¯®¤äã­ªæ¨ï 5 =============
======== “¢¥«¨ç¨âì/㬥­ìè¨âì à §¬¥à ¢¨¤¨¬®© ®¡« á⨠¬®­¨â®à . ========
============= Функция 21, подфункция 13, подподфункция 5 =============
======== Увеличить/уменьшить размер видимой области монитора. ========
======================================================================
 à ¬¥âàë:
* eax = 21 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 5 - ­®¬¥à ä㭪樨 ¤à ©¢¥à 
* edx = 0/1 - 㬥­ìè¨âì/㢥«¨ç¨âì à §¬¥à ¯® £®à¨§®­â «¨
­  ®¤­ã ¯®§¨æ¨î
* edx = 2/3 - ¢ ⥪ã饩 ॠ«¨§ æ¨¨ ­¥ ¯®¤¤¥à¦¨¢ ¥âáï; ¯« ­¨àã¥âáï
ª ª 㬥­ì襭¨¥/㢥«¨ç¥­¨¥ à §¬¥à  ¯® ¢¥à⨪ «¨ ­  ®¤­ã ¯®§¨æ¨î
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤à ©¢¥à ­¥ § £à㦥­ ¨«¨ ­¥ ¨­¨æ¨ «¨§¨à®¢ ­
* eax = 0 - ãᯥ譮
* ebx, ecx à §àãè îâáï
‡ ¬¥ç ­¨ï:
* „à ©¢¥à ¯à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ¨­¨æ¨ «¨§¨à®¢ ­ ¢ë§®¢®¬
ä㭪樨 ¤à ©¢¥à  1.
* ”ã­ªæ¨ï ¢«¨ï¥â ⮫쪮 ­  䨧¨ç¥áª¨© à §¬¥à ¨§®¡à ¦¥­¨ï
­  ¬®­¨â®à¥; «®£¨ç¥áª¨© à §¬¥à (ç¨á«® ¯¨ªá¥«¥©) ­¥ ¬¥­ï¥âáï.
Параметры:
* eax = 21 - номер функции
* ebx = 13 - номер подфункции
* ecx = 5 - номер функции драйвера
* edx = 0/1 - уменьшить/увеличить размер по горизонтали
на одну позицию
* edx = 2/3 - в текущей реализации не поддерживается; планируется
как уменьшение/увеличение размера по вертикали на одну позицию
Возвращаемое значение:
* eax = -1 - драйвер не загружен или не инициализирован
* eax = 0 - успешно
* ebx, ecx разрушаются
Замечания:
* Драйвер предварительно должен быть инициализирован вызовом
функции драйвера 1.
* Функция влияет только на физический размер изображения
на мониторе; логический размер (число пикселей) не меняется.
 
======================================================================
============ ”ã­ªæ¨ï 22 - ãáâ ­®¢¨âì á¨á⥬­ãî ¤ âã/¢à¥¬ï. ===========
============ Функция 22 - установить системную дату/время. ===========
======================================================================
 à ¬¥âàë:
* eax = 22 - ­®¬¥à ä㭪樨
* ebx = 0 - ãáâ ­®¢¨âì ¢à¥¬ï
* ecx = 0x00SSMMHH - ¢à¥¬ï ¢ ¤¢®¨ç­®-¤¥áïâ¨ç­®¬ ª®¤¥ (BCD):
* HH=ç á 00..23
* MM=¬¨­ãâ  00..59
* SS=ᥪ㭤  00..59
* ebx = 1 - ãáâ ­®¢¨âì ¤ âã
* ecx = 0x00DDMMYY - ¤ â  ¢ ¤¢®¨ç­®-¤¥áïâ¨ç­®¬ ª®¤¥ (BCD):
* DD=¤¥­ì 01..31
* MM=¬¥áïæ 01..12
* YY=£®¤ 00..99
* ebx = 2 - ãáâ ­®¢¨âì ¤¥­ì ­¥¤¥«¨
* ecx = 1 ¤«ï ¢®áªà¥á¥­ìï, ..., 7 ¤«ï áã¡¡®âë
* ebx = 3 - ãáâ ­®¢¨âì ¡ã¤¨«ì­¨ª
Параметры:
* eax = 22 - номер функции
* ebx = 0 - установить время
* ecx = 0x00SSMMHH - время в двоично-десятичном коде (BCD):
* HH=час 00..23
* MM=минута 00..59
* SS=секунда 00..59
* ebx = 1 - установить дату
* ecx = 0x00DDMMYY - дата в двоично-десятичном коде (BCD):
* DD=день 01..31
* MM=месяц 01..12
* YY=год 00..99
* ebx = 2 - установить день недели
* ecx = 1 для воскресенья, ..., 7 для субботы
* ebx = 3 - установить будильник
* ecx = 0x00SSMMHH
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ¯ à ¬¥âà § ¤ ­ ­¥¢¥à­®
* eax = 2 - CMOS-¡ â à¥©ª¨ à §à廊«¨áì
‡ ¬¥ç ­¨ï:
* –¥­­®áâì ãáâ ­®¢ª¨ ¤­ï ­¥¤¥«¨ ¯à¥¤áâ ¢«ï¥âáï ᮬ­¨â¥«ì­®©,
¯®áª®«ìªã ®­ ¬ «® £¤¥ ¨á¯®«ì§ã¥âáï
(¤¥­ì ­¥¤¥«¨ ¬®¦­® à ááç¨â âì ¯® ¤ â¥).
* ã¤¨«ì­¨ª ¬®¦­® ãáâ ­®¢¨âì ­  áà ¡ â뢠­¨¥ ¢ § ¤ ­­®¥ ¢à¥¬ï
ª ¦¤ë¥ áã⪨. à¨ í⮬ ®âª«îç¨âì ¥£® áãé¥áâ¢ãî騬¨ á¨á⥬­ë¬¨
äã­ªæ¨ï¬¨ ­¥«ì§ï.
* ‘à ¡ â뢠­¨¥ ¡ã¤¨«ì­¨ª  § ª«îç ¥âáï ¢ £¥­¥à æ¨¨ IRQ8.
* ‚®®¡é¥-â® CMOS ¯®¤¤¥à¦¨¢ ¥â ¤«ï ¡ã¤¨«ì­¨ª  ãáâ ­®¢ªã §­ ç¥­¨ï
0xFF ¢ ª ç¥á⢥ ®¤­®£® ¨§ ¯ à ¬¥â஢ ¨ ®§­ ç ¥â íâ®, çâ®
ᮮ⢥âáâ¢ãî騩 ¯ à ¬¥âà ¨£­®à¨àã¥âáï. ® ¢ ⥪ã饩 ॠ«¨§ æ¨¨
íâ® ­¥ ¯à®©¤ñâ (¢¥à­ñâáï §­ ç¥­¨¥ 1).
* ã¤¨«ì­¨ª - £«®¡ «ì­ë© á¨á⥬­ë© à¥áãàá; ãáâ ­®¢ª  ¡ã¤¨«ì­¨ª 
 ¢â®¬ â¨ç¥áª¨ ®â¬¥­ï¥â ¯à¥¤ë¤ãéãî ãáâ ­®¢ªã. ‚¯à®ç¥¬, ­  ¤ ­­ë©
¬®¬¥­â ­¨ ®¤­  ¯à®£à ¬¬  ¥£® ­¥ ¨á¯®«ì§ã¥â.
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - параметр задан неверно
* eax = 2 - CMOS-батарейки разрядились
Замечания:
* Ценность установки дня недели представляется сомнительной,
поскольку он мало где используется
(день недели можно рассчитать по дате).
* Будильник можно установить на срабатывание в заданное время
каждые сутки. При этом отключить его существующими системными
функциями нельзя.
* Срабатывание будильника заключается в генерации IRQ8.
* Вообще-то CMOS поддерживает для будильника установку значения
0xFF в качестве одного из параметров и означает это, что
соответствующий параметр игнорируется. Но в текущей реализации
это не пройдёт (вернётся значение 1).
* Будильник - глобальный системный ресурс; установка будильника
автоматически отменяет предыдущую установку. Впрочем, на данный
момент ни одна программа его не использует.
 
======================================================================
============== ”ã­ªæ¨ï 23 - ®¦¨¤ âì ᮡëâ¨ï á â ©¬ ã⮬. =============
============== Функция 23 - ожидать события с таймаутом. =============
======================================================================
…᫨ ®ç¥à¥¤ì á®®¡é¥­¨© ¯ãáâ , ¦¤ñâ ¯®ï¢«¥­¨ï á®®¡é¥­¨ï ¢ ®ç¥à¥¤¨,
­® ­¥ ¡®«¥¥ 㪠§ ­­®£® ¢à¥¬¥­¨. ‡ â¥¬ áç¨â뢠¥â á®®¡é¥­¨¥ ¨§ ®ç¥à¥¤¨.
Если очередь сообщений пуста, ждёт появления сообщения в очереди,
но не более указанного времени. Затем считывает сообщение из очереди.
 
 à ¬¥âàë:
* eax = 23 - ­®¬¥à ä㭪樨
* ebx = â ©¬ ãâ (¢ á®âëå ¤®«ïå ᥪ㭤ë)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ®ç¥à¥¤ì á®®¡é¥­¨© ¯ãáâ 
* ¨­ ç¥ eax = ᮡë⨥ (ᬮâਠᯨ᮪ ᮡë⨩)
‡ ¬¥ç ­¨ï:
* “ç¨â뢠îâáï ⮫쪮 ⥠ᮡëâ¨ï, ª®â®àë¥ ¢å®¤ïâ ¢ ¬ áªã,
ãáâ ­ ¢«¨¢ ¥¬ãî ä㭪樥© 40. ® 㬮«ç ­¨î í⮠ᮡëâ¨ï
¯¥à¥à¨á®¢ª¨, ­ ¦ â¨ï ­  ª« ¢¨è¨ ¨ ­  ª­®¯ª¨.
* „«ï ¯à®¢¥àª¨, ¥áâì «¨ á®®¡é¥­¨¥ ¢ ®ç¥à¥¤¨, ¨á¯®«ì§ã©â¥ äã­ªæ¨î 11.
—â®¡ë ¦¤ âì ᪮«ì 㣮¤­® ¤®«£®, ¨á¯®«ì§ã©â¥ äã­ªæ¨î 10.
* ¥à¥¤ ç  ebx=0 ¯à¨¢®¤¨â ª ¬®¬¥­â «ì­®¬ã ¢®§¢à é¥­¨î eax=0.
* à¨ ⥪ã饩 ॠ«¨§ æ¨¨ ¯à®¨§®©¤ñâ ­¥¬¥¤«¥­­ë© ¢®§¢à â ¨§ ä㭪樨
á eax=0, ¥á«¨ á«®¦¥­¨¥ ebx á ⥪ã騬 §­ ç¥­¨¥¬ áçñâ稪  ¢à¥¬¥­¨
¢ë§®¢¥â 32-¡¨â­®¥ ¯¥à¥¯®«­¥­¨¥.
Параметры:
* eax = 23 - номер функции
* ebx = таймаут (в сотых долях секунды)
Возвращаемое значение:
* eax = 0 - очередь сообщений пуста
* иначе eax = событие (смотри список событий)
Замечания:
* Учитываются только те события, которые входят в маску,
устанавливаемую функцией 40. По умолчанию это события
перерисовки, нажатия на клавиши и на кнопки.
* Для проверки, есть ли сообщение в очереди, используйте функцию 11.
Чтобы ждать сколь угодно долго, используйте функцию 10.
* Передача ebx=0 приводит к моментальному возвращению eax=0.
* При текущей реализации произойдёт немедленный возврат из функции
с eax=0, если сложение ebx с текущим значением счётчика времени
вызовет 32-битное переполнение.
 
======================================================================
======= ”ã­ªæ¨ï 24, ¯®¤äã­ªæ¨ï 1 - ­ ç âì ¯à®¨£à뢠âì CD-audio. ======
======= Функция 24, подфункция 1 - начать проигрывать CD-audio. ======
======================================================================
 à ¬¥âàë:
* eax = 24 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0x00FRSSMM, £¤¥
* MM = ­ ç «ì­ ï ¬¨­ãâ 
* SS = ­ ç «ì­ ï ᥪ㭤 
* FR = ­ ç «ì­ë© ä३¬
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥ ®¯à¥¤¥«¥­  ¡ §  CD
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ­ã¦­® ®¯à¥¤¥«¨âì ¡ §®¢ë© ¯®àâ CD ¢ë§®¢®¬
¯®¤ä㭪樨 3 ä㭪樨 21.
* ‚ ᥪ㭤¥ 75 ä३¬®¢, ¢ ¬¨­ã⥠60 ᥪ㭤.
* ”ã­ªæ¨ï  á¨­åà®­­  (¢®§¢à é ¥â ã¯à ¢«¥­¨¥, ª®£¤  ­ ç «®áì
¯à®¨£à뢠­¨¥).
Параметры:
* eax = 24 - номер функции
* ebx = 1 - номер подфункции
* ecx = 0x00FRSSMM, где
* MM = начальная минута
* SS = начальная секунда
* FR = начальный фрейм
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - не определена база CD
Замечания:
* Предварительно нужно определить базовый порт CD вызовом
подфункции 3 функции 21.
* В секунде 75 фреймов, в минуте 60 секунд.
* Функция асинхронна (возвращает управление, когда началось
проигрывание).
 
======================================================================
===== ”ã­ªæ¨ï 24, ¯®¤äã­ªæ¨ï 2 - ¯®«ãç¨âì ¨­ä®à¬ æ¨î ® ¤®à®¦ª å. =====
===== Функция 24, подфункция 2 - получить информацию о дорожках. =====
======================================================================
 à ¬¥âàë:
* eax = 24 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï â ¡«¨æë
(¬ ªá¨¬ã¬ 8*64h+4 ¡ ©â=100 ¤®à®¦¥ª)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥ ®¯à¥¤¥«¥­  ¡ §  CD
‡ ¬¥ç ­¨ï:
* ”®à¬ â â ¡«¨æë á ¨­ä®à¬ æ¨¥© ® ¤®à®¦ª å â ª®© ¦¥, ª ª ¨ ¤«ï
ATAPI-CD ª®¬ ­¤ë 43h (READ TOC), ®¡ëç­®© â ¡«¨æë (¯®¤ª®¬ ­¤  00h).
€¤à¥á  ¢®§¢à é îâáï ¢ ä®à¬ â¥ MSF.
* à¥¤¢ à¨â¥«ì­® ­ã¦­® ®¯à¥¤¥«¨âì ¡ §®¢ë© ¯®àâ CD ¢ë§®¢®¬
¯®¤ä㭪樨 3 ä㭪樨 21.
* ”ã­ªæ¨ï ¢®§¢à é ¥â ¨­ä®à¬ æ¨î ⮫쪮 ® ­¥ ¡®«¥¥ 祬 100
¯¥à¢ëå ¤®à®¦ª å. ‚ ¡®«ì設á⢥ á«ãç ¥¢ í⮣® ¤®áâ â®ç­®.
Параметры:
* eax = 24 - номер функции
* ebx = 2 - номер подфункции
* ecx = указатель на буфер для таблицы
(максимум 8*64h+4 байт=100 дорожек)
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - не определена база CD
Замечания:
* Формат таблицы с информацией о дорожках такой же, как и для
ATAPI-CD команды 43h (READ TOC), обычной таблицы (подкоманда 00h).
Адреса возвращаются в формате MSF.
* Предварительно нужно определить базовый порт CD вызовом
подфункции 3 функции 21.
* Функция возвращает информацию только о не более чем 100
первых дорожках. В большинстве случаев этого достаточно.
 
======================================================================
==== ”ã­ªæ¨ï 24, ¯®¤äã­ªæ¨ï 3 - ®áâ ­®¢¨âì ¯à®¨£à뢠¥¬®¥ CD-audio. ===
==== Функция 24, подфункция 3 - остановить проигрываемое CD-audio. ===
======================================================================
 à ¬¥âàë:
* eax = 24 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥ ®¯à¥¤¥«¥­  ¡ §  CD
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ­ã¦­® ®¯à¥¤¥«¨âì ¡ §®¢ë© ¯®àâ CD ¢ë§®¢®¬
¯®¤ä㭪樨 3 ä㭪樨 21.
Параметры:
* eax = 24 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - не определена база CD
Замечания:
* Предварительно нужно определить базовый порт CD вызовом
подфункции 3 функции 21.
 
======================================================================
======= ”ã­ªæ¨ï 24, ¯®¤äã­ªæ¨ï 4 - ¨§¢«¥çì «®â®ª ¯à¨¢®¤  ¤¨áª . ======
======= Функция 24, подфункция 4 - извлечь лоток привода диска. ======
======================================================================
 à ¬¥âàë:
* eax = 24 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à CD/DVD-¤¨áª 
(®â 0=Primary Master ¤® 3=Secondary Slave)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ¯®¤¤¥à¦¨¢ ¥âáï ⮫쪮 ¤«ï ATAPI-ãáâனá⢠(CD ¨ DVD).
* à¨ ¨§¢«¥ç¥­¨¨ «®âª  ¯à®¨§¢®¤¨âáï à §¡«®ª¨à®¢ª  àãç­®£® ã¯à ¢«¥­¨ï
¬¥å ­¨§¬®¬ «®âª .
* à¨ ¨§¢«¥ç¥­¨¨ «®âª  ª®¤ ¯à®¨§¢®¤¨â ®ç¨áâªã ªíè  á®®â¢¥âáâ¢ãî饣®
ãáâனá⢠.
* à¨¬¥à®¬ ¨á¯®«ì§®¢ ­¨ï ä㭪樨 ï¥âáï ¯à¨«®¦¥­¨¥ CD_tray.
Параметры:
* eax = 24 - номер функции
* ebx = 4 - номер подфункции
* ecx = номер CD/DVD-диска
(от 0=Primary Master до 3=Secondary Slave)
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Функция поддерживается только для ATAPI-устройств (CD и DVD).
* При извлечении лотка производится разблокировка ручного управления
механизмом лотка.
* При извлечении лотка код производит очистку кэша соответствующего
устройства.
* Примером использования функции является приложение CD_tray.
 
======================================================================
====== ”ã­ªæ¨ï 24, ¯®¤äã­ªæ¨ï 5 - § £à㧨âì «®â®ª ¯à¨¢®¤  ¤¨áª . =====
====== Функция 24, подфункция 5 - загрузить лоток привода диска. =====
======================================================================
 à ¬¥âàë:
* eax = 24 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à CD/DVD-¤¨áª 
(®â 0=Primary Master ¤® 3=Secondary Slave)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ¯®¤¤¥à¦¨¢ ¥âáï ⮫쪮 ¤«ï ATAPI-ãáâனá⢠(CD ¨ DVD).
* à¨¬¥à®¬ ¨á¯®«ì§®¢ ­¨ï ä㭪樨 ï¥âáï ¯à¨«®¦¥­¨¥ CD_tray.
Параметры:
* eax = 24 - номер функции
* ebx = 5 - номер подфункции
* ecx = номер CD/DVD-диска
(от 0=Primary Master до 3=Secondary Slave)
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Функция поддерживается только для ATAPI-устройств (CD и DVD).
* Примером использования функции является приложение CD_tray.
 
======================================================================
========== ”ã­ªæ¨ï 25 - § ¯¨á âì ®¡« áâì ­  á«®© ä®­ . ===============
========== Функция 25 - записать область на слой фона. ===============
======================================================================
 à ¬¥âàë:
* eax = 25 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¯à¥¤¢ à¨â¥«ì­® ¢ë¤¥«¥­­ãî ®¡« áâì ¯ ¬ïâ¨,
£¤¥ à §¬¥é¥­® ¨á室­®¥ ¨§®¡à ¦¥­¨¥ ¢ ä®à¬ â¥ BBGGRRTTBBGGRRTT...
* ecx = [à §¬¥à ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Š®®à¤¨­ âë ®¡« á⨠- íâ® ª®®à¤¨­ âë ¢¥àå­¥£® «¥¢®£® 㣫 
®¡« á⨠®â­®á¨â¥«ì­® íªà ­ .
*  §¬¥à ¨§®¡à ¦¥­¨ï ¢ ¡ ©â å ¥áâì 4*xsize*ysize.
* TT - ¡ ©â 㪠§ â¥«ì ¯à®§à ç­®áâ¨, ¢ ­ áâ®ï饥 ¢à¥¬ï:
®â 1 ¤® FF - ­¥¯à®§à ç­®, ®â 0 - ¯à®§à ç­®.
* ”ã­ªæ¨ï à §¬¥é ¥â ¨§®¡à ¦¥­¨¥ ­¥ ­  ä®­®¢®¥ ¨§®¡à ¦¥­¨¥ (ä.15),
  ­ ¯àï¬ãî ¢ LFB. Ž¯æ¨¨ ä.15 ¤«ï ä. 25 ­¥ ¨¬¥îâ á¬ëá« .
Параметры:
* eax = 25 - номер функции
* ebx = указатель на предварительно выделенную область памяти,
где размещено исходное изображение в формате BBGGRRTTBBGGRRTT...
* ecx = [размер по оси x]*65536 + [размер по оси y]
* edx = [координата по оси x]*65536 + [координата по оси y]
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Координаты области - это координаты верхнего левого угла
области относительно экрана.
* Размер изображения в байтах есть 4*xsize*ysize.
* TT - байт указатель прозрачности, в настоящее время:
от 1 до FF - непрозрачно, от 0 - прозрачно.
* Функция размещает изображение не на фоновое изображение (ф.15),
а напрямую в LFB. Опции ф.15 для ф. 25 не имеют смысла.
 
======================================================================
===== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì ¡ §®¢ë© ¯®àâ MPU MIDI. =====
===== Функция 26, подфункция 1 - получить базовый порт MPU MIDI. =====
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ­®¬¥à ¯®àâ 
‡ ¬¥ç ­¨ï:
* “áâ ­®¢¨âì ¡ §®¢ë© ¯®àâ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 1 ä㭪樨 21.
Параметры:
* eax = 26 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = номер порта
Замечания:
* Установить базовый порт можно вызовом
подфункции 1 функции 21.
 
======================================================================
====== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 2 - ¯®«ãç¨âì à áª« ¤ªã ª« ¢¨ âãàë. =====
====== Функция 26, подфункция 2 - получить раскладку клавиатуры. =====
======================================================================
 áª« ¤ª  ª« ¢¨ âãàë ¨á¯®«ì§ã¥âáï ¤«ï ¯à¥®¡à §®¢ ­¨ï ᪠­ª®¤®¢,
¯®áâ㯠îé¨å ®â ª« ¢¨ âãàë, ¢ ASCII-ª®¤ë, áç¨â뢠¥¬ë¥ ä㭪樥© 2.
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ª ªãî à áª« ¤ªã ¯®«ãç âì:
* 1 = ­®à¬ «ì­ãî
* 2 = à áª« ¤ªã ¯à¨ ­ ¦ â®¬ Shift
* 3 = à áª« ¤ªã ¯à¨ ­ ¦ â®¬ Alt
* edx = 㪠§ â¥«ì ­  ¡ãä¥à ¤«¨­®© 128 ¡ ©â, ªã¤  ¡ã¤¥â ᪮¯¨à®¢ ­ 
à áª« ¤ª 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
ˆ«¨:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
Раскладка клавиатуры используется для преобразования сканкодов,
поступающих от клавиатуры, в ASCII-коды, считываемые функцией 2.
Параметры:
* eax = 26 - номер функции
* ebx = 2 - номер подфункции
* ecx = какую раскладку получать:
* 1 = нормальную
* 2 = раскладку при нажатом Shift
* 3 = раскладку при нажатом Alt
* edx = указатель на буфер длиной 128 байт, куда будет скопирована
раскладка
Возвращаемое значение:
* функция не возвращает значения
Или:
* eax = 26 - номер функции
* ebx = 2 - номер подфункции
* ecx = 9
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¨¤¥­â¨ä¨ª â®à áâà ­ë (1=eng, 2=fi, 3=ger, 4=rus)
‡ ¬¥ç ­¨ï:
* …᫨ ­ ¦ â Alt, â® ¨á¯®«ì§ã¥âáï à áª« ¤ª  á Alt;
¥á«¨ ­¥ ­ ¦ â Alt, ­® ­ ¦ â Shift, â® ¨á¯®«ì§ã¥âáï
à áª« ¤ª  á Shift;
¥á«¨ ­¥ ­ ¦ âë Alt ¨ Shift, ­® ­ ¦ â Ctrl, â® ¨á¯®«ì§ã¥âáï
­®à¬ «ì­ ï à áª« ¤ª , ¯®á«¥ 祣® ¨§ ª®¤  ¢ëç¨â ¥âáï 0x60;
¥á«¨ ­¥ ­ ¦ â  ­¨ ®¤­  ¨§ ã¯à ¢«ïîé¨å ª« ¢¨è, â® ¨á¯®«ì§ã¥âáï
­®à¬ «ì­ ï à áª« ¤ª .
* “áâ ­®¢¨âì à áª« ¤ª¨ ¨ ¨¤¥­â¨ä¨ª â®à áâà ­ë ¬®¦­® á ¯®¬®éìî
¯®¤ä㭪樨 2 ä㭪樨 21.
* ˆ¤¥­â¨ä¨ª â®à áâà ­ë - £«®¡ «ì­ ï á¨á⥬­ ï ¯¥à¥¬¥­­ ï, ª®â®à ï
á ¬¨¬ ï¤à®¬ ­¥ ¨á¯®«ì§ã¥âáï; ®¤­ ª® ¯à¨«®¦¥­¨¥ @panel ®â®¡à ¦ ¥â
ᮮ⢥âáâ¢ãîéãî ⥪ã饩 áâà ­¥ ¨ª®­ªã
(¨á¯®«ì§ãï ®¯¨á뢠¥¬ãî äã­ªæ¨î).
* à¨«®¦¥­¨¥ @panel ¯¥à¥ª«îç ¥â à áª« ¤ª¨ ¯® § ¯à®áã ¯®«ì§®¢ â¥«ï.
Возвращаемое значение:
* eax = идентификатор страны (1=eng, 2=fi, 3=ger, 4=rus)
Замечания:
* Если нажат Alt, то используется раскладка с Alt;
если не нажат Alt, но нажат Shift, то используется
раскладка с Shift;
если не нажаты Alt и Shift, но нажат Ctrl, то используется
нормальная раскладка, после чего из кода вычитается 0x60;
если не нажата ни одна из управляющих клавиш, то используется
нормальная раскладка.
* Установить раскладки и идентификатор страны можно с помощью
подфункции 2 функции 21.
* Идентификатор страны - глобальная системная переменная, которая
самим ядром не используется; однако приложение @panel отображает
соответствующую текущей стране иконку
(используя описываемую функцию).
* Приложение @panel переключает раскладки по запросу пользователя.
 
======================================================================
============ ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 3 - ¯®«ãç¨âì ¡ §ã CD. ============
============ Функция 26, подфункция 3 - получить базу CD. ============
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¡ §  CD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
‡ ¬¥ç ­¨ï:
*  §  CD ¨á¯®«ì§ã¥âáï ä㭪樥© 24.
* “áâ ­®¢¨âì ¡ §ã CD ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 3 ä㭪樨 21.
Параметры:
* eax = 26 - номер функции
* ebx = 3 - номер подфункции
Возвращаемое значение:
* eax = база CD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
Замечания:
* База CD используется функцией 24.
* Установить базу CD можно вызовом подфункции 3 функции 21.
 
======================================================================
========== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 5 - ¯®«ãç¨âì ï§ëª á¨á⥬ë. =========
========== Функция 26, подфункция 5 - получить язык системы. =========
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ï§ëª á¨á⥬ë (1=eng, 2=fi, 3=ger, 4=rus)
‡ ¬¥ç ­¨ï:
* Ÿ§ëª á¨á⥬ë - £«®¡ «ì­ ï á¨á⥬­ ï ¯¥à¥¬¥­­ ï, ­¨ª ª
­¥ ¨á¯®«ì§ã¥¬ ï á ¬¨¬ ï¤à®¬, ®¤­ ª® ¯à¨«®¦¥­¨¥ @panel à¨áã¥â
ᮮ⢥âáâ¢ãîéãî ¨ª®­ªã (¨á¯®«ì§ãï ®¯¨á뢠¥¬ãî äã­ªæ¨î).
* “áâ ­®¢¨âì ï§ëª á¨áâ¥¬ë ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 5 ä㭪樨 21.
Параметры:
* eax = 26 - номер функции
* ebx = 5 - номер подфункции
Возвращаемое значение:
* eax = язык системы (1=eng, 2=fi, 3=ger, 4=rus)
Замечания:
* Язык системы - глобальная системная переменная, никак
не используемая самим ядром, однако приложение @panel рисует
соответствующую иконку (используя описываемую функцию).
* Установить язык системы можно вызовом подфункции 5 функции 21.
 
======================================================================
============ ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 7 - ¯®«ãç¨âì ¡ §ã HD. ============
============ Функция 26, подфункция 7 - получить базу HD. ============
======================================================================
 §  HD ­ã¦­  ¤«ï ®¯à¥¤¥«¥­¨ï, ­  ª ª®© ¦ñá⪨© ¤¨áª ¯¨á âì, ¯à¨
¨á¯®«ì§®¢ ­¨¨ ãáâ à¥¢è¥£® ᨭ⠪á¨á  /HD ¢ ãáâ à¥¢è¥© ä㭪樨 58;
¯à¨ ¨á¯®«ì§®¢ ­¨¨ ᮢ६¥­­®£® ᨭ⠪á¨á  /HD0,/HD1,/HD2,/HD3
¡ §  ãáâ ­ ¢«¨¢ ¥âáï  ¢â®¬ â¨ç¥áª¨.
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¡ §  HD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
‡ ¬¥ç ­¨ï:
* ‹î¡®¥ ¯à¨«®¦¥­¨¥ ¢ «î¡®© ¬®¬¥­â ¢à¥¬¥­¨ ¬®¦¥â ¨§¬¥­¨âì ¡ §ã.
* “áâ ­®¢¨âì ¡ §ã ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 7 ä㭪樨 21.
* ®«ãç¨âì ¨á¯®«ì§ã¥¬ë© à §¤¥« ¦ñá⪮£® ¤¨áª  ¬®¦­® ¯®¤ä㭪樥© 8.
База HD нужна для определения, на какой жёсткий диск писать, при
использовании устаревшего синтаксиса /HD в устаревшей функции 58;
при использовании современного синтаксиса /HD0,/HD1,/HD2,/HD3
база устанавливается автоматически.
Параметры:
* eax = 26 - номер функции
* ebx = 7 - номер подфункции
Возвращаемое значение:
* eax = база HD: 1=IDE0, 2=IDE1, 3=IDE2, 4=IDE3
Замечания:
* Любое приложение в любой момент времени может изменить базу.
* Установить базу можно вызовом подфункции 7 функции 21.
* Получить используемый раздел жёсткого диска можно подфункцией 8.
 
======================================================================
=========== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 8 - ¯®«ãç¨âì à §¤¥« HD. ===========
=========== Функция 26, подфункция 8 - получить раздел HD. ===========
======================================================================
 §¤¥« HD ­ã¦¥­ ¤«ï ®¯à¥¤¥«¥­¨ï, ­  ª ª®© à §¤¥« ¦ñá⪮£® ¤¨áª 
¯¨á âì, ¯à¨ ¨á¯®«ì§®¢ ­¨¨ ãáâ à¥¢è¥£® ᨭ⠪á¨á  /HD ¢ ãáâ à¥¢è¥©
ä㭪樨 58; ¯à¨ ¨á¯®«ì§®¢ ­¨¨ ᮢ६¥­­®£® ᨭ⠪á¨á 
/HD0,/HD1,/HD2,/HD3 ¡ §  ¨ à §¤¥« ãáâ ­ ¢«¨¢ îâáï  ¢â®¬ â¨ç¥áª¨.
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = à §¤¥« HD (áç¨â ï á 1)
‡ ¬¥ç ­¨ï:
* ‹î¡®¥ ¯à¨«®¦¥­¨¥ ¢ «î¡®© ¬®¬¥­â ¢à¥¬¥­¨ ¬®¦¥â ¨§¬¥­¨âì à §¤¥«.
* “áâ ­®¢¨âì à §¤¥« ¬®¦­® ¢ë§®¢®¬ ¯®¤ä㭪樨 8 ä㭪樨 21.
* “§­ âì ç¨á«® à §¤¥«®¢ ­  ¦ñá⪮¬ ¤¨áª¥ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 11 ä㭪樨 18.
* ®«ãç¨âì ¨á¯®«ì§ã¥¬ãî ¡ §ã ¦ñá⪮£® ¤¨áª  ¬®¦­® ¯®¤ä㭪樥© 7.
Раздел HD нужен для определения, на какой раздел жёсткого диска
писать, при использовании устаревшего синтаксиса /HD в устаревшей
функции 58; при использовании современного синтаксиса
/HD0,/HD1,/HD2,/HD3 база и раздел устанавливаются автоматически.
Параметры:
* eax = 26 - номер функции
* ebx = 8 - номер подфункции
Возвращаемое значение:
* eax = раздел HD (считая с 1)
Замечания:
* Любое приложение в любой момент времени может изменить раздел.
* Установить раздел можно вызовом подфункции 8 функции 21.
* Узнать число разделов на жёстком диске можно вызовом
подфункции 11 функции 18.
* Получить используемую базу жёсткого диска можно подфункцией 7.
 
======================================================================
=== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 9 - ¯®«ãç¨âì §­ ç¥­¨¥ áçñâ稪  ¢à¥¬¥­¨. ===
=== Функция 26, подфункция 9 - получить значение счётчика времени. ===
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® á®âëå ¤®«¥© ᥪ㭤ë, ¯à®è¥¤è¨å á ¬®¬¥­â 
§ ¯ã᪠ á¨á⥬ë
‡ ¬¥ç ­¨ï:
* ‘çñâ稪 ¡¥àñâáï ¯® ¬®¤ã«î 2^32, ç⮠ᮮ⢥âáâ¢ã¥â ­¥¬­®£¨¬ ¡®«¥¥
497 áã⮪.
* ‘¨á⥬­®¥ ¢à¥¬ï ¬®¦­® ¯®«ãç¨âì ä㭪樥© 3.
Параметры:
* eax = 26 - номер функции
* ebx = 9 - номер подфункции
Возвращаемое значение:
* eax = число сотых долей секунды, прошедших с момента
запуска системы
Замечания:
* Счётчик берётся по модулю 2^32, что соответствует немногим более
497 суток.
* Системное время можно получить функцией 3.
 
======================================================================
====================== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 11 =====================
=========== “§­ âì, à §à¥èñ­ «¨ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª HD. ==========
====================== Функция 26, подфункция 11 =====================
=========== Узнать, разрешён ли низкоуровневый доступ к HD. ==========
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0/1 - § ¯à¥éñ­/à §à¥èñ­
‡ ¬¥ç ­¨ï:
* ˆá¯®«ì§ã¥âáï ¯à¨ LBA-ç⥭¨¨ (¯®¤äã­ªæ¨ï 8 ä㭪樨 58).
* “áâ ­®¢¨âì ⥪ã饥 á®áâ®ï­¨¥ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 11 ä㭪樨 21.
Параметры:
* eax = 26 - номер функции
* ebx = 11 - номер подфункции
Возвращаемое значение:
* eax = 0/1 - запрещён/разрешён
Замечания:
* Используется при LBA-чтении (подфункция 8 функции 58).
* Установить текущее состояние можно вызовом
подфункции 11 функции 21.
 
======================================================================
====================== ”ã­ªæ¨ï 26, ¯®¤äã­ªæ¨ï 12 =====================
========== “§­ âì, à §à¥èñ­ «¨ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI. ==========
====================== Функция 26, подфункция 12 =====================
========== Узнать, разрешён ли низкоуровневый доступ к PCI. ==========
======================================================================
 à ¬¥âàë:
* eax = 26 - ­®¬¥à ä㭪樨
* ebx = 12 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0/1 - § ¯à¥éñ­/à §à¥èñ­
‡ ¬¥ç ­¨ï:
* ˆá¯®«ì§ã¥âáï ¯à¨ à ¡®â¥ á 設®© PCI (äã­ªæ¨ï 62).
* ’¥ªãé ï ॠ«¨§ æ¨ï ¨á¯®«ì§ã¥â ⮫쪮 ¬« ¤è¨© ¡¨â ecx.
* “áâ ­®¢¨âì ⥪ã饥 á®áâ®ï­¨¥ ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 12 ä㭪樨 21.
Параметры:
* eax = 26 - номер функции
* ebx = 12 - номер подфункции
Возвращаемое значение:
* eax = 0/1 - запрещён/разрешён
Замечания:
* Используется при работе с шиной PCI (функция 62).
* Текущая реализация использует только младший бит ecx.
* Установить текущее состояние можно вызовом
подфункции 12 функции 21.
 
======================================================================
================ ”ã­ªæ¨ï 29 - ¯®«ãç¨âì á¨á⥬­ãî ¤ âã. ===============
================ Функция 29 - получить системную дату. ===============
======================================================================
 à ¬¥âàë:
* eax = 29 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0x00DDMMYY, £¤¥
(¨á¯®«ì§ã¥âáï ¤¢®¨ç­®-¤¥áïâ¨ç­®¥ ª®¤¨à®¢ ­¨¥, BCD)
* YY = ¤¢¥ ¬« ¤è¨¥ æ¨äàë £®¤  (00..99)
* MM = ¬¥áïæ (01..12)
* DD = ¤¥­ì (01..31)
‡ ¬¥ç ­¨ï:
* ‘¨á⥬­ãî ¤ âã ¬®¦­® ãáâ ­®¢¨âì ä㭪樥© 22.
Параметры:
* eax = 29 - номер функции
Возвращаемое значение:
* eax = 0x00DDMMYY, где
(используется двоично-десятичное кодирование, BCD)
* YY = две младшие цифры года (00..99)
* MM = месяц (01..12)
* DD = день (01..31)
Замечания:
* Системную дату можно установить функцией 22.
 
======================================================================
================ ”ã­ªæ¨ï 30 - à ¡®â  á ⥪ã饩 ¯ ¯ª®©. ===============
================ Функция 30 - работа с текущей папкой. ===============
======================================================================
 
-------- ®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì ⥪ãéãî ¯ ¯ªã ¤«ï ¯®â®ª . ---------
 à ¬¥âàë:
* eax = 30 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¯ãâñ¬ ª ­®¢®© ⥪ã饩 ¯ ¯ª¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
-------- Подфункция 1 - установить текущую папку для потока. ---------
Параметры:
* eax = 30 - номер функции
* ebx = 1 - номер подфункции
* ecx = указатель на ASCIIZ-строку с путём к новой текущей папке
Возвращаемое значение:
* функция не возвращает значения
 
--------- ®¤äã­ªæ¨ï 2 - ¯®«ãç¨âì ⥪ãéãî ¯ ¯ªã ¤«ï ¯®â®ª . ----------
 à ¬¥âàë:
* eax = 30 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à
* edx = à §¬¥à ¡ãä¥à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¤«¨­  ¨¬¥­¨ ⥪ã饩 ¯ ¯ª¨ (¢ª«îç ï § ¢¥àè î騩 0)
‡ ¬¥ç ­¨ï:
* …᫨ à §¬¥à  ¡ãä¥à  ­¥¤®áâ â®ç­® ¤«ï ª®¯¨à®¢ ­¨ï ¢á¥£® ¨¬¥­¨,
ª®¯¨àãîâáï ⮫쪮 ¯¥à¢ë¥ (edx-1) ¡ ©â ¨ ¢ ª®­æ¥ áâ ¢¨âáï
§ ¢¥àè î騩 0.
* ® 㬮«ç ­¨î, ⥪ãé ï ¯ ¯ª  ¤«ï ¯®â®ª  - "/rd/1".
* à¨ ᮧ¤ ­¨¨ ¯à®æ¥áá /¯®â®ª  ⥪ãé ï ¯ ¯ª  ­ á«¥¤ã¥âáï ®â
த¨â¥«ï.
--------- Подфункция 2 - получить текущую папку для потока. ----------
Параметры:
* eax = 30 - номер функции
* ebx = 2 - номер подфункции
* ecx = указатель на буфер
* edx = размер буфера
Возвращаемое значение:
* eax = длина имени текущей папки (включая завершающий 0)
Замечания:
* Если размера буфера недостаточно для копирования всего имени,
копируются только первые (edx-1) байт и в конце ставится
завершающий 0.
* По умолчанию, текущая папка для потока - "/rd/1".
* При создании процесса/потока текущая папка наследуется от
родителя.
 
======================================================================
========= ”ã­ªæ¨ï 34 - 㧭 âì ª®¬ã ¯à¨­ ¤«¥¦¨â â®çª  íªà ­ . =========
========= Функция 34 - узнать кому принадлежит точка экрана. =========
======================================================================
 à ¬¥âàë:
* eax = 34 - ­®¬¥à ä㭪樨
* ebx = x-ª®®à¤¨­ â  (®â­®á¨â¥«ì­® íªà ­ )
* ecx = y-ª®®à¤¨­ â  (®â­®á¨â¥«ì­® íªà ­ )
Параметры:
* eax = 34 - номер функции
* ebx = x-координата (относительно экрана)
* ecx = y-координата (относительно экрана)
 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0x000000XX - â®çª  ¯à¨­ ¤«¥¦¨â á«®âã ®ª­  N
à¨ ­¥ª®à४â­ëå §­ ç¥­¨ïå ebx ¨ ecx äã­ªæ¨ï ¢®§¢à é ¥â 0
* ”ã­ªæ¨ï ¡¥à¥â §­ ç¥­¨ï ¨§ ®¡« á⨠[_WinMapAddress]
Возвращаемое значение:
* eax = 0x000000XX - точка принадлежит слоту окна N
При некорректных значениях ebx и ecx функция возвращает 0
* Функция берет значения из области [_WinMapAddress]
 
======================================================================
============ ”ã­ªæ¨ï 35 - ¯à®ç¨â âì 梥â â®çª¨ ­  íªà ­¥. ============
============ Функция 35 - прочитать цвет точки на экране. ============
======================================================================
 à ¬¥âàë:
Параметры:
* eax = 35
* ebx = y*xsize+x, £¤¥
* (x,y) = ª®®à¤¨­ âë â®çª¨ (áç¨â ï ®â 0)
* xsize = à §¬¥à íªà ­  ¯® £®à¨§®­â «¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 梥â 0x00RRGGBB
‡ ¬¥ç ­¨ï:
* “§­ âì à §¬¥àë íªà ­  ¬®¦­® ¢ë§®¢®¬ ä㭪樨 14. Ž¡à â¨â¥ ¢­¨¬ ­¨¥,
çâ® ®­  ¢ëç¨â ¥â 1 ¨§ ®¡®¨å à §¬¥à®¢.
* Š ¢¨¤¥®¯ ¬ï⨠¥áâì â ª¦¥ ¯àאַ© ¤®áâ㯠(¡¥§ ¢ë§®¢®¢ á¨á⥬­ëå
ä㭪権) ç¥à¥§ ᥫ¥ªâ®à gs.  à ¬¥âàë ⥪ã饣® ¢¨¤¥®à¥¦¨¬ 
¬®¦­® ¯®«ãç¨âì ä㭪樥© 61.
* ebx = y*xsize+x, где
* (x,y) = координаты точки (считая от 0)
* xsize = размер экрана по горизонтали
Возвращаемое значение:
* eax = цвет 0x00RRGGBB
Замечания:
* Узнать размеры экрана можно вызовом функции 14. Обратите внимание,
что она вычитает 1 из обоих размеров.
* К видеопамяти есть также прямой доступ (без вызовов системных
функций) через селектор gs. Параметры текущего видеорежима
можно получить функцией 61.
 
======================================================================
=============== ”ã­ªæ¨ï 36 - ¯à®ç¨â âì ®¡« áâì íªà ­ . ===============
=============== Функция 36 - прочитать область экрана. ===============
======================================================================
 à ¬¥âàë:
* eax = 36 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¯à¥¤¢ à¨â¥«ì­® ¢ë¤¥«¥­­ãî ®¡« áâì ¯ ¬ïâ¨,
ªã¤  ¡ã¤¥â ¯®¬¥é¥­® ¨§®¡à ¦¥­¨¥ ¢ ä®à¬ â¥ BBGGRRBBGGRR...
* ecx = [à §¬¥à ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Š®®à¤¨­ âë ®¡« á⨠- íâ® ª®®à¤¨­ âë ¢¥àå­¥£® «¥¢®£® 㣫 
®¡« á⨠®â­®á¨â¥«ì­® íªà ­ .
*  §¬¥à ¨§®¡à ¦¥­¨ï ¢ ¡ ©â å ¥áâì 3*xsize*ysize.
Параметры:
* eax = 36 - номер функции
* ebx = указатель на предварительно выделенную область памяти,
куда будет помещено изображение в формате BBGGRRBBGGRR...
* ecx = [размер по оси x]*65536 + [размер по оси y]
* edx = [координата по оси x]*65536 + [координата по оси y]
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Координаты области - это координаты верхнего левого угла
области относительно экрана.
* Размер изображения в байтах есть 3*xsize*ysize.
 
======================================================================
==================== ”ã­ªæ¨ï 37 - à ¡®â  á ¬ëèìî. ====================
==================== Функция 37 - работа с мышью. ====================
======================================================================
 
-------------- ®¤äã­ªæ¨ï 0 - íªà ­­ë¥ ª®®à¤¨­ âë ¬ëè¨ ---------------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = x*65536 + y, (x,y)=ª®®à¤¨­ âë ªãàá®à  ¬ëè¨ (áç¨â ï ®â 0)
-------------- Подфункция 0 - экранные координаты мыши ---------------
Параметры:
* eax = 37 - номер функции
* ebx = 0 - номер подфункции
Возвращаемое значение:
* eax = x*65536 + y, (x,y)=координаты курсора мыши (считая от 0)
 
---------- ®¤äã­ªæ¨ï 1 - ª®®à¤¨­ âë ¬ëè¨ ®â­®á¨â¥«ì­® ®ª­  ----------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = x*65536 + y, (x,y)=ª®®à¤¨­ âë ªãàá®à  ¬ëè¨ ®â­®á¨â¥«ì­®
®ª­  ¯à¨«®¦¥­¨ï (áç¨â ï ®â 0)
‡ ¬¥ç ­¨ï:
* ‡­ ç¥­¨¥ ¢ëç¨á«ï¥âáï ¯® ä®à¬ã«¥ (x-xwnd)*65536 + (y-ywnd).
…᫨ y>=ywnd, â® ¬« ¤è¥¥ á«®¢® ­¥®âà¨æ â¥«ì­® ¨ ᮤ¥à¦¨â
®â­®á¨â¥«ì­ãî y-ª®®à¤¨­ âã,   áâ à襥 - ®â­®á¨â¥«ì­ãî x-ª®®à¤¨­ âã
(¯à ¢¨«ì­®£® §­ ª ). ‚ ¯à®â¨¢­®¬ á«ãç ¥ ¬« ¤è¥¥ á«®¢® ®âà¨æ â¥«ì­®
¨ ¢áñ à ¢­® ᮤ¥à¦¨â ®â­®á¨â¥«ì­ãî y-ª®®à¤¨­ âã,
  ª áâ à襬ã á«®¢ã á«¥¤ã¥â ¯à¨¡ ¢¨âì 1.
---------- Подфункция 1 - координаты мыши относительно окна ----------
Параметры:
* eax = 37 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = x*65536 + y, (x,y)=координаты курсора мыши относительно
окна приложения (считая от 0)
Замечания:
* Значение вычисляется по формуле (x-xwnd)*65536 + (y-ywnd).
Если y>=ywnd, то младшее слово неотрицательно и содержит
относительную y-координату, а старшее - относительную x-координату
(правильного знака). В противном случае младшее слово отрицательно
и всё равно содержит относительную y-координату,
а к старшему слову следует прибавить 1.
 
----------------- ®¤äã­ªæ¨ï 2 - ­ ¦ âë¥ ª­®¯ª¨ ¬ëè¨ -----------------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax ᮤ¥à¦¨â ¨­ä®à¬ æ¨î ® ­ ¦ âëå ª­®¯ª å ¬ëè¨:
* ¡¨â 0 ãáâ ­®¢«¥­ = «¥¢ ï ª­®¯ª  ­ ¦ â 
* ¡¨â 1 ãáâ ­®¢«¥­ = ¯à ¢ ï ª­®¯ª  ­ ¦ â 
* ¡¨â 2 ãáâ ­®¢«¥­ = á।­ïï ª­®¯ª  ­ ¦ â 
* ¡¨â 3 ãáâ ­®¢«¥­ = 4-ï ª­®¯ª  ­ ¦ â 
* ¡¨â 4 ãáâ ­®¢«¥­ = 5-ï ª­®¯ª  ­ ¦ â 
* ¯à®ç¨¥ ¡¨âë á¡à®è¥­ë
----------------- Подфункция 2 - нажатые кнопки мыши -----------------
Параметры:
* eax = 37 - номер функции
* ebx = 2 - номер подфункции
Возвращаемое значение:
* eax содержит информацию о нажатых кнопках мыши:
* бит 0 установлен = левая кнопка нажата
* бит 1 установлен = правая кнопка нажата
* бит 2 установлен = средняя кнопка нажата
* бит 3 установлен = 4-я кнопка нажата
* бит 4 установлен = 5-я кнопка нажата
* прочие биты сброшены
 
------------------ ®¤äã­ªæ¨ï 4 - § £à㧨âì ªãàá®à -------------------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* dx = ¨áâ®ç­¨ª ¤ ­­ëå:
* dx = LOAD_FROM_FILE = 0 - ¤ ­­ë¥ ¢ ä ©«¥
* ecx = 㪠§ â¥«ì ­  ¯®«­ë© ¯ãâì ª ä ©«ã ªãàá®à 
* ä ©« ªãàá®à  ¤®«¦¥­ ¡ëâì ¢ ä®à¬ â¥ .cur, áâ ­¤ àâ­®¬ ¤«ï
MS Windows, ¯à¨çñ¬ à §¬¥à®¬ 32*32 ¯¨ªá¥«ï
* dx = LOAD_FROM_MEM = 1 - ¤ ­­ë¥ ä ©«  㦥 § £à㦥­ë ¢ ¯ ¬ïâì
* ecx = 㪠§ â¥«ì ­  ¤ ­­ë¥ ä ©«  ªãàá®à 
* ä®à¬ â ¤ ­­ëå â ª®© ¦¥, ª ª ¨ ¢ ¯à¥¤ë¤ã饬 á«ãç ¥
* dx = LOAD_INDIRECT = 2 - ¤ ­­ë¥ ¢ ¯ ¬ïâ¨
* ecx = 㪠§ â¥«ì ­  ®¡à § ªãàá®à  ¢ ä®à¬ â¥ ARGB 32*32 ¯¨ªá¥«ï
* edx = 0xXXYY0002, £¤¥
* XX = x-ª®®à¤¨­ â  "£®àï祩 â®çª¨" ªãàá®à 
* YY = y-ª®®à¤¨­ â 
------------------ Подфункция 4 - загрузить курсор -------------------
Параметры:
* eax = 37 - номер функции
* ebx = 4 - номер подфункции
* dx = источник данных:
* dx = LOAD_FROM_FILE = 0 - данные в файле
* ecx = указатель на полный путь к файлу курсора
* файл курсора должен быть в формате .cur, стандартном для
MS Windows, причём размером 32*32 пикселя
* dx = LOAD_FROM_MEM = 1 - данные файла уже загружены в память
* ecx = указатель на данные файла курсора
* формат данных такой же, как и в предыдущем случае
* dx = LOAD_INDIRECT = 2 - данные в памяти
* ecx = указатель на образ курсора в формате ARGB 32*32 пикселя
* edx = 0xXXYY0002, где
* XX = x-координата "горячей точки" курсора
* YY = y-координата
* 0 <= XX, YY <= 31
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ­¥ã¤ ç 
* ¨­ ç¥ eax = åí­¤« ªãàá®à 
Возвращаемое значение:
* eax = 0 - неудача
* иначе eax = хэндл курсора
 
------------------ ®¤äã­ªæ¨ï 5 - ãáâ ­®¢¨âì ªãàá®à ------------------
“áâ ­ ¢«¨¢ ¥â ­®¢ë© ªãàá®à ¤«ï ®ª­  ⥪ã饣® ¯®â®ª .
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ªãàá®à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = åí­¤« ¯à¥¤ë¤ã饣® ãáâ ­®¢«¥­­®£® ªãàá®à 
‡ ¬¥ç ­¨ï:
* …᫨ ¯¥à¥¤ ­ ­¥ª®à४â­ë© åí­¤«, â® äã­ªæ¨ï ¢®ááâ ­®¢¨â ªãàá®à
¯® 㬮«ç ­¨î (áâ ­¤ àâ­ãî áâ५ªã). ‚ ç áâ­®áâ¨, ª ¢®ááâ ­®¢«¥­¨î
ªãàá®à  ¯® 㬮«ç ­¨î ¯à¨¢®¤¨â ¯¥à¥¤ ç  ecx=0.
------------------ Подфункция 5 - установить курсор ------------------
Устанавливает новый курсор для окна текущего потока.
Параметры:
* eax = 37 - номер функции
* ebx = 5 - номер подфункции
* ecx = хэндл курсора
Возвращаемое значение:
* eax = хэндл предыдущего установленного курсора
Замечания:
* Если передан некорректный хэндл, то функция восстановит курсор
по умолчанию (стандартную стрелку). В частности, к восстановлению
курсора по умолчанию приводит передача ecx=0.
 
------------------- ®¤äã­ªæ¨ï 6 - 㤠«¨âì ªãàá®à --------------------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ªãàá®à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* Šãàá®à ¤®«¦¥­ ¡ë« ¡ëâì à ­¥¥ § £à㦥­ ⥪ã騬 ¯®â®ª®¬
(¢ë§®¢®¬ ¯®¤ä㭪樨 4). ”ã­ªæ¨ï ­¥ 㤠«ï¥â á¨á⥬­ë¥ ªãàá®àë ¨
ªãàá®àë, § £à㦥­­ë¥ ¤à㣨¬¨ ¯à¨«®¦¥­¨ï¬¨.
* …᫨ 㤠«ï¥âáï  ªâ¨¢­ë© (ãáâ ­®¢«¥­­ë© ¯®¤ä㭪樥© 5) ªãàá®à, â®
¢®ááâ ­ ¢«¨¢ ¥âáï ªãàá®à ¯® 㬮«ç ­¨î (áâ ­¤ àâ­ ï áâ५ª ).
------------------- Подфункция 6 - удалить курсор --------------------
Параметры:
* eax = 37 - номер функции
* ebx = 6 - номер подфункции
* ecx = хэндл курсора
Возвращаемое значение:
* eax разрушается
Замечания:
* Курсор должен был быть ранее загружен текущим потоком
(вызовом подфункции 4). Функция не удаляет системные курсоры и
курсоры, загруженные другими приложениями.
* Если удаляется активный (установленный подфункцией 5) курсор, то
восстанавливается курсор по умолчанию (стандартная стрелка).
 
------------------ ®¤äã­ªæ¨ï 7 - ¤ ­­ë¥ ¯à®ªàã⪨ -------------------
 à ¬¥âàë:
* eax = 37 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
------------------ Подфункция 7 - данные прокрутки -------------------
Параметры:
* eax = 37 - номер функции
* ebx = 7 - номер подфункции
Возвращаемое значение:
* eax = [horizontal offset]*65536 + [vertical offset]
‡ ¬¥ç ­¨ï:
* „ ­­ë¥ ¤®áâ㯭ë ⮫쪮  ªâ¨¢­®¬ã ®ª­ã.
* ®á«¥ ¯à®ç⥭¨ï §­ ç¥­¨ï ®¡­ã«ïîâáï.
* „ ­­ë¥ ¨¬¥îâ §­ ª®¢ë¥ §­ ç¥­¨ï.
Замечания:
* Данные доступны только активному окну.
* После прочтения значения обнуляются.
* Данные имеют знаковые значения.
 
======================================================================
================== ”ã­ªæ¨ï 38 - ­ à¨á®¢ âì ®â१®ª. ==================
================== Функция 38 - нарисовать отрезок. ==================
======================================================================
 à ¬¥âàë:
* eax = 38 - ­®¬¥à ä㭪樨
* ebx = [ª®®à¤¨­ â  ­ ç «  ¯® ®á¨ x]*65536 +
[ª®®à¤¨­ â  ª®­æ  ¯® ®á¨ x]
* ecx = [ª®®à¤¨­ â  ­ ç «  ¯® ®á¨ y]*65536 +
[ª®®à¤¨­ â  ª®­æ  ¯® ®á¨ y]
* edx = 0x00RRGGBB - 梥â
edx = 0x01xxxxxx - à¨á®¢ âì ¨­¢¥àá­ë© ®â१®ª
(¬« ¤è¨¥ 24 ¡¨â  ¨£­®à¨àãîâáï)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Š®®à¤¨­ âë ¡¥àãâáï ®â­®á¨â¥«ì­® ®ª­ .
* Š®­¥ç­ ï â®çª  â ª¦¥ à¨áã¥âáï.
Параметры:
* eax = 38 - номер функции
* ebx = [координата начала по оси x]*65536 +
[координата конца по оси x]
* ecx = [координата начала по оси y]*65536 +
[координата конца по оси y]
* edx = 0x00RRGGBB - цвет
edx = 0x01xxxxxx - рисовать инверсный отрезок
(младшие 24 бита игнорируются)
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Координаты берутся относительно окна.
* Конечная точка также рисуется.
 
======================================================================
== ”ã­ªæ¨ï 39, ¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì à §¬¥à ä®­®¢®£® ¨§®¡à ¦¥­¨ï. ==
== Функция 39, подфункция 1 - получить размер фонового изображения. ==
======================================================================
 à ¬¥âàë:
* eax = 39 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = [è¨à¨­ ]*65536 + [¢ëá®â ]
‡ ¬¥ç ­¨ï:
* …áâì ¯ à­ ï ª®¬ ­¤  ãáâ ­®¢ª¨ à §¬¥à®¢ ä®­®¢®£® ¨§®¡à ¦¥­¨ï -
¯®¤äã­ªæ¨ï 1 ä㭪樨 15. ®á«¥ ª®â®à®©, ࠧ㬥¥âáï, á«¥¤ã¥â
§ ­®¢® ®¯à¥¤¥«¨âì á ¬® ¨§®¡à ¦¥­¨¥.
Параметры:
* eax = 39 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = [ширина]*65536 + [высота]
Замечания:
* Есть парная команда установки размеров фонового изображения -
подфункция 1 функции 15. После которой, разумеется, следует
заново определить само изображение.
 
======================================================================
= ”ã­ªæ¨ï 39, ¯®¤äã­ªæ¨ï 2 - ¯à®ç¨â âì â®çªã á ä®­®¢®£® ¨§®¡à ¦¥­¨ï. =
= Функция 39, подфункция 2 - прочитать точку с фонового изображения. =
======================================================================
 à ¬¥âàë:
* eax = 39 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ᬥ饭¨¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0x00RRGGBB - 梥â â®çª¨, ¥á«¨ ᬥ饭¨¥ ¤®¯ãá⨬®
(¬¥­ìè¥ 0x160000-16)
* eax = 2 - ¨­ ç¥
‡ ¬¥ç ­¨ï:
* ¥ á«¥¤ã¥â ¯®« £ âìáï ­  ¢®§¢à é ¥¬®¥ §­ ç¥­¨¥ ¢ á«ãç ¥ ­¥¢¥à­®£®
ᬥ饭¨ï, ®­® ¬®¦¥â ¨§¬¥­¨âìáï ¢ á«¥¤ãîé¨å ¢¥àá¨ïå ï¤à .
* ‘¬¥é¥­¨¥ â®çª¨ á ª®®à¤¨­ â ¬¨ (x,y) ¢ëç¨á«ï¥âáï ª ª (x+y*xsize)*3.
* …áâì ¯ à­ ï äã­ªæ¨ï ãáâ ­®¢ª¨ â®çª¨ ­  ä®­®¢®¬ ¨§®¡à ¦¥­¨¨ -
¯®¤äã­ªæ¨ï 2 ä㭪樨 15.
Параметры:
* eax = 39 - номер функции
* ebx = 2 - номер подфункции
* ecx = смещение
Возвращаемое значение:
* eax = 0x00RRGGBB - цвет точки, если смещение допустимо
(меньше 0x160000-16)
* eax = 2 - иначе
Замечания:
* Не следует полагаться на возвращаемое значение в случае неверного
смещения, оно может измениться в следующих версиях ядра.
* Смещение точки с координатами (x,y) вычисляется как (x+y*xsize)*3.
* Есть парная функция установки точки на фоновом изображении -
подфункция 2 функции 15.
 
======================================================================
====== ”ã­ªæ¨ï 39, ¯®¤äã­ªæ¨ï 4 - ¯®«ãç¨âì ०¨¬ ®âà¨á®¢ª¨ ä®­ . =====
====== Функция 39, подфункция 4 - получить режим отрисовки фона. =====
======================================================================
 à ¬¥âàë:
* eax = 39 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 1 - § ¬®áâ¨âì
* eax = 2 - à áâï­ãâì
‡ ¬¥ç ­¨ï:
* …áâì ¯ à­ ï äã­ªæ¨ï ãáâ ­®¢ª¨ ०¨¬  ®âà¨á®¢ª¨ ä®­  -
¯®¤äã­ªæ¨ï 4 ä㭪樨 15.
Параметры:
* eax = 39 - номер функции
* ebx = 4 - номер подфункции
Возвращаемое значение:
* eax = 1 - замостить
* eax = 2 - растянуть
Замечания:
* Есть парная функция установки режима отрисовки фона -
подфункция 4 функции 15.
 
======================================================================
======== ”ã­ªæ¨ï 40 - ãáâ ­®¢¨âì ¬ áªã ¤«ï ®¦¨¤ ¥¬ëå ᮡë⨩. ========
======== Функция 40 - установить маску для ожидаемых событий. ========
======================================================================
Œ áª  ¤«ï ®¦¨¤ ¥¬ëå ᮡë⨩ ¢«¨ï¥â ­  ä㭪樨 à ¡®âë á ᮡëâ¨ï¬¨ 10,
11, 23 - ®­¨ á®®¡é îâ ⮫쪮 ® ᮡëâ¨ïå, à §à¥èñ­­ëå í⮩ ¬ áª®©.
 à ¬¥âàë:
* eax = 40 - ­®¬¥à ä㭪樨
* ebx = ¬ áª : ¡¨â i ᮮ⢥âáâ¢ã¥â ᮡëâ¨î i+1 (á¬. ᯨ᮪ ᮡë⨩)
(ãáâ ­®¢«¥­­ë© ¡¨â à §à¥è ¥â ¨§¢¥é¥­¨¥ ® ᮡë⨨)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¯à¥¤ë¤ã饥 §­ ç¥­¨¥ ¬ áª¨
‡ ¬¥ç ­¨ï:
* Œ áª  ¯® 㬮«ç ­¨î (7=111b) à §à¥è ¥â ¨§¢¥é¥­¨ï ® ¯¥à¥à¨á®¢ª¥
¨ ­ ¦ â¨ïå ª« ¢¨è ¨ ª­®¯®ª.
â®£® ¤®áâ â®ç­® ¤«ï ¡®«ì設á⢠ ¯à¨«®¦¥­¨©.
* ‘®¡ëâ¨ï, § ¯à¥éñ­­ë¥ ¢ ¬ áª¥, ¢áñ à ¢­® á®åà ­ïîâáï, ¥á«¨
¯à¨å®¤ïâ; ® ­¨å ¯à®áâ® ­¥ ¨§¢¥é îâ ä㭪樨 à ¡®âë á ᮡëâ¨ï¬¨.
* ”㭪樨 à ¡®âë á ᮡëâ¨ï¬¨ ãç¨â뢠îâ ¬ áªã ­  ¬®¬¥­â
¢ë§®¢  ä㭪樨,   ­¥ ­  ¬®¬¥­â ¯®áâ㯫¥­¨ï á®®¡é¥­¨ï.
Маска для ожидаемых событий влияет на функции работы с событиями 10,
11, 23 - они сообщают только о событиях, разрешённых этой маской.
Параметры:
* eax = 40 - номер функции
* ebx = маска: бит i соответствует событию i+1 (см. список событий)
(установленный бит разрешает извещение о событии)
Возвращаемое значение:
* eax = предыдущее значение маски
Замечания:
* Маска по умолчанию (7=111b) разрешает извещения о перерисовке
и нажатиях клавиш и кнопок.
Этого достаточно для большинства приложений.
* События, запрещённые в маске, всё равно сохраняются, если
приходят; о них просто не извещают функции работы с событиями.
* Функции работы с событиями учитывают маску на момент
вызова функции, а не на момент поступления сообщения.
 
 
======================================================================
=================== ”ã­ªæ¨ï 43 - ¢¢®¤/¢ë¢®¤ ¢ ¯®àâ. ==================
=================== Функция 43 - ввод/вывод в порт. ==================
======================================================================
 
------------------------ ‚뢮¤ ¤ ­­ëå ¢ ¯®àâ -------------------------
 à ¬¥âàë:
* eax = 43 - ­®¬¥à ä㭪樨
* bl = ¡ ©â ¤«ï ¢ë¢®¤ 
* ecx = ­®¬¥à ¯®àâ  0xnnnn (®â 0 ¤® 0xFFFF)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ¯®â®ª ­¥ § à¥§¥à¢¨à®¢ « 㪠§ ­­ë© ¯®àâ
------------------------ Вывод данных в порт -------------------------
Параметры:
* eax = 43 - номер функции
* bl = байт для вывода
* ecx = номер порта 0xnnnn (от 0 до 0xFFFF)
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - поток не зарезервировал указанный порт
 
------------------------ ‚¢®¤ ¤ ­­ëå ¨§ ¯®àâ  ------------------------
 à ¬¥âàë:
* eax = 43 - ­®¬¥à ä㭪樨
* ebx ¨£­®à¨àã¥âáï
* ecx = 0x8000nnnn, £¤¥ nnnn = ­®¬¥à ¯®àâ  (®â 0 ¤® 0xFFFF)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¯à¨ í⮬ ebx = ¢¢¥¤ñ­­ë© ¡ ©â
* eax = 1 - ¯®â®ª ­¥ § à¥§¥à¢¨à®¢ « ¤ ­­ë© ¯®àâ
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¯®â®ª ¤®«¦¥­ § à¥§¥à¢¨à®¢ âì §  ᮡ®©
㪠§ ­­ë© ¯®àâ ä㭪樥© 46.
* „«ï § à¥§¥à¢¨à®¢ ­­ëå ¯®à⮢ ¢¬¥áâ® ¢ë§®¢  íâ¨å ä㭪権
«ãçè¥ ¨á¯®«ì§®¢ âì ª®¬ ­¤ë ¯à®æ¥áá®à  in/out - íâ® §­ ç¨â¥«ì­®
¡ëáâ॥ ¨ ­¥áª®«ìª® ª®à®ç¥ ¨ ¯à®é¥. ˆ§ ­¥§ à¥§¥à¢¨à®¢ ­­ëå
¯®à⮢ ç¨â âì ¢áñ à ¢­® ­¥«ì§ï.
------------------------ Ввод данных из порта ------------------------
Параметры:
* eax = 43 - номер функции
* ebx игнорируется
* ecx = 0x8000nnnn, где nnnn = номер порта (от 0 до 0xFFFF)
Возвращаемое значение:
* eax = 0 - успешно, при этом ebx = введённый байт
* eax = 1 - поток не зарезервировал данный порт
Замечания:
* Предварительно поток должен зарезервировать за собой
указанный порт функцией 46.
* Для зарезервированных портов вместо вызова этих функций
лучше использовать команды процессора in/out - это значительно
быстрее и несколько короче и проще. Из незарезервированных
портов читать всё равно нельзя.
 
 
======================================================================
= ”ã­ªæ¨ï 46 - § à¥§¥à¢¨à®¢ âì/®á¢®¡®¤¨âì £à㯯㠯®à⮢ ¢¢®¤ /¢ë¢®¤ .
= Функция 46 - зарезервировать/освободить группу портов ввода/вывода.
======================================================================
Š § à¥§¥à¢¨à®¢ ­­ë¬ ¯®àâ ¬ ¬®¦­® ®¡à é âìáï ­ ¯àï¬ãî ¨§ ¯à¨«®¦¥­¨ï
ª®¬ ­¤ ¬¨ in/out (४®¬¥­¤ã¥¬ë© ᯮᮡ) ¨ ¢ë§®¢®¬ ä㭪樨 43
(­¥à¥ª®¬¥­¤ã¥¬ë© ᯮᮡ).
 à ¬¥âàë:
* eax = 46 - ­®¬¥à ä㭪樨
* ebx = 0 - § à¥§¥à¢¨à®¢ âì, 1 - ®á¢®¡®¤¨âì
* ecx = ­®¬¥à ­ ç «  ¤¨ ¯ §®­  ¯®à⮢
* edx = ­®¬¥à ª®­æ  ¤¨ ¯ §®­  ¯®à⮢ (¢ª«îç¨â¥«ì­®)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ®è¨¡ª 
‡ ¬¥ç ­¨ï:
* ‚ á«ãç ¥ १¥à¢¨à®¢ ­¨ï ¯®à⮢ ®è¨¡ª®© áç¨â ¥âáï ¢ë¯®«­¥­¨¥
®¤­®£® ¨§ ãá«®¢¨©:
* ­ ç «ì­ë©  ¤à¥á ¡®«ìè¥ ª®­¥ç­®£®;
* 㪠§ ­­ë© ¤¨ ¯ §®­ ᮤ¥à¦¨â ­¥ª®à४â­ë© ­®¬¥à ¯®àâ 
(ª®à४â­ë¥ - ®â 0 ¤® 0xFFFF);
* ¯à¥¢ë襭® ®£à ­¨ç¥­¨¥ ­  ®¡é¥¥ ç¨á«® § à¥§¥à¢¨à®¢ ­­ëå ®¡« á⥩
- ¤®¯ã᪠¥âáï ¬ ªá¨¬ã¬ 255;
* 㪠§ ­­ë© ¤¨ ¯ §®­ ¯¥à¥á¥ª ¥âáï á ®¤­¨¬ ¨§
à ­¥¥ § à¥§¥à¢¨à®¢ ­­ëå
* ‚ á«ãç ¥ ®á¢®¡®¦¤¥­¨ï ¯®à⮢ ®è¨¡ª®© áç¨â ¥âáï ¯®¯ë⪠
®á¢®¡®¦¤¥­¨ï ¤¨ ¯ §®­ , ª®â®àë© à ­¥¥ ­¥ ¡ë« 楫¨ª®¬
§ à¥§¥à¢¨à®¢ ­ í⮩ ¦¥ ä㭪樥© (á â ª¨¬¨ ¦¥ §­ ç¥­¨ï¬¨ ecx,edx).
* à¨ ®¡­ à㦥­¨¨ ®è¨¡ª¨ (¢ ®¡®¨å á«ãç ïå) ­¨ª ª¨å ¤¥©á⢨©
­¥ ¯à®¨§¢®¤¨âáï.
* à¨ § £à㧪¥ á¨á⥬  १¥à¢¨àã¥â §  ᮡ®© ¯®àâë
0..0x2d, 0x30..0x4d, 0x50..0xdf, 0xe5..0xff (¢ª«îç¨â¥«ì­®).
* à¨ § ¢¥à襭¨¨ ¯®â®ª   ¢â®¬ â¨ç¥áª¨ ®á¢®¡®¦¤ îâáï ¢á¥
§ à¥§¥à¢¨à®¢ ­­ë¥ ¨¬ ¯®àâë.
К зарезервированным портам можно обращаться напрямую из приложения
командами in/out (рекомендуемый способ) и вызовом функции 43
(нерекомендуемый способ).
Параметры:
* eax = 46 - номер функции
* ebx = 0 - зарезервировать, 1 - освободить
* ecx = номер начала диапазона портов
* edx = номер конца диапазона портов (включительно)
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - ошибка
Замечания:
* В случае резервирования портов ошибкой считается выполнение
одного из условий:
* начальный адрес больше конечного;
* указанный диапазон содержит некорректный номер порта
(корректные - от 0 до 0xFFFF);
* превышено ограничение на общее число зарезервированных областей
- допускается максимум 255;
* указанный диапазон пересекается с одним из
ранее зарезервированных
* В случае освобождения портов ошибкой считается попытка
освобождения диапазона, который ранее не был целиком
зарезервирован этой же функцией (с такими же значениями ecx,edx).
* При обнаружении ошибки (в обоих случаях) никаких действий
не производится.
* При загрузке система резервирует за собой порты
0..0x2d, 0x30..0x4d, 0x50..0xdf, 0xe5..0xff (включительно).
* При завершении потока автоматически освобождаются все
зарезервированные им порты.
 
======================================================================
================= ”ã­ªæ¨ï 47 - ¢ë¢¥á⨠ç¨á«® ¢ ®ª­®. =================
================= Функция 47 - вывести число в окно. =================
======================================================================
 à ¬¥âàë:
* eax = 47 - ­®¬¥à ä㭪樨
* ebx = ¯ à ¬¥âàë ¯à¥®¡à §®¢ ­¨ï ç¨á«  ¢ ⥪áâ:
* bl = 0 - ecx ᮤ¥à¦¨â ç¨á«®
* bl = 1 - ecx ᮤ¥à¦¨â 㪠§ â¥«ì ­  dword/qword-ç¨á«®
* bh = 0 - ®â®¡à ¦ âì ¢ ¤¥áïâ¨ç­®© á¨á⥬¥ áç¨á«¥­¨ï
* bh = 1 - ®â®¡à ¦ âì ¢ è¥áâ­ ¤æ â¥à¨ç­®© á¨á⥬¥
* bh = 2 - ®â®¡à ¦ âì ¢ ¤¢®¨ç­®© á¨á⥬¥
* ¡¨âë 16-21 = ᪮«ìª® æ¨äà ®â®¡à ¦ âì
* ¡¨âë 22-29 § à¥§¥à¢¨à®¢ ­ë ¨ ¤®«¦­ë ¡ëâì ãáâ ­®¢«¥­ë ¢ 0
* ¡¨â 30 ãáâ ­®¢«¥­ = ¢ë¢®¤¨âì qword (64-¡¨â­®¥ ç¨á«®);
¯à¨ í⮬ ¤®«¦­® ¡ëâì bl = 1
* ¡¨â 31 ãáâ ­®¢«¥­ = ­¥ ¢ë¢®¤¨âì ¢¥¤ã騥 ­ã«¨ ç¨á« 
* ecx = ç¨á«® (¯à¨ bl=0) ¨«¨ 㪠§ â¥«ì (¯à¨ bl=1)
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
Параметры:
* eax = 47 - номер функции
* ebx = параметры преобразования числа в текст:
* bl = 0 - ecx содержит число
* bl = 1 - ecx содержит указатель на dword/qword-число
* bh = 0 - отображать в десятичной системе счисления
* bh = 1 - отображать в шестнадцатеричной системе
* bh = 2 - отображать в двоичной системе
* биты 16-21 = сколько цифр отображать
* биты 22-29 зарезервированы и должны быть установлены в 0
* бит 30 установлен = выводить qword (64-битное число);
при этом должно быть bl = 1
* бит 31 установлен = не выводить ведущие нули числа
* ecx = число (при bl=0) или указатель (при bl=1)
* edx = [координата по оси x]*65536 + [координата по оси y]
* esi = 0xX0RRGGBB:
* RR, GG, BB § ¤ îâ 梥â
* X = ABnn (¡¨âë)
* nn = èà¨äâ (0/1)
* A ¨£­®à¨àã¥âáï
* B=1 - § ªà è¨¢ âì ä®­ 梥⮬ edi
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* “ª § ­­ ï ¤«¨­  ­¥ ¤®«¦­  ¯à¥¢®á室¨âì 60.
* ‚뢮¤¨âáï ஢­® 㪠§ ­­®¥ ª®«¨ç¥á⢮ æ¨äà. …᫨ ç¨á«® ¬ «® ¨
¬®¦¥â ¡ëâì § ¯¨á ­® ¬¥­ì訬 ª®«¨ç¥á⢮¬ æ¨äà, ®­® ¤®¯®«­ï¥âáï
¢¥¤ã騬¨ ­ã«ï¬¨; ¥á«¨ ç¨á«® ¢¥«¨ª® ¨ ­¥ ¬®¦¥â ¡ëâì § ¯¨á ­®
â ª¨¬ ª®«¨ç¥á⢮¬ æ¨äà, "«¨è­¨¥" ¢¥¤ã騥 æ¨äàë ®¡à¥§ îâáï.
*  à ¬¥âàë èà¨ä⮢ 㪠§ ­ë ¢ ®¯¨á ­¨¨ ä㭪樨 4 (¢ë¢®¤  ⥪áâ ).
* RR, GG, BB задают цвет
* X = ABnn (биты)
* nn = шрифт (0/1)
* A игнорируется
* B=1 - закрашивать фон цветом edi
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Указанная длина не должна превосходить 60.
* Выводится ровно указанное количество цифр. Если число мало и
может быть записано меньшим количеством цифр, оно дополняется
ведущими нулями; если число велико и не может быть записано
таким количеством цифр, "лишние" ведущие цифры обрезаются.
* Параметры шрифтов указаны в описании функции 4 (вывода текста).
 
======================================================================
======= ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 0 - ¯à¨¬¥­¨âì ­ áâனª¨ íªà ­ . =======
======= Функция 48, подфункция 0 - применить настройки экрана. =======
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0 - § à¥§¥à¢¨à®¢ ­®
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ¯¥à¥à¨á®¢ë¢ ¥â íªà ­ ¯®á«¥ ¨§¬¥­¥­¨ï ¯ à ¬¥â஢
¯®¤äã­ªæ¨ï¬¨ 1 ¨ 2.
* ‚맮¢ ä㭪樨 ¡¥§ ¯à¥¤è¥áâ¢ãîé¨å ¢ë§®¢®¢ 㪠§ ­­ëå ¯®¤ä㭪権
¨£­®à¨àã¥âáï.
* ‚맮¢ ä㭪樨 á ­¥­ã«¥¢ë¬ ecx ¨£­®à¨àã¥âáï.
Параметры:
* eax = 48 - номер функции
* ebx = 0 - номер подфункции
* ecx = 0 - зарезервировано
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Функция перерисовывает экран после изменения параметров
подфункциями 1 и 2.
* Вызов функции без предшествующих вызовов указанных подфункций
игнорируется.
* Вызов функции с ненулевым ecx игнорируется.
 
======================================================================
========= ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì áâ¨«ì ª­®¯®ª. ========
========= Функция 48, подфункция 1 - установить стиль кнопок. ========
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ⨯ ª­®¯®ª:
* 0 = ¯«®áª¨¥
* 1 = ®¡êñ¬­ë¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ®á«¥ ¢ë§®¢  ®¯¨á뢠¥¬®© ä㭪樨 á«¥¤ã¥â ¯¥à¥à¨á®¢ âì íªà ­
¯®¤ä㭪樥© 0.
* ’¨¯ ª­®¯®ª ¢«¨ï¥â ⮫쪮 ­  ¨å ¯à®à¨á®¢ªã ä㭪樥© 8.
Параметры:
* eax = 48 - номер функции
* ebx = 1 - номер подфункции
* ecx = тип кнопок:
* 0 = плоские
* 1 = объёмные
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* После вызова описываемой функции следует перерисовать экран
подфункцией 0.
* Тип кнопок влияет только на их прорисовку функцией 8.
 
======================================================================
==== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 2 - ãáâ ­®¢¨âì áâ ­¤ àâ­ë¥ æ¢¥â  ®ª®­. ===
==== Функция 48, подфункция 2 - установить стандартные цвета окон. ===
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  â ¡«¨æã 梥⮢
* edx = à §¬¥à â ¡«¨æë 梥⮢
(¤®«¦¥­ ¡ëâì 40 ¡ ©â ¤«ï ¡ã¤ã饩 ᮢ¬¥á⨬®áâ¨)
”®à¬ â â ¡«¨æë 梥⮢ 㪠§ ­ ¢ ®¯¨á ­¨¨ ¯®¤ä㭪樨 3.
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ®á«¥ ¢ë§®¢  ®¯¨á뢠¥¬®© ä㭪樨 á«¥¤ã¥â ¯¥à¥à¨á®¢ âì íªà ­
¯®¤ä㭪樥© 0.
* ’ ¡«¨æ  áâ ­¤ àâ­ëå 梥⮢ ¢«¨ï¥â ⮫쪮 ­  ¯à¨«®¦¥­¨ï,
ª®â®àë¥ íâã â ¡«¨æã ï¢­ë¬ ®¡à §®¬ ¯®«ãç îâ (¯®¤ä㭪樥© 3) ¨
¨á¯®«ì§ãîâ (㪠§ë¢ ï æ¢¥â  ¨§ ­¥ñ ¯à¨ ¢ë§®¢ å ä㭪権 à¨á®¢ ­¨ï).
* ’ ¡«¨æ  áâ ­¤ àâ­ëå 梥⮢ ¢å®¤¨â ¢ ᪨­ ¨ ãáâ ­ ¢«¨¢ ¥âáï § ­®¢®
¯à¨ ãáâ ­®¢ª¥ ᪨­  (¯®¤ä㭪樨 8).
* ’ ¡«¨æã 梥⮢ ¬®¦­® ¯à®á¬ âਢ âì/¨§¬¥­ïâì ¨­â¥à ªâ¨¢­® á ¯®¬®éìî
¯à¨«®¦¥­¨ï desktop.
Параметры:
* eax = 48 - номер функции
* ebx = 2 - номер подфункции
* ecx = указатель на таблицу цветов
* edx = размер таблицы цветов
(должен быть 40 байт для будущей совместимости)
Формат таблицы цветов указан в описании подфункции 3.
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* После вызова описываемой функции следует перерисовать экран
подфункцией 0.
* Таблица стандартных цветов влияет только на приложения,
которые эту таблицу явным образом получают (подфункцией 3) и
используют (указывая цвета из неё при вызовах функций рисования).
* Таблица стандартных цветов входит в скин и устанавливается заново
при установке скина (подфункции 8).
* Таблицу цветов можно просматривать/изменять интерактивно с помощью
приложения desktop.
 
======================================================================
===== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 3 - ¯®«ãç¨âì áâ ­¤ àâ­ë¥ æ¢¥â  ®ª®­. ====
===== Функция 48, подфункция 3 - получить стандартные цвета окон. ====
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à à §¬¥à®¬ edx ¡ ©â,
ªã¤  ¡ã¤¥â § ¯¨á ­  â ¡«¨æ 
* edx = à §¬¥à â ¡«¨æë 梥⮢
(¤®«¦¥­ ¡ëâì 40 ¡ ©â ¤«ï ¡ã¤ã饩 ᮢ¬¥á⨬®áâ¨)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
”®à¬ â â ¡«¨æë 梥⮢: ª ¦¤ë© í«¥¬¥­â -
dword-§­ ç¥­¨¥ æ¢¥â  0x00RRGGBB
* +0: dword: frames - 梥â à ¬ª¨
* +4: dword: grab - 梥⠧ £®«®¢ª 
* +8: dword: grab_button - 梥⠪­®¯ª¨ ­  ¯®«®á¥ § £®«®¢ª 
* +12 = +0xC: dword: grab_button_text - 梥â ⥪áâ  ­  ª­®¯ª¥
­  ¯®«®á¥ § £®«®¢ª 
* +16 = +0x10: dword: grab_text - 梥â ⥪áâ  ­  § £®«®¢ª¥
* +20 = +0x14: dword: work - 梥â à ¡®ç¥© ®¡« áâ¨
* +24 = +0x18: dword: work_button - 梥⠪­®¯ª¨ ¢ à ¡®ç¥© ®¡« áâ¨
* +28 = +0x1C: dword: work_button_text - 梥â ⥪áâ  ­  ª­®¯ª¥
¢ à ¡®ç¥© ®¡« áâ¨
* +32 = +0x20: dword: work_text - 梥â ⥪áâ  ¢ à ¡®ç¥© ®¡« áâ¨
* +36 = +0x24: dword: work_graph - 梥⠣à ä¨ª¨ ¢ à ¡®ç¥© ®¡« áâ¨
‡ ¬¥ç ­¨ï:
* ‘âàãªâãà  â ¡«¨æë 梥⮢ ®¯¨á ­  ¢ áâ ­¤ àâ­®¬ ¢ª«îç ¥¬®¬ ä ©«¥
macros.inc ¯®¤ ­ §¢ ­¨¥¬ system_colors; ­ ¯à¨¬¥à, ¬®¦­® ¯¨á âì:
sc system_colors ; ®¡ê¥­¨¥ ¯¥à¥¬¥­­®©
... ; £¤¥-â® ­ ¤® ¢ë§¢ âì
; ®¯¨á뢠¥¬ãî äã­ªæ¨î á ecx=sc
mov ecx, [sc.work_button_text] ; ç¨â ¥¬ 梥â ⥪áâ 
; ­  ª­®¯ª¥ ¢ à ¡®ç¥© ®¡« áâ¨
* ˆá¯®«ì§®¢ ­¨¥/­¥¨á¯®«ì§®¢ ­¨¥ íâ¨å 梥⮢ - ¤¥«® ¨áª«îç¨â¥«ì­®
á ¬®© ¯à®£à ¬¬ë. „«ï ¨á¯®«ì§®¢ ­¨ï ­ã¦­® ¯à®áâ® ¯à¨ ¢ë§®¢¥ ä㭪権
à¨á®¢ ­¨ï 㪠§ë¢ âì 梥â, ¢§ïâë© ¨§ í⮩ â ¡«¨æë.
* à¨ ¨§¬¥­¥­¨¨ â ¡«¨æë áâ ­¤ àâ­ëå 梥⮢ (¯®¤ä㭪樥© 2 á
¯®á«¥¤ãî騬 ¯à¨¬¥­¥­¨¥¬ ¨§¬¥­¥­¨© ¯®¤ä㭪樥© 0 ¨«¨
¯à¨ ãáâ ­®¢ª¥ ᪨­  ¯®¤ä㭪樥© 8) ¢á¥¬ ®ª­ ¬ ¯®áë« ¥âáï á®®¡é¥­¨¥
® ­¥®¡å®¤¨¬®á⨠¯¥à¥à¨á®¢ª¨ (ᮡë⨥ á ª®¤®¬ 1).
* ‘â ­¤ àâ­ë¥ æ¢¥â  ¬®¦­® ¯à®á¬ âਢ âì/¨§¬¥­ïâì ¨­â¥à ªâ¨¢­®
á ¯®¬®éìî ¯à¨«®¦¥­¨ï desktop.
Параметры:
* eax = 48 - номер функции
* ebx = 3 - номер подфункции
* ecx = указатель на буфер размером edx байт,
куда будет записана таблица
* edx = размер таблицы цветов
(должен быть 40 байт для будущей совместимости)
Возвращаемое значение:
* функция не возвращает значения
Формат таблицы цветов: каждый элемент -
dword-значение цвета 0x00RRGGBB
* +0: dword: frames - цвет рамки
* +4: dword: grab - цвет заголовка
* +8: dword: grab_button - цвет кнопки на полосе заголовка
* +12 = +0xC: dword: grab_button_text - цвет текста на кнопке
на полосе заголовка
* +16 = +0x10: dword: grab_text - цвет текста на заголовке
* +20 = +0x14: dword: work - цвет рабочей области
* +24 = +0x18: dword: work_button - цвет кнопки в рабочей области
* +28 = +0x1C: dword: work_button_text - цвет текста на кнопке
в рабочей области
* +32 = +0x20: dword: work_text - цвет текста в рабочей области
* +36 = +0x24: dword: work_graph - цвет графики в рабочей области
Замечания:
* Структура таблицы цветов описана в стандартном включаемом файле
macros.inc под названием system_colors; например, можно писать:
sc system_colors ; объявление переменной
... ; где-то надо вызвать
; описываемую функцию с ecx=sc
mov ecx, [sc.work_button_text] ; читаем цвет текста
; на кнопке в рабочей области
* Использование/неиспользование этих цветов - дело исключительно
самой программы. Для использования нужно просто при вызове функций
рисования указывать цвет, взятый из этой таблицы.
* При изменении таблицы стандартных цветов (подфункцией 2 с
последующим применением изменений подфункцией 0 или
при установке скина подфункцией 8) всем окнам посылается сообщение
о необходимости перерисовки (событие с кодом 1).
* Стандартные цвета можно просматривать/изменять интерактивно
с помощью приложения desktop.
 
======================================================================
========== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 4 - ¯®«ãç¨âì ¢ëá®âã ᪨­ . =========
========== Функция 48, подфункция 4 - получить высоту скина. =========
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¢ëá®â  ᪨­ 
‡ ¬¥ç ­¨ï:
* ‚ëá®â®© ᪨­  ¯® ®¯à¥¤¥«¥­¨î áç¨â ¥âáï ¢ëá®â  § £®«®¢ª  ®ª®­,
¨á¯®«ì§ãîé¨å ᪨­.
* ‘¬®âਠ⠪¦¥ ®¡éãî áâàãªâãàã ®ª­  ¢ ®¯¨á ­¨¨ ä㭪樨 0.
Параметры:
* eax = 48 - номер функции
* ebx = 4 - номер подфункции
Возвращаемое значение:
* eax = высота скина
Замечания:
* Высотой скина по определению считается высота заголовка окон,
использующих скин.
* Смотри также общую структуру окна в описании функции 0.
 
======================================================================
===== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 5 - ¯®«ãç¨âì à ¡®çãî ®¡« áâì íªà ­ . ====
===== Функция 48, подфункция 5 - получить рабочую область экрана. ====
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Параметры:
* eax = 48 - номер функции
* ebx = 5 - номер подфункции
Возвращаемое значение:
* eax = [left]*65536 + [right]
* ebx = [top]*65536 + [bottom]
‡ ¬¥ç ­¨ï:
*  ¡®ç ï ®¡« áâì íªà ­  ®¯à¥¤¥«ï¥â ¯®«®¦¥­¨¥ ¨ ª®®à¤¨­ âë
¬ ªá¨¬¨§¨à®¢ ­­®£® ®ª­ .
*  ¡®ç ï ®¡« áâì íªà ­  ¯à¨ ­®à¬ «ì­®© à ¡®â¥ ¥áâì ¢¥áì íªà ­
§  ¢ëç¥â®¬ ¯ ­¥«¨ (@panel).
* (left,top) - ª®®à¤¨­ âë «¥¢®£® ¢¥àå­¥£® 㣫 ,
(right,bottom) - ª®®à¤¨­ âë ¯à ¢®£® ­¨¦­¥£®.
’ ª¨¬ ®¡à §®¬, à §¬¥à à ¡®ç¥© ®¡« á⨠¯® ®á¨ x ®¯à¥¤¥«ï¥âáï
ä®à¬ã«®© right-left+1, ¯® ®á¨ y - ä®à¬ã«®© bottom-right+1.
* ‘¬®âਠ⠪¦¥ äã­ªæ¨î 14,
¯®§¢®«ïîéãî ®¯à¥¤¥«¨âì à §¬¥àë ¢á¥£® íªà ­ .
* …áâì ¯ à­ ï äã­ªæ¨ï ãáâ ­®¢ª¨ à ¡®ç¥© ®¡« á⨠- ¯®¤äã­ªæ¨ï 6.
Замечания:
* Рабочая область экрана определяет положение и координаты
максимизированного окна.
* Рабочая область экрана при нормальной работе есть весь экран
за вычетом панели (@panel).
* (left,top) - координаты левого верхнего угла,
(right,bottom) - координаты правого нижнего.
Таким образом, размер рабочей области по оси x определяется
формулой right-left+1, по оси y - формулой bottom-right+1.
* Смотри также функцию 14,
позволяющую определить размеры всего экрана.
* Есть парная функция установки рабочей области - подфункция 6.
 
======================================================================
==== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 6 - ãáâ ­®¢¨âì à ¡®çãî ®¡« áâì íªà ­ . ===
==== Функция 48, подфункция 6 - установить рабочую область экрана. ===
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
Параметры:
* eax = 48 - номер функции
* ebx = 6 - номер подфункции
* ecx = [left]*65536 + [right]
* edx = [top]*65536 + [bottom]
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
*  ¡®ç ï ®¡« áâì íªà ­  ®¯à¥¤¥«ï¥â ¯®«®¦¥­¨¥ ¨ ª®®à¤¨­ âë
¬ ªá¨¬¨§¨à®¢ ­­®£® ®ª­ .
* â  äã­ªæ¨ï ¨á¯®«ì§ã¥âáï ⮫쪮 ¯à¨«®¦¥­¨¥¬ @panel,
ãáâ ­ ¢«¨¢ î騬 à ¡®ç¥© ®¡« áâìî ¢¥áì íªà ­ §  ¢ëç¥â®¬ ¯ ­¥«¨.
* (left,top) - ª®®à¤¨­ âë «¥¢®£® ¢¥àå­¥£® 㣫 ,
(right,bottom) - ª®®à¤¨­ âë ¯à ¢®£® ­¨¦­¥£®.
’ ª¨¬ ®¡à §®¬, à §¬¥à à ¡®ç¥© ®¡« á⨠¯® ®á¨ x ®¯à¥¤¥«ï¥âáï
ä®à¬ã«®© right-left+1, ¯® ®á¨ y - ä®à¬ã«®© bottom-right+1.
* …᫨ left>=right, â® x-ª®®à¤¨­ âë à ¡®ç¥© ®¡« á⨠­¥ ¨§¬¥­ïîâáï.
…᫨ left<0, â® left ­¥ ãáâ ­ ¢«¨¢ ¥âáï. …᫨ right ¡®«ìè¥
¨«¨ à ¢­® è¨à¨­ë íªà ­ , â® right ­¥ ãáâ ­ ¢«¨¢ ¥âáï.
€­ «®£¨ç­® ¯® ®á¨ y.
* ‘¬®âਠ⠪¦¥ äã­ªæ¨î 14,
¯®§¢®«ïîéãî ®¯à¥¤¥«¨âì à §¬¥àë ¢á¥£® íªà ­ .
* …áâì ¯ à­ ï äã­ªæ¨ï ¯®«ã祭¨ï à ¡®ç¥© ®¡« á⨠-
¯®¤äã­ªæ¨ï 5.
* â  äã­ªæ¨ï  ¢â®¬ â¨ç¥áª¨ ¯¥à¥à¨á®¢ë¢ ¥â íªà ­, ¯® 室㠤¥« 
®¡­®¢«ï¥â ª®®à¤¨­ âë ¨ à §¬¥àë ¬ ªá¨¬¨§¨à®¢ ­­ëå ®ª®­.
‚ᥠ®ª­  ¨§¢¥é îâáï ® ­¥®¡å®¤¨¬®á⨠¯¥à¥à¨á®¢ª¨ (ᮡë⨥ 1).
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Рабочая область экрана определяет положение и координаты
максимизированного окна.
* Эта функция используется только приложением @panel,
устанавливающим рабочей областью весь экран за вычетом панели.
* (left,top) - координаты левого верхнего угла,
(right,bottom) - координаты правого нижнего.
Таким образом, размер рабочей области по оси x определяется
формулой right-left+1, по оси y - формулой bottom-right+1.
* Если left>=right, то x-координаты рабочей области не изменяются.
Если left<0, то left не устанавливается. Если right больше
или равно ширины экрана, то right не устанавливается.
Аналогично по оси y.
* Смотри также функцию 14,
позволяющую определить размеры всего экрана.
* Есть парная функция получения рабочей области -
подфункция 5.
* Эта функция автоматически перерисовывает экран, по ходу дела
обновляет координаты и размеры максимизированных окон.
Все окна извещаются о необходимости перерисовки (событие 1).
 
======================================================================
====================== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 7 ======================
============ ®«ãç¨âì ®¡« áâì ᪨­  ¤«ï ⥪áâ  § £®«®¢ª . ============
====================== Функция 48, подфункция 7 ======================
============ Получить область скина для текста заголовка. ============
======================================================================
‚®§¢à é ¥â ®¡« áâì § £®«®¢ª  ®ª­  ᮠ᪨­®¬, ¯à¥¤­ §­ ç¥­­ãî
¤«ï ¢ë¢®¤  ⥪áâ  § £®«®¢ª .
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
Возвращает область заголовка окна со скином, предназначенную
для вывода текста заголовка.
Параметры:
* eax = 48 - номер функции
* ebx = 7 - номер подфункции
Возвращаемое значение:
* eax = [left]*65536 + [right]
* ebx = [top]*65536 + [bottom]
‡ ¬¥ç ­¨ï:
* ˆá¯®«ì§®¢ ­¨¥/­¥¨á¯®«ì§®¢ ­¨¥ í⮩ ä㭪樨 -
«¨ç­®¥ ¤¥«® ¯à¨«®¦¥­¨ï.
* ¥ª®¬¥­¤ã¥âáï ãç¨â뢠âì §­ ç¥­¨ï, ¢®§¢à é ¥¬ë¥ í⮩ ä㭪樥©,
¯à¨ ¢ë¡®à¥ ¬¥áâ  ¤«ï à¨á®¢ ­¨ï ⥪áâ  § £®«®¢ª  (ä㭪樥© 4) ¨«¨
ª ª®£®-­¨¡ã¤ì § ¬¥­¨â¥«ï ⥪áâ  § £®«®¢ª 
(¯® ãᬮâ७¨î ¯à¨«®¦¥­¨ï).
Замечания:
* Использование/неиспользование этой функции -
личное дело приложения.
* Рекомендуется учитывать значения, возвращаемые этой функцией,
при выборе места для рисования текста заголовка (функцией 4) или
какого-нибудь заменителя текста заголовка
(по усмотрению приложения).
 
======================================================================
==== ”ã­ªæ¨ï 48, ¯®¤äã­ªæ¨ï 8 - ãáâ ­®¢¨âì ¨á¯®«ì§ã¥¬ë© ᪨­ ®ª®­. ===
==== Функция 48, подфункция 8 - установить используемый скин окон. ===
======================================================================
 à ¬¥âàë:
* eax = 48 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¨¬ï ä ©«  ᪨­ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥ 㤠«®áì § £à㧨âì ä ©«
* eax = 2 - ä ©« ­¥ ï¥âáï ä ©«®¬ ᪨­ 
‡ ¬¥ç ­¨ï:
* à¨ ãᯥ譮© § £à㧪¥ ᪨­  ¢á¥ ®ª­  ¨§¢¥é îâáï ® ­¥®¡å®¤¨¬®áâ¨
¯¥à¥à¨á®¢ª¨ (ᮡë⨥ 1).
* à¨ § £à㧪¥ á¨á⥬  áç¨â뢠¥â ᪨­ ¨§ ä ©«  default.skn
­  à ¬¤¨áª¥.
* ®«ì§®¢ â¥«ì ¬®¦¥â ¨§¬¥­ïâì ᪨­ áâ â¨ç¥áª¨, ᮧ¤ ¢ ᢮©
default.skn, ¨«¨ ¤¨­ ¬¨ç¥áª¨ á ¯®¬®éìî ¯à¨«®¦¥­¨ï desktop.
Параметры:
* eax = 48 - номер функции
* ebx = 8 - номер подфункции
* ecx = указатель на имя файла скина
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - не удалось загрузить файл
* eax = 2 - файл не является файлом скина
Замечания:
* При успешной загрузке скина все окна извещаются о необходимости
перерисовки (событие 1).
* При загрузке система считывает скин из файла default.skn
на рамдиске.
* Пользователь может изменять скин статически, создав свой
default.skn, или динамически с помощью приложения desktop.
 
======================================================================
============ ”ã­ªæ¨ï 49 - Advanced Power Management (APM). ===========
============ Функция 49 - Advanced Power Management (APM). ===========
======================================================================
 à ¬¥âàë:
* eax = 49 - ­®¬¥à ä㭪樨
* dx = ­®¬¥à ä㭪樨 APM ( ­ «®£ ax ¢ ᯥæ¨ä¨ª æ¨¨)
* bx, cx = ¯ à ¬¥âàë ä㭪樨 APM
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* 16-¡¨â­ë¥ ॣ¨áâàë ax, bx, cx, dx, si, di ¨ ä« £ CF
ãáâ ­®¢«¥­ë ¢ ᮮ⢥âá⢨¨ ᮠᯥæ¨ä¨ª æ¨¥© APM
* áâ à訥 ¯®«®¢¨­ë 32-¡¨â­ëå ॣ¨áâ஢ eax, ebx, ecx,
edx, esi, edi à §àãè îâáï
‡ ¬¥ç ­¨ï:
* ‘¯¥æ¨ä¨ª æ¨ï APM 1.2 ®¯¨á뢠¥âáï ¢ ¤®ªã¬¥­â¥
Параметры:
* eax = 49 - номер функции
* dx = номер функции APM (аналог ax в спецификации)
* bx, cx = параметры функции APM
Возвращаемое значение:
* 16-битные регистры ax, bx, cx, dx, si, di и флаг CF
установлены в соответствии со спецификацией APM
* старшие половины 32-битных регистров eax, ebx, ecx,
edx, esi, edi разрушаются
Замечания:
* Спецификация APM 1.2 описывается в документе
"Advanced Power Management (APM) BIOS Specification"
(Revision 1.2), ¤®áâ㯭®¬ ­ 
(Revision 1.2), доступном на
http://www.microsoft.com/whdc/archive/amp_12.mspx;
ªà®¬¥ ⮣®, ®­  ¢ª«î祭  ¢ ¨§¢¥áâ­ë© Interrupt List by Ralf Brown
кроме того, она включена в известный Interrupt List by Ralf Brown
(http://www.pobox.com/~ralf/files.html,
ftp://ftp.cs.cmu.edu/afs/cs/user/ralf/pub/).
 
======================================================================
================= ”ã­ªæ¨ï 50 - ãáâ ­®¢ª  ä®à¬ë ®ª­ . =================
================= Функция 50 - установка формы окна. =================
======================================================================
Ž¡ëç­ë¥ ®ª­  ¯à¥¤áâ ¢«ïîâ ᮡ®© ¯àאַ㣮«ì­¨ª¨. ‘ ¯®¬®éìî í⮩ ä㭪樨
®ª­ã ¬®¦­® ¯à¨¤ âì ¯à®¨§¢®«ì­ãî ä®à¬ã. ”®à¬  § ¤ ñâáï ­ ¡®à®¬ â®ç¥ª
¢­ãâਠ®¡à ¬«ïî饣® ¯àאַ㣮«ì­¨ª , ¯à¨­ ¤«¥¦ é¨å ®ª­ã. ®«®¦¥­¨¥ ¨
à §¬¥àë ®¡à ¬«ïî饣® ¯àאַ㣮«ì­¨ª  § ¤ îâáï ä㭪樥© 0 ¨ ¨§¬¥­ïîâáï
ä㭪樥© 67.
Обычные окна представляют собой прямоугольники. С помощью этой функции
окну можно придать произвольную форму. Форма задаётся набором точек
внутри обрамляющего прямоугольника, принадлежащих окну. Положение и
размеры обрамляющего прямоугольника задаются функцией 0 и изменяются
функцией 67.
 
--------------- “áâ ­®¢ª  ¤ ­­ëå á ¨­ä®à¬ æ¨¥© ® ä®à¬¥ ---------------
 à ¬¥âàë:
* eax = 50 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¤ ­­ë¥ ä®à¬ë (¬ áᨢ ¡ ©â 0/1)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
--------------- Установка данных с информацией о форме ---------------
Параметры:
* eax = 50 - номер функции
* ebx = 0 - номер подфункции
* ecx = указатель на данные формы (массив байт 0/1)
Возвращаемое значение:
* функция не возвращает значения
 
------------------ “áâ ­®¢ª  ¬ áèâ ¡  ¤ ­­ëå ä®à¬ë -------------------
 à ¬¥âàë:
* eax = 50 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx § ¤ ñâ ¬ áèâ ¡: ª ¦¤ë© ¡ ©â ¤ ­­ëå ®¯à¥¤¥«ï¥â
(2^scale)*(2^scale) ¯¨ªá¥«¥©
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Œ áèâ ¡ ¯® 㬮«ç ­¨î à ¢¥­ 0 (¬ áèâ ¡¨àãî騩 ¬­®¦¨â¥«ì 1). …᫨ ¢
¤ ­­ëå ä®à¬ë ®¤¨­ ¡ ©â ᮮ⢥âáâ¢ã¥â ®¤­®¬ã ¯¨ªá¥«î, â® ¬ áèâ ¡
¬®¦­® ­¥ ãáâ ­ ¢«¨¢ âì.
* Ž¡®§­ ç¨¬ xsize = è¨à¨­  ®ª­  (¢ ¯¨ªá¥«ïå), ysize = ¢ëá®â ;
®¡à â¨â¥ ¢­¨¬ ­¨¥, çâ® ®­¨ ­  ¥¤¨­¨æã ¡®«ìè¥, 祬 ãáâ ­ ¢«¨¢ ¥¬ë¥
äã­ªæ¨ï¬¨ 0, 67.
* ® ®¯à¥¤¥«¥­¨î ¬ áèâ ¡  xsize ¨ ysize ¤®«¦­ë ¤¥«¨âìáï ­  2^scale.
*  ©â ¤ ­­ëå ¯® ᬥ饭¨î a ¤®«¦¥­ ¡ëâì 0/1 ¨
®¯à¥¤¥«ï¥â ¯à¨­ ¤«¥¦­®áâì ®ª­ã ª¢ ¤à â  á® áâ®à®­®© 2^scale
(¯à¨ scale=0 ¯®«ãç ¥¬ ¯¨ªá¥«ì) ¨ ª®®à¤¨­ â ¬¨ «¥¢®£® ¢¥àå­¥£® 㣫 
------------------ Установка масштаба данных формы -------------------
Параметры:
* eax = 50 - номер функции
* ebx = 1 - номер подфункции
* ecx задаёт масштаб: каждый байт данных определяет
(2^scale)*(2^scale) пикселей
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Масштаб по умолчанию равен 0 (масштабирующий множитель 1). Если в
данных формы один байт соответствует одному пикселю, то масштаб
можно не устанавливать.
* Обозначим xsize = ширина окна (в пикселях), ysize = высота;
обратите внимание, что они на единицу больше, чем устанавливаемые
функциями 0, 67.
* По определению масштаба xsize и ysize должны делиться на 2^scale.
* Байт данных по смещению a должен быть 0/1 и
определяет принадлежность окну квадрата со стороной 2^scale
(при scale=0 получаем пиксель) и координатами левого верхнего угла
(a mod (xsize shr scale), a div (xsize shr scale))
*  §¬¥à ¤ ­­ëå: (xsize shr scale)*(ysize shr scale).
* „ ­­ë¥ ¤®«¦­ë ¯à¨áãâá⢮¢ âì ¢ ¯ ¬ï⨠¨ ­¥ ¬¥­ïâìáï
¯®á«¥ ãáâ ­®¢ª¨ ä®à¬ë.
* ‘¨á⥬  ¯à®á¬ âਢ ¥â ¤ ­­ë¥ ® ä®à¬¥ ¯à¨ ª ¦¤®© ¯¥à¥à¨á®¢ª¥ ®ª­ 
ä㭪樥© 0.
* ‚맮¢ ¯®¤ä㭪樨 0 á ­ã«¥¢ë¬ 㪠§ â¥«¥¬ ¯à¨¢®¤¨â ª ¢®§¢à âã
ª ¯àאַ㣮«ì­®© ä®à¬¥.
* Размер данных: (xsize shr scale)*(ysize shr scale).
* Данные должны присутствовать в памяти и не меняться
после установки формы.
* Система просматривает данные о форме при каждой перерисовке окна
функцией 0.
* Вызов подфункции 0 с нулевым указателем приводит к возврату
к прямоугольной форме.
 
======================================================================
===================== ”ã­ªæ¨ï 51 - ᮧ¤ âì ¯®â®ª. ====================
===================== Функция 51 - создать поток. ====================
======================================================================
 à ¬¥âàë:
* eax = 51 - ­®¬¥à ä㭪樨
* ebx = 1 - ¥¤¨­á⢥­­ ï ¯®¤äã­ªæ¨ï
* ecx =  ¤à¥á â®çª¨ ¢å®¤  ¯®â®ª  (­ ç «ì­ë© eip)
* edx = 㪠§ â¥«ì áâíª  ¯®â®ª  (­ ç «ì­ë© esp)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ®è¨¡ª  (¢ á¨á⥬¥ ᫨誮¬ ¬­®£® ¯®â®ª®¢)
* ¨­ ç¥ eax = TID - ¨¤¥­â¨ä¨ª â®à ¯®â®ª 
Параметры:
* eax = 51 - номер функции
* ebx = 1 - единственная подфункция
* ecx = адрес точки входа потока (начальный eip)
* edx = указатель стэка потока (начальный esp)
Возвращаемое значение:
* eax = -1 - ошибка (в системе слишком много потоков)
* иначе eax = TID - идентификатор потока
 
======================================================================
= ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 0 - ¯®«ãç¨âì ª®­ä¨£ãà æ¨î á¥â¥¢®£® ¤à ©¢¥à .
= Функция 52, подфункция 0 - получить конфигурацию сетевого драйвера.
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¤¢®©­®¥ á«®¢® ª®­ä¨£ãà æ¨¨
‡ ¬¥ç ­¨ï:
* ‘«®¢® ª®­ä¨£ãà æ¨¨ ¬®¦­® ãáâ ­®¢¨âì ¯®¤ä㭪樥© 2.
* Ÿ¤à® ­¥ ¨á¯®«ì§ã¥â ᮮ⢥âáâ¢ãîéãî ¯¥à¥¬¥­­ãî.
–¥­­®áâì í⮩ ¯¥à¥¬¥­­®© ¨ à ¡®â îé¨å á ­¥© ¯®¤ä㭪権 0 ¨ 2
¯à¥¤áâ ¢«ï¥âáï ᮬ­¨â¥«ì­®©.
Параметры:
* eax = 52 - номер функции
* ebx = 0 - номер подфункции
Возвращаемое значение:
* eax = двойное слово конфигурации
Замечания:
* Слово конфигурации можно установить подфункцией 2.
* Ядро не использует соответствующую переменную.
Ценность этой переменной и работающих с ней подфункций 0 и 2
представляется сомнительной.
 
======================================================================
======= ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì «®ª «ì­ë© IP- ¤à¥á. ======
======= Функция 52, подфункция 1 - получить локальный IP-адрес. ======
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = IP- ¤à¥á (4 ¡ ©â )
‡ ¬¥ç ­¨ï:
* ‹®ª «ì­ë© IP- ¤à¥á ãáâ ­ ¢«¨¢ ¥âáï ¯®¤ä㭪樥© 3.
Параметры:
* eax = 52 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = IP-адрес (4 байта)
Замечания:
* Локальный IP-адрес устанавливается подфункцией 3.
 
======================================================================
”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 2 - ãáâ ­®¢¨âì ª®­ä¨£ãà æ¨î á¥â¥¢®£® ¤à ©¢¥à .
Функция 52, подфункция 2 - установить конфигурацию сетевого драйвера.
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¤¢®©­®¥ á«®¢® ª®­ä¨£ãà æ¨¨; ¥á«¨ ¬« ¤è¨¥ 7 ¡¨â ®¡à §ãîâ
ç¨á«® 3, íâ® ¢®á¯à¨­¨¬ ¥âáï ª ª § ¯à®á ­  [¯¥à¥-]¨­¨æ¨ «¨§ æ¨î
Ethernet-ª àâë, ¢ ¯à®â¨¢­®¬ á«ãç ¥ Ethernet ¢ëª«îç ¥âáï
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ­¥ § ¯à®è¥­ Ethernet-¨­â¥à䥩á, â® ¢®§¢à é ¥âáï eax=2,
­® íâ® ¬®¦¥â ¨§¬¥­¨âìáï ¢ ¡ã¤ãé¨å ¢¥àá¨ïå ï¤à 
* ¥á«¨ § ¯à®è¥­ Ethernet-¨­â¥à䥩á, â® eax=0 ®§­ ç ¥â ®è¨¡ªã
(®âáãâá⢨¥ Ethernet-ª àâë),   ­¥­ã«¥¢®¥ §­ ç¥­¨¥ - ãᯥå
‡ ¬¥ç ­¨ï:
* ‘«®¢® ª®­ä¨£ãà æ¨¨ ¬®¦­® ¯à®ç¨â âì ¯®¤ä㭪樥© 0.
* Ÿ¤à® ­¥ ¨á¯®«ì§ã¥â ᮮ⢥âáâ¢ãîéãî ¯¥à¥¬¥­­ãî.
–¥­­®áâì í⮩ ¯¥à¥¬¥­­®©, ¯®¤ä㭪樨 0 ¨ ç á⨠¯®¤ä㭪樨 2,
ãáâ ­ ¢«¨¢ î饩 íâã ¯¥à¥¬¥­­ãî, ¯à¥¤áâ ¢«ï¥âáï ᮬ­¨â¥«ì­®©.
Параметры:
* eax = 52 - номер функции
* ebx = 2 - номер подфункции
* ecx = двойное слово конфигурации; если младшие 7 бит образуют
число 3, это воспринимается как запрос на [пере-]инициализацию
Ethernet-карты, в противном случае Ethernet выключается
Возвращаемое значение:
* если не запрошен Ethernet-интерфейс, то возвращается eax=2,
но это может измениться в будущих версиях ядра
* если запрошен Ethernet-интерфейс, то eax=0 означает ошибку
(отсутствие Ethernet-карты), а ненулевое значение - успех
Замечания:
* Слово конфигурации можно прочитать подфункцией 0.
* Ядро не использует соответствующую переменную.
Ценность этой переменной, подфункции 0 и части подфункции 2,
устанавливающей эту переменную, представляется сомнительной.
 
======================================================================
====== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 3 - ãáâ ­®¢¨âì «®ª «ì­ë© IP- ¤à¥á. =====
====== Функция 52, подфункция 3 - установить локальный IP-адрес. =====
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = IP- ¤à¥á (4 ¡ ©â )
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ⥪ãé ï ॠ«¨§ æ¨ï ¢®§¢à é ¥â eax=3, ­® íâ® ¬®¦¥â ¡ëâì ¨§¬¥­¥­®
¢ ¡ã¤ãé¨å ¢¥àá¨ïå
‡ ¬¥ç ­¨ï:
* ‹®ª «ì­ë© IP- ¤à¥á ¬®¦­® ¯®«ãç¨âì ¯®¤ä㭪樥© 1.
Параметры:
* eax = 52 - номер функции
* ebx = 3 - номер подфункции
* ecx = IP-адрес (4 байта)
Возвращаемое значение:
* текущая реализация возвращает eax=3, но это может быть изменено
в будущих версиях
Замечания:
* Локальный IP-адрес можно получить подфункцией 1.
 
======================================================================
= ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 6 - ¤®¡ ¢¨âì ¤ ­­ë¥ ¢ á⥪ ¢å®¤­®© ®ç¥à¥¤¨. =
= Функция 52, подфункция 6 - добавить данные в стек входной очереди. =
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
* edx = à §¬¥à ¤ ­­ëå
* esi = 㪠§ â¥«ì ­  ¤ ­­ë¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ®è¨¡ª 
* eax = 0 - ãᯥ譮
‡ ¬¥ç ­¨ï:
* â  äã­ªæ¨ï ¯à¥¤­ §­ ç¥­  ⮫쪮 ¤«ï ¬¥¤«¥­­ëå á¥â¥¢ëå ¤à ©¢¥à®¢
Параметры:
* eax = 52 - номер функции
* ebx = 6 - номер подфункции
* edx = размер данных
* esi = указатель на данные
Возвращаемое значение:
* eax = -1 - ошибка
* eax = 0 - успешно
Замечания:
* Эта функция предназначена только для медленных сетевых драйверов
(PPP, SLIP).
*  §¬¥à ¤ ­­ëå ­¥ ¤®«¦¥­ ¯à¥¢®á室¨âì 1500 ¡ ©â,
å®âï ¯à®¢¥à®ª ª®à४⭮á⨠­¥ ¤¥« ¥âáï.
* Размер данных не должен превосходить 1500 байт,
хотя проверок корректности не делается.
 
======================================================================
====================== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 8 ======================
============= à®ç¨â âì ¤ ­­ë¥ ¨§ á¥â¥¢®© ®ç¥à¥¤¨ ¢ë¢®¤ . ============
====================== Функция 52, подфункция 8 ======================
============= Прочитать данные из сетевой очереди вывода. ============
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* esi = 㪠§ â¥«ì ­  ¡ãä¥à à §¬¥à®¬ 1500 ¡ ©â
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¯à®ç¨â ­­ëå ¡ ©â (¢ ⥪ã饩 ॠ«¨§ æ¨¨
«¨¡® 0 = ­¥â ¤ ­­ëå, «¨¡® 1500)
* ¤ ­­ë¥ ᪮¯¨à®¢ ­ë ¢ ¡ãä¥à
‡ ¬¥ç ­¨ï:
* â  äã­ªæ¨ï ¯à¥¤­ §­ ç¥­  ⮫쪮 ¤«ï ¬¥¤«¥­­ëå á¥â¥¢ëå ¤à ©¢¥à®¢
Параметры:
* eax = 52 - номер функции
* ebx = 8 - номер подфункции
* esi = указатель на буфер размером 1500 байт
Возвращаемое значение:
* eax = число прочитанных байт (в текущей реализации
либо 0 = нет данных, либо 1500)
* данные скопированы в буфер
Замечания:
* Эта функция предназначена только для медленных сетевых драйверов
(PPP, SLIP).
 
======================================================================
=========== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 9 - ¯®«ãç¨âì gateway IP. ==========
=========== Функция 52, подфункция 9 - получить gateway IP. ==========
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = gateway IP (4 ¡ ©â )
Параметры:
* eax = 52 - номер функции
* ebx = 9 - номер подфункции
Возвращаемое значение:
* eax = gateway IP (4 байта)
 
======================================================================
========= ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 10 - ¯®«ãç¨âì ¬ áªã ¯®¤á¥â¨. ========
========= Функция 52, подфункция 10 - получить маску подсети. ========
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 10 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¬ áª  ¯®¤á¥â¨
Параметры:
* eax = 52 - номер функции
* ebx = 10 - номер подфункции
Возвращаемое значение:
* eax = маска подсети
 
======================================================================
========= ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 11 - ãáâ ­®¢¨âì gateway IP. =========
========= Функция 52, подфункция 11 - установить gateway IP. =========
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
* ecx = gateway IP (4 ¡ ©â )
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ⥪ãé ï ॠ«¨§ æ¨ï ¢®§¢à é ¥â eax=11, ­® íâ® ¬®¦¥â ¡ëâì ¨§¬¥­¥­®
¢ ¡ã¤ãé¨å ॠ«¨§ æ¨ïå
Параметры:
* eax = 52 - номер функции
* ebx = 11 - номер подфункции
* ecx = gateway IP (4 байта)
Возвращаемое значение:
* текущая реализация возвращает eax=11, но это может быть изменено
в будущих реализациях
 
======================================================================
======== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 12 - ãáâ ­®¢¨âì ¬ áªã ¯®¤á¥â¨. =======
======== Функция 52, подфункция 12 - установить маску подсети. =======
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 12 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¬ áª  ¯®¤á¥â¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ⥪ãé ï ॠ«¨§ æ¨ï ¢®§¢à é ¥â eax=12, ­® íâ® ¬®¦¥â ¡ëâì ¨§¬¥­¥­®
¢ ¡ã¤ãé¨å ¢¥àá¨ïå
Параметры:
* eax = 52 - номер функции
* ebx = 12 - номер подфункции
* ecx = маска подсети
Возвращаемое значение:
* текущая реализация возвращает eax=12, но это может быть изменено
в будущих версиях
 
======================================================================
============ ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 13 - ¯®«ãç¨âì DNS IP. ============
============ Функция 52, подфункция 13 - получить DNS IP. ============
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = DNS IP (4 ¡ ©â )
Параметры:
* eax = 52 - номер функции
* ebx = 13 - номер подфункции
Возвращаемое значение:
* eax = DNS IP (4 байта)
 
======================================================================
=========== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 14 - ãáâ ­®¢¨âì DNS IP. ===========
=========== Функция 52, подфункция 14 - установить DNS IP. ===========
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 14 - ­®¬¥à ¯®¤ä㭪樨
* ecx = DNS IP (4 ¡ ©â )
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ⥪ãé ï ॠ«¨§ æ¨ï ¢®§¢à é ¥â eax=14, ­® íâ® ¬®¦¥â ¡ëâì ¨§¬¥­¥­®
¢ á«¥¤ãîé¨å ¢¥àá¨ïå
Параметры:
* eax = 52 - номер функции
* ebx = 14 - номер подфункции
* ecx = DNS IP (4 байта)
Возвращаемое значение:
* текущая реализация возвращает eax=14, но это может быть изменено
в следующих версиях
 
======================================================================
====== ”ã­ªæ¨ï 52, ¯®¤äã­ªæ¨ï 15 - ¯®«ãç¨âì «®ª «ì­ë© MAC- ¤à¥á. =====
====== Функция 52, подфункция 15 - получить локальный MAC-адрес. =====
======================================================================
 à ¬¥âàë:
* eax = 52 - ­®¬¥à ä㭪樨
* ebx = 15 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 0 - ç¨â âì ¯¥à¢ë¥ 4 ¡ ©â ,
ecx = 4 - ç¨â âì ¯®á«¥¤­¨¥ 2 ¡ ©â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¤«ï ecx=0: eax = ¯¥à¢ë¥ 4 ¡ ©â  MAC- ¤à¥á 
* ¤«ï ecx=4: ax = ¯®á«¥¤­¨¥ 2 ¡ ©â  MAC- ¤à¥á ,
áâ àè ï ¯®«®¢¨­  eax à §àãè ¥âáï
* ¤«ï ¤à㣨å ecx: eax = -1 ª ª ¯à¨§­ ª ®è¨¡ª¨
Параметры:
* eax = 52 - номер функции
* ebx = 15 - номер подфункции
* ecx = 0 - читать первые 4 байта,
ecx = 4 - читать последние 2 байта
Возвращаемое значение:
* для ecx=0: eax = первые 4 байта MAC-адреса
* для ecx=4: ax = последние 2 байта MAC-адреса,
старшая половина eax разрушается
* для других ecx: eax = -1 как признак ошибки
 
======================================================================
============ ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 0 - ®âªàëâì UDP-᮪¥â. ===========
============ Функция 53, подфункция 0 - открыть UDP-сокет. ===========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
* ecx = «®ª «ì­ë© ¯®àâ (ãç¨â뢠¥âáï ⮫쪮 ¬« ¤è¥¥ á«®¢®),
ecx = 0 - ¯à¥¤®áâ ¢¨âì á¨á⥬¥ ¢ë¡®à «®ª «ì­®£® ¯®àâ 
* edx = 㤠«ñ­­ë© ¯®àâ (ãç¨â뢠¥âáï ⮫쪮 ¬« ¤è¥¥ á«®¢®)
* esi = 㤠«ñ­­ë© IP
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 = 0xFFFFFFFF - ®è¨¡ª ; ebx à §àãè ¥âáï
* eax = åí­¤« ᮪¥â  (­¥ª®â®à®¥ ç¨á«®, ®¤­®§­ ç­® ¨¤¥­â¨ä¨æ¨àãî饥
᮪¥â ¨ ¨¬¥î饥 á¬ë᫠⮫쪮 ¤«ï á¨á⥬ë) - ãᯥ譮;
ebx à §àãè ¥âáï
Параметры:
* eax = 53 - номер функции
* ebx = 0 - номер подфункции
* ecx = локальный порт (учитывается только младшее слово),
ecx = 0 - предоставить системе выбор локального порта
* edx = удалённый порт (учитывается только младшее слово)
* esi = удалённый IP
Возвращаемое значение:
* eax = -1 = 0xFFFFFFFF - ошибка; ebx разрушается
* eax = хэндл сокета (некоторое число, однозначно идентифицирующее
сокет и имеющее смысл только для системы) - успешно;
ebx разрушается
 
======================================================================
============ ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 1 - § ªàëâì UDP-᮪¥â. ===========
============ Функция 53, подфункция 1 - закрыть UDP-сокет. ===========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ­¥¢¥à­ë© åí­¤«
* eax = 0 - ãᯥ譮
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* ’¥ªãé ï ॠ«¨§ æ¨ï ­¥ § ªà뢠¥â  ¢â®¬ â¨ç¥áª¨ ¢á¥ ᮪¥âë ¯®â®ª 
¯à¨ ¥£® § ¢¥à襭¨¨. ‚ ç áâ­®áâ¨, ­¥ á«¥¤ã¥â ¯à¨¡¨¢ âì ¯®â®ª
á ªã祩 ®âªàëâëå ᮪¥â®¢ - ¡ã¤¥â ãâ¥çª  à¥áãàᮢ.
Параметры:
* eax = 53 - номер функции
* ebx = 1 - номер подфункции
* ecx = хэндл сокета
Возвращаемое значение:
* eax = -1 - неверный хэндл
* eax = 0 - успешно
* ebx разрушается
Замечания:
* Текущая реализация не закрывает автоматически все сокеты потока
при его завершении. В частности, не следует прибивать поток
с кучей открытых сокетов - будет утечка ресурсов.
 
======================================================================
============== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 2 - ®¯à®á ᮪¥â . ==============
============== Функция 53, подфункция 2 - опрос сокета. ==============
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¯®«ã祭­ëå ¡ ©â, 0 ¤«ï ­¥¢¥à­®£® åí­¤« 
* ebx à §àãè ¥âáï
Параметры:
* eax = 53 - номер функции
* ebx = 2 - номер подфункции
* ecx = хэндл сокета
Возвращаемое значение:
* eax = число полученных байт, 0 для неверного хэндла
* ebx разрушается
 
======================================================================
======== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 3 - ¯à®ç¨â âì ¡ ©â ¨§ ᮪¥â . ========
======== Функция 53, подфункция 3 - прочитать байт из сокета. ========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ­¥â ¯à¨­ïâëå ¤ ­­ëå ¨«¨ 㪠§ ­ ­¥¢¥à­ë© åí­¤«:
eax=0, bl=0, ¯à®ç¨¥ ¡ ©âë ebx à §àãè îâáï
* ¥á«¨ ¡ë«¨ ¯à¨­ïâë¥ ¤ ­­ë¥: eax=ç¨á«® ®áâ ¢è¨åáï ¡ ©â
(¢®§¬®¦­®, 0), bl=¯à®ç¨â ­­ë© ¡ ©â, ¯à®ç¨¥ ¡ ©âë ebx à §àãè îâáï
Параметры:
* eax = 53 - номер функции
* ebx = 3 - номер подфункции
* ecx = хэндл сокета
Возвращаемое значение:
* если нет принятых данных или указан неверный хэндл:
eax=0, bl=0, прочие байты ebx разрушаются
* если были принятые данные: eax=число оставшихся байт
(возможно, 0), bl=прочитанный байт, прочие байты ebx разрушаются
 
======================================================================
========== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 4 - § ¯¨á âì ¢ UDP-᮪¥â. ==========
========== Функция 53, подфункция 4 - записать в UDP-сокет. ==========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
* edx = ç¨á«® ¡ ©â ¤«ï § ¯¨á¨
* esi = 㪠§ â¥«ì ­  ¤ ­­ë¥ ¤«ï § ¯¨á¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0xffffffff - ®è¨¡ª  (­¥¢¥à­ë© åí­¤« ¨«¨ ­¥¤®áâ â®ç­® ¯ ¬ïâ¨)
* eax = 0 - ãᯥ譮
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* —¨á«® ¡ ©â ¤«ï § ¯¨á¨ ­¥ ¬®¦¥â ¯à¥¢ëè âì 1500-28, å®âï
ᮮ⢥âáâ¢ãî饩 ¯à®¢¥àª¨ ­¥ ¤¥« ¥âáï.
Параметры:
* eax = 53 - номер функции
* ebx = 4 - номер подфункции
* ecx = хэндл сокета
* edx = число байт для записи
* esi = указатель на данные для записи
Возвращаемое значение:
* eax = 0xffffffff - ошибка (неверный хэндл или недостаточно памяти)
* eax = 0 - успешно
* ebx разрушается
Замечания:
* Число байт для записи не может превышать 1500-28, хотя
соответствующей проверки не делается.
 
======================================================================
============ ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 5 - ®âªàëâì TCP-᮪¥â. ===========
============ Функция 53, подфункция 5 - открыть TCP-сокет. ===========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = «®ª «ì­ë© ¯®àâ (ãç¨â뢠¥âáï ⮫쪮 ¬« ¤è¥¥ á«®¢®),
ecx = 0 - ¯à¥¤®áâ ¢¨âì á¨á⥬¥ ¢ë¡®à «®ª «ì­®£® ¯®àâ 
* edx = 㤠«ñ­­ë© ¯®àâ (ãç¨â뢠¥âáï ⮫쪮 ¬« ¤è¥¥ á«®¢®)
* esi = 㤠«ñ­­ë© IP
* edi = ०¨¬ ®âªàëâ¨ï: SOCKET_PASSIVE=0 ¨«¨ SOCKET_ACTIVE=1
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 = 0xFFFFFFFF - ®è¨¡ª ; ebx à §àãè ¥âáï
* eax = åí­¤« ᮪¥â  (­¥ª®â®à®¥ ç¨á«®, ®¤­®§­ ç­® ¨¤¥­â¨ä¨æ¨àãî饥
᮪¥â ¨ ¨¬¥î饥 á¬ë᫠⮫쪮 ¤«ï á¨á⥬ë) - ãᯥ譮;
ebx à §àãè ¥âáï
Параметры:
* eax = 53 - номер функции
* ebx = 5 - номер подфункции
* ecx = локальный порт (учитывается только младшее слово),
ecx = 0 - предоставить системе выбор локального порта
* edx = удалённый порт (учитывается только младшее слово)
* esi = удалённый IP
* edi = режим открытия: SOCKET_PASSIVE=0 или SOCKET_ACTIVE=1
Возвращаемое значение:
* eax = -1 = 0xFFFFFFFF - ошибка; ebx разрушается
* eax = хэндл сокета (некоторое число, однозначно идентифицирующее
сокет и имеющее смысл только для системы) - успешно;
ebx разрушается
 
======================================================================
====== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 6 - ¯®«ãç¨âì á®áâ®ï­¨¥ TCP-᮪¥â . =====
====== Функция 53, подфункция 6 - получить состояние TCP-сокета. =====
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 ¤«ï ­¥¢¥à­®£® ᮪¥â  ¨«¨ áâ âãá: ®¤­® ¨§
Параметры:
* eax = 53 - номер функции
* ebx = 6 - номер подфункции
* ecx = хэндл сокета
Возвращаемое значение:
* eax = 0 для неверного сокета или статус: одно из
* TCB_LISTEN = 1
* TCB_SYN_SENT = 2
* TCB_SYN_RECEIVED = 3
2677,1275 → 2677,1275
* TCB_LAST_ASK = 9
* TCB_TIME_WAIT = 10
* TCB_CLOSED = 11
* ebx à §àãè ¥âáï
* ebx разрушается
 
======================================================================
========== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 7 - § ¯¨á âì ¢ TCP-᮪¥â. ==========
========== Функция 53, подфункция 7 - записать в TCP-сокет. ==========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
* edx = ç¨á«® ¡ ©â ¤«ï § ¯¨á¨
* esi = 㪠§ â¥«ì ­  ¤ ­­ë¥ ¤«ï § ¯¨á¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0xffffffff - ®è¨¡ª  (­¥¢¥à­ë© åí­¤« ¨«¨ ­¥¤®áâ â®ç­® ¯ ¬ïâ¨)
* eax = 0 - ãᯥ譮
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* —¨á«® ¡ ©â ¤«ï § ¯¨á¨ ­¥ ¬®¦¥â ¯à¥¢ëè âì 1500-40,
å®âï ᮮ⢥âáâ¢ãî饩 ¯à®¢¥àª¨ ­¥ ¤¥« ¥âáï.
Параметры:
* eax = 53 - номер функции
* ebx = 7 - номер подфункции
* ecx = хэндл сокета
* edx = число байт для записи
* esi = указатель на данные для записи
Возвращаемое значение:
* eax = 0xffffffff - ошибка (неверный хэндл или недостаточно памяти)
* eax = 0 - успешно
* ebx разрушается
Замечания:
* Число байт для записи не может превышать 1500-40,
хотя соответствующей проверки не делается.
 
======================================================================
============ ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 8 - § ªàëâì TCP-᮪¥â. ===========
============ Функция 53, подфункция 8 - закрыть TCP-сокет. ===========
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ®è¨¡ª  (­¥¢¥à­ë© åí­¤« ¨«¨
­¥¤®áâ â®ç­® ¯ ¬ï⨠¤«ï ¯ ª¥â  § ªàëâ¨ï ᮪¥â )
* eax = 0 - ãᯥ譮
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* ’¥ªãé ï ॠ«¨§ æ¨ï ­¥ § ªà뢠¥â  ¢â®¬ â¨ç¥áª¨ ¢á¥ ᮪¥âë ¯®â®ª 
¯à¨ ¥£® § ¢¥à襭¨¨. ‚ ç áâ­®áâ¨, ­¥ á«¥¤ã¥â ¯à¨¡¨¢ âì ¯®â®ª
á ªã祩 ®âªàëâëå ᮪¥â®¢ - ¡ã¤¥â ãâ¥çª  à¥áãàᮢ.
Параметры:
* eax = 53 - номер функции
* ebx = 8 - номер подфункции
* ecx = хэндл сокета
Возвращаемое значение:
* eax = -1 - ошибка (неверный хэндл или
недостаточно памяти для пакета закрытия сокета)
* eax = 0 - успешно
* ebx разрушается
Замечания:
* Текущая реализация не закрывает автоматически все сокеты потока
при его завершении. В частности, не следует прибивать поток
с кучей открытых сокетов - будет утечка ресурсов.
 
======================================================================
== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 9 - ¯à®¢¥à¨âì, ᢮¡®¤¥­ «¨ «®ª «ì­ë© ¯®àâ. =
== Функция 53, подфункция 9 - проверить, свободен ли локальный порт. =
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à «®ª «ì­®£® ¯®àâ  (¨á¯®«ì§ãîâáï ⮫쪮 ¬« ¤è¨¥ 16 ¡¨â)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ¯®à⠨ᯮ«ì§ã¥âáï
* eax = 1 - ¯®àâ ᢮¡®¤¥­
* ebx à §àãè ¥âáï
Параметры:
* eax = 53 - номер функции
* ebx = 9 - номер подфункции
* ecx = номер локального порта (используются только младшие 16 бит)
Возвращаемое значение:
* eax = 0 - порт используется
* eax = 1 - порт свободен
* ebx разрушается
 
======================================================================
==== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 10 - ¯®«ãç¨âì áâ âãá ª ¡¥«ï Ethernet. ====
==== Функция 53, подфункция 10 - получить статус кабеля Ethernet. ====
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 10 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* al = -1 - ¤à ©¢¥à á¥â¥¢®© ª àâë ­¥ § £à㦥­ ¨«¨
­¥ ¯®¤¤¥à¦¨¢ ¥â íâã äã­ªæ¨î
* al = 0 - ª ¡¥«ì ­¥ ¯®¤ª«îçñ­
* al = 1 - ª ¡¥«ì ¯®¤ª«îçñ­
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* ’¥ªãé ï ॠ«¨§ æ¨ï ï¤à  ¯®¤¤¥à¦¨¢ ¥â íâã äã­ªæ¨î
⮫쪮 ¤«ï á¥â¥¢ëå ª àâ RTL8139.
Параметры:
* eax = 53 - номер функции
* ebx = 10 - номер подфункции
Возвращаемое значение:
* al = -1 - драйвер сетевой карты не загружен или
не поддерживает эту функцию
* al = 0 - кабель не подключён
* al = 1 - кабель подключён
* ebx разрушается
Замечания:
* Текущая реализация ядра поддерживает эту функцию
только для сетевых карт RTL8139.
 
======================================================================
==== ”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 11 - ¯à®ç¨â âì ¤ ­­ë¥ á¥â¥¢®£® á⥪ . ====
==== Функция 53, подфункция 11 - прочитать данные сетевого стека. ====
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
* ecx = åí­¤« ᮪¥â 
* edx = 㪠§ â¥«ì ­  ¡ãä¥à
* esi = ç¨á«® ¡ ©â ¤«ï ç⥭¨ï;
* esi = 0 - ç¨â âì ¢á¥ ¤ ­­ë¥ (¬ ªá¨¬ã¬ 4096 ¡ ©â)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¯à®ç¨â ­­ëå ¡ ©â (0 ¯à¨ ­¥¢¥à­®¬ åí­¤«¥)
* ebx à §àãè ¥âáï
Параметры:
* eax = 53 - номер функции
* ebx = 11 - номер подфункции
* ecx = хэндл сокета
* edx = указатель на буфер
* esi = число байт для чтения;
* esi = 0 - читать все данные (максимум 4096 байт)
Возвращаемое значение:
* eax = число прочитанных байт (0 при неверном хэндле)
* ebx разрушается
 
======================================================================
”ã­ªæ¨ï 53, ¯®¤äã­ªæ¨ï 255 - ®â« ¤®ç­ ï ¨­ä®à¬ æ¨ï á¥â¥¢®£® ¤à ©¢¥à .
Функция 53, подфункция 255 - отладочная информация сетевого драйвера.
======================================================================
 à ¬¥âàë:
* eax = 53 - ­®¬¥à ä㭪樨
* ebx = 255 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ⨯ § ¯à è¨¢ ¥¬®© ¨­ä®à¬ æ¨¨ (ᬮâਠ­¨¦¥)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = § ¯à®è¥­­ ï ¨­ä®à¬ æ¨ï
* ebx à §àãè ¥âáï
‚®§¬®¦­ë¥ §­ ç¥­¨ï ecx:
* 100: ¤«¨­  ®ç¥à¥¤¨ 0 (empty queue)
* 101: ¤«¨­  ®ç¥à¥¤¨ 1 (ip-out queue)
* 102: ¤«¨­  ®ç¥à¥¤¨ 2 (ip-in queue)
* 103: ¤«¨­  ®ç¥à¥¤¨ 3 (net1out queue)
* 200: ç¨á«® í«¥¬¥­â®¢ ¢ â ¡«¨æ¥ ARP
* 201: à §¬¥à â ¡«¨æë ARP (¢ í«¥¬¥­â å) (20 ¢ ⥪ã饩 ¢¥àᨨ)
* 202: ¯à®ç¨â âì í«¥¬¥­â edx â ¡«¨æë ARP ¢® ¢à¥¬¥­­ë© ¡ãä¥à, ®âªã¤ 
¡¥àãâ ¨­ä®à¬ æ¨î 5 ¯®á«¥¤ãîé¨å ⨯®¢;
¢ í⮬ á«ãç ¥ eax ­¥®¯à¥¤¥«ñ­
* 203: IP- ¤à¥á, § ¯®¬­¥­­ë© ⨯®¬ 202
* 204: áâ à襥 dword MAC- ¤à¥á , § ¯®¬­¥­­®£® ⨯®¬ 202
* 205: ¬« ¤è¥¥ word MAC- ¤à¥á , § ¯®¬­¥­­®£® ⨯®¬ 202
* 206: á«®¢® áâ âãá , § ¯®¬­¥­­®¥ ⨯®¬ 202
* 207: á«®¢® ttl, § ¯®¬­¥­­®¥ ⨯®¬ 202
* 2: ®¡é¥¥ ç¨á«® ¯®«ã祭­ëå IP-¯ ª¥â®¢
* 3: ®¡é¥¥ ç¨á«® ¯¥à¥¤ ­­ëå IP-¯ ª¥â®¢
* 4: ®¡é¥¥ ç¨á«® ᤠ¬¯«¥­­ëå ¯®«ã祭­ëå ¯ ª¥â®¢
* 5: ®¡é¥¥ ç¨á«® ¯®«ã祭­ëå ARP-¯ ª¥â®¢
* 6: áâ âãá ¤à ©¢¥à  ¯ ª¥â®¢, 0=­¥ ªâ¨¢¥­,
­¥­ã«¥¢®¥ §­ ç¥­¨¥= ªâ¨¢¥­
Параметры:
* eax = 53 - номер функции
* ebx = 255 - номер подфункции
* ecx = тип запрашиваемой информации (смотри ниже)
Возвращаемое значение:
* eax = запрошенная информация
* ebx разрушается
Возможные значения ecx:
* 100: длина очереди 0 (empty queue)
* 101: длина очереди 1 (ip-out queue)
* 102: длина очереди 2 (ip-in queue)
* 103: длина очереди 3 (net1out queue)
* 200: число элементов в таблице ARP
* 201: размер таблицы ARP (в элементах) (20 в текущей версии)
* 202: прочитать элемент edx таблицы ARP во временный буфер, откуда
берут информацию 5 последующих типов;
в этом случае eax неопределён
* 203: IP-адрес, запомненный типом 202
* 204: старшее dword MAC-адреса, запомненного типом 202
* 205: младшее word MAC-адреса, запомненного типом 202
* 206: слово статуса, запомненное типом 202
* 207: слово ttl, запомненное типом 202
* 2: общее число полученных IP-пакетов
* 3: общее число переданных IP-пакетов
* 4: общее число сдампленных полученных пакетов
* 5: общее число полученных ARP-пакетов
* 6: статус драйвера пакетов, 0=неактивен,
ненулевое значение=активен
 
======================================================================
====================== ”ã­ªæ¨ï 55, ¯®¤äã­ªæ¨ï 55 =====================
==========  ç âì ¯à®¨£à뢠âì ¤ ­­ë¥ ­  ¢áâ஥­­®¬ ᯨª¥à¥. ==========
====================== Функция 55, подфункция 55 =====================
========== Начать проигрывать данные на встроенном спикере. ==========
======================================================================
 à ¬¥âàë:
* eax = 55 - ­®¬¥à ä㭪樨
* ebx = 55 - ­®¬¥à ¯®¤ä㭪樨
* esi = 㪠§ â¥«ì ­  ¤ ­­ë¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 55 - ®è¨¡ª  (ᯨª¥à ®âª«îçñ­ ¨«¨ § ­ïâ)
„ ­­ë¥ - íâ® ¬ áᨢ í«¥¬¥­â®¢ ¯¥à¥¬¥­­®© ¤«¨­ë.
”®à¬ â ª ¦¤®£® í«¥¬¥­â  ®¯à¥¤¥«ï¥âáï ¯¥à¢ë¬ ¡ ©â®¬:
* 0 = ª®­¥æ ¤ ­­ëå
* 1..0x80 = § ¤ ñâ ¤«¨â¥«ì­®áâì §¢ãç ­¨ï ¢ á®âëå ¤®«ïå ᥪ㭤ë
­®âë, ®¯à¥¤¥«ï¥¬®© ­¥¯®á।á⢥­­ë¬ §­ ç¥­¨¥¬ ç áâ®âë
* á«¥¤ãî饥 á«®¢® (2 ¡ ©â ) ᮤ¥à¦¨â ¤¥«¨â¥«ì ç áâ®âë;
ç áâ®â  ®¯à¥¤¥«ï¥âáï ª ª 1193180/divider
Параметры:
* eax = 55 - номер функции
* ebx = 55 - номер подфункции
* esi = указатель на данные
Возвращаемое значение:
* eax = 0 - успешно
* eax = 55 - ошибка (спикер отключён или занят)
Данные - это массив элементов переменной длины.
Формат каждого элемента определяется первым байтом:
* 0 = конец данных
* 1..0x80 = задаёт длительность звучания в сотых долях секунды
ноты, определяемой непосредственным значением частоты
* следующее слово (2 байта) содержит делитель частоты;
частота определяется как 1193180/divider
* 0x81 = invalid
* 0x82..0xFF = ­®â , ®¯à¥¤¥«ï¥¬ ï ®ªâ ¢®© ¨ ­®¬¥à®¬:
* ¤«¨â¥«ì­®áâì ¢ á®âëå ¤®«ïå ᥪ㭤ë = (¯¥à¢ë© ¡ ©â)-0x81
* ¯à¨áãâáâ¢ã¥â ¥éñ ®¤¨­ ¡ ©â;
* (¢â®à®© ¡ ©â)=0xFF - ¯ ã§ 
* ¨­ ç¥ ®­ ¨¬¥¥â ¢¨¤ a*0x10+b, £¤¥ b=­®¬¥à ­®âë ¢ ®ªâ ¢¥ ®â 1
¤® 12, a=­®¬¥à ®ªâ ¢ë (áç¨â ï á 0)
‡ ¬¥ç ­¨ï:
* ¨é ­¨¥ ᯨª¥à®¬ ¬®¦¥â ¡ëâì § ¯à¥é¥­®/à §à¥è¥­® ¯®¤ä㭪樥© 8
ä㭪樨 18.
* ”ã­ªæ¨ï ¢®§¢à é ¥â ã¯à ¢«¥­¨¥, á®®¡é¨¢ ªã¤  á«¥¤ã¥â ¨­ä®à¬ æ¨î
® § ¯à®á¥. ‘ ¬® ¯à®¨£à뢠­¨¥ ¨¤ñâ ­¥§ ¢¨á¨¬® ®â ¯à®£à ¬¬ë.
* „ ­­ë¥ ¤®«¦­ë á®åà ­ïâìáï ¢ ¯ ¬ï⨠¯® ªà ©­¥© ¬¥à¥
¤® ª®­æ  ¯à®¨£à뢠­¨ï.
* 0x82..0xFF = нота, определяемая октавой и номером:
* длительность в сотых долях секунды = (первый байт)-0x81
* присутствует ещё один байт;
* (второй байт)=0xFF - пауза
* иначе он имеет вид a*0x10+b, где b=номер ноты в октаве от 1
до 12, a=номер октавы (считая с 0)
Замечания:
* Пищание спикером может быть запрещено/разрешено подфункцией 8
функции 18.
* Функция возвращает управление, сообщив куда следует информацию
о запросе. Само проигрывание идёт независимо от программы.
* Данные должны сохраняться в памяти по крайней мере
до конца проигрывания.
 
======================================================================
======================= ”ã­ªæ¨ï 57 - PCI BIOS. =======================
======================= Функция 57 - PCI BIOS. =======================
======================================================================
 à ¬¥âàë:
* eax = 57 - ­®¬¥à ä㭪樨
* ebp ᮮ⢥âáâ¢ã¥â ॣ¨áâàã al ¢ ᯥæ¨ä¨ª æ¨¨ PCI BIOS
* ®áâ «ì­ë¥ ॣ¨áâàë - ¯® ᯥæ¨ä¨ª æ¨¨ PCI BIOS
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* CF ­¥ ®¯à¥¤¥«ñ­
* ®áâ «ì­ë¥ ॣ¨áâàë - ¯® ᯥæ¨ä¨ª æ¨¨ PCI BIOS
‡ ¬¥ç ­¨ï:
* Œ­®£¨å १ã«ìâ â®¢ í⮩ ä㭪樨 ¬®¦­® â ª¦¥ ¤®¡¨âìáï ¢ë§®¢®¬
ᮮ⢥âáâ¢ãîé¨å ¯®¤ä㭪権 ä㭪樨 62.
* ”ã­ªæ¨ï ¢ë§ë¢ ¥â à áè¨à¥­¨¥ PCI32 BIOS, ¤®ªã¬¥­â¨à®¢ ­­®¥,
­ ¯à¨¬¥à, ¢ http://alpha1.dyns.net/files/PCI/bios21.pdf.
* …᫨ BIOS ­¥ ¯®¤¤¥à¦¨¢ ¥â íâ® à áè¨à¥­¨¥, ¯®¢¥¤¥­¨¥ ä㭪樨
í¬ã«¨àã¥âáï (ç¥à¥§  ­ «®£¨ ¯®¤ä㭪権 ä㭪樨 62 ०¨¬  ï¤à ).
Параметры:
* eax = 57 - номер функции
* ebp соответствует регистру al в спецификации PCI BIOS
* остальные регистры - по спецификации PCI BIOS
Возвращаемое значение:
* CF не определён
* остальные регистры - по спецификации PCI BIOS
Замечания:
* Многих результатов этой функции можно также добиться вызовом
соответствующих подфункций функции 62.
* Функция вызывает расширение PCI32 BIOS, документированное,
например, в http://alpha1.dyns.net/files/PCI/bios21.pdf.
* Если BIOS не поддерживает это расширение, поведение функции
эмулируется (через аналоги подфункций функции 62 режима ядра).
 
======================================================================
============== ”ã­ªæ¨ï 58 - à ¡®â  á ä ©«®¢®© á¨á⥬®©. ==============
============== Функция 58 - работа с файловой системой. ==============
======================================================================
 à ¬¥âàë:
Параметры:
* eax = 58
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮; ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ¢ § ¢¨á¨¬®á⨠®â ¯®¤ä㭪樨 ¬®¦¥â ¢®§¢à é âìáï §­ ç¥­¨¥ ¨
¢ ¤à㣨å ॣ¨áâà å
Ž¡é¨© ä®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ­®¬¥à ¡«®ª 
* +8: dword: à §¬¥à
* +12 = +0xC: dword: 㪠§ â¥«ì ­  ¤ ­­ë¥
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¯ ¬ïâì ¤«ï à ¡®âë á¨á⥬ë
(4096 ¡ ©â)
* +20 = +0x14: n db: ASCIIZ-áâப  á ¨¬¥­¥¬ ä ©« 
“â®ç­¥­¨ï - ¢ ¤®ªã¬¥­â æ¨¨ ­  ᮮ⢥âáâ¢ãîéãî ¯®¤äã­ªæ¨î.
ˆ¬ï ä ©«  ­¥çã¢á⢨⥫쭮 ª ॣ¨áâàã « â¨­áª¨å ¡ãª¢,
àãá᪨¥ ¡ãª¢ë ¤®«¦­ë ¡ëâì § £« ¢­ë¬¨.
”®à¬ â ¨¬¥­¨ ä ©« :
* ebx = указатель на информационную структуру
Возвращаемое значение:
* eax = 0 - успешно; иначе код ошибки файловой системы
* в зависимости от подфункции может возвращаться значение и
в других регистрах
Общий формат информационной структуры:
* +0: dword: номер подфункции
* +4: dword: номер блока
* +8: dword: размер
* +12 = +0xC: dword: указатель на данные
* +16 = +0x10: dword: указатель на память для работы системы
(4096 байт)
* +20 = +0x14: n db: ASCIIZ-строка с именем файла
Уточнения - в документации на соответствующую подфункцию.
Имя файла нечувствительно к регистру латинских букв,
русские буквы должны быть заглавными.
Формат имени файла:
/base/number/dir1/dir2/.../dirn/file,
£¤¥ /base/number ¨¤¥­â¨ä¨æ¨àã¥â ãáâனá⢮, ­  ª®â®à®¬ ¨é¥âáï ä ©«:
®¤­® ¨§
* /RD/1 = /RAMDISK/1 ¤«ï ¤®áâ㯠 ª à ¬¤¨áªã
* /FD/1 = /FLOPPYDISK/1 ¤«ï ¤®áâ㯠 ª ¯¥à¢®¬ã ä«®¯¯¨-¤¨áª®¢®¤ã,
/FD/2 = /FLOPPYDISK/2 ¤«ï ¢â®à®£® ä«®¯¯¨-¤¨áª®¢®¤ 
* /HD/x = /HARDDISK/x - ãáâ à¥¢è¨© ¢ à¨ ­â ¤®áâ㯠 ª ¦ñá⪮¬ã ¤¨áªã
(¢ í⮬ á«ãç ¥ ¡ §  ®¯à¥¤¥«ï¥âáï ¯®¤ä㭪樥© 7 ä㭪樨 21),
x - ­®¬¥à à §¤¥«  (áç¨â ï á 1)
* /HD0/x, /HD1/x, /HD2/x, /HD3/x ¤«ï ¤®áâ㯠 ᮮ⢥âá⢥­­®
ª ãáâனá⢠¬ IDE0 (Primary Master), IDE1 (Primary Slave),
где /base/number идентифицирует устройство, на котором ищется файл:
одно из
* /RD/1 = /RAMDISK/1 для доступа к рамдиску
* /FD/1 = /FLOPPYDISK/1 для доступа к первому флоппи-дисководу,
/FD/2 = /FLOPPYDISK/2 для второго флоппи-дисковода
* /HD/x = /HARDDISK/x - устаревший вариант доступа к жёсткому диску
(в этом случае база определяется подфункцией 7 функции 21),
x - номер раздела (считая с 1)
* /HD0/x, /HD1/x, /HD2/x, /HD3/x для доступа соответственно
к устройствам IDE0 (Primary Master), IDE1 (Primary Slave),
IDE2 (Secondary Master), IDE3 (Secondary Slave);
x - ­®¬¥à à §¤¥«  ­  ¢ë¡à ­­®¬ ¢¨­ç¥áâ¥à¥, ¨§¬¥­ï¥âáï ®â 1 ¤® 255
(­  ª ¦¤®¬ ¨§ ¢¨­ç¥áâ¥à®¢ ­ã¬¥à æ¨ï ­ ç¨­ ¥âáï á 1)
‡ ¬¥ç ­¨ï:
* ‚ ¯¥à¢ëå ¤¢ãå á«ãç ïå ¤®¯ã᪠¥âáï ¨á¯®«ì§®¢ ­¨¥ FIRST ¢¬¥áâ® 1,
SECOND ¢¬¥áâ® 2, ­® ¨á¯®«ì§®¢ âì íâã ¢®§¬®¦­®áâì
­¥ ४®¬¥­¤ã¥âáï ¤«ï 㤮¡á⢠ ¯¥à¥å®¤  ­  ¡ã¤ã騥 à áè¨à¥­¨ï.
*  ª« ¤ë¢ ¥âáï ®£à ­¨ç¥­¨¥ n<=39.
* ˆ¬¥­  ¯ ¯®ª ¨ ä ©«  dir1,...,dirn,file ¤®«¦­ë ¡ëâì ¢ ä®à¬ â¥ 8.3:
¨¬ï ­¥ ¡®«¥¥ 8 ᨬ¢®«®¢, â®çª , à áè¨à¥­¨¥ ­¥ ¡®«¥¥ 3 ᨬ¢®«®¢.
•¢®áâ®¢ë¥ ¯à®¡¥«ë ¨£­®à¨àãîâáï. „àã£¨å ¯à®¡¥«®¢ ¡ëâì ­¥ ¤®«¦­®.
…᫨ ¨¬ï § ­¨¬ ¥â ஢­® 8 ᨬ¢®«®¢, â®çªã ¬®¦­® ®¯ãáâ¨âì
(å®âï ¯®«ì§®¢ âìáï í⨬ ­¥ ४®¬¥­¤ã¥âáï ¤«ï 㤮¡á⢠ ¯¥à¥å®¤ 
­  ¡ã¤ã騥 à áè¨à¥­¨ï).
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥â ¯ ¯®ª ­  à ¬¤¨áª¥.
à¨¬¥àë:
x - номер раздела на выбранном винчестере, изменяется от 1 до 255
(на каждом из винчестеров нумерация начинается с 1)
Замечания:
* В первых двух случаях допускается использование FIRST вместо 1,
SECOND вместо 2, но использовать эту возможность
не рекомендуется для удобства перехода на будущие расширения.
* Накладывается ограничение n<=39.
* Имена папок и файла dir1,...,dirn,file должны быть в формате 8.3:
имя не более 8 символов, точка, расширение не более 3 символов.
Хвостовые пробелы игнорируются. Других пробелов быть не должно.
Если имя занимает ровно 8 символов, точку можно опустить
(хотя пользоваться этим не рекомендуется для удобства перехода
на будущие расширения).
* Функция не поддерживает папок на рамдиске.
Примеры:
* '/RAMDISK/FIRST/KERNEL.ASM',0
'/rd/1/kernel.asm',0
* '/HD0/1/kernel.asm',0
* '/hd0/1/menuet/pics/tanzania.bmp',0
„®áâã¯­ë¥ ¯®¤ä㭪樨:
* ¯®¤äã­ªæ¨ï 0 - ç⥭¨¥ ä ©« /¯ ¯ª¨
* ¯®¤äã­ªæ¨ï 8 - LBA-ç⥭¨¥ á ãáâனá⢠
* ¯®¤äã­ªæ¨ï 15 - ¯®«ã祭¨¥ ¨­ä®à¬ æ¨¨ ® ä ©«®¢®© á¨á⥬¥
Доступные подфункции:
* подфункция 0 - чтение файла/папки
* подфункция 8 - LBA-чтение с устройства
* подфункция 15 - получение информации о файловой системе
 
======================================================================
========== ”ã­ªæ¨ï 58, ¯®¤äã­ªæ¨ï 0 - ¯à®ç¨â âì ä ©«/¯ ¯ªã. ==========
========== Функция 58, подфункция 0 - прочитать файл/папку. ==========
======================================================================
 à ¬¥âàë:
Параметры:
* eax = 58
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 0 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ­®¬¥à ¡«®ª  ¤«ï ç⥭¨ï (áç¨â ï á 0)
* +8: dword: ç¨á«® ¡«®ª®¢ ¤«ï ç⥭¨ï
* +12 = +0xC: dword: 㪠§ â¥«ì ­  ¡ãä¥à, ªã¤  ¡ã¤ãâ § ¯¨á ­ë ¤ ­­ë¥
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï à ¡®âë á¨á⥬ë
(4096 ¡ ©â)
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx = à §¬¥à ä ©«  (¢ ¡ ©â å) ¨«¨
-1=0xffffffff, ¥á«¨ ä ©« ­¥ ­ ©¤¥­
‡ ¬¥ç ­¨ï:
*  §¬¥à ¡«®ª  - 512 ¡ ©â.
* â  äã­ªæ¨ï ãáâ à¥« , ¤«ï ç⥭¨ï ä ©«®¢ ¨á¯®«ì§ã©â¥ ¯®¤äã­ªæ¨î 0
ä㭪樨 70, ¤«ï ç⥭¨ï ¯ ¯®ª - ¯®¤äã­ªæ¨î 1 ä㭪樨 70.
* ”ã­ªæ¨ï ¯®§¢®«ï¥â ç¨â âì ᮤ¥à¦¨¬®¥ ¯ ¯ª¨. ˆ§ ä ©«®¢ëå á¨á⥬
¯®¤¤¥à¦¨¢ ¥âáï ⮫쪮 FAT. ”®à¬ â FAT-¯ ¯ª¨ ®¯¨á ­ ¢ «î¡®©
¤®ªã¬¥­â æ¨¨ ¯® FAT.
*  §¬¥à ¯ ¯ª¨ ®¯à¥¤¥«ï¥âáï ¯® à §¬¥àã 楯®çª¨ ª« áâ¥à®¢ ¢ FAT.
* …᫨ ä ©« ª®­ç¨«áï à ­ìè¥, 祬 ¡ë« ¯à®ç¨â ­ ¯®á«¥¤­¨© § ¯à®è¥­­ë©
¡«®ª, â® äã­ªæ¨ï ¯à®ç¨â ¥â, ᪮«ìª® ᬮ¦¥â, ¯®á«¥ 祣® ¢¥à­ñâ
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 0 = номер подфункции
* +4: dword: номер блока для чтения (считая с 0)
* +8: dword: число блоков для чтения
* +12 = +0xC: dword: указатель на буфер, куда будут записаны данные
* +16 = +0x10: dword: указатель на буфер для работы системы
(4096 байт)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx = размер файла (в байтах) или
-1=0xffffffff, если файл не найден
Замечания:
* Размер блока - 512 байт.
* Эта функция устарела, для чтения файлов используйте подфункцию 0
функции 70, для чтения папок - подфункцию 1 функции 70.
* Функция позволяет читать содержимое папки. Из файловых систем
поддерживается только FAT. Формат FAT-папки описан в любой
документации по FAT.
* Размер папки определяется по размеру цепочки кластеров в FAT.
* Если файл кончился раньше, чем был прочитан последний запрошенный
блок, то функция прочитает, сколько сможет, после чего вернёт
eax=6 (EOF).
* ”ã­ªæ¨ï ¯®§¢®«ï¥â ç¨â âì ª®à­¥¢ë¥ ¯ ¯ª¨ /rd/1,/fd/x,/hd[n]/x, ­®
¢ ¯¥à¢ëå ¤¢ãå á«ãç ïå ⥪ãé ï ॠ«¨§ æ¨ï ­¥ á«¥¤ã¥â
ãáâ ­®¢«¥­­ë¬ ¯à ¢¨« ¬:
¤«ï /rd/1:
* ¥á«¨ 㪠§ ­® 0 ¡«®ª®¢ ¤«ï ç⥭¨ï, áç¨â ¥âáï,
çâ® § ¯à è¨¢ ¥âáï 1;
* ¥á«¨ § ¯à è¨¢ ¥âáï ¡®«ìè¥ 14 ¡«®ª®¢ ¨«¨ ­ ç «ì­ë© ¡«®ª
­¥ ¬¥­ìè¥ 14-£®, â® ¢®§¢à é ¥âáï eax=5 (not found) ¨ ebx=-1;
* à §¬¥à ª®à­¥¢®£® ª â «®£  à ¬¤¨áª  = 14 ¡«®ª®¢,
0x1C00=7168 ¡ ©â; ­® ¢®§¢à é ¥âáï ebx=0
(§  ¨áª«î祭¨¥¬ á«ãç ï ¯à¥¤ë¤ã饣® ¯ã­ªâ );
* ª ª ­¨ áâà ­­®, ¬®¦­® ¯à®ç¨â âì 14-© ¡«®ª (â ¬, ¢®®¡é¥ £®¢®àï,
¬ãá®à - ­ ¯®¬¨­ î, áçñâ ¢¥¤ñâáï á 0);
* ¥á«¨ ¡ë« § ¯à®è¥­ å®âï ¡ë ®¤¨­ ¡«®ª á ­®¬¥à®¬, ­¥ ¬¥­ì訬 14,
â® ¢®§¢à é ¥âáï eax=6(EOF); ¨­ ç¥ eax=0.
„«ï /fd/x:
* ¥á«¨ ­ ç «ì­ë© ¡«®ª ­¥ ¬¥­ìè¥ 14-£®, â® ¢®§¢à é ¥âáï
eax=5 (not found) ¨ ebx=0;
* ªáâ â¨ £®¢®àï, ä®à¬ â FAT12 ¤®¯ã᪠¥â ¤¨áª¥âë á à §¬¥à®¬
ª®à­¥¢®£® ª â «®£  ¬¥­ìè¥ ¨«¨ ¡®«ìè¥ 14 ¡«®ª®¢;
* ¯à®¢¥àª¨ ¤«¨­ë ­¥ ¤¥« ¥âáï;
* ¥á«¨ 㤠«®áì ¯à®ç¨â âì ¤ ­­ë¥ á ¤¨áª¥âë, ¢®§¢à é ¥âáï
eax=0,ebx=0; ¢ ¯à®â¨¢­®¬ á«ãç ¥ eax=10 (access denied), ebx=-1.
* ”ã­ªæ¨ï ®¡à ¡ â뢠¥â ç⥭¨¥ ᯥ樠«ì­ëå ¯ ¯®ª /,/rd,/fd,/hd[n];
­® १ã«ìâ â ­¥ ᮮ⢥âáâ¢ã¥â ®¦¨¤ ¥¬®¬ã
(¯® à ¡®â¥ á ®¡ëç­ë¬¨ ä ©« ¬¨/¯ ¯ª ¬¨), ­¥ á«¥¤ã¥â ãáâ ­®¢«¥­­ë¬
¯à ¢¨« ¬, ¬®¦¥â ¨§¬¥­¨âìáï ¢ á«¥¤ãîé¨å ¢¥àá¨ïå ï¤à  ¨ ¯®â®¬ã
­¥ ®¯¨á뢠¥âáï. „«ï ¯®«ã祭¨ï ¨­ä®à¬ æ¨¨ ®¡ ®¡®à㤮¢ ­¨¨
¨á¯®«ì§ã©â¥ ¯®¤äã­ªæ¨î 11 ä㭪樨 18 ¨«¨
ç¨â ©â¥ ᮮ⢥âáâ¢ãî騥 ¯ ¯ª¨ ¯®¤ä㭪樥© 1 ä㭪樨 70.
* Функция позволяет читать корневые папки /rd/1,/fd/x,/hd[n]/x, но
в первых двух случаях текущая реализация не следует
установленным правилам:
для /rd/1:
* если указано 0 блоков для чтения, считается,
что запрашивается 1;
* если запрашивается больше 14 блоков или начальный блок
не меньше 14-го, то возвращается eax=5 (not found) и ebx=-1;
* размер корневого каталога рамдиска = 14 блоков,
0x1C00=7168 байт; но возвращается ebx=0
(за исключением случая предыдущего пункта);
* как ни странно, можно прочитать 14-й блок (там, вообще говоря,
мусор - напоминаю, счёт ведётся с 0);
* если был запрошен хотя бы один блок с номером, не меньшим 14,
то возвращается eax=6(EOF); иначе eax=0.
Для /fd/x:
* если начальный блок не меньше 14-го, то возвращается
eax=5 (not found) и ebx=0;
* кстати говоря, формат FAT12 допускает дискеты с размером
корневого каталога меньше или больше 14 блоков;
* проверки длины не делается;
* если удалось прочитать данные с дискеты, возвращается
eax=0,ebx=0; в противном случае eax=10 (access denied), ebx=-1.
* Функция обрабатывает чтение специальных папок /,/rd,/fd,/hd[n];
но результат не соответствует ожидаемому
(по работе с обычными файлами/папками), не следует установленным
правилам, может измениться в следующих версиях ядра и потому
не описывается. Для получения информации об оборудовании
используйте подфункцию 11 функции 18 или
читайте соответствующие папки подфункцией 1 функции 70.
 
======================================================================
========= ”ã­ªæ¨ï 58, ¯®¤äã­ªæ¨ï 8 - LBA-ç⥭¨¥ á ãáâனá⢠. ========
========= Функция 58, подфункция 8 - LBA-чтение с устройства. ========
======================================================================
 à ¬¥âàë:
* eax = 58 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 8 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ­®¬¥à ¡«®ª  ¤«ï ç⥭¨ï (áç¨â ï á 0)
* +8: dword: ¨£­®à¨àã¥âáï (ãáâ ­ ¢«¨¢ ©â¥ ¢ 1)
* +12 = +0xC: dword: 㪠§ â¥«ì ­  ¡ãä¥à, ªã¤  ¡ã¤ãâ § ¯¨á ­ë ¤ ­­ë¥
(512 ¡ ©â)
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï à ¡®âë á¨á⥬ë
(4096 ¡ ©â)
* +20 = +0x14: ASCIIZ-¨¬ï ãáâனá⢠: ­¥çã¢á⢨⥫쭮 ª ॣ¨áâàã,
®¤­® ¨§ /rd/1 = /RamDisk/1, /hd/n = /HardDisk/n,
1<=n<=4 - ­®¬¥à ãáâனá⢠: 1=IDE0, ..., 4=IDE3.
‚¬¥áâ® æ¨äà ¤®¯ã᪠¥âáï, å®âï ¨ ­¥ ४®¬¥­¤ã¥âáï ¤«ï 㤮¡á⢠
¯¥à¥å®¤  ­  ¡ã¤ã騥 à áè¨à¥­¨ï,
¨á¯®«ì§®¢ ­¨¥ 'first','second','third','fourth'.
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ 㪠§ ­® ¨¬ï ãáâனá⢠ /hd/xxx, £¤¥ xxx ­¥ ­ å®¤¨âáï
¢ ᯨ᪥ ¢ëè¥:
Параметры:
* eax = 58 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 8 = номер подфункции
* +4: dword: номер блока для чтения (считая с 0)
* +8: dword: игнорируется (устанавливайте в 1)
* +12 = +0xC: dword: указатель на буфер, куда будут записаны данные
(512 байт)
* +16 = +0x10: dword: указатель на буфер для работы системы
(4096 байт)
* +20 = +0x14: ASCIIZ-имя устройства: нечувствительно к регистру,
одно из /rd/1 = /RamDisk/1, /hd/n = /HardDisk/n,
1<=n<=4 - номер устройства: 1=IDE0, ..., 4=IDE3.
Вместо цифр допускается, хотя и не рекомендуется для удобства
перехода на будущие расширения,
использование 'first','second','third','fourth'.
Возвращаемое значение:
* если указано имя устройства /hd/xxx, где xxx не находится
в списке выше:
* eax = ebx = 1
* ¥á«¨ 㪠§ ­® ­¥¯à ¢¨«ì­®¥ ¨¬ï ãáâனá⢠
(§  ¨áª«î祭¨¥¬ ¯à¥¤ë¤ã饣® á«ãç ï):
* если указано неправильное имя устройства
(за исключением предыдущего случая):
* eax = 5
* ebx ­¥ ¬¥­ï¥âáï
* ¥á«¨ LBA-¤®áâ㯠§ ¯à¥éñ­ ¯®¤ä㭪樥© 11 ä㭪樨 21:
* ebx не меняется
* если LBA-доступ запрещён подфункцией 11 функции 21:
* eax = 2
* ebx à §àãè ¥âáï
* ¤«ï à ¬¤¨áª : ¯®¯ë⪠ ç⥭¨ï ¡«®ª  §  ¯à¥¤¥« ¬¨ à ¬¤¨áª 
(18*2*80 ¡«®ª®¢) ¯à¨¢®¤¨â ª
* ebx разрушается
* для рамдиска: попытка чтения блока за пределами рамдиска
(18*2*80 блоков) приводит к
* eax = 3
* ebx = 0
* ¯à¨ ãᯥ譮¬ ç⥭¨¨:
* при успешном чтении:
* eax = ebx = 0
‡ ¬¥ç ­¨ï:
*  §¬¥à ¡«®ª  - 512 ¡ ©â; ç¨â ¥âáï ®¤¨­ ¡«®ª.
* ¥ á«¥¤ã¥â ¯®« £ âìáï ­  ¢®§¢à é ¥¬®¥ §­ ç¥­¨¥,
®­® ¬®¦¥â ¨§¬¥­¨âìáï ¢ á«¥¤ãîé¨å ¢¥àá¨ïå.
* ’ॡã¥âáï, çâ®¡ë ¡ë« à §à¥èñ­ LBA-¤®áâ㯠ª ãáâனá⢠¬
¯®¤ä㭪樥© 11 ä㭪樨 21. “§­ âì íâ® ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樥© 11 ä㭪樨 26.
* LBA-ç⥭¨¥ ¤¨áª¥âë ­¥ ¯®¤¤¥à¦¨¢ ¥âáï.
* ”ã­ªæ¨ï áç¨â뢠¥â ¤ ­­ë¥ 䨧¨ç¥áª®£® ¦ñá⪮£® ¤¨áª ;
¥á«¨ ¯® ª ª¨¬-â® ¯à¨ç¨­ ¬ ­ã¦­ë ¤ ­­ë¥ ª®­ªà¥â­®£® à §¤¥« ,
¯à¨¤ñâáï ®¯à¥¤¥«ïâì ­ ç «ì­ë© ᥪâ®à í⮣® à §¤¥« 
(«¨¡® ­ ¯àï¬ãî ç¥à¥§ MBR, «¨¡® ¨§ à áè¨à¥­­®© áâàãªâãàë,
¢®§¢à é ¥¬®© ⮩ ¦¥ ¯®¤ä㭪樥© 11 ä㭪樨 18).
* ”ã­ªæ¨ï ­¥ ¯à®¢¥àï¥â ª®¤ ®è¨¡ª¨ ¦ñá⪮£® ¤¨áª , â ª çâ® § ¯à®á
­¥áãé¥áâ¢ãî饣® ᥪâ®à  ¢áñ à ¢­® çâ®-â® ¯à®ç¨â ¥â
(¢¥à®ïâ­¥¥ ¢á¥£®, ­ã«¨, ­® íâ® ®¯à¥¤¥«ï¥âáï ãáâனá⢮¬) ¨
íâ® ¡ã¤¥â áç¨â âìáï ãᯥ宬 (eax=0).
Замечания:
* Размер блока - 512 байт; читается один блок.
* Не следует полагаться на возвращаемое значение,
оно может измениться в следующих версиях.
* Требуется, чтобы был разрешён LBA-доступ к устройствам
подфункцией 11 функции 21. Узнать это можно вызовом
подфункцией 11 функции 26.
* LBA-чтение дискеты не поддерживается.
* Функция считывает данные физического жёсткого диска;
если по каким-то причинам нужны данные конкретного раздела,
придётся определять начальный сектор этого раздела
(либо напрямую через MBR, либо из расширенной структуры,
возвращаемой той же подфункцией 11 функции 18).
* Функция не проверяет код ошибки жёсткого диска, так что запрос
несуществующего сектора всё равно что-то прочитает
(вероятнее всего, нули, но это определяется устройством) и
это будет считаться успехом (eax=0).
 
======================================================================
= ”ã­ªæ¨ï 58, ¯®¤äã­ªæ¨ï 15 - ¯®«ãç¨âì ¨­ä®à¬ æ¨î ® ä ©«®¢®© á¨á⥬¥.
= Функция 58, подфункция 15 - получить информацию о файловой системе.
======================================================================
 à ¬¥âàë:
* eax = 58 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 15 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¨£­®à¨àã¥âáï
* +8: dword: ¨£­®à¨àã¥âáï
* +12 = +0xC: dword: ¨£­®à¨àã¥âáï
* +16 = +0x10: dword: ¨£­®à¨àã¥âáï
* +20 = +0x14: (¯à®¢¥àï¥âáï ⮫쪮 ¢â®à®© ᨬ¢®«, áࠧ㠯®á«¥ á«íè )
/rd=/RAMDISK ¨«¨ /hd=/HARDDISK
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¥á«¨ ¢â®à®© ᨬ¢®« ­¥ ¯à¨­ ¤«¥¦¨â ¬­®¦¥áâ¢ã {'r','R','h','H'}:
Параметры:
* eax = 58 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 15 = номер подфункции
* +4: dword: игнорируется
* +8: dword: игнорируется
* +12 = +0xC: dword: игнорируется
* +16 = +0x10: dword: игнорируется
* +20 = +0x14: (проверяется только второй символ, сразу после слэша)
/rd=/RAMDISK или /hd=/HARDDISK
Возвращаемое значение:
* если второй символ не принадлежит множеству {'r','R','h','H'}:
* eax = 3
* ebx = ecx = dword [fileinfo] = 0
* ¤«ï à ¬¤¨áª :
* eax = 0 (ãᯥå)
* ebx = ®¡é¥¥ ç¨á«® ª« áâ¥à®¢ = 2847
* ecx = ç¨á«® ᢮¡®¤­ëå ª« áâ¥à®¢
* dword [fileinfo] = à §¬¥à ª« áâ¥à  = 512
* ¤«ï ¦ñá⪮£® ¤¨áª : ¡ §  ¨ à §¤¥« ®¯à¥¤¥«ïîâáï ¯®¤äã­ªæ¨ï¬¨ 7 ¨ 8
ä㭪樨 21:
* eax = 0 (ãᯥå)
* ebx = ®¡é¥¥ ç¨á«® ª« áâ¥à®¢
* ecx = ç¨á«® ᢮¡®¤­ëå ª« áâ¥à®¢
* dword [fileinfo] = à §¬¥à ª« áâ¥à  (¢ ¡ ©â å)
‡ ¬¥ç ­¨ï:
* ¥ 㤨¢«ï©â¥áì áâà ­­®¬ã à á¯®«®¦¥­¨î 4-£® ¢®§¢à é ¥¬®£®
¯ à ¬¥âà  - ª®£¤  ¯¨á «áï íâ®â ª®¤, ¯à¨ á¨á⥬­ëå ¢ë§®¢ å
¯à¨«®¦¥­¨î ¢®§¢à é «¨áì ⮫쪮 ॣ¨áâàë eax,ebx,ecx (¨§
pushad-áâàãªâãàë, ¯¥à¥¤ î饩áï ª ª  à£ã¬¥­â á¨á⥬­®© ä㭪樨).
’¥¯¥àì íâ® ¨á¯à ¢«¥­®, â ª çâ®, ¢®§¬®¦­®, ¨¬¥¥â á¬ëá« ¢®§¢à é âì
à §¬¥à ª« áâ¥à  ¢ edx, ¯®ª  íâã äã­ªæ¨î ­¥ ­ ç «¨ ¨á¯®«ì§®¢ âì.
* ‚®®¡é¥-â® ¥éñ áãé¥áâ¢ã¥â ¯®¤äã­ªæ¨ï 11 ä㭪樨 18, ¢®§¢à é îé ï
¨­ä®à¬ æ¨î ® ä ©«®¢®© á¨á⥬¥. ® à áè¨à¥­­®© â ¡«¨æ¥ ¤¨áª®¢®©
¯®¤á¨áâ¥¬ë ¬®¦­® ®¯à¥¤¥«¨âì à §¬¥à ª« áâ¥à  (â ¬ ®­ åà ­¨âáï
¢ ᥪâ®à å) ¨ ®¡é¥¥ ç¨á«® ª« áâ¥à®¢ ¤«ï ¦ñáâª¨å ¤¨áª®¢.
* для рамдиска:
* eax = 0 (успех)
* ebx = общее число кластеров = 2847
* ecx = число свободных кластеров
* dword [fileinfo] = размер кластера = 512
* для жёсткого диска: база и раздел определяются подфункциями 7 и 8
функции 21:
* eax = 0 (успех)
* ebx = общее число кластеров
* ecx = число свободных кластеров
* dword [fileinfo] = размер кластера (в байтах)
Замечания:
* Не удивляйтесь странному расположению 4-го возвращаемого
параметра - когда писался этот код, при системных вызовах
приложению возвращались только регистры eax,ebx,ecx (из
pushad-структуры, передающейся как аргумент системной функции).
Теперь это исправлено, так что, возможно, имеет смысл возвращать
размер кластера в edx, пока эту функцию не начали использовать.
* Вообще-то ещё существует подфункция 11 функции 18, возвращающая
информацию о файловой системе. По расширенной таблице дисковой
подсистемы можно определить размер кластера (там он хранится
в секторах) и общее число кластеров для жёстких дисков.
 
======================================================================
=========== ”ã­ªæ¨ï 60 - Inter Process Communication (IPC). ==========
=========== Функция 60 - Inter Process Communication (IPC). ==========
======================================================================
IPC ¯à¨¬¥­ï¥âáï ¤«ï ¯®áë«®ª á®®¡é¥­¨© ®â ®¤­®£® ¯à®æ¥áá /¯®â®ª 
¤à㣮¬ã. à¨ í⮬ á«¥¤ã¥â ¯à¥¤¢ à¨â¥«ì­® ¤®£®¢®à¨âìáï ® ⮬, ª ª
¨­â¥à¯à¥â¨à®¢ âì ª®­ªà¥â­®¥ á®®¡é¥­¨¥.
IPC применяется для посылок сообщений от одного процесса/потока
другому. При этом следует предварительно договориться о том, как
интерпретировать конкретное сообщение.
 
-------- ®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì ®¡« áâì ¤«ï ¯®«ã祭¨ï IPC ---------
‚ë§ë¢ ¥âáï ¯à®æ¥áᮬ-¯à¨ñ¬­¨ª®¬.
 à ¬¥âàë:
* eax = 60 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à
* edx = à §¬¥à ¡ãä¥à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ¢á¥£¤  ãᯥ譮
”®à¬ â IPC-¡ãä¥à :
* +0: dword: ¥á«¨ §¤¥áì ­¥ 0, â® ¡ãä¥à áç¨â ¥âáï § ¡«®ª¨à®¢ ­­ë¬;
¡«®ª¨àã©â¥/à §¡«®ª¨àã©â¥ ¡ãä¥à, ª®£¤  ¢ë á ­¨¬  ªâ¨¢­® à ¡®â ¥â¥
¨ ¢ ¬ ­ ¤®, çâ®¡ë ¨§¢­¥ ­¥ ¨§¬¥­ï«¨áì ¤ ­­ë¥ ¡ãä¥à 
(­¥ ¯®áâ㯠«¨ ­®¢ë¥ á®®¡é¥­¨ï)
* +4: dword: § ­ïâ® ¬¥áâ  ¢ ¡ãä¥à¥ (¢ ¡ ©â å)
* +8: ¯¥à¢®¥ á®®¡é¥­¨¥
* +8+n: ¢â®à®¥ á®®¡é¥­¨¥
-------- Подфункция 1 - установить область для получения IPC ---------
Вызывается процессом-приёмником.
Параметры:
* eax = 60 - номер функции
* ebx = 1 - номер подфункции
* ecx = указатель на буфер
* edx = размер буфера
Возвращаемое значение:
* eax = 0 - всегда успешно
Формат IPC-буфера:
* +0: dword: если здесь не 0, то буфер считается заблокированным;
блокируйте/разблокируйте буфер, когда вы с ним активно работаете
и вам надо, чтобы извне не изменялись данные буфера
(не поступали новые сообщения)
* +4: dword: занято места в буфере (в байтах)
* +8: первое сообщение
* +8+n: второе сообщение
* ...
”®à¬ â á®®¡é¥­¨ï:
* +0: dword: PID ¯à®æ¥áá /¯®â®ª , ¯®á« ¢è¥£® á®®¡é¥­¨¥
* +4: dword: ¤«¨­  á®®¡é¥­¨ï (­¥ áç¨â ï íâ®â § £®«®¢®ª)
* +8: n*byte: ¤ ­­ë¥ á®®¡é¥­¨ï
Формат сообщения:
* +0: dword: PID процесса/потока, пославшего сообщение
* +4: dword: длина сообщения (не считая этот заголовок)
* +8: n*byte: данные сообщения
 
--------------- ®¤äã­ªæ¨ï 2 - ¯®á« âì á®®¡é¥­¨¥ IPC. ----------------
‚ë§ë¢ ¥âáï ¯à®æ¥áᮬ-¨­¨æ¨ â®à®¬.
 à ¬¥âàë:
* eax = 60 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = PID ¯à¨ñ¬­¨ª 
* edx = 㪠§ â¥«ì ­  ¤ ­­ë¥ á®®¡é¥­¨ï
* esi = ¤«¨­  á®®¡é¥­¨ï (¢ ¡ ©â å)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ¯à¨ñ¬­¨ª ­¥ ®¯à¥¤¥«¨« ¡ãä¥à ¤«ï IPC-á®®¡é¥­¨©
(¬®¦¥â ¡ëâì, ¥éñ ­¥ ãᯥ«,   ¬®¦¥â ¡ëâì, íâ® ­¥ â®â ¯®â®ª,
ª®â®àë© ­ã¦¥­)
* eax = 2 - ¯à¨ñ¬­¨ª § ¡«®ª¨à®¢ « IPC-¡ãä¥à;
¯®¯à®¡ã©â¥ ­¥¬­®£® ¯®¤®¦¤ âì
* eax = 3 - ¯¥à¥¯®«­¥­¨¥ IPC-¡ãä¥à  ¯à¨ñ¬­¨ª 
* eax = 4 - ¯à®æ¥áá /¯®â®ª  á â ª¨¬ PID ­¥ áãé¥áâ¢ã¥â
‡ ¬¥ç ­¨ï:
* ‘¨á⥬  áࠧ㠯®á«¥ § ¯¨á¨ IPC-á®®¡é¥­¨ï ¢ ¡ãä¥à ¯®áë« ¥â
¯®â®ªã-¯à¨ñ¬­¨ªã ᮡë⨥ á ª®¤®¬ 7 (á¬. ª®¤ë ᮡë⨩).
--------------- Подфункция 2 - послать сообщение IPC. ----------------
Вызывается процессом-инициатором.
Параметры:
* eax = 60 - номер функции
* ebx = 2 - номер подфункции
* ecx = PID приёмника
* edx = указатель на данные сообщения
* esi = длина сообщения (в байтах)
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - приёмник не определил буфер для IPC-сообщений
(может быть, ещё не успел, а может быть, это не тот поток,
который нужен)
* eax = 2 - приёмник заблокировал IPC-буфер;
попробуйте немного подождать
* eax = 3 - переполнение IPC-буфера приёмника
* eax = 4 - процесса/потока с таким PID не существует
Замечания:
* Система сразу после записи IPC-сообщения в буфер посылает
потоку-приёмнику событие с кодом 7 (см. коды событий).
 
======================================================================
=== ”ã­ªæ¨ï 61 - ¯®«ãç¨âì ¯ à ¬¥âàë ¤«ï ¯àאַ£® ¤®áâ㯠 ª £à ä¨ª¥. ===
=== Функция 61 - получить параметры для прямого доступа к графике. ===
======================================================================
à®£à ¬¬¥ ¤®áâã¯­ë ¤ ­­ë¥ £à ä¨ç¥áª®£® íªà ­  (®¡« áâì ¯ ¬ïâ¨, ª®â®à ï
ᮡá⢥­­® ¨ ®â®¡à ¦ ¥â ᮤ¥à¦¨¬®¥ íªà ­ ) ­ ¯àï¬ãî ¡¥§ ¢ë§®¢®¢
á¨á⥬­ëå ä㭪権 ç¥à¥§ ᥫ¥ªâ®à gs:
Программе доступны данные графического экрана (область памяти, которая
собственно и отображает содержимое экрана) напрямую без вызовов
системных функций через селектор gs:
mov eax, [gs:0]
¯®¬¥áâ¨â ¢ eax ¯¥à¢ë© dword ¡ãä¥à , ᮤ¥à¦ é¨© ¨­ä®à¬ æ¨î ® 梥â¥
«¥¢®© ¢¥àå­¥© â®çª¨ (¨, ¢®§¬®¦­®, æ¢¥â  ­¥áª®«ìª¨å á«¥¤ãîé¨å).
поместит в eax первый dword буфера, содержащий информацию о цвете
левой верхней точки (и, возможно, цвета нескольких следующих).
mov [gs:0], eax
¯à¨ à ¡®â¥ ¢ ०¨¬ å VESA c LFB
ãáâ ­®¢¨â 梥⠫¥¢®© ¢¥àå­¥© â®çª¨
(¨ ¢®§¬®¦­®, æ¢¥â  ­¥áª®«ìª¨å á«¥¤ãîé¨å).
„«ï ¨­â¥à¯à¥â æ¨¨ ¤ ­­ëå £à ä¨ç¥áª®£® íªà ­  âॡã¥âáï §­ ­¨¥
­¥ª®â®àëå ¯ à ¬¥â஢, ª®â®àë¥ ¢®§¢à é îâáï í⮩ ä㭪樥©.
‡ ¬¥ç ­¨ï:
*  à ¬¥âàë £à ä¨ª¨ ®ç¥­ì ।ª® ¬¥­ïîâáï ¯à¨ à ¡®â¥ á¨á⥬ë,
  ¨¬¥­­®, ⮫쪮 ¢ á«ãç ïå, ª®£¤  ¯®«ì§®¢ â¥«ì à ¡®â ¥â
á ¯à®£à ¬¬®© VRR.
* à¨ ¨§¬¥­¥­¨¨ ¢¨¤¥®à¥¦¨¬  á¨á⥬  ¯¥à¥à¨á®¢ë¢ ¥â ¢á¥ ®ª­ 
(ᮡë⨥ á ª®¤®¬ 1) ¨ ¯¥à¥à¨á®¢ë¢ ¥â ä®­ (ᮡë⨥ 5).
â¨ ¦¥ ᮡëâ¨ï ¯à®¨á室ïâ ¨ ¢ ¤à㣨å á«ãç ïå,
ª®â®àë¥ ¢áâà¥ç îâáï §­ ç¨â¥«ì­® ç é¥, 祬 ¨§¬¥­¥­¨¥ ¢¨¤¥®à¥¦¨¬ .
* à¨ à ¡®â¥ ¢ ¢¨¤¥®à¥¦¨¬ å á LFB ᥫ¥ªâ®à gs 㪠§ë¢ ¥â ­ 
ᮡá⢥­­® LFB, â ª çâ® ç⥭¨¥/§ ¯¨áì ¯® gs ¯à¨¢®¤ïâ
­¥¯®á।á⢥­­® ª ¨§¬¥­¥­¨î ᮤ¥à¦¨¬®£® íªà ­ . à¨ à ¡®â¥ ¢
¢¨¤¥®à¥¦¨¬ å ¡¥§ LFB gs 㪠§ë¢ ¥â ­  ­¥ª®â®àãî ®¡« áâì ¤ ­­ëå
ï¤à , ¯à¨çñ¬ ¢á¥ ä㭪樨 ¢ë¢®¤  ­  íªà ­ ¤®¡à®á®¢¥áâ­® ¢ë¯®«­ïîâ
¤¢®©­ãî à ¡®âã ¯® § ¯¨á¨ ­¥¯®á।á⢥­­® ­  íªà ­ ¨ ¯® § ¯¨á¨
¢ íâ®â ¡ãä¥à. ‚ १ã«ìâ â¥ ¯à¨ ç⥭¨¨ ᮤ¥à¦¨¬®£® í⮣® ¡ãä¥à 
१ã«ìâ âë ᮮ⢥âáâ¢ãîâ ᮤ¥à¦¨¬®¬ã íªà ­ 
(á, ¢®®¡é¥ £®¢®àï, ¡®«ì訬 æ¢¥â®¢ë¬ à §à¥è¥­¨¥¬),
  § ¯¨áì ¨£­®à¨àã¥âáï.
ˆáª«î祭¨¥¬ ï¥âáï ०¨¬ 320*200, ¤«ï ª®â®à®£® ¢ £« ¢­®¬ 横«¥
á¨á⥬­®£® ¯®â®ª  ¢ë¯®«­ï¥âáï ®¡­®¢«¥­¨¥ íªà ­  ¢ ᮮ⢥âá⢨¨
á ¤¢¨¦¥­¨ï¬¨ ªãàá®à  ¬ëè¨.
при работе в режимах VESA c LFB
установит цвет левой верхней точки
(и возможно, цвета нескольких следующих).
Для интерпретации данных графического экрана требуется знание
некоторых параметров, которые возвращаются этой функцией.
Замечания:
* Параметры графики очень редко меняются при работе системы,
а именно, только в случаях, когда пользователь работает
с программой VRR.
* При изменении видеорежима система перерисовывает все окна
(событие с кодом 1) и перерисовывает фон (событие 5).
Эти же события происходят и в других случаях,
которые встречаются значительно чаще, чем изменение видеорежима.
* При работе в видеорежимах с LFB селектор gs указывает на
собственно LFB, так что чтение/запись по gs приводят
непосредственно к изменению содержимого экрана. При работе в
видеорежимах без LFB gs указывает на некоторую область данных
ядра, причём все функции вывода на экран добросовестно выполняют
двойную работу по записи непосредственно на экран и по записи
в этот буфер. В результате при чтении содержимого этого буфера
результаты соответствуют содержимому экрана
(с, вообще говоря, большим цветовым разрешением),
а запись игнорируется.
Исключением является режим 320*200, для которого в главном цикле
системного потока выполняется обновление экрана в соответствии
с движениями курсора мыши.
 
-------------------------  §à¥è¥­¨¥ íªà ­  --------------------------
 à ¬¥âàë:
* eax = 61 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = [à §à¥è¥­¨¥ ¯® ®á¨ x]*65536 + [à §à¥è¥­¨¥ ¯® ®á¨ y]
‡ ¬¥ç ­¨ï:
* Œ®¦­® ¨á¯®«ì§®¢ âì äã­ªæ¨î 14 á ãçñ⮬ ⮣®, çâ® ®­  ¢®§¢à é ¥â
à §¬¥àë ­  1 ¬¥­ìè¥. â® ¯®«­®áâìî íª¢¨¢ «¥­â­ë© ᯮᮡ.
------------------------- Разрешение экрана --------------------------
Параметры:
* eax = 61 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* eax = [разрешение по оси x]*65536 + [разрешение по оси y]
Замечания:
* Можно использовать функцию 14 с учётом того, что она возвращает
размеры на 1 меньше. Это полностью эквивалентный способ.
 
------------------------ —¨á«® ¡¨â ­  ¯¨ªá¥«ì ------------------------
 à ¬¥âàë:
* eax = 61 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¡¨â ­  ¯¨ªá¥«ì (24 ¨«¨ 32)
------------------------ Число бит на пиксель ------------------------
Параметры:
* eax = 61 - номер функции
* ebx = 2 - номер подфункции
Возвращаемое значение:
* eax = число бит на пиксель (24 или 32)
 
------------------------ —¨á«® ¡ ©â ­  áâபã ------------------------
 à ¬¥âàë:
* eax = 61 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¡ ©â, ª®â®à®¥ § ­¨¬ ¥â ®¤­  áâப  à §¢ñà⪨
(£®à¨§®­â «ì­ ï «¨­¨ï ­  íªà ­¥)
------------------------ Число байт на строку ------------------------
Параметры:
* eax = 61 - номер функции
* ebx = 3 - номер подфункции
Возвращаемое значение:
* eax = число байт, которое занимает одна строка развёртки
(горизонтальная линия на экране)
 
======================================================================
===== ”ã­ªæ¨ï 62, ¯®¤äã­ªæ¨ï 0 - ¯®«ãç¨âì ¢¥àá¨î PCI-¨­â¥à䥩á . =====
===== Функция 62, подфункция 0 - получить версию PCI-интерфейса. =====
======================================================================
 à ¬¥âàë:
* eax = 62 - ­®¬¥à ä㭪樨
* bl = 0 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤®áâ㯠ª PCI § ¯à¥éñ­; ¨­ ç¥
* ah.al = ¢¥àá¨ï PCI-¨­â¥àä¥©á  (ah=¢¥àá¨ï, al=¯®¤¢¥àá¨ï)
* áâ à襥 á«®¢® eax ®¡­ã«¥­®
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì à §à¥èñ­ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI
¤«ï ¯à¨«®¦¥­¨© ¯®¤ä㭪樥© 12 ä㭪樨 21.
* …᫨ PCI BIOS ­¥ ¯®¤¤¥à¦¨¢ ¥âáï, â® §­ ç¥­¨¥ ax ­¥®¯à¥¤¥«¥­®.
Параметры:
* eax = 62 - номер функции
* bl = 0 - номер подфункции
Возвращаемое значение:
* eax = -1 - доступ к PCI запрещён; иначе
* ah.al = версия PCI-интерфейса (ah=версия, al=подверсия)
* старшее слово eax обнулено
Замечания:
* Предварительно должен быть разрешён низкоуровневый доступ к PCI
для приложений подфункцией 12 функции 21.
* Если PCI BIOS не поддерживается, то значение ax неопределено.
 
======================================================================
==== ”ã­ªæ¨ï 62, ¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì ­®¬¥à ¯®á«¥¤­¥© PCI-設ë. ===
==== Функция 62, подфункция 1 - получить номер последней PCI-шины. ===
======================================================================
 à ¬¥âàë:
* eax = 62 - ­®¬¥à ä㭪樨
* bl = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤®áâ㯠ª PCI § ¯à¥éñ­; ¨­ ç¥
* al = ­®¬¥à ¯®á«¥¤­¥© PCI-設ë; ®á⠢訥áï ¡ ©âë eax à §àãè îâáï
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì à §à¥èñ­ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI
¤«ï ¯à¨«®¦¥­¨© ¯®¤ä㭪樥© 12 ä㭪樨 21.
* …᫨ PCI BIOS ­¥ ¯®¤¤¥à¦¨¢ ¥âáï, â® §­ ç¥­¨¥ al ­¥®¯à¥¤¥«¥­®.
Параметры:
* eax = 62 - номер функции
* bl = 1 - номер подфункции
Возвращаемое значение:
* eax = -1 - доступ к PCI запрещён; иначе
* al = номер последней PCI-шины; оставшиеся байты eax разрушаются
Замечания:
* Предварительно должен быть разрешён низкоуровневый доступ к PCI
для приложений подфункцией 12 функции 21.
* Если PCI BIOS не поддерживается, то значение al неопределено.
 
======================================================================
====================== ”ã­ªæ¨ï 62, ¯®¤äã­ªæ¨ï 2 ======================
== ®«ãç¨âì ¬¥å ­¨§¬ ®¡à é¥­¨ï ª ª®­ä¨£ãà æ¨®­­®¬ã ¯à®áâà ­áâ¢ã PCI. =
====================== Функция 62, подфункция 2 ======================
== Получить механизм обращения к конфигурационному пространству PCI. =
======================================================================
 à ¬¥âàë:
* eax = 62 - ­®¬¥à ä㭪樨
* bl = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ¤®áâ㯠ª PCI § ¯à¥éñ­; ¨­ ç¥
* al = ¬¥å ­¨§¬ (1 ¨«¨ 2); ¯à®ç¨¥ ¡ ©âë eax à §àãè îâáï
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì à §à¥èñ­ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI
¤«ï ¯à¨«®¦¥­¨© ¯®¤ä㭪樥© 12 ä㭪樨 21.
* Œ¥å ­¨§¬ ®¡à é¥­¨ï ¢ë¡¨à ¥âáï ¢ ᮮ⢥âá⢨¨
á å à ªâ¥à¨á⨪ ¬¨ ®¡®à㤮¢ ­¨ï.
* ®¤ä㭪樨 ç⥭¨ï ¨ § ¯¨á¨  ¢â®¬ â¨ç¥áª¨ à ¡®â îâ
á ¢ë¡à ­­ë¬ ¬¥å ­¨§¬®¬.
Параметры:
* eax = 62 - номер функции
* bl = 2 - номер подфункции
Возвращаемое значение:
* eax = -1 - доступ к PCI запрещён; иначе
* al = механизм (1 или 2); прочие байты eax разрушаются
Замечания:
* Предварительно должен быть разрешён низкоуровневый доступ к PCI
для приложений подфункцией 12 функции 21.
* Механизм обращения выбирается в соответствии
с характеристиками оборудования.
* Подфункции чтения и записи автоматически работают
с выбранным механизмом.
 
======================================================================
======== ”ã­ªæ¨ï 62, ¯®¤ä㭪樨 4,5,6 - ¯à®ç¨â âì PCI-ॣ¨áâà. =======
======== Функция 62, подфункции 4,5,6 - прочитать PCI-регистр. =======
======================================================================
 à ¬¥âàë:
* eax = 62 - ­®¬¥à ä㭪樨
* bl = 4 - ç¨â âì ¡ ©â
* bl = 5 - ç¨â âì á«®¢®
* bl = 6 - ç¨â âì ¤¢®©­®¥ á«®¢®
* bh = ­®¬¥à PCI-設ë
* ch = dddddfff, £¤¥ ddddd = ­®¬¥à ãáâனá⢠ ­  設¥,
fff = ­®¬¥à ä㭪樨 ãáâனá⢠
* cl = ­®¬¥à ॣ¨áâà  (¤®«¦¥­ ¡ëâì çñâ­ë¬ ¤«ï bl=5,
¤¥«¨âìáï ­  4 ¤«ï bl=6)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ®è¨¡ª  (§ ¯à¥éñ­ ¤®áâ㯠ª PCI ¨«¨
­¥¯®¤¤¥à¦¨¢ ¥¬ë¥ ¯ à ¬¥âàë); ¨­ ç¥
* al/ax/eax (¢ § ¢¨á¨¬®á⨠®â § ¯à®è¥­­®£® à §¬¥à ) ᮤ¥à¦¨â ¤ ­­ë¥;
®áâ ¢è ïáï ç áâì ॣ¨áâà  eax à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì à §à¥èñ­ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI
¤«ï ¯à¨«®¦¥­¨© ¯®¤ä㭪樥© 12 ä㭪樨 21.
* Œ¥å ­¨§¬ ¤®áâ㯠 2 ¯®¤¤¥à¦¨¢ ¥â ⮫쪮 16 ãáâனá⢠­  設¥ ¨
¨£­®à¨àã¥â ­®¬¥à ä㭪樨. ®«ãç¨âì ¬¥å ­¨§¬ ¤®áâ㯠 ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 2.
* ¥ª®â®àë¥ à¥£¨áâàë áâ ­¤ àâ­ë ¨ áãé¥áâ¢ãîâ ¤«ï ¢á¥å ãáâனáâ¢,
­¥ª®â®àë¥ ®¯à¥¤¥«ïîâáï ª®­ªà¥â­ë¬ ãáâனá⢮¬. ‘¯¨á®ª ¯¥à¢ëå
¢å®¤¨â, ­ ¯à¨¬¥à, ¢ ¨§¢¥áâ­ë© Interrupt List by Ralf Brown
Параметры:
* eax = 62 - номер функции
* bl = 4 - читать байт
* bl = 5 - читать слово
* bl = 6 - читать двойное слово
* bh = номер PCI-шины
* ch = dddddfff, где ddddd = номер устройства на шине,
fff = номер функции устройства
* cl = номер регистра (должен быть чётным для bl=5,
делиться на 4 для bl=6)
Возвращаемое значение:
* eax = -1 - ошибка (запрещён доступ к PCI или
неподдерживаемые параметры); иначе
* al/ax/eax (в зависимости от запрошенного размера) содержит данные;
оставшаяся часть регистра eax разрушается
Замечания:
* Предварительно должен быть разрешён низкоуровневый доступ к PCI
для приложений подфункцией 12 функции 21.
* Механизм доступа 2 поддерживает только 16 устройств на шине и
игнорирует номер функции. Получить механизм доступа можно вызовом
подфункции 2.
* Некоторые регистры стандартны и существуют для всех устройств,
некоторые определяются конкретным устройством. Список первых
входит, например, в известный Interrupt List by Ralf Brown
(http://www.pobox.com/~ralf/files.html,
ftp://ftp.cs.cmu.edu/afs/cs/user/ralf/pub/);
ᯨ᮪ ¢â®àëå ¤®«¦¥­ ¡ëâì 㪠§ ­ ¢ ¤®ªã¬¥­â æ¨¨ ¯® ãáâனáâ¢ã.
список вторых должен быть указан в документации по устройству.
 
======================================================================
======= ”ã­ªæ¨ï 62, ¯®¤ä㭪樨 8,9,10 - § ¯¨á âì ¢ PCI-ॣ¨áâà. ======
======= Функция 62, подфункции 8,9,10 - записать в PCI-регистр. ======
======================================================================
 à ¬¥âàë:
* eax = 62 - ­®¬¥à ä㭪樨
* bl = 8 - ¯¨á âì ¡ ©â
* bl = 9 - ¯¨á âì á«®¢®
* bl = 10 - ¯¨á âì ¤¢®©­®¥ á«®¢®
* bh = ­®¬¥à PCI-設ë
* ch = dddddfff, £¤¥ ddddd = ­®¬¥à ãáâனá⢠ ­  設¥,
fff = ­®¬¥à ä㭪樨 ãáâனá⢠
* cl = ­®¬¥à ॣ¨áâà  (¤®«¦¥­ ¡ëâì çñâ­ë¬ ¤«ï bl=9,
¤¥«¨âìáï ­  4 ¤«ï bl=10)
* dl/dx/edx (¢ § ¢¨á¨¬®á⨠®â § ¯à®è¥­­®£® à §¬¥à ) ᮤ¥à¦¨â
¤ ­­ë¥ ¤«ï § ¯¨á¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - ®è¨¡ª  (§ ¯à¥éñ­ ¤®áâ㯠ª PCI ¨«¨
­¥¯®¤¤¥à¦¨¢ ¥¬ë¥ ¯ à ¬¥âàë)
* eax = 0 - ãᯥ譮
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì à §à¥èñ­ ­¨§ª®ã஢­¥¢ë© ¤®áâ㯠ª PCI
¤«ï ¯à¨«®¦¥­¨© ¯®¤ä㭪樥© 12 ä㭪樨 21.
* Œ¥å ­¨§¬ ¤®áâ㯠 2 ¯®¤¤¥à¦¨¢ ¥â ⮫쪮 16 ãáâனá⢠­  設¥ ¨
¨£­®à¨àã¥â ­®¬¥à ä㭪樨. ®«ãç¨âì ¬¥å ­¨§¬ ¤®áâ㯠 ¬®¦­® ¢ë§®¢®¬
¯®¤ä㭪樨 2.
* ¥ª®â®àë¥ à¥£¨áâàë áâ ­¤ àâ­ë ¨ áãé¥áâ¢ãîâ ¤«ï ¢á¥å ãáâனáâ¢,
­¥ª®â®àë¥ ®¯à¥¤¥«ïîâáï ª®­ªà¥â­ë¬ ãáâனá⢮¬. ‘¯¨á®ª ¯¥à¢ëå
¢å®¤¨â, ­ ¯à¨¬¥à, ¢ ¨§¢¥áâ­ë© Interrupt List by Ralf Brown;
ᯨ᮪ ¢â®àëå ¤®«¦¥­ ¡ëâì 㪠§ ­ ¢ ¤®ªã¬¥­â æ¨¨ ¯® ãáâனáâ¢ã.
Параметры:
* eax = 62 - номер функции
* bl = 8 - писать байт
* bl = 9 - писать слово
* bl = 10 - писать двойное слово
* bh = номер PCI-шины
* ch = dddddfff, где ddddd = номер устройства на шине,
fff = номер функции устройства
* cl = номер регистра (должен быть чётным для bl=9,
делиться на 4 для bl=10)
* dl/dx/edx (в зависимости от запрошенного размера) содержит
данные для записи
Возвращаемое значение:
* eax = -1 - ошибка (запрещён доступ к PCI или
неподдерживаемые параметры)
* eax = 0 - успешно
Замечания:
* Предварительно должен быть разрешён низкоуровневый доступ к PCI
для приложений подфункцией 12 функции 21.
* Механизм доступа 2 поддерживает только 16 устройств на шине и
игнорирует номер функции. Получить механизм доступа можно вызовом
подфункции 2.
* Некоторые регистры стандартны и существуют для всех устройств,
некоторые определяются конкретным устройством. Список первых
входит, например, в известный Interrupt List by Ralf Brown;
список вторых должен быть указан в документации по устройству.
 
======================================================================
================ ”ã­ªæ¨ï 63 - à ¡®â  á ¤®áª®© ®â« ¤ª¨. ===============
================ Функция 63 - работа с доской отладки. ===============
======================================================================
„®áª  ®â« ¤ª¨ ¯à¥¤áâ ¢«ï¥â ᮡ®© á¨á⥬­ë© ¡ãä¥à (­  4096 ¡ ©â),
¢ ª®â®àë© «î¡ ï ¯à®£à ¬¬  ¬®¦¥â § ¯¨á âì (¢®®¡é¥ £®¢®àï, ¯à®¨§¢®«ì­ë¥)
¤ ­­ë¥ ¨ ¨§ ª®â®à®£® ¤àã£ ï ¯à®£à ¬¬  ¬®¦¥â í⨠¤ ­­ë¥ ¯à®ç¨â âì.
…áâì ᮣ« è¥­¨¥, ¢ ᮮ⢥âá⢨¨ á ª®â®àë¬ § ¯¨á뢠¥¬ë¥ ¤ ­­ë¥ -
⥪áâ®¢ë¥ áâப¨, ¨­â¥à¯à¥â¨àã¥¬ë¥ ª ª ®â« ¤®ç­ë¥ á®®¡é¥­¨ï ® 室¥
¢ë¯®«­¥­¨ï ¯à®£à ¬¬ë. Ÿ¤à® ¢ ®¯à¥¤¥«ñ­­ëå á¨âã æ¨ïå â ª¦¥ § ¯¨á뢠¥â
­  ¤®áªã ®â« ¤ª¨ ᢥ¤¥­¨ï ® ¢ë¯®«­¥­¨¨ ­¥ª®â®àëå ä㭪権;
¯® ᮣ« è¥­¨î á®®¡é¥­¨ï ï¤à  ­ ç¨­ îâáï á ¯à¥ä¨ªá  "K : ".
„«ï ¯à®á¬®âà  ¤®áª¨ ®â« ¤ª¨ ᮧ¤ ­® ¯à¨«®¦¥­¨¥ board,
ª®â®à®¥ áç¨â뢠¥â ¤ ­­ë¥ ¨§ ¡ãä¥à  ¨ ®â®¡à ¦ ¥â ¨å ¢ ᢮ñ¬ ®ª­¥. board
¯®­¨¬ ¥â ¯®á«¥¤®¢ â¥«ì­®áâì ª®¤®¢ 13,10 ª ª ¯¥à¥å®¤ ­  ­®¢ãî áâபã.
‘¨¬¢®« á ­ã«¥¢ë¬ ª®¤®¬ ¢ ª®­æ¥ áâப¨ ­¥ ®¡ï§ â¥«¥­, ­® ¨ ­¥ ¬¥è ¥â.
‚ á¢ï§¨ á ¯®ï¢«¥­¨¥¬ ®â« ¤ç¨ª  業­®áâì ¤®áª¨ ®â« ¤ª¨ ­¥áª®«ìª®
á­¨§¨« áì, ¯®áª®«ìªã ®â« ¤ç¨ª ¯®§¢®«ï¥â ¯®«­®áâìî ª®­â஫¨à®¢ âì 室
¢ë¯®«­¥­¨ï ¯à®£à ¬¬ë, ¯à¨çñ¬ ¤«ï í⮣® ­¥ âॡã¥âáï ­¨ª ª¨å ãᨫ¨©
á® áâ®à®­ë á ¬®© ¯à®£à ¬¬ë. ’¥¬ ­¥ ¬¥­¥¥ ¢® ¬­®£¨å á«ãç ïå
¤®áª  ®â« ¤ª¨ ¯à®¤®«¦ ¥â ®áâ ¢ âìáï ¯®«¥§­®©.
Доска отладки представляет собой системный буфер (на 4096 байт),
в который любая программа может записать (вообще говоря, произвольные)
данные и из которого другая программа может эти данные прочитать.
Есть соглашение, в соответствии с которым записываемые данные -
текстовые строки, интерпретируемые как отладочные сообщения о ходе
выполнения программы. Ядро в определённых ситуациях также записывает
на доску отладки сведения о выполнении некоторых функций;
по соглашению сообщения ядра начинаются с префикса "K : ".
Для просмотра доски отладки создано приложение board,
которое считывает данные из буфера и отображает их в своём окне. board
понимает последовательность кодов 13,10 как переход на новую строку.
Символ с нулевым кодом в конце строки не обязателен, но и не мешает.
В связи с появлением отладчика ценность доски отладки несколько
снизилась, поскольку отладчик позволяет полностью контролировать ход
выполнения программы, причём для этого не требуется никаких усилий
со стороны самой программы. Тем не менее во многих случаях
доска отладки продолжает оставаться полезной.
 
---------------------------- ‡ ¯¨áì ¡ ©â  ----------------------------
 à ¬¥âàë:
* eax = 63 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* cl = ¡ ©â ¤ ­­ëå
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
*  ©â § ¯¨á뢠¥âáï ¢ ¡ãä¥à. „«¨­  ¡ãä¥à  - 512 ¡ ©â.
à¨ ¯¥à¥¯®«­¥­¨¨ ¡ãä¥à  ¢á¥ ¯®«ã祭­ë¥ ¤ ­­ë¥ â¥àïîâáï
¨ § ¯®«­¥­¨¥ ­ ç¨­ ¥âáï á­®¢  á ­ã«ï.
* „«ï ¢ë¢®¤  ­  ¤®áªã ®â« ¤ª¨ ¡®«¥¥ á«®¦­ëå ®¡ê¥ªâ®¢ (áâப, ç¨á¥«)
¤®áâ â®ç­® í⮩ ä㭪樨, ¢ë§ë¢ ¥¬®© ¢ 横«¥. Œ®¦­® ­¥ ¯¨á âì
¢àãç­ãî ᮮ⢥âáâ¢ãî騩 ª®¤,   ¢®á¯®«ì§®¢ âìáï ä ©«®¬ debug.inc,
¢å®¤ï騬 ¢ ¤¨áâਡã⨢.
---------------------------- Запись байта ----------------------------
Параметры:
* eax = 63 - номер функции
* ebx = 1 - номер подфункции
* cl = байт данных
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Байт записывается в буфер. Длина буфера - 512 байт.
При переполнении буфера все полученные данные теряются
и заполнение начинается снова с нуля.
* Для вывода на доску отладки более сложных объектов (строк, чисел)
достаточно этой функции, вызываемой в цикле. Можно не писать
вручную соответствующий код, а воспользоваться файлом debug.inc,
входящим в дистрибутив.
 
---------------------------- —⥭¨¥ ¡ ©â  ----------------------------
‡ ¡¨à ¥â ¡ ©â ¨§ ¡ãä¥à .
 à ¬¥âàë:
* eax = 63 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ebx = 0 - ¡ãä¥à ¯ãáâ
* eax = ¡ ©â, ebx = 1 - ¡ ©â ãᯥ譮 ¯à®ç¨â ­
---------------------------- Чтение байта ----------------------------
Забирает байт из буфера.
Параметры:
* eax = 63 - номер функции
* ebx = 2 - номер подфункции
Возвращаемое значение:
* eax = ebx = 0 - буфер пуст
* eax = байт, ebx = 1 - байт успешно прочитан
 
======================================================================
========== ”ã­ªæ¨ï 64 - ¯¥à¥à á¯à¥¤¥«¨âì ¯ ¬ïâì ¯à¨«®¦¥­¨ï. ==========
========== Функция 64 - перераспределить память приложения. ==========
======================================================================
 à ¬¥âàë:
* eax = 64 - ­®¬¥à ä㭪樨
* ebx = 1 - ¥¤¨­á⢥­­ ï ¯®¤äã­ªæ¨ï
* ecx = ­®¢ë© à §¬¥à ¯ ¬ïâ¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥¤®áâ â®ç­® ¯ ¬ïâ¨
‡ ¬¥ç ­¨ï:
* …áâì ¤à㣮© ᯮᮡ ¢ë¤¥«¥­¨ï/®á¢®¡®¦¤¥­¨ï ¤¨­ ¬¨ç¥áª®© ¯ ¬ï⨠-
¯®¤ä㭪樨 11, 12, 13 ä㭪樨 68.
* ”ã­ªæ¨ï ­¥ ¬®¦¥â ¨á¯®«ì§®¢ âìáï ᮢ¬¥áâ­® á 68.11, 68.12, 68.13.
‚맮¢ ä㭪樨 ¡ã¤¥â ¨£­®à¨à®¢ âìáï, ¥á«¨ ¯à¨«®¦¥­¨¥ ᮧ¤ áâ
«®ª «ì­ãî ªãç㠢맮¢®¬ 68.11.
Параметры:
* eax = 64 - номер функции
* ebx = 1 - единственная подфункция
* ecx = новый размер памяти
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - недостаточно памяти
Замечания:
* Есть другой способ выделения/освобождения динамической памяти -
подфункции 11, 12, 13 функции 68.
* Функция не может использоваться совместно с 68.11, 68.12, 68.13.
Вызов функции будет игнорироваться, если приложение создаст
локальную кучу вызовом 68.11.
 
======================================================================
========= ”ã­ªæ¨ï 65 - ¢ë¢¥á⨠¨§®¡à ¦¥­¨¥ á ¯ «¨âன ¢ ®ª­®. ========
========= Функция 65 - вывести изображение с палитрой в окно. ========
======================================================================
 à ¬¥âàë:
* eax = 65 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨§®¡à ¦¥­¨¥
* ecx = [à §¬¥à ¯® ®á¨ x]*65536 + [à §¬¥à ¯® ®á¨ y]
* edx = [ª®®à¤¨­ â  ¯® ®á¨ x]*65536 + [ª®®à¤¨­ â  ¯® ®á¨ y]
* esi = ç¨á«® ¡¨â ­  ¯¨ªá¥«ì, ¤®«¦­® ¡ëâì 1,2,4,8,9,15,16,24 ¨«¨ 32
* edi = 㪠§ â¥«ì ­  ¯ «¨âàã (2 ¢ á⥯¥­¨ esi 梥⮢ 0x00RRGGBB);
¨£­®à¨àã¥âáï ¯à¨ esi > 8
* ebp = ᬥ饭¨¥ ¤ ­­ëå ª ¦¤®© á«¥¤ãî饩 áâப¨ ¨§®¡à ¦¥­¨ï
®â­®á¨â¥«ì­® ¯à¥¤ë¤ã饩
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* Š®®à¤¨­ âë ¨§®¡à ¦¥­¨ï - íâ® ª®®à¤¨­ âë ¢¥àå­¥£® «¥¢®£® 㣫 
¨§®¡à ¦¥­¨ï ®â­®á¨â¥«ì­® ®ª­ .
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 1 ¡¨â®¬ ­  ¯¨ªá¥«ì: ª ¦¤ë© ¡ ©â ¨§®¡à ¦¥­¨ï,
§  ¨áª«î祭¨¥¬, ¡ëâì ¬®¦¥â, ¯®á«¥¤­¨å ¡ ©â®¢ áâப, ᮤ¥à¦¨â
¨­ä®à¬ æ¨î ® 梥⥠8 ¯¨ªá¥«¥©, áâ à訩 ¡¨â ᮮ⢥âáâ¢ã¥â ¯¥à¢®¬ã
¯¨ªá¥«î.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 2 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: ª ¦¤ë© ¡ ©â ¨§®¡à ¦¥­¨ï,
§  ¨áª«î祭¨¥¬, ¡ëâì ¬®¦¥â, ¯®á«¥¤­¨å ¡ ©â®¢ áâப, ᮤ¥à¦¨â
¨­ä®à¬ æ¨î ® 梥⥠4 ¯¨ªá¥«¥©, áâ à訥 ¤¢  ¡¨â  ᮮ⢥âáâ¢ãîâ
¯¥à¢®¬ã ¯¨ªá¥«î.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 4 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: ª ¦¤ë© ¡ ©â ¨§®¡à ¦¥­¨ï,
§  ¨áª«î祭¨¥¬ ¯®á«¥¤­¨å ¡ ©â®¢ áâப (¥á«¨ è¨à¨­  ¨§®¡à ¦¥­¨ï
­¥çñâ­ ), ᮤ¥à¦¨â ¨­ä®à¬ æ¨î ® 梥⥠2 ¯¨ªá¥«¥©, áâ àè ï â¥âà ¤ 
ᮮ⢥âáâ¢ã¥â ¯¥à¢®¬ã ¯¨ªá¥«î.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 8 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: ª ¦¤ë© ¡ ©â ¨§®¡à ¦¥­¨ï
à áᬠâਢ ¥âáï ª ª ¨­¤¥ªá ¢ ¯ «¨âà¥.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 9 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: ª ¦¤ë© ¡ ©â ¨§®¡à ¦¥­¨ï
(8 ¡¨â) ®¡®§­ ç ¥â ¨­â¥­á¨¢­®áâì á¥à®£® ¤«ï ®¤­®£® ¯¨ªá¥«ï, â.®.
íâ®â ⨯ ¨§®¡à ¦¥­¨ï ¨¤¥­â¨ç¥­ 8 ¡¨â ­  ¯¨ªá¥«ì ¡¥§ ¯ «¨âàë.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 15 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: 梥⠪ ¦¤®£® ¯¨ªá¥«ï
ª®¤¨àã¥âáï ª ª (¢ ¡¨â®¢®¬ ¯à¥¤áâ ¢«¥­¨¨) 0RRRRRGGGGGBBBBB -
¯® 5 ¯¨ªá¥«¥© ­  ª ¦¤ë© 梥â.
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 16 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: 梥⠪ ¦¤®£® ¯¨ªá¥«ï
ª®¤¨àã¥âáï ª ª RRRRRGGGGGGBBBBB (á奬  5+6+5).
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 24 ¡¨â ¬¨ ­  ¯¨ªá¥«ì: 梥⠪ ¦¤®£® ¯¨ªá¥«ï
ª®¤¨àã¥âáï âà¥¬ï ¡ ©â ¬¨ - ¯®á«¥¤®¢ â¥«ì­® ᨭïï, §¥«ñ­ ï, ªà á­ ï
á®áâ ¢«ïî騥 梥â .
* ”®à¬ â ¨§®¡à ¦¥­¨ï á 32 ¡¨â ¬¨ ­  ¯¨ªá¥«ì:  ­ «®£¨ç­® 24, ⮫쪮
¥áâì ¥éñ ¨£­®à¨àã¥¬ë© ç¥â¢ñàâë© ¡ ©â.
* ‚맮¢ ä㭪樨 7 íª¢¨¢ «¥­â¥­ ¢ë§®¢ã í⮩ ä㭪樨 á ¯ à ¬¥âà ¬¨
Параметры:
* eax = 65 - номер функции
* ebx = указатель на изображение
* ecx = [размер по оси x]*65536 + [размер по оси y]
* edx = [координата по оси x]*65536 + [координата по оси y]
* esi = число бит на пиксель, должно быть 1,2,4,8,9,15,16,24 или 32
* edi = указатель на палитру (2 в степени esi цветов 0x00RRGGBB);
игнорируется при esi > 8
* ebp = смещение данных каждой следующей строки изображения
относительно предыдущей
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Координаты изображения - это координаты верхнего левого угла
изображения относительно окна.
* Формат изображения с 1 битом на пиксель: каждый байт изображения,
за исключением, быть может, последних байтов строк, содержит
информацию о цвете 8 пикселей, старший бит соответствует первому
пикселю.
* Формат изображения с 2 битами на пиксель: каждый байт изображения,
за исключением, быть может, последних байтов строк, содержит
информацию о цвете 4 пикселей, старшие два бита соответствуют
первому пикселю.
* Формат изображения с 4 битами на пиксель: каждый байт изображения,
за исключением последних байтов строк (если ширина изображения
нечётна), содержит информацию о цвете 2 пикселей, старшая тетрада
соответствует первому пикселю.
* Формат изображения с 8 битами на пиксель: каждый байт изображения
рассматривается как индекс в палитре.
* Формат изображения с 9 битами на пиксель: каждый байт изображения
(8 бит) обозначает интенсивность серого для одного пикселя, т.о.
этот тип изображения идентичен 8 бит на пиксель без палитры.
* Формат изображения с 15 битами на пиксель: цвет каждого пикселя
кодируется как (в битовом представлении) 0RRRRRGGGGGBBBBB -
по 5 пикселей на каждый цвет.
* Формат изображения с 16 битами на пиксель: цвет каждого пикселя
кодируется как RRRRRGGGGGGBBBBB (схема 5+6+5).
* Формат изображения с 24 битами на пиксель: цвет каждого пикселя
кодируется тремя байтами - последовательно синяя, зелёная, красная
составляющие цвета.
* Формат изображения с 32 битами на пиксель: аналогично 24, только
есть ещё игнорируемый четвёртый байт.
* Вызов функции 7 эквивалентен вызову этой функции с параметрами
esi=24, ebp=0.
 
======================================================================
================= ”ã­ªæ¨ï 66 - à ¡®â  á ª« ¢¨ âãன. =================
================= Функция 66 - работа с клавиатурой. =================
======================================================================
¥¦¨¬ ¢¢®¤  ¢«¨ï¥â ­  १ã«ìâ âë ç⥭¨ï ª« ¢¨è ä㭪樥© 2.
à¨ § £à㧪¥ ¯à®£à ¬¬ë ¤«ï ­¥ñ ãáâ ­ ¢«¨¢ ¥âáï ASCII-०¨¬ ¢¢®¤ .
Режим ввода влияет на результаты чтения клавиш функцией 2.
При загрузке программы для неё устанавливается ASCII-режим ввода.
 
-------- ®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì ०¨¬ ¢¢®¤  á ª« ¢¨ âãàë. ---------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ०¨¬:
* 0 = ®¡ëç­ë© (ASCII-ᨬ¢®«ë)
* 1 = ᪠­ª®¤ë
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
-------- Подфункция 1 - установить режим ввода с клавиатуры. ---------
Параметры:
* eax = 66 - номер функции
* ebx = 1 - номер подфункции
* ecx = режим:
* 0 = обычный (ASCII-символы)
* 1 = сканкоды
Возвращаемое значение:
* функция не возвращает значения
 
--------- ®¤äã­ªæ¨ï 2 - ¯®«ãç¨âì ०¨¬ ¢¢®¤  á ª« ¢¨ âãàë. ----------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ⥪ã騩 ०¨¬
--------- Подфункция 2 - получить режим ввода с клавиатуры. ----------
Параметры:
* eax = 66 - номер функции
* ebx = 2 - номер подфункции
Возвращаемое значение:
* eax = текущий режим
 
------- ®¤äã­ªæ¨ï 3 - ¯®«ãç¨âì á®áâ®ï­¨¥ ã¯à ¢«ïîé¨å ª« ¢¨è. --------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ¡¨â®¢ ï ¬ áª :
* ¡¨â 0 (¬ áª  1): «¥¢ë© Shift ­ ¦ â
* ¡¨â 1 (¬ áª  2): ¯à ¢ë© Shift ­ ¦ â
* ¡¨â 2 (¬ áª  4): «¥¢ë© Ctrl ­ ¦ â
* ¡¨â 3 (¬ áª  8): ¯à ¢ë© Ctrl ­ ¦ â
* ¡¨â 4 (¬ áª  0x10): «¥¢ë© Alt ­ ¦ â
* ¡¨â 5 (¬ áª  0x20): ¯à ¢ë© Alt ­ ¦ â
* ¡¨â 6 (¬ áª  0x40): CapsLock ¢ª«îçñ­
* ¡¨â 7 (¬ áª  0x80): NumLock ¢ª«îçñ­
* ¡¨â 8 (¬ áª  0x100): ScrollLock ¢ª«îçñ­
* ¡¨â 9 (¬ áª  0x200): «¥¢ë© Win ­ ¦ â
* ¡¨â 10 (¬ áª  0x400): ¯à ¢ë© Win ­ ¦ â
* ¯à®ç¨¥ ¡¨âë á¡à®è¥­ë
------- Подфункция 3 - получить состояние управляющих клавиш. --------
Параметры:
* eax = 66 - номер функции
* ebx = 3 - номер подфункции
Возвращаемое значение:
* eax = битовая маска:
* бит 0 (маска 1): левый Shift нажат
* бит 1 (маска 2): правый Shift нажат
* бит 2 (маска 4): левый Ctrl нажат
* бит 3 (маска 8): правый Ctrl нажат
* бит 4 (маска 0x10): левый Alt нажат
* бит 5 (маска 0x20): правый Alt нажат
* бит 6 (маска 0x40): CapsLock включён
* бит 7 (маска 0x80): NumLock включён
* бит 8 (маска 0x100): ScrollLock включён
* бит 9 (маска 0x200): левый Win нажат
* бит 10 (маска 0x400): правый Win нажат
* прочие биты сброшены
 
----- ®¤äã­ªæ¨ï 4 - ãáâ ­®¢¨âì ®¡é¥á¨á⥬­ãî "£®àïçãî ª« ¢¨èã". -----
Ž ­ ¦ â¨¨ "£®àï祩 ª« ¢¨è¨" ¨§¢¥é îâáï ⮫쪮 ¯à¨«®¦¥­¨ï,
ãáâ ­®¢¨¢è¨¥ ¥ñ;  ªâ¨¢­®¥ ¯à¨«®¦¥­¨¥ (ª ª®â®à®¬ã ¯®áâ㯠¥â
¢¥áì ­®à¬ «ì­ë© ¢¢®¤) â ª¨å ª« ¢¨è ­¥ ¯®«ãç ¥â.
ˆ§¢¥é¥­¨¥ § ª«îç ¥âáï ¢ ¯®á뫪¥ ᮡëâ¨ï á ª®¤®¬ 2.
à®ç¨â âì "£®àïçãî ª« ¢¨èã" ¬®¦­® â ª ¦¥, ª ª ¨ ®¡ëç­ãî, -
ä㭪樥© 2.
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* cl § ¤ ñâ ᪠­ª®¤ ª« ¢¨è¨;
¨á¯®«ì§ã©â¥ cl=0 ¤«ï § ¤ ­¨ï ª®¬¡¨­ æ¨© ⨯  Ctrl+Shift
* edx = 0xXYZ § ¤ ñâ ¢®§¬®¦­ë¥ á®áâ®ï­¨ï ã¯à ¢«ïîé¨å ª« ¢¨è:
* Z (¬« ¤è¨¥ 4 ¡¨â ) § ¤ ñâ á®áâ®ï­¨¥ ª« ¢¨è LShift ¨ RShift:
* 0 = ­¨ ®¤­  ¨§ ª« ¢¨è ­¥ ¤®«¦­  ¡ëâì ­ ¦ â ;
* 1 = ஢­® ®¤­  ¨§ ª« ¢¨è ¤®«¦­  ¡ëâì ­ ¦ â ;
* 2 = ®¡¥ ª« ¢¨è¨ ¤®«¦­ë ¡ëâì ­ ¦ âë;
* 3 = ¤®«¦­  ¡ëâì ­ ¦ â  LShift, ­® ­¥ RShift;
* 4 = ¤®«¦­  ¡ëâì ­ ¦ â  RShift, ­® ­¥ LShift
* Y -  ­ «®£¨ç­® ¤«ï LCtrl ¨ RCtrl;
* X -  ­ «®£¨ç­® ¤«ï LAlt ¨ RAlt
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax=0 - ãᯥ譮
* eax=1 - ᫨誮¬ ¬­®£® "£®àïç¨å ª« ¢¨è" (¤®¯ã᪠¥âáï ¬ ªá¨¬ã¬ 256)
‡ ¬¥ç ­¨ï:
* ƒ®àïç ï ª« ¢¨è  ¬®¦¥â áà ¡ â뢠âì «¨¡® ¯à¨ ­ ¦ â¨¨,
«¨¡® ¯à¨ ®â¯ã᪠­¨¨. ‘ª ­ª®¤ ®â¯ã᪠­¨ï ª« ¢¨è¨ ­  128 ¡®«ìè¥,
祬 ᪠­ª®¤ ­ ¦ â¨ï (â.¥. ãáâ ­®¢«¥­ áâ à訩 ¡¨â).
* ¥áª®«ìª® ¯à¨«®¦¥­¨© ¬®£ãâ ãáâ ­®¢¨âì ®¤­ã ¨ âã ¦¥ ª®¬¡¨­ æ¨î;
® ­ ¦ â¨¨ â ª®© ª®¬¡¨­ æ¨¨ ¡ã¤ãâ ¨§¢¥é âìáï ¢á¥ â ª¨¥ ¯à¨«®¦¥­¨ï.
----- Подфункция 4 - установить общесистемную "горячую клавишу". -----
О нажатии "горячей клавиши" извещаются только приложения,
установившие её; активное приложение (к которому поступает
весь нормальный ввод) таких клавиш не получает.
Извещение заключается в посылке события с кодом 2.
Прочитать "горячую клавишу" можно так же, как и обычную, -
функцией 2.
Параметры:
* eax = 66 - номер функции
* ebx = 4 - номер подфункции
* cl задаёт сканкод клавиши;
используйте cl=0 для задания комбинаций типа Ctrl+Shift
* edx = 0xXYZ задаёт возможные состояния управляющих клавиш:
* Z (младшие 4 бита) задаёт состояние клавиш LShift и RShift:
* 0 = ни одна из клавиш не должна быть нажата;
* 1 = ровно одна из клавиш должна быть нажата;
* 2 = обе клавиши должны быть нажаты;
* 3 = должна быть нажата LShift, но не RShift;
* 4 = должна быть нажата RShift, но не LShift
* Y - аналогично для LCtrl и RCtrl;
* X - аналогично для LAlt и RAlt
Возвращаемое значение:
* eax=0 - успешно
* eax=1 - слишком много "горячих клавиш" (допускается максимум 256)
Замечания:
* Горячая клавиша может срабатывать либо при нажатии,
либо при отпускании. Сканкод отпускания клавиши на 128 больше,
чем сканкод нажатия (т.е. установлен старший бит).
* Несколько приложений могут установить одну и ту же комбинацию;
о нажатии такой комбинации будут извещаться все такие приложения.
 
------ ®¤äã­ªæ¨ï 5 - 㤠«¨âì ãáâ ­®¢«¥­­ãî "£®àïçãî ª« ¢¨èã". -------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* cl = ᪠­ª®¤ ª« ¢¨è¨ ¨ edx = 0xXYZ â ª¨¥ ¦¥, ª ª ¨ ¢ ¯®¤ä㭪樨 4
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ­¥â â ª®© £®àï祩 ª« ¢¨è¨
‡ ¬¥ç ­¨ï:
* à¨ § ¢¥à襭¨¨ ¯à®æ¥áá /¯®â®ª  㤠«ïîâáï ¢á¥ ãáâ ­®¢«¥­­ë¥ ¨¬
£®àï稥 ª« ¢¨è¨.
* ‚맮¢ ä㭪樨 ­¥ ¢«¨ï¥â ­  ¤à㣨¥ ¯à¨«®¦¥­¨ï.
…᫨ ¤à㣮¥ ¯à¨«®¦¥­¨¥ ®¯à¥¤¥«¨«® íâã ¦¥ ª®¬¡¨­ æ¨î,
®­® ¯®-¯à¥¦­¥¬ã ¡ã¤¥â ¯®«ãç âì 㢥¤®¬«¥­¨ï.
------ Подфункция 5 - удалить установленную "горячую клавишу". -------
Параметры:
* eax = 66 - номер функции
* ebx = 5 - номер подфункции
* cl = сканкод клавиши и edx = 0xXYZ такие же, как и в подфункции 4
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - нет такой горячей клавиши
Замечания:
* При завершении процесса/потока удаляются все установленные им
горячие клавиши.
* Вызов функции не влияет на другие приложения.
Если другое приложение определило эту же комбинацию,
оно по-прежнему будет получать уведомления.
 
------------- ®¤äã­ªæ¨ï 6 - § ¡«®ª¨à®¢ âì ®¡ëç­ë© ¢¢®¤. -------------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* «®ª¨àã¥âáï ®¡ëç­ë© ¢¢®¤ ¤ ­­ëå á ª« ¢¨ âãàë ¤«ï ãáâ ­®¢«¥­­ëå
"£®àïç¨å" ª« ¢¨è
* „«ï í¬ã«ï樨 ¬ëè¨ ç¥à¥§ ª« ¢¨ âãàã, ¯à¨«®¦¥­¨¥ MOUSEMUL
------------- Подфункция 6 - заблокировать обычный ввод. -------------
Параметры:
* eax = 66 - номер функции
* ebx = 6 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Блокируется обычный ввод данных с клавиатуры для установленных
"горячих" клавиш
* Для эмуляции мыши через клавиатуру, приложение MOUSEMUL
 
--------- ®¤äã­ªæ¨ï 7 - à §¡«®ª¨à®¢ âì ®¡ëç­ë© ¢¢®¤. ----------------
 à ¬¥âàë:
* eax = 66 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
*  §¡«®ª¨à®¢ ­¨¥ १ã«ìâ â®¢ ä. 66.6
* „«ï í¬ã«ï樨 ¬ëè¨ ç¥à¥§ ª« ¢¨ âãàã, ¯à¨«®¦¥­¨¥ MOUSEMUL
--------- Подфункция 7 - разблокировать обычный ввод. ----------------
Параметры:
* eax = 66 - номер функции
* ebx = 7 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Разблокирование результатов ф. 66.6
* Для эмуляции мыши через клавиатуру, приложение MOUSEMUL
 
======================================================================
============ ”ã­ªæ¨ï 67 - ¨§¬¥­¨âì ¯®«®¦¥­¨¥/à §¬¥àë ®ª­ . ===========
============ Функция 67 - изменить положение/размеры окна. ===========
======================================================================
 à ¬¥âàë:
* eax = 67 - ­®¬¥à ä㭪樨
* ebx = ­®¢ ï x-ª®®à¤¨­ â  ®ª­ 
* ecx = ­®¢ ï y-ª®®à¤¨­ â  ®ª­ 
* edx = ­®¢ë© x-à §¬¥à ®ª­ 
* esi = ­®¢ë© y-à §¬¥à ®ª­ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ‡­ ç¥­¨¥ -1 ¤«ï ¯ à ¬¥âà  ®§­ ç ¥â "­¥ ¨§¬¥­ïâì"; ­ ¯à¨¬¥à, ¤«ï
¯¥à¥¬¥é¥­¨ï ®ª­  ¡¥§ ¨§¬¥­¥­¨ï à §¬¥à®¢ ¬®¦­® 㪠§ âì edx=esi=-1.
* à¥¤¢ à¨â¥«ì­® ®ª­® ¤®«¦­® ¡ëâì ®¯à¥¤¥«¥­® ä㭪樥© 0.
Ž­  ¦¥ § ¤ ñâ ­ ç «ì­ë¥ ª®®à¤¨­ âë ¨ à §¬¥àë ®ª­ .
*  §¬¥àë ®ª­  ¯®­¨¬ îâáï ¢ á¬ëá«¥ ä㭪樨 0, â.¥.
­  ®¤¨­ ¯¨ªá¥«ì ¬¥­ìè¥, 祬 ॠ«ì­ë¥ à §¬¥àë.
* ‚맮¢ ä㭪樨 ¤«ï ¬ ªá¨¬¨§¨à®¢ ­­ëå ®ª®­ ¯à®áâ® ¨£­®à¨àã¥âáï.
* „«ï ®ª®­ ᮮ⢥âáâ¢ãîé¨å á⨫¥© ¯®«®¦¥­¨¥ ¨/¨«¨ à §¬¥àë ¬®£ãâ ¡ëâì
¨§¬¥­¥­ë ¯®«ì§®¢ â¥«¥¬; ⥪ã騥 ¯®«®¦¥­¨¥ ¨ à §¬¥àë ¬®£ãâ ¡ëâì
¯®«ãç¥­ë ¢ë§®¢®¬ ä㭪樨 9.
* ”ã­ªæ¨ï ¯®áë« ¥â ®ª­ã ᮡë⨥ ¯¥à¥à¨á®¢ª¨ (á ª®¤®¬ 1).
Параметры:
* eax = 67 - номер функции
* ebx = новая x-координата окна
* ecx = новая y-координата окна
* edx = новый x-размер окна
* esi = новый y-размер окна
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Значение -1 для параметра означает "не изменять"; например, для
перемещения окна без изменения размеров можно указать edx=esi=-1.
* Предварительно окно должно быть определено функцией 0.
Она же задаёт начальные координаты и размеры окна.
* Размеры окна понимаются в смысле функции 0, т.е.
на один пиксель меньше, чем реальные размеры.
* Вызов функции для максимизированных окон просто игнорируется.
* Для окон соответствующих стилей положение и/или размеры могут быть
изменены пользователем; текущие положение и размеры могут быть
получены вызовом функции 9.
* Функция посылает окну событие перерисовки (с кодом 1).
 
======================================================================
=== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 0 - ¯®«ãç¨âì áçñâ稪 ¯¥à¥ª«î祭¨© § ¤ ç. ==
=== Функция 68, подфункция 0 - получить счётчик переключений задач. ==
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ç¨á«® ¯¥à¥ª«î祭¨© § ¤ ç á ¬®¬¥­â  § £à㧪¨ á¨á⥬ë
(¯® ¬®¤ã«î 2^32)
Параметры:
* eax = 68 - номер функции
* ebx = 0 - номер подфункции
Возвращаемое значение:
* eax = число переключений задач с момента загрузки системы
(по модулю 2^32)
 
======================================================================
====================== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 1 ======================
============ ¥à¥ª«îç¨âìáï ­  á«¥¤ãî騩 ¯®â®ª ¢ë¯®«­¥­¨ï. ============
====================== Функция 68, подфункция 1 ======================
============ Переключиться на следующий поток выполнения. ============
======================================================================
”ã­ªæ¨ï § ¢¥àè ¥â ⥪ã騩 ª¢ ­â ¢à¥¬¥­¨, ¢ë¤¥«¥­­ë© ¯®â®ªã,
¨ ¯¥à¥ª«îç ¥âáï ­  á«¥¤ãî騩.
(Š ª®© ¯®â®ª ª ª®£® ¯à®æ¥áá  ¡ã¤¥â á«¥¤ãî騬, ¯à¥¤áª § âì ­¥«ì§ï).
®§¤­¥¥, ª®£¤  ¤® ⥪ã饣® ¯®â®ª  ¤®©¤ñâ ®ç¥à¥¤ì,
¢ë¯®«­¥­¨¥ ¢®§®¡­®¢¨âáï.
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
Функция завершает текущий квант времени, выделенный потоку,
и переключается на следующий.
(Какой поток какого процесса будет следующим, предсказать нельзя).
Позднее, когда до текущего потока дойдёт очередь,
выполнение возобновится.
Параметры:
* eax = 68 - номер функции
* ebx = 1 - номер подфункции
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
=============== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 2 - ªíè + rdpmc. ==============
=============== Функция 68, подфункция 2 - кэш + rdpmc. ==============
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = âॡ㥬®¥ ¤¥©á⢨¥:
* ecx = 0 - à §à¥è¨âì ¢ë¯®«­¥­¨¥ ¨­áâàãªæ¨¨ rdpmc
Параметры:
* eax = 68 - номер функции
* ebx = 2 - номер подфункции
* ecx = требуемое действие:
* ecx = 0 - разрешить выполнение инструкции rdpmc
(ReaD Performance-Monitoring Counters)
* ecx = 1 - 㧭 âì, ¢ª«îçñ­/¢ëª«î祭 ªíè
* ecx = 2 - ¢ª«îç¨âì ªíè
* ecx = 3 - ¢ëª«îç¨âì ªíè
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ¤«ï ecx=0:
* eax = §­ ç¥­¨¥ cr4
* ¤«ï ecx=1:
* ecx = 1 - узнать, включён/выключен кэш
* ecx = 2 - включить кэш
* ecx = 3 - выключить кэш
Возвращаемое значение:
* для ecx=0:
* eax = значение cr4
* для ecx=1:
* eax = (cr0 and 0x60000000):
* eax = 0 - ªíè ¢ª«îçñ­
* eax <> 0 - ªíè ¢ëª«î祭
* ¤«ï ecx=2 ¨ ecx=3:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
* eax = 0 - кэш включён
* eax <> 0 - кэш выключен
* для ecx=2 и ecx=3:
* функция не возвращает значения
 
======================================================================
========== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 3 - ¯à®ç¨â âì MSR-ॣ¨áâà. =========
========== Функция 68, подфункция 3 - прочитать MSR-регистр. =========
======================================================================
MSR = Model Specific Register; ¯®«­ë© ᯨ᮪ MSR-ॣ¨áâ஢ ¯à®æ¥áá®à 
ᮤ¥à¦¨âáï ¢ ¤®ªã¬¥­â æ¨¨ ¯® ¯à®æ¥áá®àã (­ ¯à¨¬¥à, IA-32 Intel
MSR = Model Specific Register; полный список MSR-регистров процессора
содержится в документации по процессору (например, IA-32 Intel
Architecture Software Developer's Manual, Volume 3, Appendix B);
ª ¦¤®¥ ᥬ¥©á⢮ ¯à®æ¥áá®à®¢ ¨¬¥¥â ᢮ñ ¯®¤¬­®¦¥á⢮ MSR-ॣ¨áâ஢.
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx ¨£­®à¨àã¥âáï
* edx =  ¤à¥á MSR
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* ebx:eax = áâ à訩:¬« ¤è¨© dword १ã«ìâ â 
‡ ¬¥ç ­¨ï:
* “ª § ­¨¥ ¢ ecx ­¥áãé¥áâ¢ãî饣® ¨«¨ ­¥à¥ «¨§®¢ ­­®£® ¤«ï ¤ ­­®£®
¯à®æ¥áá®à  MSR ¯®¢«¥çñ⠨᪫î祭¨¥ ¢ ï¤à¥, ª®â®à®¥ ¯à¨¡ìñâ ¯®â®ª.
* à¥¤¢ à¨â¥«ì­® á«¥¤ã¥â ®¯à¥¤¥«¨âì, ¯®¤¤¥à¦¨¢ îâáï «¨ MSR ¢ 楫®¬,
ª®¬ ­¤®© cpuid. ˆ­ ç¥ ¢®§­¨ª­¥â 㦥 ¤à㣮¥ ¨áª«î祭¨¥ ¢ ï¤à¥,
ª®â®à®¥ ¢áñ à ¢­® ¯à¨¡ìñâ ¯®â®ª.
каждое семейство процессоров имеет своё подмножество MSR-регистров.
Параметры:
* eax = 68 - номер функции
* ebx = 3 - номер подфункции
* ecx игнорируется
* edx = адрес MSR
Возвращаемое значение:
* ebx:eax = старший:младший dword результата
Замечания:
* Указание в ecx несуществующего или нереализованного для данного
процессора MSR повлечёт исключение в ядре, которое прибьёт поток.
* Предварительно следует определить, поддерживаются ли MSR в целом,
командой cpuid. Иначе возникнет уже другое исключение в ядре,
которое всё равно прибьёт поток.
 
======================================================================
========= ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 4 - § ¯¨á âì ¢ MSR-ॣ¨áâà. =========
========= Функция 68, подфункция 4 - записать в MSR-регистр. =========
======================================================================
MSR = Model Specific Register; ¯®«­ë© ᯨ᮪ MSR-ॣ¨áâ஢ ¯à®æ¥áá®à 
ᮤ¥à¦¨âáï ¢ ¤®ªã¬¥­â æ¨¨ ¯® ¯à®æ¥áá®àã (­ ¯à¨¬¥à, IA-32 Intel
MSR = Model Specific Register; полный список MSR-регистров процессора
содержится в документации по процессору (например, IA-32 Intel
Architecture Software Developer's Manual, Volume 3, Appendix B);
ª ¦¤®¥ ᥬ¥©á⢮ ¯à®æ¥áá®à®¢ ¨¬¥¥â ᢮ñ ¯®¤¬­®¦¥á⢮ MSR-ॣ¨áâ஢.
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* ecx ¨£­®à¨àã¥âáï
* edx =  ¤à¥á MSR
* esi:edi = áâ à訩:¬« ¤è¨© dword
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* “ª § ­¨¥ ¢ ecx ­¥áãé¥áâ¢ãî饣® ¨«¨ ­¥à¥ «¨§®¢ ­­®£® ¤«ï ¤ ­­®£®
¯à®æ¥áá®à  MSR ¯®¢«¥çñ⠨᪫î祭¨¥ ¢ ï¤à¥, ª®â®à®¥ ¯à¨¡ìñâ ¯®â®ª.
* à¥¤¢ à¨â¥«ì­® á«¥¤ã¥â ®¯à¥¤¥«¨âì, ¯®¤¤¥à¦¨¢ îâáï «¨ MSR ¢ 楫®¬,
ª®¬ ­¤®© cpuid. ˆ­ ç¥ ¢®§­¨ª­¥â 㦥 ¤à㣮¥ ¨áª«î祭¨¥ ¢ ï¤à¥,
ª®â®à®¥ ¢áñ à ¢­® ¯à¨¡ìñâ ¯®â®ª.
каждое семейство процессоров имеет своё подмножество MSR-регистров.
Параметры:
* eax = 68 - номер функции
* ebx = 4 - номер подфункции
* ecx игнорируется
* edx = адрес MSR
* esi:edi = старший:младший dword
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Указание в ecx несуществующего или нереализованного для данного
процессора MSR повлечёт исключение в ядре, которое прибьёт поток.
* Предварительно следует определить, поддерживаются ли MSR в целом,
командой cpuid. Иначе возникнет уже другое исключение в ядре,
которое всё равно прибьёт поток.
 
======================================================================
===== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 11 - ¨­¨æ¨ «¨§¨à®¢ âì ªãçã ¯à®æ¥áá . ====
===== Функция 68, подфункция 11 - инициализировать кучу процесса. ====
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 11 - ­®¬¥à ¯®¤ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ­¥ãᯥå
* ¨­ ç¥ à §¬¥à ᮧ¤ ­­®© ªãç¨
‡ ¬¥ç ­¨ï:
* ‚맮¢ ä㭪樨 ¨­¨æ¨ «¨§¨àã¥â ªãçã, ¨§ ª®â®à®© ¢¯®á«¥¤á⢨¨ ¬®¦­®
¢ë¤¥«ïâì ¨ ®á¢®¡®¦¤ âì ¡«®ª¨ ¯ ¬ï⨠¯®¤äã­ªæ¨ï¬¨ 12 ¨ 13.
 §¬¥à ªãç¨ à ¢¥­ à §¬¥à㠢ᥩ ᢮¡®¤­®© ¯ ¬ï⨠¯à¨«®¦¥­¨ï.
* à¨ ¯®¢â®à­®¬ ¢ë§®¢¥ ä㭪樨 ⥬ ¦¥ ¯à®æ¥áᮬ äã­ªæ¨ï ¢¥à­ñâ
à §¬¥à áãé¥áâ¢ãî饩 ªãç¨.
* ®á«¥ ᮧ¤ ­¨ï ªãç¨ ¢ë§®¢ë ä㭪樨 64 ¨£­®à¨àãîâáï.
Параметры:
* eax = 68 - номер функции
* ebx = 11 - номер подфункции
Возвращаемое значение:
* eax = 0 - неуспех
* иначе размер созданной кучи
Замечания:
* Вызов функции инициализирует кучу, из которой впоследствии можно
выделять и освобождать блоки памяти подфункциями 12 и 13.
Размер кучи равен размеру всей свободной памяти приложения.
* При повторном вызове функции тем же процессом функция вернёт
размер существующей кучи.
* После создания кучи вызовы функции 64 игнорируются.
 
======================================================================
========== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 12 - ¢ë¤¥«¨âì ¡«®ª ¯ ¬ïâ¨. =========
========== Функция 68, подфункция 12 - выделить блок памяти. =========
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 12 - ­®¬¥à ¯®¤ä㭪樨
* ecx = âà¥¡ã¥¬ë© à §¬¥à ¢ ¡ ©â å
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 㪠§ â¥«ì ­  ¢ë¤¥«¥­­ë© ¡«®ª
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® á«¥¤ã¥â ¨­¨æ¨ «¨§¨à®¢ âì ªãçã ¯à®æ¥áá  ¢ë§®¢®¬
¯®¤ä㭪樨 11.
* ”ã­ªæ¨ï ¢ë¤¥«ï¥â 楫®¥ ç¨á«® áâà ­¨æ (4 Š¡) â ª, çâ® ä ªâ¨ç¥áª¨©
à §¬¥à ¢ë¤¥«¥­­®£® ¡«®ª  ¡®«ìè¥ ¨«¨ à ¢¥­ § ¯à®è¥­­®¬ã.
Параметры:
* eax = 68 - номер функции
* ebx = 12 - номер подфункции
* ecx = требуемый размер в байтах
Возвращаемое значение:
* eax = указатель на выделенный блок
Замечания:
* Предварительно следует инициализировать кучу процесса вызовом
подфункции 11.
* Функция выделяет целое число страниц (4 Кб) так, что фактический
размер выделенного блока больше или равен запрошенному.
 
======================================================================
========= ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 13 - ®á¢®¡®¤¨âì ¡«®ª ¯ ¬ïâ¨. ========
========= Функция 68, подфункция 13 - освободить блок памяти. ========
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 13 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡«®ª ¯ ¬ïâ¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 1 - ãᯥ譮
* eax = 0 - ­¥ã¤ ç 
‡ ¬¥ç ­¨ï:
* «®ª ¯ ¬ï⨠¤®«¦¥­ ¡ëâì à ­¥¥ ¢ë¤¥«¥­ ¯®¤ä㭪樥© 12
¨«¨ ¯®¤ä㭪樥© 20.
Параметры:
* eax = 68 - номер функции
* ebx = 13 - номер подфункции
* ecx = указатель на блок памяти
Возвращаемое значение:
* eax = 1 - успешно
* eax = 0 - неудача
Замечания:
* Блок памяти должен быть ранее выделен подфункцией 12
или подфункцией 20.
 
======================================================================
====================== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 14 =====================
====== Ž¦¨¤ âì ¯®«ã祭¨ï ᨣ­ «  ®â ¤àã£¨å ¯à¨«®¦¥­¨©/¤à ©¢¥à®¢. =====
====================== Функция 68, подфункция 14 =====================
====== Ожидать получения сигнала от других приложений/драйверов. =====
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 14 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï ¨­ä®à¬ æ¨¨ (24 ¡ ©â )
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax à §àãè ¥âáï
* ¡ãä¥à, ­  ª®â®àë© ãª §ë¢ ¥â ecx, ᮤ¥à¦¨â á«¥¤ãîéãî ¨­ä®à¬ æ¨î:
* +0: dword: ¨¤¥­â¨ä¨ª â®à ¯®á«¥¤ãîé¨å ¤ ­­ëå ᨣ­ « 
* +4: ¤ ­­ë¥ ¯à¨­ï⮣® ᨣ­ «  (20 ¡ ©â), ä®à¬ â ª®â®àëå
®¯à¥¤¥«ï¥âáï ¯¥à¢ë¬ dword-®¬
Параметры:
* eax = 68 - номер функции
* ebx = 14 - номер подфункции
* ecx = указатель на буфер для информации (24 байта)
Возвращаемое значение:
* eax разрушается
* буфер, на который указывает ecx, содержит следующую информацию:
* +0: dword: идентификатор последующих данных сигнала
* +4: данные принятого сигнала (20 байт), формат которых
определяется первым dword-ом
 
======================================================================
=========== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 16 - § £à㧨âì ¤à ©¢¥à. ===========
=========== Функция 68, подфункция 16 - загрузить драйвер. ===========
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 16 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ¤à ©¢¥à 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ­¥ã¤ ç 
* ¨­ ç¥ eax = åí­¤« ¤à ©¢¥à 
‡ ¬¥ç ­¨ï:
* …᫨ ¤à ©¢¥à ¥éñ ­¥ § £à㦥­, ®­ § £à㦠¥âáï;
¥á«¨ ¤à ©¢¥à 㦥 § £à㦥­, ­¨ç¥£® ­¥ ¬¥­ï¥âáï.
* ˆ¬ï ¤à ©¢¥à  çã¢á⢨⥫쭮 ª ॣ¨áâàã ᨬ¢®«®¢.
Œ ªá¨¬ «ì­ ï ¤«¨­  ¨¬¥­¨ - 16 ᨬ¢®«®¢, ¢ª«îç ï § ¢¥àè î騩
­ã«¥¢®© ᨬ¢®«, ®áâ «ì­ë¥ ᨬ¢®«ë ¨£­®à¨àãîâáï.
* „à ©¢¥à á ¨¬¥­¥¬ ABC § £à㦠¥âáï ¨§ ä ©«  /rd/1/drivers/ABC.obj.
Параметры:
* eax = 68 - номер функции
* ebx = 16 - номер подфункции
* ecx = указатель на ASCIIZ-строку с именем драйвера
Возвращаемое значение:
* eax = 0 - неудача
* иначе eax = хэндл драйвера
Замечания:
* Если драйвер ещё не загружен, он загружается;
если драйвер уже загружен, ничего не меняется.
* Имя драйвера чувствительно к регистру символов.
Максимальная длина имени - 16 символов, включая завершающий
нулевой символ, остальные символы игнорируются.
* Драйвер с именем ABC загружается из файла /rd/1/drivers/ABC.obj.
 
======================================================================
========== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 17 - ã¯à ¢«¥­¨¥ ¤à ©¢¥à®¬. =========
========== Функция 68, подфункция 17 - управление драйвером. =========
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 17 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ã¯à ¢«ïîéãî áâàãªâãàã:
* +0: dword: åí­¤« ¤à ©¢¥à 
* +4: dword: ª®¤ ä㭪樨 ¤à ©¢¥à 
* +8: dword: 㪠§ â¥«ì ­  ¢å®¤­ë¥ ¤ ­­ë¥
* +12 = +0xC: dword: à §¬¥à ¢å®¤­ëå ¤ ­­ëå
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¢ë室­ë¥ ¤ ­­ë¥
* +20 = +0x14: dword: à §¬¥à ¢ë室­ëå ¤ ­­ëå
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = ®¯à¥¤¥«ï¥âáï ¤à ©¢¥à®¬
‡ ¬¥ç ­¨ï:
* Š®¤ë ä㭪権 ¨ áâàãªâãà  ¢å®¤­ëå/¢ë室­ëå ¤ ­­ëå
®¯à¥¤¥«ïîâáï ¤à ©¢¥à®¬.
* à¥¤¢ à¨â¥«ì­® ¤®«¦¥­ ¡ëâì ¯®«ã祭 åí­¤« ¤à ©¢¥à  ¯®¤ä㭪樥© 16.
Параметры:
* eax = 68 - номер функции
* ebx = 17 - номер подфункции
* ecx = указатель на управляющую структуру:
* +0: dword: хэндл драйвера
* +4: dword: код функции драйвера
* +8: dword: указатель на входные данные
* +12 = +0xC: dword: размер входных данных
* +16 = +0x10: dword: указатель на выходные данные
* +20 = +0x14: dword: размер выходных данных
Возвращаемое значение:
* eax = определяется драйвером
Замечания:
* Коды функций и структура входных/выходных данных
определяются драйвером.
* Предварительно должен быть получен хэндл драйвера подфункцией 16.
 
======================================================================
============= ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 19 - § £à㧨âì DLL. =============
============= Функция 68, подфункция 19 - загрузить DLL. =============
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 19 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¯®«­ë¬ ¯ãâñ¬ ª DLL
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ­¥ã¤ ç 
* ¨­ ç¥ eax = 㪠§ â¥«ì ­  â ¡«¨æã íªá¯®àâ  DLL
‡ ¬¥ç ­¨ï:
* ’ ¡«¨æ  íªá¯®àâ  ¯à¥¤áâ ¢«ï¥â ᮡ®© ¬ áᨢ áâàãªâãà ¯® 2 dword' ,
§ ª ­ç¨¢ î騩áï ­ã«ñ¬. ¥à¢ë© dword ¢ áâàãªâãॠï¥âáï
㪠§ â¥«¥¬ ­  ¨¬ï ä㭪樨, ¢â®à®© ᮤ¥à¦¨â  ¤à¥á ä㭪樨.
Параметры:
* eax = 68 - номер функции
* ebx = 19 - номер подфункции
* ecx = указатель на ASCIIZ-строку с полным путём к DLL
Возвращаемое значение:
* eax = 0 - неудача
* иначе eax = указатель на таблицу экспорта DLL
Замечания:
* Таблица экспорта представляет собой массив структур по 2 dword'а,
заканчивающийся нулём. Первый dword в структуре является
указателем на имя функции, второй содержит адрес функции.
 
======================================================================
====== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 20 - ¯¥à¥à á¯à¥¤¥«¨âì ¡«®ª ¯ ¬ïâ¨. =====
====== Функция 68, подфункция 20 - перераспределить блок памяти. =====
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 20 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¢ë© à §¬¥à ¢ ¡ ©â å
* edx = 㪠§ â¥«ì ­  㦥 ¢ë¤¥«¥­­ë© ¡«®ª ¯ ¬ïâ¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 㪠§ â¥«ì ­  ¯¥à¥à á¯à¥¤¥«ñ­­ë© ¡«®ª, 0 ¯à¨ ®è¨¡ª¥
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® á«¥¤ã¥â ¨­¨æ¨ «¨§¨à®¢ âì ªãçã ¯à®æ¥áá  ¢ë§®¢®¬
¯®¤ä㭪樨 11.
* ”ã­ªæ¨ï ¢ë¤¥«ï¥â 楫®¥ ç¨á«® áâà ­¨æ (4 Š¡) â ª, çâ® ä ªâ¨ç¥áª¨©
à §¬¥à ¢ë¤¥«¥­­®£® ¡«®ª  ¡®«ìè¥ ¨«¨ à ¢¥­ § ¯à®è¥­­®¬ã.
* …᫨ edx=0, â® ¢ë§®¢ ä㭪樨 íª¢¨¢ «¥­â¥­ ¢ë¤¥«¥­¨î ¯ ¬ïâ¨
¯®¤ä㭪樥© 12. ‚ ¯à®â¨¢­®¬ á«ãç ¥ ¡«®ª ¯ ¬ï⨠¯®  ¤à¥áã edx
¤®«¦¥­ ¡ëâì à ­¥¥ ¢ë¤¥«¥­ ¯®¤ä㭪樥© 12 ¨«¨
®¯¨á뢠¥¬®© ¯®¤ä㭪樥©.
* …᫨ ecx=0, â® äã­ªæ¨ï ®á¢®¡®¦¤ ¥â ¡«®ª ¯ ¬ï⨠¯®  ¤à¥áã edx ¨
¢®§¢à é ¥â 0.
* ‘®¤¥à¦¨¬®¥ ¯ ¬ï⨠¢¯«®âì ¤® ­ ¨¬¥­ì襣® ¨§ áâ à®£® ¨ ­®¢®£®
à §¬¥à®¢ á®åà ­ï¥âáï.
Параметры:
* eax = 68 - номер функции
* ebx = 20 - номер подфункции
* ecx = новый размер в байтах
* edx = указатель на уже выделенный блок памяти
Возвращаемое значение:
* eax = указатель на перераспределённый блок, 0 при ошибке
Замечания:
* Предварительно следует инициализировать кучу процесса вызовом
подфункции 11.
* Функция выделяет целое число страниц (4 Кб) так, что фактический
размер выделенного блока больше или равен запрошенному.
* Если edx=0, то вызов функции эквивалентен выделению памяти
подфункцией 12. В противном случае блок памяти по адресу edx
должен быть ранее выделен подфункцией 12 или
описываемой подфункцией.
* Если ecx=0, то функция освобождает блок памяти по адресу edx и
возвращает 0.
* Содержимое памяти вплоть до наименьшего из старого и нового
размеров сохраняется.
 
======================================================================
========= ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 21 - § £à㧨âì ¤à ©¢¥à PE. ==========
========= Функция 68, подфункция 21 - загрузить драйвер PE. ==========
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 21 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ¤à ©¢¥à 
* edx = 㪠§ â¥«ì ­  ª®¬ ­¤­ãî áâபã
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ­¥ã¤ ç 
* ¨­ ç¥ eax = åí­¤« ¤à ©¢¥à 
‡ ¬¥ç ­¨ï:
* …᫨ ¤à ©¢¥à ¥éñ ­¥ § £à㦥­, ®­ § £à㦠¥âáï;
¥á«¨ ¤à ©¢¥à 㦥 § £à㦥­, ­¨ç¥£® ­¥ ¬¥­ï¥âáï.
Параметры:
* eax = 68 - номер функции
* ebx = 21 - номер подфункции
* ecx = указатель на ASCIIZ-строку с именем драйвера
* edx = указатель на командную строку
Возвращаемое значение:
* eax = 0 - неудача
* иначе eax = хэндл драйвера
Замечания:
* Если драйвер ещё не загружен, он загружается;
если драйвер уже загружен, ничего не меняется.
 
======================================================================
=== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 22 - ®âªàëâì ¨¬¥­®¢ ­­ãî ®¡« áâì ¯ ¬ïâ¨. ==
=== Функция 68, подфункция 22 - открыть именованную область памяти. ==
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 22 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¬ï ®¡« áâ¨. Œ ªá¨¬ã¬ 31 ᨬ¢®«, ¢ª«îç ï § ¢¥àè î騩 ­®«ì
* edx = à §¬¥à ®¡« á⨠¢ ¡ ©â å ¤«ï SHM_CREATE ¨ SHM_OPEN_ALWAYS
* esi = ä« £¨ ®âªàëâ¨ï ¨ ¤®áâ㯠:
* SHM_OPEN = 0x00 - ®âªàëâì áãé¥áâ¢ãîéãî ®¡« áâì ¯ ¬ïâ¨.
…᫨ ®¡« áâì á â ª¨¬ ¨¬¥­¥¬ ­¥ áãé¥áâ¢ã¥â,
äã­ªæ¨ï ¢¥à­ñâ ª®¤ ®è¨¡ª¨ 5.
* SHM_OPEN_ALWAYS = 0x04 - ®âªàëâì áãé¥áâ¢ãîéãî ¨«¨ ᮧ¤ âì ­®¢ãî
®¡« áâì ¯ ¬ïâ¨.
* SHM_CREATE = 0x08 - ᮧ¤ âì ­®¢ãî ®¡« áâì ¯ ¬ïâ¨.
…᫨ ®¡« áâì á â ª¨¬ ¨¬¥­¥¬ 㦥 áãé¥áâ¢ã¥â,
äã­ªæ¨ï ¢¥à­ñâ ª®¤ ®è¨¡ª¨ 10.
* SHM_READ = 0x00 - ¤®áâ㯠⮫쪮 ­  ç⥭¨¥
* SHM_WRITE = 0x01 - ¤®áâ㯠­  ç⥭¨¥ ¨ § ¯¨áì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 㪠§ â¥«ì ­  ®¡« áâì ¯ ¬ïâ¨, 0 ¯à¨ ®è¨¡ª¥
* ¯à¨ ᮧ¤ ­¨¨ ­®¢®© ®¡« á⨠(SHM_CREATE ¨«¨ SHM_OPEN_ALWAYS):
edx = 0 - ãᯥå, ¨­ ç¥ - ª®¤ ®è¨¡ª¨
* ¯à¨ ®âªàë⨨ áãé¥áâ¢ãî饩 ®¡« á⨠(SHM_OPEN ¨«¨ SHM_OPEN_ALWAYS):
edx = ª®¤ ®è¨¡ª¨ (¯à¨ eax=0) ¨«¨ à §¬¥à ®¡« á⨠¢ ¡ ©â å
Š®¤ë ®è¨¡®ª:
Параметры:
* eax = 68 - номер функции
* ebx = 22 - номер подфункции
* ecx = имя области. Максимум 31 символ, включая завершающий ноль
* edx = размер области в байтах для SHM_CREATE и SHM_OPEN_ALWAYS
* esi = флаги открытия и доступа:
* SHM_OPEN = 0x00 - открыть существующую область памяти.
Если область с таким именем не существует,
функция вернёт код ошибки 5.
* SHM_OPEN_ALWAYS = 0x04 - открыть существующую или создать новую
область памяти.
* SHM_CREATE = 0x08 - создать новую область памяти.
Если область с таким именем уже существует,
функция вернёт код ошибки 10.
* SHM_READ = 0x00 - доступ только на чтение
* SHM_WRITE = 0x01 - доступ на чтение и запись
Возвращаемое значение:
* eax = указатель на область памяти, 0 при ошибке
* при создании новой области (SHM_CREATE или SHM_OPEN_ALWAYS):
edx = 0 - успех, иначе - код ошибки
* при открытии существующей области (SHM_OPEN или SHM_OPEN_ALWAYS):
edx = код ошибки (при eax=0) или размер области в байтах
Коды ошибок:
* E_NOTFOUND = 5
* E_ACCESS = 10
* E_NOMEM = 30
* E_PARAM = 33
‡ ¬¥ç ­¨ï:
* à¥¤¢ à¨â¥«ì­® á«¥¤ã¥â ¨­¨æ¨ «¨§¨à®¢ âì ªãçã ¯à®æ¥áá  ¢ë§®¢®¬
¯®¤ä㭪樨 11.
* …᫨ ᮧ¤ ñâáï ­®¢ ï ®¡« áâì, â® ä« £¨ ¤®áâ㯠 ãáâ ­ ¢«¨¢ îâ
¬ ªá¨¬ «ì­ë¥ ¯à ¢  ¤®áâ㯠 ¤«ï ®áâ «ì­ëå ¯à®æ¥áᮢ. ®¯ë⪠
®âªàëâ¨ï ¤à㣨¬ ¯®â®ª®¬ á ­¥à §à¥èñ­­ë¬¨ ¯à ¢ ¬¨ ¯à®¢ «¨âáï
á ª®¤®¬ ®è¨¡ª¨ E_ACCESS.
* à®æ¥áá, ᮧ¤ ¢è¨© ®¡« áâì, ¢á¥£¤  ¨¬¥¥â ¤®áâ㯠­  § ¯¨áì.
Замечания:
* Предварительно следует инициализировать кучу процесса вызовом
подфункции 11.
* Если создаётся новая область, то флаги доступа устанавливают
максимальные права доступа для остальных процессов. Попытка
открытия другим потоком с неразрешёнными правами провалится
с кодом ошибки E_ACCESS.
* Процесс, создавший область, всегда имеет доступ на запись.
 
======================================================================
=== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 23 - § ªàëâì ¨¬¥­®¢ ­­ãî ®¡« áâì ¯ ¬ïâ¨. ==
=== Функция 68, подфункция 23 - закрыть именованную область памяти. ==
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 23 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¬ï ®¡« áâ¨. Œ ªá¨¬ã¬ 31 ᨬ¢®«, ¢ª«îç ï § ¢¥àè î騩 ­®«ì
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* Ž¡« áâì ¯ ¬ï⨠䨧¨ç¥áª¨ ®á¢®¡®¦¤ ¥âáï (á § ¡ë¢ ­¨¥¬ ¢á¥å ¤ ­­ëå
¨ ¢ë᢮¡®¦¤¥­¨¥¬ 䨧¨ç¥áª®© ¯ ¬ïâ¨), ª®£¤  ¥ñ § ªà®îâ
¢á¥ ®âªàë¢è¨¥ ¯®â®ª¨.
* à¨ § ¢¥à襭¨¨ ¯®â®ª  ®á¢®¡®¦¤ îâáï ¢á¥ ®âªàëâë¥ ¨¬
®¡« á⨠¯ ¬ïâ¨.
Параметры:
* eax = 68 - номер функции
* ebx = 23 - номер подфункции
* ecx = имя области. Максимум 31 символ, включая завершающий ноль
Возвращаемое значение:
* eax разрушается
Замечания:
* Область памяти физически освобождается (с забыванием всех данных
и высвобождением физической памяти), когда её закроют
все открывшие потоки.
* При завершении потока освобождаются все открытые им
области памяти.
 
======================================================================
==== ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 24 - ãáâ ­®¢¨âì ®¡à ¡®â稪 ¨áª«î祭¨©. ===
==== Функция 68, подфункция 24 - установить обработчик исключений. ===
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 24 - ­®¬¥à ¯®¤ä㭪樨
* ecx =  ¤à¥á ­®¢®£® ®¡à ¡®â稪  ¨áª«î祭¨©
* edx = ¬ áª  ®¡à ¡ â뢠¥¬ëå ¨áª«î祭¨©
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax =  ¤à¥á áâ à®£® ®¡à ¡®â稪  ¨áª«î祭¨© (0, ¥á«¨ ­¥ ãáâ ­®¢«¥­)
* ebx = ¬ áª  áâ à®£® ®¡à ¡®â稪  ¨áª«î祭¨©
‡ ¬¥ç ­¨ï:
* ®¬¥à ¡¨â  ¢ ¬ áª¥ ¨áª«î祭¨© ᮮ⢥âáâ¢ã¥â ­®¬¥à㠨᪫î祭¨ï ¯®
ᯥæ¨ä¨ª æ¨¨ ­  ¯à®æ¥áá®à (Intel-PC). ’ ª, ­ ¯à¨¬¥à, ¨áª«î祭¨ï
FPU ¨¬¥îâ ­®¬¥à 16 (#MF),   SSE - 19 (#XF).
* ‚ ¤ ­­®© ॠ«¨§ æ¨¨ ¨£­®à¨àã¥âáï § ¯à®á ­  ¯¥à¥å¢ â ¨áª«î祭¨ï 7
- á¨á⥬  ®¡à ¡ â뢠¥â #NM á ¬®áâ®ï⥫쭮.
* ®«ì§®¢ â¥«ì᪨© ®¡à ¡®â稪 ¯®«ã砥⠭®¬¥à ¨áª«î祭¨ï ¯ à ¬¥â஬
¢ á⥪¥. ®í⮬㠯ࠢ¨«ì­ë© ¢ë室 ¨§ ®¡à ¡®â稪 : RET 4. ‚®§¢à â
¯à¨ í⮬ ¯à®¨§¢®¤¨âáï ­  ª®¬ ­¤ã, ¢ë§¢ ¢èãî ¨áª«î祭¨¥.
* à¨ ¯¥à¥¤ ç¥ ã¯à ¢«¥­¨ï ®¡à ¡®â稪㠨᪫î祭¨© á¡à á뢠¥âáï
ᮮ⢥âáâ¢ãî騩 ¡¨â ¢ ¬ áª¥ ¨áª«î祭¨©. ‚®§­¨ª­®¢¥­¨¥ í⮣® ¦¥
¨áª«î祭¨ï ¢¯®á«¥¤á⢨¨ ¯à¨¢¥¤ñâ ª 㬮«ç «ì­®© ®¡à ¡®âª¥ â ª®¢®£®.
€ ¨¬¥­­®: ª § ¢¥à襭¨î à ¡®âë ¯à¨«®¦¥­¨ï ¢ ®âáãâá⢨¨ ®â« ¤ç¨ª ,
¯à¨®áâ ­®¢ª  á 㢥¤®¬«¥­¨¥¬ ®â« ¦¨¢ î饣® ¯à¨«®¦¥­¨ï ¨­ ç¥.
* ®á«¥ § ¢¥à襭¨ï ªà¨â¨ç¥áª¨å ¤¥©á⢨© ¢ ®¡à ¡®â稪¥ ¯®«ì§®¢ â¥«ï
¢®ááâ ­®¢«¥­¨¥ ¡¨â  ¬ áª¨ ¤ ­­®£® ¨áª«î祭¨ï ¬®¦­® ᤥ« âì
¯®¤ä㭪樥© 25. ‘¡à®á ä« £®¢ ¨áª«î祭¨© ¢ ¬®¤ã«ïå FPU ¨ XMM â ª¦¥
¢®§« £ ¥âáï ­  ®¡à ¡®â稪 ¯®«ì§®¢ â¥«ï.
Параметры:
* eax = 68 - номер функции
* ebx = 24 - номер подфункции
* ecx = адрес нового обработчика исключений
* edx = маска обрабатываемых исключений
Возвращаемое значение:
* eax = адрес старого обработчика исключений (0, если не установлен)
* ebx = маска старого обработчика исключений
Замечания:
* Номер бита в маске исключений соответствует номеру исключения по
спецификации на процессор (Intel-PC). Так, например, исключения
FPU имеют номер 16 (#MF), а SSE - 19 (#XF).
* В данной реализации игнорируется запрос на перехват исключения 7
- система обрабатывает #NM самостоятельно.
* Пользовательский обработчик получает номер исключения параметром
в стеке. Поэтому правильный выход из обработчика: RET 4. Возврат
при этом производится на команду, вызвавшую исключение.
* При передаче управления обработчику исключений сбрасывается
соответствующий бит в маске исключений. Возникновение этого же
исключения впоследствии приведёт к умолчальной обработке такового.
А именно: к завершению работы приложения в отсутствии отладчика,
приостановка с уведомлением отлаживающего приложения иначе.
* После завершения критических действий в обработчике пользователя
восстановление бита маски данного исключения можно сделать
подфункцией 25. Сброс флагов исключений в модулях FPU и XMM также
возлагается на обработчик пользователя.
 
======================================================================
= ”ã­ªæ¨ï 68, ¯®¤äã­ªæ¨ï 25 - ¨§¬¥­¨âì á®áâ®ï­¨¥  ªâ¨¢­®á⨠ᨣ­ « . =
= Функция 68, подфункция 25 - изменить состояние активности сигнала. =
======================================================================
 à ¬¥âàë:
* eax = 68 - ­®¬¥à ä㭪樨
* ebx = 25 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ­®¬¥à ᨣ­ « 
* edx = §­ ç¥­¨¥ ãáâ ­ ¢«¨¢ ¥¬®©  ªâ¨¢­®á⨠(0/1)
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 - § ¤ ­ ­¥¢¥à­ë© ­®¬¥à ᨣ­ « 
* ¨­ ç¥ eax = áâ à®¥ §­ ç¥­¨¥  ªâ¨¢­®á⨠ᨣ­ «  (0/1)
‡ ¬¥ç ­¨ï:
* ‚ ⥪ã饩 ॠ«¨§ æ¨¨ ¨§¬¥­ï¥âáï ⮫쪮 ¬ áª  ¯®«ì§®¢ â¥«ì᪮£®
®¡à ¡®â稪  ¨áª«î祭¨©, ãáâ ­®¢«¥­­®£® ¯®¤ä㭪樥© 24. à¨ í⮬
­®¬¥à ᨣ­ «  ᮮ⢥âáâ¢ã¥â ­®¬¥à㠨᪫î祭¨ï.
Параметры:
* eax = 68 - номер функции
* ebx = 25 - номер подфункции
* ecx = номер сигнала
* edx = значение устанавливаемой активности (0/1)
Возвращаемое значение:
* eax = -1 - задан неверный номер сигнала
* иначе eax = старое значение активности сигнала (0/1)
Замечания:
* В текущей реализации изменяется только маска пользовательского
обработчика исключений, установленного подфункцией 24. При этом
номер сигнала соответствует номеру исключения.
 
======================================================================
======================== ”ã­ªæ¨ï 69 - ®â« ¤ª . =======================
======================== Функция 69 - отладка. =======================
======================================================================
à®æ¥áá ¬®¦¥â § £à㧨âì ¤à㣮© ¯à®æ¥áá ª ª ®â« ¦¨¢ ¥¬ë© ãáâ ­®¢ª®©
ᮮ⢥âáâ¢ãî饣® ¡¨â  ¯à¨ ¢ë§®¢¥ ¯®¤ä㭪樨 7 ä㭪樨 70.
“ ¯à®æ¥áá  ¬®¦¥â ¡ëâì ⮫쪮 ®¤¨­ ®â« ¤ç¨ª; ®¤¨­ ¯à®æ¥áá ¬®¦¥â
®â« ¦¨¢ âì ­¥áª®«ìª® à §­ëå. ‘¨á⥬  㢥¤®¬«ï¥â ®â« ¤ç¨ª ® ᮡëâ¨ïå,
¯à®¨á室ïé¨å á ®â« ¦¨¢ ¥¬ë¬ ¯à®æ¥áᮬ. ‘®®¡é¥­¨ï § ¯¨á뢠îâáï ¢ ¡ãä¥à,
®¯à¥¤¥«ñ­­ë© ¯®¤ä㭪樥© 0.
”®à¬ â á®®¡é¥­¨ï:
* +0: dword: ª®¤ á®®¡é¥­¨ï
* +4: dword: PID ®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá 
* +8: ¬®£ãâ ¯à¨áãâá⢮¢ âì ¤®¯®«­¨â¥«ì­ë¥ ¤ ­­ë¥,
®¯à¥¤¥«ï¥¬ë¥ ª®¤®¬ á®®¡é¥­¨ï
Š®¤ë á®®¡é¥­¨©:
* 1 = ¨áª«î祭¨¥
* ¤®¯®«­¨â¥«ì­® ¯¥à¥¤ ñâáï dword-­®¬¥à ¨áª«î祭¨ï
* ¯à®æ¥áá ¯à¨®áâ ­®¢«¥­
* 2 = ¯à®æ¥áá § ¢¥à訫áï
* ¯à¨å®¤¨â ¯à¨ «î¡®¬ § ¢¥à襭¨¨: ª ª ç¥à¥§ á¨á⥬­ãî äã­ªæ¨î -1,
â ª ¨ ¯à¨ "㡨©á⢥" «î¡ë¬ ¤à㣨¬ ¯à®æ¥áᮬ
(¢ ⮬ ç¨á«¥ á ¬¨¬ ®â« ¤ç¨ª®¬)
* 3 = ®â« ¤®ç­®¥ ¨áª«î祭¨¥ int 1 = #DB
* ¤®¯®«­¨â¥«ì­® ¯¥à¥¤ ñâáï dword-®¡à § ॣ¨áâà  DR6:
* ¡¨âë 0-3: ¢ë¯®«­¥­® ãá«®¢¨¥ ᮮ⢥âáâ¢ãî饩 â®çª¨ ®áâ ­®¢ 
(ãáâ ­®¢«¥­­®© ¯®¤ä㭪樥© 9)
* ¡¨â 14: ¨áª«î祭¨¥ ¯à®¨§®è«® ¨§-§  ०¨¬ 
¯®è £®¢®© âà áá¨à®¢ª¨ (ãáâ ­®¢«¥­ ä« £ TF)
* ¯à®æ¥áá ¯à¨®áâ ­®¢«¥­
à¨ § ¢¥à襭¨¨ ®â« ¤ç¨ª  ¯à¨¡¨¢ îâáï ¢á¥ ®â« ¦¨¢ ¥¬ë¥ ¯à®æ¥ááë.
…᫨ ®â« ¤ç¨ª í⮣® ­¥ å®ç¥â, ®­ ¤®«¦¥­ ¯à¥¤¢ à¨â¥«ì­® ®âª«îç¨âìáï
¯®¤ä㭪樥© 3.
Процесс может загрузить другой процесс как отлаживаемый установкой
соответствующего бита при вызове подфункции 7 функции 70.
У процесса может быть только один отладчик; один процесс может
отлаживать несколько разных. Система уведомляет отладчик о событиях,
происходящих с отлаживаемым процессом. Сообщения записываются в буфер,
определённый подфункцией 0.
Формат сообщения:
* +0: dword: код сообщения
* +4: dword: PID отлаживаемого процесса
* +8: могут присутствовать дополнительные данные,
определяемые кодом сообщения
Коды сообщений:
* 1 = исключение
* дополнительно передаётся dword-номер исключения
* процесс приостановлен
* 2 = процесс завершился
* приходит при любом завершении: как через системную функцию -1,
так и при "убийстве" любым другим процессом
(в том числе самим отладчиком)
* 3 = отладочное исключение int 1 = #DB
* дополнительно передаётся dword-образ регистра DR6:
* биты 0-3: выполнено условие соответствующей точки останова
(установленной подфункцией 9)
* бит 14: исключение произошло из-за режима
пошаговой трассировки (установлен флаг TF)
* процесс приостановлен
При завершении отладчика прибиваются все отлаживаемые процессы.
Если отладчик этого не хочет, он должен предварительно отключиться
подфункцией 3.
 
‚ᥠ¯®¤ä㭪樨 ¯à¨¬¥­¨¬ë ⮫쪮 ª ¯à®æ¥áá ¬/¯®â®ª ¬, § ¯ã饭­ë¬
¨§ ⥪ã饣® ä㭪樥© 70 á ãáâ ­®¢«¥­­ë¬ ä« £®¬ ®â« ¤ª¨.
Žâ« ¤ª  ¬­®£®¯®â®ç­ëå ¯à®£à ¬¬ ¯®ª  ­¥ ¯®¤¤¥à¦¨¢ ¥âáï.
®«­ë© ᯨ᮪ ¯®¤ä㭪権:
* ¯®¤äã­ªæ¨ï 0 - ®¯à¥¤¥«¨âì ®¡« áâì ¤ ­­ëå ¤«ï ®â« ¤®ç­ëå á®®¡é¥­¨©
* ¯®¤äã­ªæ¨ï 1 - ¯®«ãç¨âì á®áâ®ï­¨¥ ॣ¨áâ஢ ®â« ¦¨¢ ¥¬®£® ¯®â®ª 
* ¯®¤äã­ªæ¨ï 2 - ãáâ ­®¢¨âì á®áâ®ï­¨¥ ॣ¨áâ஢ ®â« ¦¨¢ ¥¬®£® ¯®â®ª 
* ¯®¤äã­ªæ¨ï 3 - ®âª«îç¨âìáï ®â ®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá 
* ¯®¤äã­ªæ¨ï 4 - ¯à¨®áâ ­®¢¨âì ®â« ¦¨¢ ¥¬ë© ¯®â®ª
* ¯®¤äã­ªæ¨ï 5 - ¢®§®¡­®¢¨âì ¢ë¯®«­¥­¨¥ ®â« ¦¨¢ ¥¬®£® ¯®â®ª 
* ¯®¤äã­ªæ¨ï 6 - ¯à®ç¨â âì ¨§ ¯ ¬ï⨠®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá 
* ¯®¤äã­ªæ¨ï 7 - § ¯¨á âì ¢ ¯ ¬ïâì ®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá 
* ¯®¤äã­ªæ¨ï 8 - § ¢¥àè¨âì ®â« ¦¨¢ ¥¬ë© ¯®â®ª
* ¯®¤äã­ªæ¨ï 9 - ãáâ ­®¢¨âì/á­ïâì  ¯¯ à â­ãî â®çªã ®áâ ­®¢ 
Все подфункции применимы только к процессам/потокам, запущенным
из текущего функцией 70 с установленным флагом отладки.
Отладка многопоточных программ пока не поддерживается.
Полный список подфункций:
* подфункция 0 - определить область данных для отладочных сообщений
* подфункция 1 - получить состояние регистров отлаживаемого потока
* подфункция 2 - установить состояние регистров отлаживаемого потока
* подфункция 3 - отключиться от отлаживаемого процесса
* подфункция 4 - приостановить отлаживаемый поток
* подфункция 5 - возобновить выполнение отлаживаемого потока
* подфункция 6 - прочитать из памяти отлаживаемого процесса
* подфункция 7 - записать в память отлаживаемого процесса
* подфункция 8 - завершить отлаживаемый поток
* подфункция 9 - установить/снять аппаратную точку останова
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 0 ======================
========= Ž¯à¥¤¥«¨âì ®¡« áâì ¤ ­­ëå ¤«ï ®â« ¤®ç­ëå á®®¡é¥­¨©. ========
====================== Функция 69, подфункция 0 ======================
========= Определить область данных для отладочных сообщений. ========
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 0 - ­®¬¥à ¯®¤ä㭪樨
* ecx = 㪠§ â¥«ì
”®à¬ â ®¡« á⨠¤ ­­ëå:
* +0: dword: N = à §¬¥à ¡ãä¥à  (­¥ áç¨â ï í⮣® § £®«®¢ª )
* +4: dword: § ­ïâ® ¢ ¡ãä¥à¥
* +8: N*byte: ¡ãä¥à
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* …᫨ ¯®«¥ à §¬¥à  ®âà¨æ â¥«ì­®, ¡ãä¥à áç¨â ¥âáï § ¡«®ª¨à®¢ ­­ë¬
¨ ¯à¨ ¯®áâ㯫¥­¨¨ ­®¢®£® á®®¡é¥­¨ï á¨á⥬  ¡ã¤¥â ¦¤ âì.
„«ï ᨭåà®­¨§ æ¨¨ ®¡à ¬«ï©â¥ ¢áî à ¡®âã á ¡ãä¥à®¬ ®¯¥à æ¨ï¬¨
¡«®ª¨à®¢ª¨/à §¡«®ª¨à®¢ª¨
Параметры:
* eax = 69 - номер функции
* ebx = 0 - номер подфункции
* ecx = указатель
Формат области данных:
* +0: dword: N = размер буфера (не считая этого заголовка)
* +4: dword: занято в буфере
* +8: N*byte: буфер
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Если поле размера отрицательно, буфер считается заблокированным
и при поступлении нового сообщения система будет ждать.
Для синхронизации обрамляйте всю работу с буфером операциями
блокировки/разблокировки
neg [bufsize]
* „ ­­ë¥ ¢ ¡ãä¥à¥ âà ªâãîâáï ª ª ¬ áᨢ í«¥¬¥­â®¢ ¯¥à¥¬¥­­®© ¤«¨­ë -
á®®¡é¥­¨©. ”®à¬ â á®®¡é¥­¨ï 㪠§ ­ ¢ ®¡é¥¬ ®¯¨á ­¨¨.
* Данные в буфере трактуются как массив элементов переменной длины -
сообщений. Формат сообщения указан в общем описании.
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 1 ======================
========= ®«ãç¨âì á®áâ®ï­¨¥ ॣ¨áâ஢ ®â« ¦¨¢ ¥¬®£® ¯®â®ª . =========
====================== Функция 69, подфункция 1 ======================
========= Получить состояние регистров отлаживаемого потока. =========
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à ¯®â®ª 
* edx = ¤«¨­  áâàãªâãàë ª®­â¥ªáâ , ¤®«¦­® ¡ëâì 0x28=40 ¡ ©â
* esi = 㪠§ â¥«ì ­  áâàãªâãàã ª®­â¥ªáâ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
”®à¬ â áâàãªâãàë ª®­â¥ªáâ : (FPU ¯®ª  ­¥ ¯®¤¤¥à¦¨¢ ¥âáï)
Параметры:
* eax = 69 - номер функции
* ebx = 1 - номер подфункции
* ecx = идентификатор потока
* edx = длина структуры контекста, должно быть 0x28=40 байт
* esi = указатель на структуру контекста
Возвращаемое значение:
* функция не возвращает значения
Формат структуры контекста: (FPU пока не поддерживается)
* +0: dword: eip
* +4: dword: eflags
* +8: dword: eax
3956,728 → 3956,728
* +28 = +0x1C: dword: ebp
* +32 = +0x20: dword: esi
* +36 = +0x24: dword: edi
‡ ¬¥ç ­¨ï:
* …᫨ ¯®â®ª ¢ë¯®«­ï¥â ª®¤ 0-ª®«ìæ , ¢®§¢à é ¥âáï
á®áâ®ï­¨¥ ॣ¨áâ஢ 3-ª®«ìæ .
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Замечания:
* Если поток выполняет код 0-кольца, возвращается
состояние регистров 3-кольца.
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 2 ======================
======== “áâ ­®¢¨âì á®áâ®ï­¨¥ ॣ¨áâ஢ ®â« ¦¨¢ ¥¬®£® ¯®â®ª . ========
====================== Функция 69, подфункция 2 ======================
======== Установить состояние регистров отлаживаемого потока. ========
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 2 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à ¯®â®ª 
* edx = ¤«¨­  áâàãªâãàë ª®­â¥ªáâ , ¤®«¦­® ¡ëâì 0x28=40 ¡ ©â
* esi = 㪠§ â¥«ì ­  áâàãªâãàã ª®­â¥ªáâ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
”®à¬ â áâàãªâãàë ª®­â¥ªáâ  ãª § ­ ¢ ®¯¨á ­¨¨ ¯®¤ä㭪樨 1.
‡ ¬¥ç ­¨ï:
* …᫨ ¯®â®ª ¢ë¯®«­ï¥â ª®¤ 0-ª®«ìæ , ãáâ ­ ¢«¨¢ ¥âáï
á®áâ®ï­¨¥ ॣ¨áâ஢ 3-ª®«ìæ .
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Параметры:
* eax = 69 - номер функции
* ebx = 2 - номер подфункции
* ecx = идентификатор потока
* edx = длина структуры контекста, должно быть 0x28=40 байт
* esi = указатель на структуру контекста
Возвращаемое значение:
* функция не возвращает значения
Формат структуры контекста указан в описании подфункции 1.
Замечания:
* Если поток выполняет код 0-кольца, устанавливается
состояние регистров 3-кольца.
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 3 - ®âª«îç¨âìáï ®â ®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá . =
== Функция 69, подфункция 3 - отключиться от отлаживаемого процесса. =
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 3 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* …᫨ ¯à®æ¥áá ¡ë« ¯à¨®áâ ­®¢«¥­, ®­ ¢®§®¡­®¢«ï¥â ¢ë¯®«­¥­¨¥.
Параметры:
* eax = 69 - номер функции
* ebx = 3 - номер подфункции
* ecx = идентификатор
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Если процесс был приостановлен, он возобновляет выполнение.
 
======================================================================
==== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 4 - ¯à¨®áâ ­®¢¨âì ®â« ¦¨¢ ¥¬ë© ¯®â®ª. ====
==== Функция 69, подфункция 4 - приостановить отлаживаемый поток. ====
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ¯à®æ¥áá 
* ebx = 4 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Параметры:
* eax = 69 - номер процесса
* ebx = 4 - номер подфункции
* ecx = идентификатор
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 5 ======================
============ ‚®§®¡­®¢¨âì ¢ë¯®«­¥­¨¥ ®â« ¦¨¢ ¥¬®£® ¯®â®ª . ============
====================== Функция 69, подфункция 5 ======================
============ Возобновить выполнение отлаживаемого потока. ============
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 5 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Параметры:
* eax = 69 - номер функции
* ebx = 5 - номер подфункции
* ecx = идентификатор
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 6 ======================
============= à®ç¨â âì ¨§ ¯ ¬ï⨠®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá . ============
====================== Функция 69, подфункция 6 ======================
============= Прочитать из памяти отлаживаемого процесса. ============
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 6 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
* edx = ᪮«ìª® ¡ ©â ç¨â âì
* esi =  ¤à¥á ¯ ¬ï⨠®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá 
* edi = 㪠§ â¥«ì ­  ¡ãä¥à ¤«ï ¤ ­­ëå
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 ¯à¨ ®è¨¡ª¥ (­¥¢¥à­ë© PID ¨«¨ ¡ãä¥à)
* ¨­ ç¥ eax = ç¨á«® ¯à®ç¨â ­­ëå ¡ ©â (¢®§¬®¦­®, 0,
¥á«¨ ¢ esi ᫨誮¬ ¡®«ì讥 §­ ç¥­¨¥)
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Параметры:
* eax = 69 - номер функции
* ebx = 6 - номер подфункции
* ecx = идентификатор
* edx = сколько байт читать
* esi = адрес памяти отлаживаемого процесса
* edi = указатель на буфер для данных
Возвращаемое значение:
* eax = -1 при ошибке (неверный PID или буфер)
* иначе eax = число прочитанных байт (возможно, 0,
если в esi слишком большое значение)
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 7 - § ¯¨á âì ¢ ¯ ¬ïâì ®â« ¦¨¢ ¥¬®£® ¯à®æ¥áá .
Функция 69, подфункция 7 - записать в память отлаживаемого процесса.
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 7 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
* edx = ᪮«ìª® ¡ ©â ¯¨á âì
* esi =  ¤à¥á ¯ ¬ï⨠¢ ®â« ¦¨¢ ¥¬®¬ ¯à®æ¥áá¥
* edi = 㪠§ â¥«ì ­  ¤ ­­ë¥
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = -1 ¯à¨ ®è¨¡ª¥ (­¥¢¥à­ë© PID ¨«¨ ¡ãä¥à)
* ¨­ ç¥ eax = ç¨á«® § ¯¨á ­­ëå ¡ ©â (¢®§¬®¦­®, 0,
¥á«¨ ¢ esi ᫨誮¬ ¡®«ì讥 §­ ç¥­¨¥)
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
Параметры:
* eax = 69 - номер функции
* ebx = 7 - номер подфункции
* ecx = идентификатор
* edx = сколько байт писать
* esi = адрес памяти в отлаживаемом процессе
* edi = указатель на данные
Возвращаемое значение:
* eax = -1 при ошибке (неверный PID или буфер)
* иначе eax = число записанных байт (возможно, 0,
если в esi слишком большое значение)
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
 
======================================================================
====== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 8 - § ¢¥àè¨âì ®â« ¦¨¢ ¥¬ë© ¯®â®ª. ======
====== Функция 69, подфункция 8 - завершить отлаживаемый поток. ======
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 8 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
* ”ã­ªæ¨ï  ­ «®£¨ç­  ¯®¤ä㭪樨 2 ä㭪樨 18 á ¤¢ã¬ï ®â«¨ç¨ï¬¨:
âॡã¥âáï ¢ë¯®«­¥­¨¥ ¯¥à¢®£® § ¬¥ç ­¨ï ¨ ¯à¨­¨¬ ¥âáï PID,
  ­¥ ­®¬¥à á«®â .
Параметры:
* eax = 69 - номер функции
* ebx = 8 - номер подфункции
* ecx = идентификатор
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
* Функция аналогична подфункции 2 функции 18 с двумя отличиями:
требуется выполнение первого замечания и принимается PID,
а не номер слота.
 
======================================================================
====================== ”ã­ªæ¨ï 69, ¯®¤äã­ªæ¨ï 9 ======================
============= “áâ ­®¢¨âì/á­ïâì  ¯¯ à â­ãî â®çªã ®áâ ­®¢ . ============
====================== Функция 69, подфункция 9 ======================
============= Установить/снять аппаратную точку останова. ============
======================================================================
 à ¬¥âàë:
* eax = 69 - ­®¬¥à ä㭪樨
* ebx = 9 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ¨¤¥­â¨ä¨ª â®à ¯®â®ª 
* dl = ¨­¤¥ªá â®çª¨ ®áâ ­®¢ , ®â 0 ¤® 3 ¢ª«îç¨â¥«ì­®
* dh = ä« £¨:
* ¥á«¨ áâ à訩 ¡¨â á¡à®è¥­ - ãáâ ­®¢¨âì â®çªã ®áâ ­®¢ :
* ¡¨âë 0-1 - ãá«®¢¨¥:
* 00 = â®çª  ®áâ ­®¢  ­  ¢ë¯®«­¥­¨¥
* 01 = â®çª  ®áâ ­®¢  ­  § ¯¨áì
* 11 = â®çª  ®áâ ­®¢  ­  ç⥭¨¥/§ ¯¨áì
* ¡¨âë 2-3 - ¤«¨­ ; ¤«ï â®ç¥ª ®áâ ­®¢  ­  ¨á¯®«­¥­¨¥ ¤®«¦­® ¡ëâì
00, ¢ ¯à®â¨¢­®¬ á«ãç ¥ ®¤­® ¨§
* 00 = ¡ ©â
* 01 = á«®¢®
* 11 = ¤¢®©­®¥ á«®¢®
* esi =  ¤à¥á â®çª¨ ®áâ ­®¢ ; ¤®«¦¥­ ¡ëâì ¢ë஢­¥­
ᮮ⢥âá⢥­­® ¤«¨­¥ (â.¥. ¤®«¦¥­ ¡ëâì çñâ­ë¬ ¤«ï
â®ç¥ª ®áâ ­®¢  ­  á«®¢®, ªà â¥­ 4 ¤«ï ¤¢®©­®£® á«®¢ )
* ¥á«¨ áâ à訩 ¡¨â ãáâ ­®¢«¥­ - á¡à®á¨âì â®çªã ®áâ ­®¢ 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ®è¨¡ª  ¢® ¢å®¤­ëå ¤ ­­ëå
* eax = 2 - (§ à¥§¥à¢¨à®¢ ­®, ­¨ª®£¤  ­¥ ¢®§¢à é ¥âáï
¢ ⥪ã饩 ॠ«¨§ æ¨¨) á í⨬ ¨­¤¥ªá®¬ 㦥 ãáâ ­®¢«¥­ 
£«®¡ «ì­ ï â®çª  ®áâ ­®¢ 
‡ ¬¥ç ­¨ï:
* à®æ¥áá ¤®«¦¥­ ¡ëâì § £à㦥­ ¤«ï ®â« ¤ª¨ (ª ª 㪠§ ­® ¢
®¡é¥¬ ®¯¨á ­¨¨).
* €¯¯ à â­ë¥ â®çª¨ ®áâ ­®¢  ॠ«¨§ãîâáï ç¥à¥§ DRx-ॣ¨áâàë
¯à®æ¥áá®à , ®âá ¢á¥ ®£à ­¨ç¥­¨ï.
* ”ã­ªæ¨ï ¬®¦¥â ¯¥à¥ãáâ ­®¢¨âì à ­¥¥ ãáâ ­®¢«¥­­ãî ¥© ¦¥
â®çªã ®áâ ­®¢  (­¨ª ª ­¥ á®®¡é ï ®¡ í⮬).
‚¥¤¨â¥ ᯨ᮪ ãáâ ­®¢«¥­­ëå â®ç¥ª ®áâ ­®¢  ¢ ®â« ¤ç¨ª¥.
* ‘à ¡ â뢠­¨¥ â®çª¨ ®áâ ­®¢  § ª«îç ¥âáï ¢ £¥­¥à¨à®¢ ­¨¨
®â« ¤®ç­®£® ¨áª«î祭¨ï #DB, ® ª®â®à®¬ á¨á⥬  á®®¡é ¥â ®â« ¤ç¨ªã.
* ’®çª  ®áâ ­®¢  ­  § ¯¨áì ¨ ç⥭¨¥/§ ¯¨áì áà ¡ â뢠¥â ¯®á«¥
¢ë¯®«­¥­¨ï ¢ë§¢ ¢è¥© ¥ñ ¨­áâàãªæ¨¨.
Параметры:
* eax = 69 - номер функции
* ebx = 9 - номер подфункции
* ecx = идентификатор потока
* dl = индекс точки останова, от 0 до 3 включительно
* dh = флаги:
* если старший бит сброшен - установить точку останова:
* биты 0-1 - условие:
* 00 = точка останова на выполнение
* 01 = точка останова на запись
* 11 = точка останова на чтение/запись
* биты 2-3 - длина; для точек останова на исполнение должно быть
00, в противном случае одно из
* 00 = байт
* 01 = слово
* 11 = двойное слово
* esi = адрес точки останова; должен быть выровнен
соответственно длине (т.е. должен быть чётным для
точек останова на слово, кратен 4 для двойного слова)
* если старший бит установлен - сбросить точку останова
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - ошибка во входных данных
* eax = 2 - (зарезервировано, никогда не возвращается
в текущей реализации) с этим индексом уже установлена
глобальная точка останова
Замечания:
* Процесс должен быть загружен для отладки (как указано в
общем описании).
* Аппаратные точки останова реализуются через DRx-регистры
процессора, отсюда все ограничения.
* Функция может переустановить ранее установленную ей же
точку останова (никак не сообщая об этом).
Ведите список установленных точек останова в отладчике.
* Срабатывание точки останова заключается в генерировании
отладочного исключения #DB, о котором система сообщает отладчику.
* Точка останова на запись и чтение/запись срабатывает после
выполнения вызвавшей её инструкции.
 
======================================================================
= ”ã­ªæ¨ï 70 - à ¡®â  á ä ©«®¢®© á¨á⥬®© á ¯®¤¤¥à¦ª®© ¤«¨­­ëå ¨¬ñ­. =
= Функция 70 - работа с файловой системой с поддержкой длинных имён. =
======================================================================
 à ¬¥âàë:
Параметры:
* eax = 70
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮; ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ¢ § ¢¨á¨¬®á⨠®â ¯®¤ä㭪樨 ¬®¦¥â ¢®§¢à é âìáï §­ ç¥­¨¥ ¨
¢ ¤à㣨å ॣ¨áâà å
Ž¡é¨© ä®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ᬥ饭¨¥ ¢ ä ©«¥
* +8: dword: áâ à訩 dword ᬥ饭¨ï (¤®«¦¥­ ¡ëâì 0) ¨«¨ ¯®«¥ ä« £®¢
* +12 = +0xC: dword: à §¬¥à
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¤ ­­ë¥
* +20 = +0x14: n db: ASCIIZ-áâப  á ¨¬¥­¥¬ ä ©« 
¨«¨
* ebx = указатель на информационную структуру
Возвращаемое значение:
* eax = 0 - успешно; иначе код ошибки файловой системы
* в зависимости от подфункции может возвращаться значение и
в других регистрах
Общий формат информационной структуры:
* +0: dword: номер подфункции
* +4: dword: смещение в файле
* +8: dword: старший dword смещения (должен быть 0) или поле флагов
* +12 = +0xC: dword: размер
* +16 = +0x10: dword: указатель на данные
* +20 = +0x14: n db: ASCIIZ-строка с именем файла
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
“â®ç­¥­¨ï - ¢ ¤®ªã¬¥­â æ¨¨ ­  ᮮ⢥âáâ¢ãîéãî ¯®¤äã­ªæ¨î.
ˆ¬ï ä ©«  ­¥çã¢á⢨⥫쭮 ª ॣ¨áâà㠡㪢. ãá᪨¥ ¡ãª¢ë ¤®«¦­ë ¡ëâì
§ ¯¨á ­ë ¢ ª®¤¨à®¢ª¥ cp866 (DOS).
”®à¬ â ¨¬¥­¨ ä ©« :
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Уточнения - в документации на соответствующую подфункцию.
Имя файла нечувствительно к регистру букв. Русские буквы должны быть
записаны в кодировке cp866 (DOS).
Формат имени файла:
/base/number/dir1/dir2/.../dirn/file,
£¤¥ /base/number ¨¤¥­â¨ä¨æ¨àã¥â ãáâனá⢮, ­  ª®â®à®¬ ¨é¥âáï ä ©«:
®¤­® ¨§
* /RD/1 = /RAMDISK/1 ¤«ï ¤®áâ㯠 ª à ¬¤¨áªã
* /FD/1 = /FLOPPYDISK/1 ¤«ï ¤®áâ㯠 ª ¯¥à¢®¬ã ä«®¯¯¨-¤¨áª®¢®¤ã,
/FD/2 = /FLOPPYDISK/2 ¤«ï ¢â®à®£® ä«®¯¯¨-¤¨áª®¢®¤ 
* /HD0/x, /HD1/x, /HD2/x, /HD3/x ¤«ï ¤®áâ㯠 ᮮ⢥âá⢥­­®
ª ¦ñá⪨¬ ¤¨áª ¬ ­  IDE0 (Primary Master), IDE1 (Primary Slave),
где /base/number идентифицирует устройство, на котором ищется файл:
одно из
* /RD/1 = /RAMDISK/1 для доступа к рамдиску
* /FD/1 = /FLOPPYDISK/1 для доступа к первому флоппи-дисководу,
/FD/2 = /FLOPPYDISK/2 для второго флоппи-дисковода
* /HD0/x, /HD1/x, /HD2/x, /HD3/x для доступа соответственно
к жёстким дискам на IDE0 (Primary Master), IDE1 (Primary Slave),
IDE2 (Secondary Master), IDE3 (Secondary Slave);
x - ­®¬¥à à §¤¥«  ­  ¢ë¡à ­­®¬ ¢¨­ç¥áâ¥à¥, ¨§¬¥­ï¥âáï ®â 1 ¤® 255
(­  ª ¦¤®¬ ¨§ ¢¨­ç¥áâ¥à®¢ ­ã¬¥à æ¨ï ­ ç¨­ ¥âáï á 1)
* /CD0/1, /CD1/1, /CD2/1, /CD3/1 ¤«ï ¤®áâ㯠 ᮮ⢥âá⢥­­®
ª CD ­  IDE0 (Primary Master), IDE1 (Primary Slave),
x - номер раздела на выбранном винчестере, изменяется от 1 до 255
(на каждом из винчестеров нумерация начинается с 1)
* /CD0/1, /CD1/1, /CD2/1, /CD3/1 для доступа соответственно
к CD на IDE0 (Primary Master), IDE1 (Primary Slave),
IDE2 (Secondary Master), IDE3 (Secondary Slave)
* /SYS - ®¯à¥¤¥«ï¥â á¨á⥬­ãî ¯ ¯ªã; ¯à¨ ®¡ëç­®© § £à㧪¥ á¨á⥬ë
á ¤¨áª¥âë íª¢¨¢ «¥­â­® /RD/1
à¨¬¥àë:
* /SYS - определяет системную папку; при обычной загрузке системы
с дискеты эквивалентно /RD/1
Примеры:
* '/rd/1/kernel.asm',0
* '/HD0/1/kernel.asm',0
* '/hd0/2/menuet/pics/tanzania.bmp',0
* '/hd0/1/Program files/NameOfProgram/SomeFile.SomeExtension',0
* '/sys/MySuperApp.ini',0
’ ª¦¥ äã­ªæ¨ï ¯®¤¤¥à¦¨¢ ¥â ®â­®á¨â¥«ì­ë¥ ¨¬¥­ . …᫨ ¯ãâì ­ ç¨­ ¥âáï
­¥ á '/', â® ®­ áç¨â ¥âáï ®â­®á¨â¥«ì­® ⥪ã饩 ¯ ¯ª¨. ®«ãç¨âì ¨«¨
ãáâ ­®¢¨âì ⥪ãéãî ¯ ¯ªã ¬®¦­® á ¯®¬®éìî á¨áä㭪樨 30.
Также функция поддерживает относительные имена. Если путь начинается
не с '/', то он считается относительно текущей папки. Получить или
установить текущую папку можно с помощью сисфункции 30.
 
„®áâã¯­ë¥ ¯®¤ä㭪樨:
* ¯®¤äã­ªæ¨ï 0 - ç⥭¨¥ ä ©« 
* ¯®¤äã­ªæ¨ï 1 - ç⥭¨¥ ¯ ¯ª¨
* ¯®¤äã­ªæ¨ï 2 - ᮧ¤ ­¨¥/¯¥à¥§ ¯¨áì ä ©« 
* ¯®¤äã­ªæ¨ï 3 - § ¯¨áì ¢ áãé¥áâ¢ãî騩 ä ©«
* ¯®¤äã­ªæ¨ï 4 - ãáâ ­®¢ª  à §¬¥à  ä ©« 
* ¯®¤äã­ªæ¨ï 5 - ¯®«ã祭¨¥  âਡã⮢ ä ©« /¯ ¯ª¨
* ¯®¤äã­ªæ¨ï 6 - ãáâ ­®¢ª   âਡã⮢ ä ©« /¯ ¯ª¨
* ¯®¤äã­ªæ¨ï 7 - § ¯ã᪠¯à®£à ¬¬ë
* ¯®¤äã­ªæ¨ï 8 - 㤠«¥­¨¥ ä ©« /¯ ¯ª¨
* ¯®¤äã­ªæ¨ï 9 - ᮧ¤ ­¨¥ ¯ ¯ª¨
„«ï CD-¯à¨¢®¤®¢ ¢ á¢ï§¨ á  ¯¯ à â­ë¬¨ ®£à ­¨ç¥­¨ï¬¨ ¤®áâ㯭ë
⮫쪮 ¯®¤ä㭪樨 0,1,5 ¨ 7, ¢ë§®¢ ¤àã£¨å ¯®¤ä㭪権 § ¢¥àè¨âáï
®è¨¡ª®© á ª®¤®¬ 2.
à¨ ¯¥à¢®¬ ®¡à é¥­¨¨ ¯®¤ä㭪権 0,1,5,7 ª ãáâனá⢠¬ ATAPI
(CD ¨ DVD) ¯à®¨§¢®¤¨âáï ¡«®ª¨à®¢ª  àãç­®£® ã¯à ¢«¥­¨ï ¬¥å ­¨§¬®¬
«®âª . â® á¢ï§ ­® á ªíè¨à®¢ ­¨¥¬ ¤ ­­ëå, ¯®«ã祭­ëå ®â ¯à¨¢®¤ .
 §¡«®ª¨à®¢ª  ®áãé¥á⢫ï¥âáï ¯à¨ ®¡à é¥­¨¨ ¯®¤ä㭪樨 4 ä㭪樨 24
ª ᮮ⢥âáâ¢ãî饬ã ãáâனáâ¢ã.
Доступные подфункции:
* подфункция 0 - чтение файла
* подфункция 1 - чтение папки
* подфункция 2 - создание/перезапись файла
* подфункция 3 - запись в существующий файл
* подфункция 4 - установка размера файла
* подфункция 5 - получение атрибутов файла/папки
* подфункция 6 - установка атрибутов файла/папки
* подфункция 7 - запуск программы
* подфункция 8 - удаление файла/папки
* подфункция 9 - создание папки
Для CD-приводов в связи с аппаратными ограничениями доступны
только подфункции 0,1,5 и 7, вызов других подфункций завершится
ошибкой с кодом 2.
При первом обращении подфункций 0,1,5,7 к устройствам ATAPI
(CD и DVD) производится блокировка ручного управления механизмом
лотка. Это связано с кэшированием данных, полученных от привода.
Разблокировка осуществляется при обращении подфункции 4 функции 24
к соответствующему устройству.
 
======================================================================
= ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 0 - ç⥭¨¥ ä ©«  á ¯®¤¤¥à¦ª®© ¤«¨­­ëå ¨¬ñ­. =
= Функция 70, подфункция 0 - чтение файла с поддержкой длинных имён. =
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 0 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¯®§¨æ¨ï ¢ ä ©«¥ (¢ ¡ ©â å)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­® ¯®¤ áâ à訩 dword ¯®§¨æ¨¨)
* +12 = +0xC: dword: ᪮«ìª® ¡ ©â ç¨â âì
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à, ªã¤  ¡ã¤ãâ § ¯¨á ­ë ¤ ­­ë¥
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 0 = номер подфункции
* +4: dword: позиция в файле (в байтах)
* +8: dword: 0 (зарезервировано под старший dword позиции)
* +12 = +0xC: dword: сколько байт читать
* +16 = +0x10: dword: указатель на буфер, куда будут записаны данные
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx = ç¨á«® ¯à®ç¨â ­­ëå ¡ ©â ¨«¨
-1=0xffffffff, ¥á«¨ ä ©« ­¥ ­ ©¤¥­
‡ ¬¥ç ­¨ï:
* …᫨ ä ©« ª®­ç¨«áï à ­ìè¥, 祬 ¡ë« ¯à®ç¨â ­ ¯®á«¥¤­¨© § ¯à®è¥­­ë©
¡«®ª, â® äã­ªæ¨ï ¯à®ç¨â ¥â, ᪮«ìª® ᬮ¦¥â, ¯®á«¥ 祣® ¢¥à­ñâ
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx = число прочитанных байт или
-1=0xffffffff, если файл не найден
Замечания:
* Если файл кончился раньше, чем был прочитан последний запрошенный
блок, то функция прочитает, сколько сможет, после чего вернёт
eax=6 (EOF).
* ”ã­ªæ¨ï ­¥ ¯®§¢®«ï¥â ç¨â âì ¯ ¯ª¨
(¢¥à­ñâáï eax=10, access denied).
* Функция не позволяет читать папки
(вернётся eax=10, access denied).
 
======================================================================
= ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 1 - ç⥭¨¥ ¯ ¯ª¨ á ¯®¤¤¥à¦ª®© ¤«¨­­ëå ¨¬ñ­. =
= Функция 70, подфункция 1 - чтение папки с поддержкой длинных имён. =
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 1 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¨­¤¥ªá ­ ç «ì­®£® ¡«®ª  (áç¨â ï á 0)
* +8: dword: ¯®«¥ ä« £®¢:
* ¡¨â 0 (¬ áª  1): ¢ ª ª®¬ ä®à¬ â¥ ¢®§¢à é âì ¨¬¥­ ,
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 1 = номер подфункции
* +4: dword: индекс начального блока (считая с 0)
* +8: dword: поле флагов:
* бит 0 (маска 1): в каком формате возвращать имена,
0=ANSI, 1=UNICODE
* ¯à®ç¨¥ ¡¨âë § à¥§¥à¢¨à®¢ ­ë ¨ ¤®«¦­ë ¡ëâì ãáâ ­®¢«¥­ë ¢ 0
¤«ï ¡ã¤ã饩 ᮢ¬¥á⨬®áâ¨
* +12 = +0xC: dword: ᪮«ìª® ¡«®ª®¢ ç¨â âì
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à, ªã¤  ¡ã¤ãâ § ¯¨á ­ë
¤ ­­ë¥, à §¬¥à ¡ãä¥à  ¤®«¦¥­ ¡ëâì ­¥ ¬¥­ìè¥ 32 + [+12]*560 ¡ ©â
* +20 = +0x14: ASCIIZ-¨¬ï ¯ ¯ª¨, ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
* прочие биты зарезервированы и должны быть установлены в 0
для будущей совместимости
* +12 = +0xC: dword: сколько блоков читать
* +16 = +0x10: dword: указатель на буфер, куда будут записаны
данные, размер буфера должен быть не меньше 32 + [+12]*560 байт
* +20 = +0x14: ASCIIZ-имя папки, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx = ç¨á«® ä ©«®¢, ¨­ä®à¬ æ¨ï ® ª®â®àëå ¡ë«  § ¯¨á ­  ¢ ¡ãä¥à,
¨«¨ -1=0xffffffff, ¥á«¨ ¯ ¯ª  ­¥ ­ ©¤¥­ 
‘âàãªâãà  ¡ãä¥à :
* +0: 32*byte: § £®«®¢®ª
* +32 = +0x20: n1*byte: ¡«®ª á ¨­ä®à¬ æ¨¥© ® ä ©«¥ 1
* +32+n1: n2*byte: ¡«®ª á ¨­ä®à¬ æ¨¥© ® ä ©«¥ 2
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx = число файлов, информация о которых была записана в буфер,
или -1=0xffffffff, если папка не найдена
Структура буфера:
* +0: 32*byte: заголовок
* +32 = +0x20: n1*byte: блок с информацией о файле 1
* +32+n1: n2*byte: блок с информацией о файле 2
* ...
‘âàãªâãà  § £®«®¢ª :
* +0: dword: ¢¥àá¨ï áâàãªâãàë (⥪ãé ï ¢¥àá¨ï = 1)
* +4: dword: ª®«¨ç¥á⢮ à §¬¥éñ­­ëå ¡«®ª®¢; ­¥ ¡®«ìè¥, 祬 § ¯à®è¥­®
¢ ¯®«¥ +12 ¨­ä®à¬ æ¨®­­®© áâàãªâãàë; ¬®¦¥â ¡ëâì ¬¥­ìè¥,
¥á«¨ ¢ ¯ ¯ª¥ ª®­ç¨«¨áì ä ©«ë (â® ¦¥ á ¬®¥, çâ® ¨ ¢ ebx)
* +8: dword: ®¡é¥¥ ç¨á«® ä ©«®¢ ¢ ¯ ¯ª¥
* +12 = +0xC: 20*byte: § à¥§¥à¢¨à®¢ ­® (­ã«¨)
‘âàãªâãà  ¡«®ª  ¤ ­­ëå ¢å®¤  ª â «®£  („‚Š):
* +0: dword:  âਡãâë ä ©« :
* ¡¨â 0 (¬ áª  1): ä ©« ⮫쪮 ¤«ï ç⥭¨ï
* ¡¨â 1 (¬ áª  2): ä ©« ï¥âáï áªàëâë¬
* ¡¨â 2 (¬ áª  4): ä ©« ï¥âáï á¨á⥬­ë¬
* ¡¨â 3 (¬ áª  8): íâ® ­¥ ä ©«,   ¬¥âª  ⮬ 
(­  § ¤ ­­®¬ à §¤¥«¥ ¢áâà¥ç ¥âáï ­¥ ¡®«¥¥ ®¤­®£® à §  ¨
⮫쪮 ¢ ª®à­¥¢®© ¯ ¯ª¥)
* ¡¨â 4 (¬ áª  0x10): íâ® ¯ ¯ª 
* ¡¨â 5 (¬ áª  0x20): ä ©« ­¥  à娢¨à®¢ «áï - ¬­®£¨¥ ¯à®£à ¬¬ë
 à娢 æ¨¨ ¨¬¥îâ ®¯æ¨î, ¯® ª®â®à®©  à娢¨àãîâáï ⮫쪮 ä ©«ë
á ãáâ ­®¢«¥­­ë¬ í⨬ ¡¨â®¬, ¯®á«¥ 祣® íâ®â ¡¨â á¡à á뢠¥âáï -
íâ® ¬®¦¥â ¡ëâì ¯®«¥§­® ¤«ï  ¢â®¬ â¨ç¥áª®£® ᮧ¤ ­¨ï
backup- à娢®¢, ¨¡® ¯à¨ § ¯¨á¨ ¡¨â ®¡ëç­® ãáâ ­ ¢«¨¢ ¥âáï
(­¥ ¢ Kolibri, ¯à ¢¤ )
* +4: byte: ⨯ ¤ ­­ëå ¨¬¥­¨:
(ᮢ¯ ¤ ¥â á ¡¨â®¬ 0 ä« £®¢ ¨­ä®à¬ æ¨®­­®© áâàãªâãàë)
* 0 = ASCII = 1-¡ ©â­®¥ ¯à¥¤áâ ¢«¥­¨¥ ª ¦¤®£® ᨬ¢®« 
* 1 = UNICODE = 2-¡ ©â­®¥ ¯à¥¤áâ ¢«¥­¨¥ ª ¦¤®£® ᨬ¢®« 
* +5: 3*byte: § à¥§¥à¢¨à®¢ ­® (­ã«¨)
* +8: 4*byte: ¢à¥¬ï ᮧ¤ ­¨ï ä ©« 
* +12 = +0xC: 4*byte: ¤ â  ᮧ¤ ­¨ï ä ©« 
* +16 = +0x10: 4*byte: ¢à¥¬ï ¯®á«¥¤­¥£® ¤®áâ㯠 (ç⥭¨¥ ¨«¨ § ¯¨áì)
* +20 = +0x14: 4*byte: ¤ â  ¯®á«¥¤­¥£® ¤®áâ㯠
* +24 = +0x18: 4*byte: ¢à¥¬ï ¯®á«¥¤­¥© ¬®¤¨ä¨ª æ¨¨
* +28 = +0x1C: 4*byte: ¤ â  ¯®á«¥¤­¥© ¬®¤¨ä¨ª æ¨¨
* +32 = +0x20: qword: à §¬¥à ä ©«  ¢ ¡ ©â å (¤® 16777216 ’¡)
* +40 = +0x28: ¨¬ï
* ¤«ï ä®à¬ â  ASCII: ¬ ªá¨¬ «ì­ ï ¤«¨­  ¨¬¥­¨ 263 ᨬ¢®« 
(263 ¡ ©â ), ¡ ©â ¯®á«¥ ¨¬¥­¨ ¨¬¥¥â §­ ç¥­¨¥ 0
* ¤«ï ä®à¬ â  UNICODE: ¬ ªá¨¬ «ì­ ï ¤«¨­  ¨¬¥­¨ 259 ᨬ¢®«®¢
(518 ¡ ©â), ¤¢  ¡ ©â  ¯®á«¥ ¨¬¥­¨ ¨¬¥îâ §­ ç¥­¨¥ 0
”®à¬ â ¢à¥¬¥­¨:
* +0: byte: ᥪ㭤ë
* +1: byte: ¬¨­ãâë
* +2: byte: ç áë
* +3: byte: § à¥§¥à¢¨à®¢ ­® (0)
* ­ ¯à¨¬¥à, 23.59.59 § ¯¨á뢠¥âáï ª ª (¢ hex) 3B 3B 17 00
”®à¬ â ¤ âë:
* +0: byte: ¤¥­ì
* +1: byte: ¬¥áïæ
* +2: word: £®¤
* ­ ¯à¨¬¥à, 25.11.1979 § ¯¨á뢠¥âáï ª ª (¢ hex) 19 0B BB 07
‡ ¬¥ç ­¨ï:
* …᫨ ¢ „‚Š ¯à¨áãâáâ¢ã¥â ¨¬ï ¢ ASCII, â® ¤«¨­  „‚Š á®áâ ¢«ï¥â
304 ¡ ©â , ¥á«¨ ¢ UNICODE - 560 ¡ ©â. ‡­ ç¥­¨¥ ¤«¨­ë ¢ëà ¢­¥­®
­  楫®¥ ªà â­®¥ 16 ¡ ©â
(¤«ï ã᪮७¨ï ®¡à ¡®âª¨ ¢ ªíè-¯ ¬ï⨠CPU).
* ¥à¢ë© ᨬ¢®« ¯®á«¥ ¨¬¥­¨ ­ã«¥¢®© (ASCIIZ-áâப ). „ «ì­¥©è¨¥
¤ ­­ë¥ ᮤ¥à¦ â ¬ãá®à.
* …᫨ ä ©«ë ¢ ¯ ¯ª¥ ª®­ç¨«¨áì à ­ìè¥, 祬 ¡ë«® ¯à®ç¨â ­®
§ ¯à®è¥­­®¥ ª®«¨ç¥á⢮, â® äã­ªæ¨ï ¯à®ç¨â ¥â, ᪮«ìª® ᬮ¦¥â,
¯®á«¥ 祣® ¢¥à­ñâ eax=6 (EOF).
* ‹î¡ ï ¯ ¯ª  ­  ¤¨áª¥, ªà®¬¥ ª®à­¥¢®©, ᮤ¥à¦¨â ¤¢  ᯥ樠«ì­ëå
¢å®¤  "." ¨ "..", ¨¤¥­â¨ä¨æ¨àãîé¨å ᮮ⢥âá⢥­­® ᠬ㠯 ¯ªã ¨
த¨â¥«ìáªãî ¯ ¯ªã.
* ”ã­ªæ¨ï ¯®§¢®«ï¥â â ª¦¥ ç¨â âì ¢¨àâã «ì­ë¥ ¯ ¯ª¨ "/", "/rd",
"/fd", "/hd[n]", ¯à¨ í⮬  âਡãâë ¯®¤¯ ¯®ª ¯®« £ îâáï à ¢­ë¬¨
0x10,   ¢à¥¬¥­  ¨ ¤ âë ®¡­ã«¥­ë. €«ìâ¥à­ â¨¢­ë© ᯮᮡ ¯®«ã祭¨ï
¨­ä®à¬ æ¨¨ ®¡ ®¡®à㤮¢ ­¨¨ - ¯®¤äã­ªæ¨ï 11 ä㭪樨 18.
Структура заголовка:
* +0: dword: версия структуры (текущая версия = 1)
* +4: dword: количество размещённых блоков; не больше, чем запрошено
в поле +12 информационной структуры; может быть меньше,
если в папке кончились файлы (то же самое, что и в ebx)
* +8: dword: общее число файлов в папке
* +12 = +0xC: 20*byte: зарезервировано (нули)
Структура блока данных входа каталога (БДВК):
* +0: dword: атрибуты файла:
* бит 0 (маска 1): файл только для чтения
* бит 1 (маска 2): файл является скрытым
* бит 2 (маска 4): файл является системным
* бит 3 (маска 8): это не файл, а метка тома
(на заданном разделе встречается не более одного раза и
только в корневой папке)
* бит 4 (маска 0x10): это папка
* бит 5 (маска 0x20): файл не архивировался - многие программы
архивации имеют опцию, по которой архивируются только файлы
с установленным этим битом, после чего этот бит сбрасывается -
это может быть полезно для автоматического создания
backup-архивов, ибо при записи бит обычно устанавливается
(не в Kolibri, правда)
* +4: byte: тип данных имени:
(совпадает с битом 0 флагов информационной структуры)
* 0 = ASCII = 1-байтное представление каждого символа
* 1 = UNICODE = 2-байтное представление каждого символа
* +5: 3*byte: зарезервировано (нули)
* +8: 4*byte: время создания файла
* +12 = +0xC: 4*byte: дата создания файла
* +16 = +0x10: 4*byte: время последнего доступа (чтение или запись)
* +20 = +0x14: 4*byte: дата последнего доступа
* +24 = +0x18: 4*byte: время последней модификации
* +28 = +0x1C: 4*byte: дата последней модификации
* +32 = +0x20: qword: размер файла в байтах (до 16777216 Тб)
* +40 = +0x28: имя
* для формата ASCII: максимальная длина имени 263 символа
(263 байта), байт после имени имеет значение 0
* для формата UNICODE: максимальная длина имени 259 символов
(518 байт), два байта после имени имеют значение 0
Формат времени:
* +0: byte: секунды
* +1: byte: минуты
* +2: byte: часы
* +3: byte: зарезервировано (0)
* например, 23.59.59 записывается как (в hex) 3B 3B 17 00
Формат даты:
* +0: byte: день
* +1: byte: месяц
* +2: word: год
* например, 25.11.1979 записывается как (в hex) 19 0B BB 07
Замечания:
* Если в БДВК присутствует имя в ASCII, то длина БДВК составляет
304 байта, если в UNICODE - 560 байт. Значение длины выравнено
на целое кратное 16 байт
(для ускорения обработки в кэш-памяти CPU).
* Первый символ после имени нулевой (ASCIIZ-строка). Дальнейшие
данные содержат мусор.
* Если файлы в папке кончились раньше, чем было прочитано
запрошенное количество, то функция прочитает, сколько сможет,
после чего вернёт eax=6 (EOF).
* Любая папка на диске, кроме корневой, содержит два специальных
входа "." и "..", идентифицирующих соответственно саму папку и
родительскую папку.
* Функция позволяет также читать виртуальные папки "/", "/rd",
"/fd", "/hd[n]", при этом атрибуты подпапок полагаются равными
0x10, а времена и даты обнулены. Альтернативный способ получения
информации об оборудовании - подфункция 11 функции 18.
 
======================================================================
====================== ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 2 ======================
======== ‘®§¤ ­¨¥/¯¥à¥§ ¯¨áì ä ©«  á ¯®¤¤¥à¦ª®© ¤«¨­­ëå ¨¬ñ­. ========
====================== Функция 70, подфункция 2 ======================
======== Создание/перезапись файла с поддержкой длинных имён. ========
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 2 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +12 = +0xC: dword: ᪮«ìª® ¡ ©â ¯¨á âì
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¤ ­­ë¥
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 2 = номер подфункции
* +4: dword: 0 (зарезервировано)
* +8: dword: 0 (зарезервировано)
* +12 = +0xC: dword: сколько байт писать
* +16 = +0x10: dword: указатель на данные
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx = ç¨á«® § ¯¨á ­­ëå ¡ ©â (¢®§¬®¦­®, 0)
‡ ¬¥ç ­¨ï:
* …᫨ ä ©« á â ª¨¬ ¨¬¥­¥¬ ­¥ áãé¥á⢮¢ «, ®­ ᮧ¤ ñâáï; ¥á«¨
áãé¥á⢮¢ «, â® ¯¥à¥§ ¯¨á뢠¥âáï.
* …᫨ ᢮¡®¤­®£® ¬¥áâ  ­  ¤¨áª¥ ­¥¤®áâ â®ç­®, â® äã­ªæ¨ï § ¯¨è¥â,
᪮«ìª® ᬮ¦¥â, ¯®á«¥ 祣® ¢¥à­ñâ ª®¤ ®è¨¡ª¨ 8.
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx = число записанных байт (возможно, 0)
Замечания:
* Если файл с таким именем не существовал, он создаётся; если
существовал, то перезаписывается.
* Если свободного места на диске недостаточно, то функция запишет,
сколько сможет, после чего вернёт код ошибки 8.
* Функция не поддерживается для CD (вернётся код ошибки 2).
 
======================================================================
====================== ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 3 ======================
======== ‡ ¯¨áì ¢ áãé¥áâ¢ãî騩 ä ©« á ¯®¤¤¥à¦ª®© ¤«¨­­ëå ¨¬ñ­. =======
====================== Функция 70, подфункция 3 ======================
======== Запись в существующий файл с поддержкой длинных имён. =======
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 3 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¯®§¨æ¨ï ¢ ä ©«¥ (¢ ¡ ©â å)
* +8: dword: áâ à訩 dword ¯®§¨æ¨¨ (¤®«¦¥­ ¡ëâì 0 ¤«ï FAT)
* +12 = +0xC: dword: ᪮«ìª® ¡ ©â ¯¨á âì
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¤ ­­ë¥
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 3 = номер подфункции
* +4: dword: позиция в файле (в байтах)
* +8: dword: старший dword позиции (должен быть 0 для FAT)
* +12 = +0xC: dword: сколько байт писать
* +16 = +0x10: dword: указатель на данные
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx = ç¨á«® § ¯¨á ­­ëå ¡ ©â (¢®§¬®¦­®, 0)
‡ ¬¥ç ­¨ï:
* ” ©« ¤®«¦¥­ 㦥 áãé¥á⢮¢ âì, ¨­ ç¥ ¢¥à­ñâáï eax=5.
* …¤¨­á⢥­­ë¬ १ã«ìâ â®¬ § ¯¨á¨ 0 ¡ ©â ï¥âáï ãáâ ­®¢ª  ¢
 âਡãâ å ä ©«  ¤ âë/¢à¥¬¥­¨ ¬®¤¨ä¨ª æ¨¨ ¨ ¤®áâ㯠 ¢ ⥪ãéãî.
* …᫨ ­ ç «ì­ ï ¨/¨«¨ ª®­¥ç­ ï ¯®§¨æ¨ï ¢ë室¨â §  ¯à¥¤¥«ë ä ©« 
(§  ¨áª«î祭¨¥¬ ¯à¥¤ë¤ã饣® á«ãç ï), ä ©« à áè¨àï¥âáï ¤®
­¥®¡å®¤¨¬®£® à §¬¥à  ­ã«¥¢ë¬¨ ᨬ¢®« ¬¨.
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx = число записанных байт (возможно, 0)
Замечания:
* Файл должен уже существовать, иначе вернётся eax=5.
* Единственным результатом записи 0 байт является установка в
атрибутах файла даты/времени модификации и доступа в текущую.
* Если начальная и/или конечная позиция выходит за пределы файла
(за исключением предыдущего случая), файл расширяется до
необходимого размера нулевыми символами.
* Функция не поддерживается для CD (вернётся код ошибки 2).
 
======================================================================
========= ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 4 - ãáâ ­®¢ª  à §¬¥à  ä ©« . ========
========= Функция 70, подфункция 4 - установка размера файла. ========
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 4 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¬« ¤è¨© dword ­®¢®£® à §¬¥à  ä ©« 
* +8: dword: áâ à訩 dword ­®¢®£® à §¬¥à  ä ©« 
(¤®«¦¥­ ¡ëâì 0 ¤«ï FAT)
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 4 = номер подфункции
* +4: dword: младший dword нового размера файла
* +8: dword: старший dword нового размера файла
(должен быть 0 для FAT)
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: 0 (зарезервировано)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* …᫨ ­®¢ë© à §¬¥à ä ©«  ¬¥­ìè¥ áâ à®£®, ä ©« ãᥪ ¥âáï. …᫨
­®¢ë© à §¬¥à ¡®«ìè¥ áâ à®£®, ä ©« à áè¨àï¥âáï ­ã«¥¢ë¬¨ ᨬ¢®« ¬¨.
…᫨ ­®¢ë© à §¬¥à à ¢¥­ áâ à®¬ã, ¥¤¨­á⢥­­ë¬ १ã«ìâ â®¬ ¢ë§®¢ 
ï¥âáï ãáâ ­®¢ª  ¤ âë/¢à¥¬¥­¨ ¬®¤¨ä¨ª æ¨¨ ¨ ¤®áâ㯠 ¢ ⥪ã騥.
* …᫨ ᢮¡®¤­®£® ¬¥áâ  ­  ¤¨áª¥ ­¥¤®áâ â®ç­® ¤«ï à áè¨à¥­¨ï ä ©« ,
â® äã­ªæ¨ï à áè¨à¨â ­ áª®«ìª® ¢®§¬®¦­®, ¯®á«¥ 祣® ¢¥à­ñâ
ª®¤ ®è¨¡ª¨ 8.
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx разрушается
Замечания:
* Если новый размер файла меньше старого, файл усекается. Если
новый размер больше старого, файл расширяется нулевыми символами.
Если новый размер равен старому, единственным результатом вызова
является установка даты/времени модификации и доступа в текущие.
* Если свободного места на диске недостаточно для расширения файла,
то функция расширит насколько возможно, после чего вернёт
код ошибки 8.
* Функция не поддерживается для CD (вернётся код ошибки 2).
 
======================================================================
=== ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 5 - ¯®«ã祭¨¥ ¨­ä®à¬ æ¨¨ ® ä ©«¥/¯ ¯ª¥. ===
=== Функция 70, подфункция 5 - получение информации о файле/папке. ===
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 5 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à, ªã¤  ¡ã¤ãâ § ¯¨á ­ë ¤ ­­ë¥
(40 ¡ ©â)
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 5 = номер подфункции
* +4: dword: 0 (зарезервировано)
* +8: dword: 0 (зарезервировано)
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: указатель на буфер, куда будут записаны данные
(40 байт)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
ˆ­ä®à¬ æ¨ï ® ä ©«¥ ¢®§¢à é ¥âáï ¢ ä®à¬ â¥ „‚Š
(¡«®ª  ¤ ­­ëå ¢å®¤  ª â «®£ ), 㪠§ ­­®¬ ¢ ®¯¨á ­¨¨
¯®¤ä㭪樨 1, ­® ¡¥§ ¨¬¥­¨ ä ©« 
(â® ¥áâì ¯¥à¢ë¥ 40 = 0x28 ¡ ©â).
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥â ¢¨àâã «ì­ë¥ ¯ ¯ª¨ ⨯  /, /rd ¨
ª®à­¥¢ë¥ ¯ ¯ª¨ ⨯  /rd/1.
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx разрушается
Информация о файле возвращается в формате БДВК
(блока данных входа каталога), указанном в описании
подфункции 1, но без имени файла
(то есть первые 40 = 0x28 байт).
Замечания:
* Функция не поддерживает виртуальные папки типа /, /rd и
корневые папки типа /rd/1.
 
======================================================================
===== ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 6 - ãáâ ­®¢ª   âਡã⮢ ä ©« /¯ ¯ª¨. ====
===== Функция 70, подфункция 6 - установка атрибутов файла/папки. ====
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 6 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 㪠§ â¥«ì ­  ¡ãä¥à á  âਡãâ ¬¨ (32 ¡ ©â )
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 6 = номер подфункции
* +4: dword: 0 (зарезервировано)
* +8: dword: 0 (зарезервировано)
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: указатель на буфер с атрибутами (32 байта)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
€âਡãâë ä ©«  - ¯¥à¢ë¥ 32 ¡ ©â  ¢ „‚Š (¡«®ª¥ ¤ ­­ëå ¢å®¤  ª â «®£ ),
ä®à¬ â ª®â®à®£® 㪠§ ­ ¢ ®¯¨á ­¨¨ ¯®¤ä㭪樨 1
(â® ¥áâì ¡¥§ ¨¬¥­¨ ¨ à §¬¥à  ä ©« ). €âਡãâ ä ©«/¯ ¯ª /¬¥âª  ⮬ 
(¡¨âë 3,4 ¢ dword'¥ +0) ­¥ ¬¥­ï¥âáï.
 ©â +4 (ä®à¬ â ¨¬¥­¨) ¨£­®à¨àã¥âáï.
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥â ¢¨àâã «ì­ë¥ ¯ ¯ª¨ ⨯  /, /rd ¨
ª®à­¥¢ë¥ ¯ ¯ª¨ ⨯  /rd/1.
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx разрушается
Атрибуты файла - первые 32 байта в БДВК (блоке данных входа каталога),
формат которого указан в описании подфункции 1
(то есть без имени и размера файла). Атрибут файл/папка/метка тома
(биты 3,4 в dword'е +0) не меняется.
Байт +4 (формат имени) игнорируется.
Замечания:
* Функция не поддерживает виртуальные папки типа /, /rd и
корневые папки типа /rd/1.
* Функция не поддерживается для CD (вернётся код ошибки 2).
 
======================================================================
============ ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 7 - § ¯ã᪠¯à®£à ¬¬ë. ============
============ Функция 70, подфункция 7 - запуск программы. ============
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 7 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: ¯®«¥ ä« £®¢:
* ¡¨â 0: § ¯ãáâ¨âì ¯à®æ¥áá ª ª ®â« ¦¨¢ ¥¬ë©
* ®áâ «ì­ë¥ ¡¨âë § à¥§¥à¢¨à®¢ ­ë ¨ ¤®«¦­ë ¡ëâì ãáâ ­®¢«¥­ë ¢ 0
* +8: dword: 0 ¨«¨ 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¯ à ¬¥âà ¬¨
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 7 = номер подфункции
* +4: dword: поле флагов:
* бит 0: запустить процесс как отлаживаемый
* остальные биты зарезервированы и должны быть установлены в 0
* +8: dword: 0 или указатель на ASCIIZ-строку с параметрами
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: 0 (зарезервировано)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax > 0 - ¯à®£à ¬¬  § £à㦥­ , eax ᮤ¥à¦¨â PID
* eax < 0 - ¯à®¨§®è«  ®è¨¡ª , -eax ᮤ¥à¦¨â
ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* Š®¬ ­¤­ ï áâப  ¤®«¦­  § ª ­ç¨¢ âìáï ᨬ¢®«®¬ á ª®¤®¬ 0
(ASCIIZ-áâப ); ãç¨â뢠îâáï «¨¡® ¢á¥ ᨬ¢®«ë ¤® § ¢¥àè î饣® ­ã«ï
¢ª«îç¨â¥«ì­®, «¨¡® ¯¥à¢ë¥ 256 ᨬ¢®«®¢, ¢ § ¢¨á¨¬®á⨠®â ⮣®,
çâ® ¬¥­ìè¥.
* …᫨ ¯à®æ¥áá § ¯ã᪠¥âáï ª ª ®â« ¦¨¢ ¥¬ë©, ®­ ᮧ¤ ñâáï
¢ § ¬®à®¦¥­­®¬ á®áâ®ï­¨¨; ¤«ï § ¯ã᪠ ¨á¯®«ì§ã©â¥
¯®¤äã­ªæ¨î 5 ä㭪樨 69.
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax > 0 - программа загружена, eax содержит PID
* eax < 0 - произошла ошибка, -eax содержит
код ошибки файловой системы
* ebx разрушается
Замечания:
* Командная строка должна заканчиваться символом с кодом 0
(ASCIIZ-строка); учитываются либо все символы до завершающего нуля
включительно, либо первые 256 символов, в зависимости от того,
что меньше.
* Если процесс запускается как отлаживаемый, он создаётся
в замороженном состоянии; для запуска используйте
подфункцию 5 функции 69.
 
======================================================================
========== ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 8 - 㤠«¥­¨¥ ä ©« /¯ ¯ª¨. ==========
========== Функция 70, подфункция 8 - удаление файла/папки. ==========
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 8 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +20 = +0x14: ASCIIZ-¨¬ï ä ©« , ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 8 = номер подфункции
* +4: dword: 0 (зарезервировано)
* +8: dword: 0 (зарезервировано)
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: 0 (зарезервировано)
* +20 = +0x14: ASCIIZ-имя файла, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ä ©« 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* Œ®¦­® 㤠«ïâì ⮫쪮 ¯ãáâë¥ ¯ ¯ª¨ (¯®¯ë⪠ 㤠«¥­¨ï ­¥¯ãá⮩ ¯ ¯ª¨
¯à¨¢¥¤ñâ ª ®è¨¡ª¥ á ª®¤®¬ 10, "¤®áâ㯠§ ¯à¥éñ­").
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем файла
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx разрушается
Замечания:
* Функция не поддерживается для CD (вернётся код ошибки 2).
* Можно удалять только пустые папки (попытка удаления непустой папки
приведёт к ошибке с кодом 10, "доступ запрещён").
 
======================================================================
============= ”ã­ªæ¨ï 70, ¯®¤äã­ªæ¨ï 9 - ᮧ¤ ­¨¥ ¯ ¯ª¨. =============
============= Функция 70, подфункция 9 - создание папки. =============
======================================================================
 à ¬¥âàë:
* eax = 70 - ­®¬¥à ä㭪樨
* ebx = 㪠§ â¥«ì ­  ¨­ä®à¬ æ¨®­­ãî áâàãªâãàã
”®à¬ â ¨­ä®à¬ æ¨®­­®© áâàãªâãàë:
* +0: dword: 9 = ­®¬¥à ¯®¤ä㭪樨
* +4: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +8: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +12 = +0xC: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +16 = +0x10: dword: 0 (§ à¥§¥à¢¨à®¢ ­®)
* +20 = +0x14: ASCIIZ-¨¬ï ¯ ¯ª¨, ¯à ¢¨«  ä®à¬¨à®¢ ­¨ï ¨¬ñ­ 㪠§ ­ë ¢
®¡é¥¬ ®¯¨á ­¨¨
¨«¨
Параметры:
* eax = 70 - номер функции
* ebx = указатель на информационную структуру
Формат информационной структуры:
* +0: dword: 9 = номер подфункции
* +4: dword: 0 (зарезервировано)
* +8: dword: 0 (зарезервировано)
* +12 = +0xC: dword: 0 (зарезервировано)
* +16 = +0x10: dword: 0 (зарезервировано)
* +20 = +0x14: ASCIIZ-имя папки, правила формирования имён указаны в
общем описании
или
* +20 = +0x14: db 0
* +21 = +0x15: dd 㪠§ â¥«ì ­  ASCIIZ-áâபã á ¨¬¥­¥¬ ¯ ¯ª¨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮, ¨­ ç¥ ª®¤ ®è¨¡ª¨ ä ©«®¢®© á¨á⥬ë
* ebx à §àãè ¥âáï
‡ ¬¥ç ­¨ï:
* ”ã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï CD (¢¥à­ñâáï ª®¤ ®è¨¡ª¨ 2).
* ®¤¨â¥«ìáª ï ¯ ¯ª  ¤®«¦­  㦥 áãé¥á⢮¢ âì.
* …᫨ ¯ ¯ª  㦥 áãé¥áâ¢ã¥â, äã­ªæ¨ï § ¢¥àè¨âáï ãᯥ譮 (eax=0).
* +21 = +0x15: dd указатель на ASCIIZ-строку с именем папки
Возвращаемое значение:
* eax = 0 - успешно, иначе код ошибки файловой системы
* ebx разрушается
Замечания:
* Функция не поддерживается для CD (вернётся код ошибки 2).
* Родительская папка должна уже существовать.
* Если папка уже существует, функция завершится успешно (eax=0).
 
======================================================================
=== ”ã­ªæ¨ï 71, ¯®¤äã­ªæ¨ï 1 - ãáâ ­®¢¨âì § £®«®¢®ª ®ª­  ¯à®£à ¬¬ë. ==
=== Функция 71, подфункция 1 - установить заголовок окна программы. ==
======================================================================
 à ¬¥âàë:
* eax = 71 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx =  ¤à¥á áâப¨ § £®«®¢ª 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
‡ ¬¥ç ­¨ï:
* ‘âப  § £®«®¢ª  ¤®«¦­  ¡ëâì ¢ ä®à¬ â¥ ASCIIZ. ‚ § £®«®¢ª¥
®â®¡à ¦ ¥âáï ­¥ ¡®«¥¥ 255 ᨬ¢®«®¢ ­¥§ ¢¨á¨¬® ®â ¯®«­®© ¤«¨­ë
áâப¨.
* —⮡ë ã¡à âì § £®«®¢®ª, ¯¥à¥¤ ©â¥ NULL ¢ ecx.
Параметры:
* eax = 71 - номер функции
* ebx = 1 - номер подфункции
* ecx = адрес строки заголовка
Возвращаемое значение:
* функция не возвращает значения
Замечания:
* Строка заголовка должна быть в формате ASCIIZ. В заголовке
отображается не более 255 символов независимо от полной длины
строки.
* Чтобы убрать заголовок, передайте NULL в ecx.
 
======================================================================
================ ”ã­ªæ¨ï 72 - ¯®á« âì á®®¡é¥­¨¥ ®ª­ã. ================
================ Функция 72 - послать сообщение окну. ================
======================================================================
 
--- ®¤äã­ªæ¨ï 1 - ¯®á« âì á®®¡é¥­¨¥ á ¯ à ¬¥â஬  ªâ¨¢­®¬ã ®ª­ã. ----
 à ¬¥âàë:
* eax = 72 - ­®¬¥à ä㭪樨
* ebx = 1 - ­®¬¥à ¯®¤ä㭪樨
* ecx = ª®¤ ᮡëâ¨ï: 2 ¨«¨ 3
* edx = ª®¤ ª« ¢¨è¨ ¤«ï ecx=2, ¨¤¥­â¨ä¨ª â®à ª­®¯ª¨ ¤«ï ecx=3
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* eax = 0 - ãᯥ譮
* eax = 1 - ¡ãä¥à § ¯®«­¥­
--- Подфункция 1 - послать сообщение с параметром активному окну. ----
Параметры:
* eax = 72 - номер функции
* ebx = 1 - номер подфункции
* ecx = код события: 2 или 3
* edx = код клавиши для ecx=2, идентификатор кнопки для ecx=3
Возвращаемое значение:
* eax = 0 - успешно
* eax = 1 - буфер заполнен
 
======================================================================
===================== ”ã­ªæ¨ï 73 - blit bitmap =====================
===================== Функция 73 - blit bitmap =====================
======================================================================
¡«¨â - ª®¯¨à®¢ ­¨¥ ¡¨â®¢®£® ¬ áᨢ
блит - копирование битового массив
 
 à ¬¥âàë:
* eax = 73 - ­®¬¥à ä㭪樨
Параметры:
* eax = 73 - номер функции
 
* ebx = ROP ¨ ®¯æ¨®­ «ì­ë¥ ä« £¨
* ebx = ROP и опциональные флаги
31 6 5 4 3 0
[ reserved ][T][B][ROP]
ROP - ª®¤ à áâ஢ëå ®¯¥à æ¨©
0: ª®¯¨à®¢ âì
1-15: ‡ à¥§¥à¢¨à®¢ ­®
B - ¡«¨â ­  ä®­®¢ãî ¯®ä¥àå­®áâì
T - ¡«¨â á ¯à®§à ç­®áâìî
ROP - код растровых операций
0: копировать
1-15: Зарезервировано
B - блит на фоновую поферхность
T - блит с прозрачностью
 
* ecx = 㪠§ â¥«ì ­  ¯ à ¬¥âàë ä㭪樨
ᬥ饭¨¥ 楫¨ÿ¨ ®âá¥ç¥­¨¥
+0 signed dword: ᬥ饭¨¥ ¯® X ®ª­ , ¤«ï 楫¥¢®£® ¯àאַ㣮«ì­¨ª 
¢¥àå­¨© «¥¢ë© 㣮«
+4 signed dword: ᬥ饭¨¥ ¯® Y ®ª­ , ¤«ï 楫¥¢®£® ¯àאַ㣮«ì­¨ª 
¢¥àå­¨© «¥¢ë© 㣮«
+8 dword: è¨à¨­  楫¥¢®£® ¯àאַ㣮«ì­¨ª 
+12 dword: ¢ëá®â  楫¥¢®£® ¯àאַ㣮«ì­¨ª 
* ecx = указатель на параметры функции
смещение цели и отсечение
+0 signed dword: смещение по X окна, для целевого прямоугольника
верхний левый угол
+4 signed dword: смещение по Y окна, для целевого прямоугольника
верхний левый угол
+8 dword: ширина целевого прямоугольника
+12 dword: высота целевого прямоугольника
 
ᬥ饭¨¥ ¨á室­¨ª ÿ¨ ®âá¥ç¥­¨¥
+16 signed dword: ᬥ饭¨¥ ¯® X bitmap, ¤«ï ¨á室­®£® ¯àאַ㣮«ì­¨ª 
¢¥àå­¨© «¥¢ë© 㣮«
+20 signed dword: ᬥ饭¨¥ ¯® Y bitmap, ¤«ï ¨á室­®£® ¯àאַ㣮«ì­¨ª 
¢¥àå­¨© «¥¢ë© 㣮«
+24 dword: è¨à¨­  ¨á室­®£® ¯àאַ㣮«ì­¨ª 
+28 dword: ¢ëá®â  ¨á室­®£® ¯àאַ㣮«ì­¨ª 
смещение исходника и отсечение
+16 signed dword: смещение по X bitmap, для исходного прямоугольника
верхний левый угол
+20 signed dword: смещение по Y bitmap, для исходного прямоугольника
верхний левый угол
+24 dword: ширина исходного прямоугольника
+28 dword: высота исходного прямоугольника
 
+32: dword: ¤ ­­ë¥ bitmap - ¤®«¦­ë ¡ëâì 32bpp
+36: dword: à §¬¥à áâப¨ bitmap ¢ ¡ ©â å
+32: dword: данные bitmap - должны быть 32bpp
+36: dword: размер строки bitmap в байтах
 
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â §­ ç¥­¨ï
Возвращаемое значение:
* функция не возвращает значения
 
======================================================================
========== ”ã­ªæ¨ï -1 - § ¢¥àè¨âì ¢ë¯®«­¥­¨¥ ¯®â®ª /¯à®æ¥áá  =========
========== Функция -1 - завершить выполнение потока/процесса =========
======================================================================
 à ¬¥âàë:
* eax = -1 - ­®¬¥à ä㭪樨
‚®§¢à é ¥¬®¥ §­ ç¥­¨¥:
* äã­ªæ¨ï ­¥ ¢®§¢à é ¥â ­¨ §­ ç¥­¨ï, ­¨ ã¯à ¢«¥­¨ï
‡ ¬¥ç ­¨ï:
* …᫨ ¯à®æ¥áá ® ­¥ ᮧ¤ ¢ « ¯®â®ª®¢, â® ã ­¥£® ¥áâì ⮫쪮
®¤¨­ ¯®â®ª, § ¢¥à襭¨¥ ª®â®à®£® ¯à¨¢®¤¨â ª § ¢¥à襭¨î ¯à®æ¥áá .
* …᫨ ⥪ã騩 ¯®â®ª - ¯®á«¥¤­¨© ¢ ¯à®æ¥áá¥, â® ¥£® § ¢¥à襭¨¥
â ª¦¥ ¯à¨¢®¤¨â ª § ¢¥à襭¨î ¯à®æ¥áá .
* â  äã­ªæ¨ï § ¢¥àè ¥â ⥪ã騩 ¯®â®ª. „à㣮© ¯®â®ª ¬®¦­® ¯à¨¡¨âì
¢ë§®¢®¬ ¯®¤ä㭪樨 2 ä㭪樨 18.
Параметры:
* eax = -1 - номер функции
Возвращаемое значение:
* функция не возвращает ни значения, ни управления
Замечания:
* Если процесс явно не создавал потоков, то у него есть только
один поток, завершение которого приводит к завершению процесса.
* Если текущий поток - последний в процессе, то его завершение
также приводит к завершению процесса.
* Эта функция завершает текущий поток. Другой поток можно прибить
вызовом подфункции 2 функции 18.
 
======================================================================
=========================== ‘¯¨á®ª ᮡë⨩ ===========================
=========================== Список событий ===========================
======================================================================
Žç¥à¥¤­®¥ ᮡë⨥ ¬®¦­® ¯®«ãç¨âì ¢ë§®¢®¬ ®¤­®© ¨§ ä㭪権 10
(®¦¨¤ âì ᮡëâ¨ï), 11 (¯à®¢¥à¨âì ¡¥§ ®¦¨¤ ­¨ï), 23
(®¦¨¤ âì ¢ â¥ç¥­¨¥ § ¤ ­­®£® ¢à¥¬¥­¨).
â¨ ä㭪樨 ¢®§¢à é îâ ⮫쪮 ⥠ᮡëâ¨ï, ª®â®àë¥ ¢å®¤ïâ ¢ ¬ áªã,
ãáâ ­ ¢«¨¢ ¥¬ãî ä㭪樥© 40. ® 㬮«ç ­¨î íâ® ¯¥à¢ë¥ âà¨, 祣®
¢¯®«­¥ ¤®áâ â®ç­® ¤«ï ¬­®£¨å ¯à¨«®¦¥­¨©.
Š®¤ë ᮡë⨩:
* 1 = á®®¡é¥­¨¥ ® ¯¥à¥à¨á®¢ª¥ (á¡à á뢠¥âáï ¯à¨ ¢ë§®¢¥ ä㭪樨 0)
* 2 = ­ ¦ â  ª« ¢¨è  ­  ª« ¢¨ âãॠ(¯®áâ㯠¥â, ⮫쪮 ª®£¤  ®ª­®
 ªâ¨¢­®) ¨«¨ ­ ¦ â  "£®àïç ï ª« ¢¨è ";
á¡à á뢠¥âáï, ª®£¤  ¢á¥ ª« ¢¨è¨ ¨§ ¡ãä¥à  áç¨â ­ë ä㭪樥© 2
* 3 = ­ ¦ â  ª­®¯ª , ®¯à¥¤¥«ñ­­ ï à ­¥¥ ä㭪樥© 8 (¨«¨ ª­®¯ª 
§ ªàëâ¨ï, ᮧ¤ ­­ ï ­¥ï¢­® ä㭪樥© 0; ª­®¯ª  ¬¨­¨¬¨§ æ¨¨
®¡à ¡ â뢠¥âáï á¨á⥬®© ¨ ® ­¥© á®®¡é¥­¨ï ­¥ ¯à¨å®¤¨â;
¯®áâ㯠¥â, ⮫쪮 ª®£¤  ®ª­®  ªâ¨¢­®; á¡à á뢠¥âáï, ª®£¤  ¢á¥
ª­®¯ª¨ ¨§ ¡ãä¥à  áç¨â ­ë ä㭪樥© 17)
* 4 = § à¥§¥à¢¨à®¢ ­® (¢ ⥪ã饩 ॠ«¨§ æ¨¨ ­¨ª®£¤  ­¥ ¯à¨å®¤¨â ¤ ¦¥
¯à¨ à §¬ áª¨à®¢ª¥ ä㭪樥© 40)
* 5 = § ¢¥à訫 áì ¯¥à¥à¨á®¢ª  ä®­  à ¡®ç¥£® á⮫ 
* 6 = ᮡë⨥ ®â ¬ëè¨ (çâ®-â® á«ã稫®áì - ­ ¦ â¨¥ ­  ª­®¯ªã ¬ëè¨
¨«¨ ¯¥à¥¬¥é¥­¨¥; á¡à á뢠¥âáï ¯à¨ ¯à®ç⥭¨¨)
* 7 = ¯à®¨§®è«® ᮡë⨥ IPC (ᬮâਠäã­ªæ¨î 60 - Inter Process
Communication; á¡à á뢠¥âáï ¯à¨ ¯à®ç⥭¨¨)
* 8 = ¯à®¨§®è«® á¥â¥¢®¥ ᮡë⨥ (á¡à á뢠¥âáï ¯à¨ ¯à®ç⥭¨¨;
ᬮâਠࠡ®âã á á¥âìî)
* 9 = ¯à®¨§®è«® ®â« ¤®ç­®¥ ᮡë⨥ (á¡à á뢠¥âáï ¯à¨ ¯à®ç⥭¨¨;
ᬮâਠ®â« ¤®ç­ãî ¯®¤á¨á⥬ã)
* 16..31 = ¯à®¨§®è«® ᮡë⨥ á ᮮ⢥âáâ¢ãî騬 IRQ
(16=IRQ0, 31=IRQ15) (á¡à á뢠¥âáï ¯à¨ áç¨â뢠­¨¨ ¢á¥å ¤ ­­ëå IRQ)
Очередное событие можно получить вызовом одной из функций 10
(ожидать события), 11 (проверить без ожидания), 23
(ожидать в течение заданного времени).
Эти функции возвращают только те события, которые входят в маску,
устанавливаемую функцией 40. По умолчанию это первые три, чего
вполне достаточно для многих приложений.
Коды событий:
* 1 = сообщение о перерисовке (сбрасывается при вызове функции 0)
* 2 = нажата клавиша на клавиатуре (поступает, только когда окно
активно) или нажата "горячая клавиша";
сбрасывается, когда все клавиши из буфера считаны функцией 2
* 3 = нажата кнопка, определённая ранее функцией 8 (или кнопка
закрытия, созданная неявно функцией 0; кнопка минимизации
обрабатывается системой и о ней сообщения не приходит;
поступает, только когда окно активно; сбрасывается, когда все
кнопки из буфера считаны функцией 17)
* 4 = зарезервировано (в текущей реализации никогда не приходит даже
при размаскировке функцией 40)
* 5 = завершилась перерисовка фона рабочего стола
* 6 = событие от мыши (что-то случилось - нажатие на кнопку мыши
или перемещение; сбрасывается при прочтении)
* 7 = произошло событие IPC (смотри функцию 60 - Inter Process
Communication; сбрасывается при прочтении)
* 8 = произошло сетевое событие (сбрасывается при прочтении;
смотри работу с сетью)
* 9 = произошло отладочное событие (сбрасывается при прочтении;
смотри отладочную подсистему)
* 16..31 = произошло событие с соответствующим IRQ
(16=IRQ0, 31=IRQ15) (сбрасывается при считывании всех данных IRQ)
 
======================================================================
==================== Š®¤ë ®è¨¡®ª ä ©«®¢®© á¨á⥬ë ====================
==================== Коды ошибок файловой системы ====================
======================================================================
* 0 = ãᯥ譮
* 1 = ­¥ ®¯à¥¤¥«¥­  ¡ §  ¨/¨«¨ à §¤¥« ¦ñá⪮£® ¤¨áª  (¯®¤äã­ªæ¨ï¬¨
7, 8 ä㭪樨 21)
* 2 = äã­ªæ¨ï ­¥ ¯®¤¤¥à¦¨¢ ¥âáï ¤«ï ¤ ­­®© ä ©«®¢®© á¨á⥬ë
* 3 = ­¥¨§¢¥áâ­ ï ä ©«®¢ ï á¨á⥬ 
* 4 = § à¥§¥à¢¨à®¢ ­®, ­¨ª®£¤  ­¥ ¢®§¢à é ¥âáï ¢ ⥪ã饩 ॠ«¨§ æ¨¨
* 5 = ä ©« ­¥ ­ ©¤¥­
* 6 = ä ©« § ª®­ç¨«áï
* 7 = 㪠§ â¥«ì ¢­¥ ¯ ¬ï⨠¯à¨«®¦¥­¨ï
* 8 = ¤¨áª § ¯®«­¥­
* 9 = â ¡«¨æ  FAT à §àã襭 
* 10 = ¤®áâ㯠§ ¯à¥éñ­
* 11 = ®è¨¡ª  ãáâனá⢠
à¨ § ¯ã᪥ ¯à®£à ¬¬ë ¢®§¬®¦­ë â ª¦¥ á«¥¤ãî騥 ª®¤ë ®è¨¡®ª:
* 30 = 0x1E = ­¥¤®áâ â®ç­® ¯ ¬ïâ¨
* 31 = 0x1F = ä ©« ­¥ ï¥âáï ¨á¯®«­¨¬ë¬
* 32 = 0x20 = ᫨誮¬ ¬­®£® ¯à®æ¥áᮢ
* 0 = успешно
* 1 = не определена база и/или раздел жёсткого диска (подфункциями
7, 8 функции 21)
* 2 = функция не поддерживается для данной файловой системы
* 3 = неизвестная файловая система
* 4 = зарезервировано, никогда не возвращается в текущей реализации
* 5 = файл не найден
* 6 = файл закончился
* 7 = указатель вне памяти приложения
* 8 = диск заполнен
* 9 = таблица FAT разрушена
* 10 = доступ запрещён
* 11 = ошибка устройства
При запуске программы возможны также следующие коды ошибок:
* 30 = 0x1E = недостаточно памяти
* 31 = 0x1F = файл не является исполнимым
* 32 = 0x20 = слишком много процессов
/kernel/branches/Kolibri-acpi/docs/sysfuncs.txt
2923,9 → 2923,9
* 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
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
* 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
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)
/kernel/branches/Kolibri-acpi/docs/usbapi.txt
0,0 → 1,183
When the kernel detects a connected USB device, it configures the device in
terms of USB protocol - SET_ADDRESS + SET_CONFIGURATION, the first
configuration is always selected. The kernel also reads device descriptor to
print some information, reads and parses configuration descriptor. For every
interface the kernel looks for class code of this interface and loads the
corresponding COFF driver. Currently the correspondence is hardcoded into
the kernel code and looks as follows: 3 = usbhid.obj, 8 = usbstor.obj,
9 is handled by the kernel itself, other = usbother.obj.
 
The driver must be standard driver in COFF format, exporting procedure
named "START" and a variable named "version". Loader calls "START" procedure
as stdcall with one parameter DRV_ENTRY = 1; if initialization is successful,
the "START" procedure is also called by shutdown code with one parameter
DRV_EXIT = -1.
 
The driver must register itself as a USB driver in "START" procedure.
This is done by call to exported function RegUSBDriver and passing the returned
value as result of "START" procedure.
 
void* __stdcall RegUSBDriver(
const char* name,
void* handler,
const USBFUNC* usbfunc
);
 
The parameter 'name' should match the name of driver, "usbhid" for usbhid.obj.
The parameter 'handler' is optional; if it is non-NULL, it should point to
the standard handler for IOCTL interface as in non-USB drivers.
The parameter 'usbfunc' is a pointer to the following structure:
 
struc USBFUNC
{
.strucsize dd ? ; size of the structure, including this field
.add_device dd ? ; pointer to AddDevice function in the driver
; required
.device_disconnect dd ? ; pointer to DeviceDisconnected function in the driver
; optional, may be NULL
; other functions may be added in the future
}
 
The driver should implement the function
 
void* __stdcall AddDevice(
void* pipe0,
void* configdescr,
void* interfacedescr
);
 
The parameter 'controlpipe' is a handle of the control pipe for endpoint zero
of the device. It can be used as the argument of USBControlTransferAsync.
The parameter 'configdescr' points to USB configuration descriptor
and all associated data, as returned by GET_DESCRIPTOR request.
The total length of all associated data is contained in the configuration
descriptor.
The parameter 'interfacedescr' points to USB interface descriptor corresponding
to the interface which is initializing. This is a pointer inside data
associated with the configuration descriptor.
Note that one device can implement many interfaces, so AddDevice may be
called several times with the same 'configdescr' and different 'interfacedescr'.
The returned value NULL means that the initialization has failed.
Any other value means that configuration was successful; the kernel does not
try to interpret the value. It can be, for example, pointer to the internal
data allocated with Kmalloc, or index in some internal table. Remember that
Kmalloc() is NOT stdcall, it destroys ebx.
 
The driver can implement the function
 
void __stdcall DeviceDisconnected(
void* devicedata
);
 
If this function is implemented, the kernel calls it when the device is
disconnected, passing the returned value of AddDevice as 'devicedata'.
 
The driver can use the following functions exported by the kernel.
 
void* __stdcall USBOpenPipe(
void* pipe0,
int endpoint,
int maxpacketsize,
int type,
int interval
);
 
The parameter 'pipe0' is a handle of the pipe for endpoint zero for
the device, as passed to AddDevice. It is used to identify the device.
The parameter 'endpoint' is endpoint number as defined by USB. Lower
4 bits form the number itself, bit 7 - highest bit of low byte -
is 0/1 for OUT/IN endpoints, other bits should be zero.
The parameter 'maxpacketsize' sets the maximum packet size for this pipe.
The parameter 'type' selects the type of the endpoint as defined by USB:
0 = control, 1 = isochronous (not supported yet), 2 = bulk, 3 = interrupt.
The parameter 'interval' is ignored for control and bulk endpoints.
For interrupt endpoints, it sets the polling interval in milliseconds.
The function returns a handle to the pipe or NULL on failure.
 
void* __stdcall USBNormalTransferAsync(
void* pipe,
void* buffer,
int size,
void* callback,
void* calldata,
int flags
);
void* __stdcall USBControlTransferAsync(
void* pipe,
void* config,
void* buffer,
int size,
void* callback,
void* calldata,
int flags
);
 
The first function inserts a bulk or interrupt transfer to the transfer queue
for given pipe. Type and direction of transfer are fixed for bulk and interrupt
endpoints and are set in USBOpenPipe. The second function inserts a control
transfer to the transfer queue for given pipe. Direction of a control transfer
is concluded from 'config' packet, bit 7 of byte 0 is set for IN transfers
and cleared for OUT transfers. These function return immediately; when data
are transferred, the callback function will be called.
 
The parameter 'pipe' is a handle returned by USBOpenPipe.
The parameter 'config' of USBControlTransferAsync points to 8-byte
configuration packet as defined by USB.
The parameter 'buffer' is a pointer to buffer. For IN transfers, it will be
filled with the data. For OUT transfers, it should contain data to be
transferred. It can be NULL for an empty transfer or if no additional data are
required for a control transfer.
The parameter 'size' is size of data to transfer. It can be 0 for an empty
transfer or if no additional data are required for a control transfer.
The parameter 'callback' is a pointer to a function which will be called
when the transfer will be done.
The parameter 'calldata' will be passed as is to the callback function.
For example, it can be NULL, it can be a pointer to device data or it can be
a pointer to data used to pass additional parameters between caller and
callback. The transfer-specific data can also be associated with 'buffer',
preceding (negative offsets from 'buffer') or following (offsets more than
or equal to 'size') the buffer itself.
The parameter 'flags' is the bitmask.
The bit 0 is ignored for OUT transfers, for IN transfers it controls whether
the device can transfer less data than 'size' bytes. If the bit is 0, a small
transfer is an error; if the bit is 1, a small transfer is OK.
All other bits are reserved and should be zero.
The returned value is NULL if an error occured and non-NULL if the transfer
was successfully queued. If an error will occur later, the callback function
will be notified.
 
void __stdcall CallbackFunction(
void* pipe,
int status,
void* buffer,
int length,
void* calldata
);
 
The parameters 'pipe', 'buffer', 'calldata' are the same as for the
corresponding USB*TransferAsync.
The parameter 'length' is the number of bytes transferred. For
control transfers, this includes 8 bytes from SETUP stage, so
0 means that SETUP stage failed and 'size'+8 means full transfer.
The parameter 'status' is nonzero if an error occured.
USB_STATUS_OK = 0 ; no error
USB_STATUS_CRC = 1 ; CRC error
USB_STATUS_BITSTUFF = 2 ; bit stuffing violation
USB_STATUS_TOGGLE = 3 ; data toggle mismatch
USB_STATUS_STALL = 4 ; device returned STALL
USB_STATUS_NORESPONSE = 5 ; device not responding
USB_STATUS_PIDCHECK = 6 ; invalid PID check bits
USB_STATUS_WRONGPID = 7 ; unexpected PID value
USB_STATUS_OVERRUN = 8 ; too many data from endpoint
USB_STATUS_UNDERRUN = 9 ; too few data from endpoint
USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer
; possible only for isochronous transfers
USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer
; possible only for isochronous transfers
USB_STATUS_DISCONNECTED = 16 ; device disconnected
 
If several transfers are queued for the same pipe, their callback functions
are called in the same order as they were queued.
When the device is disconnected, all callback functions are called
with USB_STATUS_DISCONNECTED. The call to DeviceDisconnected() occurs after
all callbacks.
/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm
174,68 → 174,68
 
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,27 → 257,27
; 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
284,14 → 284,14
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
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
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
jmp .EndMouseInterrupt
 
.Error:
; Ïðîèçîøåë ñáîé â ïîðÿäêå ïåðåäà÷è èíôîðìàöèè îò
; ìûøè, îáíóëèòü ñ÷åò÷èê áàéòîâ ïàêåòà äàííûõ
; Произошел сбой в порядке передачи информации от
; мыши, обнулить счетчик байтов пакета данных
 
mov [esi+COM_MOUSE_DATA.MouseByteNumber], 0
.EndMouseInterrupt:
340,9 → 340,9
align 4
 
struc COM_MOUSE_DATA {
; Íîìåð ïðèíèìàåìîãî îò ìûøè áàéòà
; Номер принимаемого от мыши байта
.MouseByteNumber db ?
; Òðåõáàéòîâàÿ ñòðóêòóðà äàííûõ, ïåðåäàâàåìàÿ ìûøüþ
; Трехбайтовая структура данных, передаваемая мышью
.FirstByte db ?
.SecondByte db ?
.ThirdByte db ?
/kernel/branches/Kolibri-acpi/drivers/emu10k1x.asm
19,7 → 19,7
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
/kernel/branches/Kolibri-acpi/drivers/ensoniq.asm
17,7 → 17,7
 
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
/kernel/branches/Kolibri-acpi/drivers/fm801.asm
17,7 → 17,7
 
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
/kernel/branches/Kolibri-acpi/drivers/imports.inc
97,6 → 97,14
GetDisplay,\
SetScreen,\
\
RegUSBDriver,\
USBOpenPipe,\
USBNormalTransferAsync,\
USBControlTransferAsync,\
\
DiskAdd,\
DiskMediaChanged,\
DiskDel
DiskDel,\
\
TimerHS,\
CancelTimerHS
/kernel/branches/Kolibri-acpi/drivers/intelac97.asm
21,7 → 21,7
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
/kernel/branches/Kolibri-acpi/drivers/sis.asm
21,7 → 21,7
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
/kernel/branches/Kolibri-acpi/drivers/vt823x.asm
19,7 → 19,7
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
/kernel/branches/Kolibri-acpi/encoding.inc
0,0 → 1,149
; fetch the UTF-8 character in string+offs to char
; common part for all encodings: translate pseudographics
; Pseudographics for the boot screen:
; 0x2500 -> 0xC4, 0x2502 -> 0xB3, 0x250C -> 0xDA, 0x2510 -> 0xBF,
; 0x2514 -> 0xC0, 0x2518 -> 0xD9, 0x252C -> 0xC2, 0x2534 -> 0xC1, 0x2551 -> 0xBA
macro fetch_utf8_char string, offs, char, graph
{ local first_byte, b
virtual at 0
db string
if offs >= $
char = -1
else
; fetch first byte
load first_byte byte from offs
if first_byte < 0x80
char = first_byte
offs = offs + 1
else if first_byte < 0xC0
.err Invalid UTF-8 string
else if first_byte < 0xE0
char = first_byte and 0x1F
load b byte from offs + 1
char = (char shl 6) + (b and 0x3F)
offs = offs + 2
else if first_byte < 0xF0
char = first_byte and 0xF
load b byte from offs + 1
char = (char shl 6) + (b and 0x3F)
load b byte from offs + 2
char = (char shl 6) + (b and 0x3F)
offs = offs + 3
else if first_byte < 0xF8
char = first_byte and 0x7
load b byte from offs + 1
char = (char shl 6) + (b and 0x3F)
load b byte from offs + 2
char = (char shl 6) + (b and 0x3F)
load b byte from offs + 3
char = (char shl 6) + (b and 0x3F)
offs = offs + 4
else
.err Invalid UTF-8 string
end if
end if
end virtual
if char = 0x2500
graph = 0xC4
else if char = 0x2502
graph = 0xB3
else if char = 0x250C
graph = 0xDA
else if char = 0x2510
graph = 0xBF
else if char = 0x2514
graph = 0xC0
else if char = 0x2518
graph = 0xD9
else if char = 0x252C
graph = 0xC2
else if char = 0x2534
graph = 0xC1
else if char = 0x2551
graph = 0xBA
else
graph = 0
end if
}
 
; Russian: use CP866.
; 0x00-0x7F - trivial map
; 0x410-0x43F -> 0x80-0xAF
; 0x440-0x44F -> 0xE0-0xEF
; 0x401 -> 0xF0, 0x451 -> 0xF1
macro cp866 [arg]
{ local offs, char, graph
offs = 0
while 1
fetch_utf8_char arg, offs, char, graph
if char = -1
break
end if
if graph
db graph
else if char < 0x80
db char
else if char = 0x401
db 0xF0
else if char = 0x451
db 0xF1
else if (char < 0x410) | (char > 0x44F)
.err Failed to convert to CP866
else if char < 0x440
db char - 0x410 + 0x80
else
db char - 0x440 + 0xE0
end if
end while
}
 
; Latin-1 encoding
; 0x00-0xFF - trivial map
macro latin1 [arg]
{ local offs, char, graph
offs = 0
while 1
fetch_utf8_char arg, offs, char, graph
if char = -1
break
end if
if graph
db graph
else if char < 0x100
db char
else
.err Failed to convert to Latin-1
end if
end while
}
 
; CP850 encoding
macro cp850 [arg]
{ local offs, char, graph
offs = 0
while 1
fetch_utf8_char arg, offs, char, graph
if char = -1
break
end if
if graph
db graph
else if char < 0x80
db char
else if char = 0xBF
db 0xA8
else if char = 0xE1
db 0xA0
else if char = 0xE9
db 0x82
else if char = 0xED
db 0xA1
else if char = 0xF3
db 0xA2
else if char = 0xFA
db 0xA3
else
err Failed to convert to CP850
end if
end while
}
/kernel/branches/Kolibri-acpi/fs/fat12.inc
119,9 → 119,9
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
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
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
 
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
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
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:
/kernel/branches/Kolibri-acpi/fs/fs.inc
441,7 → 441,7
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
;* output eax - number
;*******************************************
StringToNumber:
; ÏÅÐÅÂÎÄ ÑÒÐÎÊÎÂÎÃÎ ×ÈÑËÀ  ×ÈÑËÎÂÎÉ ÂÈÄ
; Âõîä:
; EDI - àäðåñ ñòðîêè ñ ÷èñëîì. Êîíåö ÷èñëà îòìå÷åí êîäîì 0Dh
; Âûõîä:
; CF - èíäèêàòîð îøèáîê:
; 0 - îøèáîê íåò;
; 1 - îøèáêà
; Åñëè CF=0, òî AX - ÷èñëî.
; ПЕРЕВОД СТРОКОВОГО ЧИСЛА В ЧИСЛОВОЙ ВИД
; Вход:
; EDI - адрес строки с числом. Конец числа отмечен кодом 0Dh
; Выход:
; CF - индикатор ошибок:
; 0 - ошибок нет;
; 1 - ошибка
; Если CF=0, то AX - число.
 
push bx
push cx
/kernel/branches/Kolibri-acpi/fs/iso9660.inc
137,7 → 137,7
.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
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
.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
.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
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
.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
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
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
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
inc eax
mov [edi+4], eax
@@:
; ïîëó÷àåì ðàçìåð ôàéëà â áàéòàõ
; получаем размер файла в байтах
xor eax, eax
mov [edi+32+4], eax
mov eax, [ebp-23]
503,7 → 503,7
; 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
510,7 → 510,7
jne .access_denied
 
call prevent_medium_removal
; òåñòîâîå ÷òåíèå
; тестовое чтение
mov [CDSectorAddress], dword 16
mov [CDDataBuf_pointer], CDDataBuf
call ReadCDWRetr;_1
517,7 → 517,7
cmp [DevErrorCode], 0
jne .access_denied
 
; âû÷èñëåíèå ïîñëåäíåé ñåññèè
; вычисление последней сессии
call WaitUnitReady
cmp [DevErrorCode], 0
jne .access_denied
539,37 → 539,37
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
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
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
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
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
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
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
/kernel/branches/Kolibri-acpi/fs/part_set.inc
77,10 → 77,10
.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
;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
 
; 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
/kernel/branches/Kolibri-acpi/gui/event.inc
21,8 → 21,8
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
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
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
.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
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
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,7 → 142,7
push edi
;--------------------------------------
align 4
.small: ; êðèâî êàê-òî...
.small: ; криво как-то...
pop edi
pushfd
cli
149,15 → 149,15
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)
165,7 → 165,6
; esi - event data (=0 => skip)
;scratched: ebx,ecx,esi,edi
call NotDummyTest ; not returned for fail !!!
mov [check_idle_semaphore], 5
or esi, esi
jz @f
lea edi, [ebx+EVENT.code]
206,8 → 205,8
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)
252,24 → 251,24
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
291,12 → 290,12
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)
327,16 → 326,16
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
362,12 → 361,12
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 !!!
;--------------------------------------
386,17 → 385,17
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
;--------------------------------------
407,15 → 406,15
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
459,7 → 458,7
;--------------------------------------
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:
471,9 → 470,9
;-----------------------------------------------------------------------------
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
495,15 → 494,15
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]
511,13 → 510,13
and ecx, 0x7FFFFFFF
;--------------------------------------
align 4
.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)
.loop: ; пока не исчерпаем все биты маски
bsr eax, ecx ; находим ненулевой бит маски (31 -> 0)
jz .no_events ; исчерпали все биты маски, но ничего не нашли ???
btr ecx, eax ; сбрасываем проверяемый бит маски
; переходим на обработчик этого (eax) бита
cmp eax, 10
jae .loop ; eax=[10..31], ignored (event 10...32)
 
cmp eax, 3
je .loop ; eax=3, ignored (event 4)
528,7 → 527,7
cmp eax, 5
je .mouse_check ; eax=5, retvals=eax+1 (event 6)
 
ja .FlagAutoReset ; eax=[6..8], retvals=eax+1 (event 7...9)
ja .FlagAutoReset ; eax=[6..9], retvals=eax+1 (event 7...10)
 
cmp eax, 1
jae .BtKy ; eax=[1,2], retvals=eax+1 (event 2,3)
590,6 → 589,7
cmp edx, 0xFFFF ;-ID for Minimize-Button of Form
jne .result
mov [window_minimize], 1
call wakeup_osloop
dec byte[BTN_COUNT]
jmp .loop
;--------------------------------------
/kernel/branches/Kolibri-acpi/gui/mouse.inc
66,13 → 66,9
; NOTE: this code wouldn't be necessary if we knew window did
; already redraw itself after call above
or eax, eax
jz @f
jnz .exit
 
and [mouse.state.buttons], 0
jmp .exit
 
; is there any system button under cursor?
@@:
call mouse._.find_sys_button_under_cursor
or eax, eax
jz .check_buttons_released
/kernel/branches/Kolibri-acpi/gui/window.inc
992,7 → 992,6
jz .do_not_draw
 
; yes it is, activate and update screen buffer
mov byte[MOUSE_DOWN], 1
call window._.window_activate
 
pushad
1025,12 → 1024,9
; no it's not, just activate the window
call window._.window_activate
xor eax, eax
mov byte[MOUSE_BACKGROUND], al
mov byte[DONT_DRAW_MOUSE], al
;--------------------------------------
align 4
.exit:
mov byte[MOUSE_DOWN], 0
inc eax
ret
;------------------------------------------------------------------------------
1183,7 → 1179,6
add edx, ebx
call ebp
inc [_display.mask_seqno]
mov byte[MOUSE_BACKGROUND], 0
;--------------------------------------
align 4
.exit:
1629,6 → 1624,7
; Otherwise the user can see cursor specified by f.37.5 from another window.
; He will be really unhappy! He is terrible in rage - usually he throws stones!
mov [redrawmouse_unconditional], 1
call wakeup_osloop
; NOTE: commented out since doesn't provide necessary functionality
; anyway, to be reworked
; mov eax, [timer_ticks] ; [0xfdf0]
/kernel/branches/Kolibri-acpi/hid/keyboard.inc
339,6 → 339,7
jne .noctrlaltdel
mov [ctrl_alt_del], 1
call wakeup_osloop
.noctrlaltdel:
test dl, VKEY_CONTROL ; ctrl on ?
jz @f
490,7 → 491,6
mov [KEY_COUNT], al
mov [KEY_COUNT+eax], bl
.exit.irq1:
mov [check_idle_semaphore], 5
ret
;---------------------------------------------------------------------
set_lights:
526,9 → 526,14
ret 8
 
;// mike.dld ]
check_lights_state:
proc check_lights_state_has_work?
mov al, [kb_lights]
cmp al, [old_kb_lights]
ret
endp
 
check_lights_state:
call check_lights_state_has_work?
jz .nothing
mov [old_kb_lights], al
call set_lights
/kernel/branches/Kolibri-acpi/hid/mousedrv.inc
494,9 → 494,9
;--------------------------------------
align 4
@@M1:
cmp ax, [Screen_Max_X];ScreenLength
cmp ax, word [Screen_Max_X];ScreenLength
jl @@M2
mov ax, [Screen_Max_X];ScreenLength-1
mov ax, word [Screen_Max_X];ScreenLength-1
;--------------------------------------
align 4
@@M2:
514,9 → 514,9
;--------------------------------------
align 4
@@M3:
cmp ax, [Screen_Max_Y];ScreenHeigth
cmp ax, word [Screen_Max_Y];ScreenHeigth
jl @@M4
mov ax, [Screen_Max_Y];ScreenHeigth-1
mov ax, word [Screen_Max_Y];ScreenHeigth-1
;--------------------------------------
align 4
@@M4:
531,6 → 531,7
mov [mouse_active], 1
mov eax, [timer_ticks]
mov [mouse_timer_ticks], eax
call wakeup_osloop
ret
endp
;-----------------------------------------------------------------------------
/kernel/branches/Kolibri-acpi/init.inc
310,7 → 310,7
test al, al
jnz .PCI_BIOS32_not_found
 
; çäåñü ñîçäàþòñÿ äèñêðèïòîðû äëÿ PCI BIOS
; здесь создаются дискрипторы для PCI BIOS
 
add ebx, OS_BASE
dec ecx
334,7 → 334,7
mov [(pci_bios_entry-OS_BASE)], edx
; jmp .end
.PCI_BIOS32_not_found:
; çäåñü äîëæíà çàïîëíÿòñÿ pci_emu_dat
; здесь должна заполнятся pci_emu_dat
.BIOS32_not_found:
.end:
ret
/kernel/branches/Kolibri-acpi/kernel.asm
77,11 → 77,14
USE_COM_IRQ equ 1 ; make irq 3 and irq 4 available for PCI devices
 
; Enabling the next line will enable serial output console
;debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used
debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used
; The following constant, if nonzero, duplicates debug output to the screen.
debug_direct_print equ 0
 
include "proc32.inc"
include "kglobals.inc"
include "lang.inc"
include "encoding.inc"
 
include "const.inc"
max_processes equ 255
160,6 → 163,35
include "bus/pci/pci16.inc"
include "detect/biosdisk.inc"
 
FDD_BUFF equ (OS_BASE+0x000D000)
 
sys_pgdir equ (OS_BASE+0x006F000)
 
VGABasePtr equ (OS_BASE+0x00A0000)
 
RAMDISK equ (OS_BASE+0x0100000)
 
CLEAN_ZONE equ 0x284000
IDE_DMA equ 0x284000
 
BOOT_VAR equ (OS_BASE+0x02E0000)
 
TASK_COUNT equ (CURRENT_TASK+0x04)
TASK_BASE equ (CURRENT_TASK+0x10)
TASK_DATA equ (CURRENT_TASK+0x20)
TASK_EVENT equ (CURRENT_TASK+0x20)
 
BPSLine_calc_area equ (OS_BASE+0x02C4000)
 
d_width_calc_area equ (OS_BASE+0x02CA000)
 
stack_data_start equ (OS_BASE+0x02F0000)
eth_data_start equ (OS_BASE+0x02F0000)
stack_data equ (OS_BASE+0x02F4000)
stack_data_end equ (OS_BASE+0x030ffff)
resendQ equ (OS_BASE+0x0310000)
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; SWITCH TO 32 BIT PROTECTED MODE ;;
346,6 → 378,9
mov ecx, unpack_mutex
call mutex_init
 
mov ecx, application_table_mutex
call mutex_init
 
; SAVE REAL MODE VARIABLES
mov ax, [BOOT_VAR + BOOT_IDE_BASE_ADDR]
mov [IDEContrRegsBaseAddr], ax
384,8 → 419,6
mov al, [BOOT_VAR+BOOT_DMA] ; DMA access
mov [allow_dma_access], al
movzx eax, byte [BOOT_VAR+BOOT_BPP] ; bpp
mov [ScreenBPP], al
 
mov [_display.bpp], eax
mov [_display.vrefresh], 60
 
401,17 → 434,15
dec eax
mov [Screen_Max_Y], eax
mov [screen_workarea.bottom], eax
movzx eax, word [BOOT_VAR+BOOT_VESA_MODE]; screen mode
mov [SCR_MODE], eax
; mov eax, [BOOT_VAR+0x9014] ; Vesa 1.2 bnk sw add
; mov [BANK_SWITCH], eax
mov [BytesPerScanLine], word 640*4 ; Bytes PerScanLine
cmp [SCR_MODE], word 0x13 ; 320x200
mov ax, word [BOOT_VAR+BOOT_VESA_MODE] ; screen mode
mov [SCR_MODE], ax
mov [BytesPerScanLine], 640*4 ; Bytes PerScanLine
cmp [SCR_MODE], 0x13 ; 320x200
je @f
cmp [SCR_MODE], word 0x12 ; VGA 640x480
cmp [SCR_MODE], 0x12 ; VGA 640x480
je @f
movzx eax, word[BOOT_VAR+BOOT_PITCH] ; for other modes
mov [BytesPerScanLine], ax
mov [BytesPerScanLine], eax
mov [_display.pitch], eax
@@:
mov eax, [_display.width]
444,7 → 475,7
setvesa20:
mov [PUTPIXEL], dword Vesa20_putpixel24 ; Vesa 2.0
mov [GETPIXEL], dword Vesa20_getpixel24
cmp [ScreenBPP], byte 24
cmp byte [_display.bpp], 24
jz v20ga24
v20ga32:
mov [PUTPIXEL], dword Vesa20_putpixel32
491,9 → 522,9
 
; !!!! 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
507,12 → 538,8
stdcall alloc_page
stdcall map_page, tss-0xF80, eax, PG_SW
stdcall alloc_page
inc eax
mov [SLOT_BASE+256+APPDATA.io_map], eax
stdcall map_page, tss+0x80, eax, PG_SW
stdcall alloc_page
inc eax
mov dword [SLOT_BASE+256+APPDATA.io_map+4], eax
stdcall map_page, tss+0x1080, eax, PG_SW
 
; LOAD IDT
521,7 → 548,7
;lidt [idtreg]
 
call init_kernel_heap
stdcall kernel_alloc, RING0_STACK_SIZE+512
stdcall kernel_alloc, (RING0_STACK_SIZE+512) * 2
mov [os_stack_seg], eax
 
lea esp, [eax+RING0_STACK_SIZE]
601,10 → 628,6
 
xor eax, eax
inc eax
mov [CURRENT_TASK], eax ;dword 1
mov [TASK_COUNT], eax ;dword 1
mov [TASK_BASE], dword TASK_DATA
mov [current_slot], SLOT_BASE+256
 
; set background
 
619,56 → 642,30
mov esi, boot_setostask
call boot_log
 
xor eax, eax
mov dword [SLOT_BASE+APPDATA.fpu_state], fpu_data
mov dword [SLOT_BASE+APPDATA.exc_handler], eax
mov dword [SLOT_BASE+APPDATA.except_mask], eax
mov edx, SLOT_BASE+256
mov ebx, [os_stack_seg]
add ebx, 0x2000
call setup_os_slot
mov dword [edx], 'IDLE'
sub [edx+APPDATA.saved_esp], 4
mov eax, [edx+APPDATA.saved_esp]
mov dword [eax], idle_thread
mov ecx, IDLE_PRIORITY
call scheduler_add_thread
 
; name for OS/IDLE process
mov edx, SLOT_BASE+256*2
mov ebx, [os_stack_seg]
call setup_os_slot
mov dword [edx], 'OS'
xor ecx, ecx
call scheduler_add_thread
 
mov dword [SLOT_BASE+256+APPDATA.app_name], dword 'OS/I'
mov dword [SLOT_BASE+256+APPDATA.app_name+4], dword 'DLE '
mov edi, [os_stack_seg]
mov dword [SLOT_BASE+256+APPDATA.pl0_stack], edi
add edi, 0x2000-512
mov dword [SLOT_BASE+256+APPDATA.fpu_state], edi
mov dword [SLOT_BASE+256+APPDATA.saved_esp0], edi; just for case
mov dword [SLOT_BASE+256+APPDATA.terminate_protection], 80000001h
mov dword [CURRENT_TASK], 2
mov dword [TASK_COUNT], 2
mov dword [current_slot], SLOT_BASE + 256*2
mov dword [TASK_BASE], CURRENT_TASK + 32*2
 
mov esi, fpu_data
mov ecx, 512/4
cld
rep movsd
 
mov dword [SLOT_BASE+256+APPDATA.exc_handler], eax
mov dword [SLOT_BASE+256+APPDATA.except_mask], eax
 
mov ebx, SLOT_BASE+256+APP_OBJ_OFFSET
mov dword [SLOT_BASE+256+APPDATA.fd_obj], ebx
mov dword [SLOT_BASE+256+APPDATA.bk_obj], ebx
 
mov dword [SLOT_BASE+256+APPDATA.cur_dir], sysdir_path
mov dword [SLOT_BASE+256+APPDATA.tls_base], eax
 
; task list
mov dword [TASK_DATA+TASKDATA.mem_start], eax; process base address
inc eax
mov dword [CURRENT_TASK], eax
mov dword [TASK_COUNT], eax
mov [current_slot], SLOT_BASE+256
mov [TASK_BASE], dword TASK_DATA
mov byte[TASK_DATA+TASKDATA.wnd_number], al ; on screen number
mov dword [TASK_DATA+TASKDATA.pid], eax ; process id number
 
mov [SLOT_BASE + 256 + APPDATA.dir_table], sys_pgdir - OS_BASE
 
stdcall kernel_alloc, 0x10000/8
mov edi, eax
mov [network_free_ports], eax
or eax, -1
mov ecx, 0x10000/32
rep stosd
 
; REDIRECT ALL IRQ'S TO INT'S 0x20-0x2f
mov esi, boot_initirq
call boot_log
801,6 → 798,10
mov [pci_access_enabled], 1
call pci_enum
 
stdcall load_driver, szVidintel
 
call usb_init
 
; SET PRELIMINARY WINDOW STACK AND POSITIONS
 
mov esi, boot_windefs
822,20 → 823,21
 
call init_display
mov eax, [def_cursor]
mov [SLOT_BASE+APPDATA.cursor], eax
mov [SLOT_BASE+APPDATA.cursor+256], eax
mov [SLOT_BASE+APPDATA.cursor+256*2], eax
 
; READ TSC / SECOND
; PRINT CPU FREQUENCY
 
mov esi, boot_tsc
mov esi, boot_cpufreq
call boot_log
cli
rdtsc ;call _rdtsc
 
cli ;FIXME check IF
rdtsc
mov ecx, eax
mov esi, 250 ; wait 1/4 a second
call delay_ms
rdtsc ;call _rdtsc
sti
rdtsc
 
sub eax, ecx
xor edx, edx
shld edx, eax, 2
842,19 → 844,19
shl eax, 2
mov dword [cpu_freq], eax
mov dword [cpu_freq+4], edx
; PRINT CPU FREQUENCY
mov esi, boot_cpufreq
call boot_log
mov ebx, 1000000
div ebx
mov ebx, eax
 
mov ebx, edx
movzx ecx, word [boot_y]
if lang eq ru
add ecx, (10+19*6) shl 16 - 10 ; 'Determining amount of memory'
add ecx, (10+19*6) shl 16 - 10
else if lang eq sp
add ecx, (10+25*6) shl 16 - 10 ; 'Determining amount of memory'
add ecx, (10+25*6) shl 16 - 10
else
add ecx, (10+17*6) shl 16 - 10 ; 'Determining amount of memory'
add ecx, (10+17*6) shl 16 - 10
end if
 
mov edx, 0xFFFFFF
xor edi, edi
mov eax, 0x00040000
902,12 → 904,6
stdcall map_page, tss._io_map_1, \
[SLOT_BASE+256+APPDATA.io_map+4], PG_MAP
 
mov ax, [OS_BASE+0x10000+bx_from_load]
cmp ax, 'r1'; if not rused ram disk - load network configuration from files {SPraid.simba}
je no_st_network
call set_network_conf
no_st_network:
 
; LOAD FIRST APPLICATION
cli
 
940,10 → 936,6
 
cli
 
;mov [TASK_COUNT],dword 2
push 1
pop dword [CURRENT_TASK] ; set OS task fisrt
 
; SET KEYBOARD PARAMETERS
mov al, 0xf6 ; reset keyboard, scan enabled
call kb_write
1044,8 → 1036,6
@@:
DEBUGF 1, "K : %d CPU detected\n", eax
 
; call print_mem
 
; START MULTITASKING
 
; A 'All set - press ESC to start' messages if need
1063,7 → 1053,7
mov [timer_ticks_enable], 1 ; for cd driver
 
sti
call change_task
; call change_task
 
jmp osloop
 
1092,6 → 1082,52
 
ret
 
; in: edx -> APPDATA for OS/IDLE slot
; in: ebx = stack base
proc setup_os_slot
xor eax, eax
mov ecx, 256/4
mov edi, edx
rep stosd
 
mov eax, tss+0x80
call get_pg_addr
inc eax
mov [edx+APPDATA.io_map], eax
mov eax, tss+0x1080
call get_pg_addr
inc eax
mov [edx+APPDATA.io_map+4], eax
 
mov dword [edx+APPDATA.pl0_stack], ebx
lea edi, [ebx+0x2000-512]
mov dword [edx+APPDATA.fpu_state], edi
mov dword [edx+APPDATA.saved_esp0], edi
mov dword [edx+APPDATA.saved_esp], edi
mov dword [edx+APPDATA.terminate_protection], 1 ; make unkillable
 
mov esi, fpu_data
mov ecx, 512/4
cld
rep movsd
 
lea eax, [edx+APP_OBJ_OFFSET]
mov dword [edx+APPDATA.fd_obj], eax
mov dword [edx+APPDATA.bk_obj], eax
 
mov dword [edx+APPDATA.cur_dir], sysdir_path
 
mov [edx + APPDATA.dir_table], sys_pgdir - OS_BASE
 
mov eax, edx
shr eax, 3
add eax, CURRENT_TASK - (SLOT_BASE shr 3)
mov [eax+TASKDATA.wnd_number], dh
mov byte [eax+TASKDATA.pid], dh
 
ret
endp
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; MAIN OS LOOP START ;
1099,6 → 1135,13
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 32
osloop:
mov edx, osloop_has_work?
xor ecx, ecx
call Wait_events
xor eax, eax
xchg eax, [osloop_nonperiodic_work]
test eax, eax
jz .no_periodic
; call [draw_pointer]
call __sys_draw_pointer
call window_check_events
1105,8 → 1148,8
call mouse_check_events
call checkmisc
call checkVga_N13
.no_periodic:
call stack_handler
call checkidle
call check_fdd_motor_status
call check_ATAPI_device_event
call check_lights_state
1117,39 → 1160,47
; MAIN OS LOOP END ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
checkidle:
pushad
call change_task
jmp idle_loop_entry
idle_loop:
cmp eax, [idlemem] ; eax == [timer_ticks]
jne idle_exit
rdtsc ;call _rdtsc
mov ecx, eax
hlt
rdtsc ;call _rdtsc
sub eax, ecx
add [idleuse], eax
idle_loop_entry:
mov eax, [timer_ticks]; eax = [timer_ticks]
cmp [check_idle_semaphore], 0
je idle_loop
dec [check_idle_semaphore]
idle_exit:
mov [idlemem], eax ; eax == [timer_ticks]
popad
proc osloop_has_work?
cmp [osloop_nonperiodic_work], 0
jnz .yes
call stack_handler_has_work?
jnz .yes
call check_fdd_motor_status_has_work?
jnz .yes
call check_ATAPI_device_event_has_work?
jnz .yes
call check_lights_state_has_work?
jnz .yes
call check_timers_has_work?
jnz .yes
.no:
xor eax, eax
ret
.yes:
xor eax, eax
inc eax
ret
endp
 
proc wakeup_osloop
mov [osloop_nonperiodic_work], 1
ret
endp
 
uglobal
idlemem dd 0x0
idleuse dd 0x0
idleusesec dd 0x0
check_idle_semaphore dd 0x0
align 4
osloop_nonperiodic_work dd ?
endg
 
align 4
idle_thread:
sti
idle_loop:
hlt
jmp idle_loop
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; INCLUDED SYSTEM FILES ;
1195,7 → 1246,7
 
 
iglobal
process_number dd 0x1
process_number dd 0x2
endg
 
set_variables:
1212,17 → 1263,17
mov ax, [BOOT_VAR+BOOT_X_RES]
shr ax, 1
mov [MOUSE_X], eax
call wakeup_osloop
 
xor eax, eax
mov [BTN_ADDR], dword BUTTON_INFO ; address of button list
 
mov byte [MOUSE_BUFF_COUNT], al ; mouse buffer
mov byte [KEY_COUNT], al ; keyboard buffer
mov byte [BTN_COUNT], al ; button buffer
; mov [MOUSE_X],dword 100*65536+100 ; mouse x/y
 
;!! IP 04.02.2005:
mov byte [DONT_SWITCH], al; change task if possible
; mov byte [DONT_SWITCH], al; change task if possible
pop eax
ret
 
1992,6 → 2043,13
popa
@@:
;--------------------------------------
; kill all sockets this process owns
pusha
mov edx, [TASK_BASE]
mov edx, [edx+TASKDATA.pid]
call SOCKET_process_end
popa
;--------------------------------------
mov ecx, [current_slot]
mov eax, [ecx+APPDATA.tls_base]
test eax, eax
2002,6 → 2060,7
 
mov eax, [TASK_BASE]
mov [eax+TASKDATA.state], 3; terminate this program
call wakeup_osloop
 
waitterm: ; wait here for termination
mov ebx, 100
2035,6 → 2094,7
mov [current_cursor], esi
@@:
mov [redrawmouse_unconditional], 1
call wakeup_osloop
popfd
ret
;------------------------------------------------------------------------------
2087,6 → 2147,7
mov eax, [TASK_COUNT]
mov [SYS_SHUTDOWN], al
mov [shutdown_processes], eax
call wakeup_osloop
and dword [esp+32], 0
exit_for_anyone:
ret
2115,6 → 2176,12
test eax, eax
jz noprocessterminate
;--------------------------------------
; terminate all network sockets it used
pusha
mov eax, edx
call SOCKET_process_end
popa
;--------------------------------------
cmp [_display.select_cursor], 0
je .restore_end
; restore default cursor before killing
2132,11 → 2199,12
;--------------------------------------
;call MEM_Heap_Lock ;guarantee that process isn't working with heap
mov [ecx], byte 3; clear possible i40's
call wakeup_osloop
;call MEM_Heap_UnLock
 
cmp edx, [application_table_status]; clear app table stat
cmp edx, [application_table_owner]; clear app table stat
jne noatsc
and [application_table_status], 0
call unlock_application_table
noatsc:
noprocessterminate:
add esp, 4
2145,14 → 2213,7
sysfn_terminate2:
;lock application_table_status mutex
.table_status:
cli
cmp [application_table_status], 0
je .stf
sti
call change_task
jmp .table_status
.stf:
call set_application_table_status
call lock_application_table
mov eax, ecx
call pid_to_slot
test eax, eax
2160,12 → 2221,12
mov ecx, eax
cli
call sysfn_terminate
and [application_table_status], 0
call unlock_application_table
sti
and dword [esp+32], 0
ret
.not_found:
mov [application_table_status], 0
call unlock_application_table
or dword [esp+32], -1
ret
;------------------------------------------------------------------------------
2186,11 → 2247,6
lea esi, [WIN_POS + esi * 2]
call window._.window_deactivate
 
xor eax, eax
mov byte[MOUSE_BACKGROUND], al
mov byte[DONT_DRAW_MOUSE], al
mov byte[MOUSE_DOWN], 0
 
call syscall_display_settings._.calculate_whole_screen
call syscall_display_settings._.redraw_whole_screen
.nowindowdeactivate:
2214,6 → 2270,7
@@:
;-------------------------------------
mov [window_minimize], 2; restore window if minimized
call wakeup_osloop
 
movzx esi, word [WIN_STACK + ecx*2]
cmp esi, [TASK_COUNT]
2229,7 → 2286,7
ret
;------------------------------------------------------------------------------
sysfn_getidletime: ; 18.4 = GET IDLETIME
mov eax, [idleusesec]
mov eax, [CURRENT_TASK+32+TASKDATA.cpu_usage]
mov [esp+32], eax
ret
;------------------------------------------------------------------------------
2271,6 → 2328,7
;------------------------------------------------------------------------------
sysfn_minimize: ; 18.10 = minimize window
mov [window_minimize], 1
call wakeup_osloop
ret
;------------------------------------------------------------------------------
align 4
2333,6 → 2391,7
mov eax, [Screen_Max_Y]
shr eax, 1
mov [MOUSE_Y], ax
call wakeup_osloop
; ret
;* mouse centered - end code- Mario79
xor eax, eax
2377,6 → 2436,7
cmp dx, word[Screen_Max_X]
ja .end
mov [MOUSE_X], edx
call wakeup_osloop
ret
.set_mouse_button:
; cmp ecx,5 ; set mouse button features
2384,6 → 2444,7
jnz .end
mov [BTN_DOWN], dl
mov [mouse_active], 1
call wakeup_osloop
.end:
ret
;------------------------------------------------------------------------------
2799,9 → 2860,12
cmp ebx, 8
jnz nosb8
 
mov eax, [BG_Rect_X_left_right]
mov ecx, [current_slot]
xor eax, eax
xchg eax, [ecx+APPDATA.draw_bgr_x]
mov [esp + 32], eax ; eax = [left]*65536 + [right]
mov eax, [BG_Rect_Y_top_bottom]
xor eax, eax
xchg eax, [ecx+APPDATA.draw_bgr_y]
mov [esp + 20], eax ; ebx = [top]*65536 + [bottom]
ret
;------------------------------------------------------------------------------
2843,6 → 2907,7
mov [draw_data+32 + RECT.bottom], edx
 
inc byte[REDRAW_BACKGROUND]
call wakeup_osloop
;--------------------------------------
align 4
.exit:
2869,6 → 2934,7
mov [draw_data+32 + RECT.bottom], ebx
pop ebx eax
inc byte[REDRAW_BACKGROUND]
call wakeup_osloop
ret
;------------------------------------------------------------------------------
align 4
2878,7 → 2944,7
jnz nogb1
mov eax, [BgrDataWidth]
shl eax, 16
mov ax, [BgrDataHeight]
mov ax, word [BgrDataHeight]
mov [esp+32], eax
ret
;------------------------------------------------------------------------------
3294,8 → 3360,8
;now counter in ecx
;(edx:eax) esi:edi => edx:esi
; Fast Call MSR can't be destroy
; ® MSR_AMD_EFER ¬®¦­® ¨§¬¥­ïâì, â.ª. ¢ í⮬ ॣ¨áâॠ«¨è
; ¢ª«îç îâáï/¢ëª«îç îâáï à áè¨à¥­­ë¥ ¢®§¬®¦­®áâ¨
; Но MSR_AMD_EFER можно изменять, т.к. в этом регистре лиш
; включаются/выключаются расширенные возможности
cmp edx, MSR_SYSENTER_CS
je @f
cmp edx, MSR_SYSENTER_ESP
3451,8 → 3517,7
jz nobackgr
;--------------------------------------
align 4
@@:
push eax
backgr:
mov eax, [draw_data+32 + RECT.left]
shl eax, 16
add eax, [draw_data+32 + RECT.right]
3462,7 → 3527,6
shl eax, 16
add eax, [draw_data+32 + RECT.bottom]
mov [BG_Rect_Y_top_bottom], eax ; [top]*65536 + [bottom]
pop eax
 
call drawbackground
; DEBUGF 1, "K : drawbackground\n"
3476,20 → 3540,46
align 4
set_bgr_event:
add edi, 256
mov eax, [BG_Rect_X_left_right]
mov edx, [BG_Rect_Y_top_bottom]
cmp [edi+SLOT_BASE+APPDATA.draw_bgr_x], 0
jz .set
.join:
cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_x+2], ax
jbe @f
mov word [edi+SLOT_BASE+APPDATA.draw_bgr_x+2], ax
@@:
shr eax, 16
cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_x], ax
jae @f
mov word [edi+SLOT_BASE+APPDATA.draw_bgr_x], ax
@@:
cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_y+2], dx
jbe @f
mov word [edi+SLOT_BASE+APPDATA.draw_bgr_y+2], dx
@@:
shr edx, 16
cmp word [edi+SLOT_BASE+APPDATA.draw_bgr_y], dx
jae @f
mov word [edi+SLOT_BASE+APPDATA.draw_bgr_y], dx
@@:
jmp .common
.set:
mov [edi+SLOT_BASE+APPDATA.draw_bgr_x], eax
mov [edi+SLOT_BASE+APPDATA.draw_bgr_y], edx
.common:
or [edi+SLOT_BASE+APPDATA.event_mask], 10000b ; set event 5
loop set_bgr_event
pop edi ecx
; call change_task - because the application must have time to call f.15.8
call change_task
;--------- set event 5 stop -----------
dec byte[REDRAW_BACKGROUND] ; got new update request?
jnz @b
jnz backgr
 
xor eax, eax
mov [draw_data+32 + RECT.left], eax
mov [draw_data+32 + RECT.top], eax
mov [draw_data+32 + RECT.right], eax
mov [draw_data+32 + RECT.bottom], eax
mov [MOUSE_BACKGROUND], byte 0
;--------------------------------------
align 4
nobackgr:
3527,6 → 3617,7
@@:
add edx, 0x20
loop markz
call wakeup_osloop
;--------------------------------------
align 4
@@:
3678,6 → 3769,7
align 4
@@:
add byte[REDRAW_BACKGROUND], dl
call wakeup_osloop
jmp newdw8
;--------------------------------------
align 4
3699,6 → 3791,7
cmp dword [esp], 1
jne nobgrd
inc byte[REDRAW_BACKGROUND]
call wakeup_osloop
;--------------------------------------
align 4
newdw8:
4670,10 → 4763,39
end if
 
mov [msg_board_data+ecx], bl
if debug_direct_print
pusha
iglobal
msg_board_pos dd 234*65536+10
endg
lea edx, [msg_board_data+ecx]
mov ecx, 0x40FFFFFF
mov ebx, [msg_board_pos]
mov edi, 1
mov esi, 1
call dtext
popa
add word [msg_board_pos+2], 6
cmp bl, 10
jnz @f
mov word [msg_board_pos+2], 234
add word [msg_board_pos], 10
mov ax, [Screen_Max_Y]
cmp word [msg_board_pos], ax
jbe @f
mov word [msg_board_pos], 10
@@:
end if
if 0
pusha
mov al, bl
mov edx, 402h
out dx, al
popa
end if
inc ecx
and ecx, msg_board_data_size - 1
mov [msg_board_count], ecx
mov [check_idle_semaphore], 5
ret
.smbl1:
cmp eax, 2
4873,12 → 4995,12
.1: ; resolution
mov eax, [Screen_Max_X]
shl eax, 16
mov ax, [Screen_Max_Y]
mov ax, word [Screen_Max_Y]
add eax, 0x00010001
mov [esp+32], eax
ret
.2: ; bits per pixel
movzx eax, byte [ScreenBPP]
mov eax, [_display.bpp]
mov [esp+32], eax
ret
.3: ; bytes per scanline
4972,9 → 5094,9
 
align 4
syscall_getscreensize: ; GetScreenSize
mov ax, [Screen_Max_X]
mov eax, [Screen_Max_X]
shl eax, 16
mov ax, [Screen_Max_Y]
mov ax, word [Screen_Max_Y]
mov [esp + 32], eax
ret
 
5265,28 → 5387,6
 
align 4
 
stack_driver_stat:
 
call app_stack_handler ; Stack status
 
; mov [check_idle_semaphore],5 ; enable these for zero delay
; call change_task ; between sent packet
 
mov [esp+32], eax
ret
 
align 4
 
socket: ; Socket interface
call app_socket_handler
 
; mov [check_idle_semaphore],5 ; enable these for zero delay
; call change_task ; between sent packet
 
mov [esp+36], eax
mov [esp+24], ebx
ret
 
paleholder:
ret
;------------------------------------------------------------------------------
/kernel/branches/Kolibri-acpi/kernel32.inc
126,25 → 126,29
tls_base dd ? ;+104
dlls_list_ptr dd ? ;+108
event_filter dd ? ;+112
rb 12 ;+116
draw_bgr_x dd ? ;+116
draw_bgr_y dd ? ;+120
dd ? ;+124
 
wnd_shape dd ? ;+128
wnd_shape_scale dd ? ;+132
dd ? ;+136
mem_size dd ? ;+140
saved_box BOX
ipc_start dd ?
ipc_size dd ?
event_mask dd ?
debugger_slot dd ?
terminate_protection dd ?
keyboard_mode db ?
saved_box BOX ;+144
ipc_start dd ? ;+160
ipc_size dd ? ;+164
event_mask dd ? ;+168
debugger_slot dd ? ;+172
terminate_protection dd ? ;+176
keyboard_mode db ? ;+180
rb 3
dir_table dd ?
dbg_event_mem dd ?
dbg_regs DBG_REGS
wnd_caption dd ?
wnd_clientbox BOX
dir_table dd ? ;+184
dbg_event_mem dd ? ;+188
dbg_regs DBG_REGS ;+192
wnd_caption dd ? ;+212
wnd_clientbox BOX ;+216
priority dd ? ;+232
in_schedule LHEAD ;+236
 
ends
 
220,6 → 224,9
 
include "bus/pci/pci32.inc"
 
; USB functions
include "bus/usb/init.inc"
 
; Floppy drive controller
 
include "blkdev/fdc.inc"
/kernel/branches/Kolibri-acpi/kernelsp.inc
1,4 → 1,4
; 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,$
/kernel/branches/Kolibri-acpi/makefile
1,12 → 1,11
FASM=fasm
FLAGS=-m 65536
languages=en|ru|ge|et
drivers_src=com_mouse emu10k1x ensoniq fm801 infinity sis sound uart viasound vmode vt823\(x\)
skins_src=default
languages=en|ru|ge|et|sp
drivers_src=com_mouse emu10k1x fm801 infinity sis sound vt823x
 
.PHONY: all kernel drivers skins clean
.PHONY: all kernel drivers bootloader clean
 
all: kernel drivers skins
all: kernel drivers bootloader
 
kernel: check_lang
@echo "*** building kernel with language '$(lang)' ..."
23,16 → 22,16
echo "--- building 'bin/drivers/$${f}.obj' ..."; \
$(FASM) $(FLAGS) "$${f}.asm" "../bin/drivers/$${f}.obj" || exit $?; \
done
@mv bin/drivers/vmode.obj bin/drivers/vmode.mdr
 
skins:
@echo "*** building skins ..."
@mkdir -p bin/skins
@cd skin; for f in $(skins_src); do \
echo "--- building 'bin/skins/$${f}.skn' ..."; \
$(FASM) $(FLAGS) $${f}.asm ../bin/skins/$${f}.skn || exit $?; \
done
bootloader: check_lang
@echo "*** building bootloader with language '$(lang)' ..."
@mkdir -p bin
@echo "lang fix $(lang)" > lang.inc
@echo "--- building 'bin/boot_fat12.bin' ..."
@$(FASM) $(FLAGS) bootloader/boot_fat12.asm bin/boot_fat12.bin
@rm -f lang.inc
 
 
check_lang:
@case "$(lang)" in \
$(languages)) \
/kernel/branches/Kolibri-acpi/network/ARP.inc
0,0 → 1,645
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ARP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June- 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3386 $
 
ARP_NO_ENTRY = 0
ARP_VALID_MAPPING = 1
ARP_AWAITING_RESPONSE = 2
ARP_RESPONSE_TIMEOUT = 3
 
ARP_REQUEST_TTL = 31 ; 20 s
ARP_ENTRY_TTL = 937 ; 600 s
ARP_STATIC_ENTRY = -1
 
ARP_REQ_OPCODE = 0x0100 ; request
ARP_REP_OPCODE = 0x0200 ; reply
 
ARP_TABLE_SIZE = 20 ; Size of table
 
struct ARP_entry
 
IP dd ?
MAC dp ?
Status dw ?
TTL dw ?
 
ends
 
struct ARP_header
 
HardwareType dw ?
ProtocolType dw ?
HardwareSize db ?
ProtocolSize db ?
Opcode dw ?
SenderMAC dp ?
SenderIP dd ?
TargetMAC dp ?
TargetIP dd ?
 
ends
 
align 4
uglobal
 
NumARP dd ?
 
ARP_table rb ARP_TABLE_SIZE * sizeof.ARP_entry ; TODO: separate ARP table and stats per interface
 
ARP_PACKETS_TX rd MAX_NET_DEVICES
ARP_PACKETS_RX rd MAX_NET_DEVICES
ARP_CONFLICTS rd MAX_NET_DEVICES
 
 
endg
 
 
 
;-----------------------------------------------------------------
;
; ARP_init
;
; This function resets all ARP variables
;
;-----------------------------------------------------------------
macro ARP_init {
 
xor eax, eax
mov [NumARP], eax
 
mov edi, ARP_PACKETS_TX
mov ecx, 3*MAX_NET_DEVICES
rep stosd
 
}
 
;---------------------------------------------------------------------------
;
; ARP_decrease_entry_ttls
;
;---------------------------------------------------------------------------
 
macro ARP_decrease_entry_ttls {
 
local .loop
local .exit
 
; The TTL field is decremented every second, and is deleted when it reaches 0.
; It is refreshed every time a packet is received.
; If the TTL field is 0xFFFF it is a static entry and is never deleted.
; The status field can be the following values:
; 0x0000 entry not used
; 0x0001 entry holds a valid mapping
; 0x0002 entry contains an IP address, awaiting ARP response
; 0x0003 No response received to ARP request.
; The last status value is provided to allow the network layer to delete
; a packet that is queued awaiting an ARP response
 
mov ecx, [NumARP]
test ecx, ecx
jz .exit
 
mov esi, ARP_table
.loop:
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY
je .next
 
dec [esi + ARP_entry.TTL]
jz .time_out
 
.next:
add esi, sizeof.ARP_entry
dec ecx
jnz .loop
jmp .exit
 
.time_out:
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE
je .response_timeout
 
push esi ecx
call ARP_del_entry
pop ecx esi
 
jmp .next
 
.response_timeout:
mov [esi + ARP_entry.Status], ARP_RESPONSE_TIMEOUT
mov [esi + ARP_entry.TTL], 10
 
jmp .next
 
.exit:
 
}
 
 
;-----------------------------------------------------------------
;
; ARP_input
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; packet size (without ethernet header) in ecx
; packet ptr in edx
; device ptr in ebx
; OUT: /
;
;-----------------------------------------------------------------
align 4
ARP_input:
 
;-----------------------------------------
; Check validity and print some debug info
 
cmp ecx, sizeof.ARP_header
jb .exit
 
call NET_ptr_to_num
cmp edi, -1
jz .exit
 
inc [ARP_PACKETS_RX + 4*edi] ; update stats
 
DEBUGF 1,"ARP_input: got packet from %u.%u.%u.%u through device %u\n",\
[edx + ARP_header.SenderIP]:1, [edx + ARP_header.SenderIP + 1]:1,\
[edx + ARP_header.SenderIP + 2]:1, [edx + ARP_header.SenderIP + 3]:1, edi
 
;------------------------------
; First, check for IP collision
 
mov eax, [edx + ARP_header.SenderIP]
cmp eax, [IP_LIST + 4*edi]
je .collision
 
;---------------------
; Handle reply packets
 
cmp [edx + ARP_header.Opcode], ARP_REP_OPCODE
jne .maybe_request
 
DEBUGF 1,"ARP_input: It's a reply\n"
 
mov ecx, [NumARP]
test ecx, ecx
jz .exit
 
mov esi, ARP_table
.loop:
cmp [esi + ARP_entry.IP], eax
je .gotit
add esi, sizeof.ARP_entry
dec ecx
jnz .loop
 
DEBUGF 1,"ARP_input: no matching entry found\n"
jmp .exit
 
.gotit:
DEBUGF 1,"ARP_input: found matching entry\n"
 
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY ; if it is a static entry, dont touch it
je .exit
 
DEBUGF 1,"ARP_input: updating entry\n"
 
mov [esi + ARP_entry.Status], ARP_VALID_MAPPING
mov [esi + ARP_entry.TTL], ARP_ENTRY_TTL
 
mov eax, dword [edx + ARP_header.SenderMAC]
mov dword [esi + ARP_entry.MAC], eax
mov cx, word [edx + ARP_header.SenderMAC + 4]
mov word [esi + ARP_entry.MAC + 4], cx
 
jmp .exit
 
;-----------------------
; Handle request packets
 
.maybe_request:
cmp [edx + ARP_header.Opcode], ARP_REQ_OPCODE
jne .exit
 
DEBUGF 1,"ARP_input: its a request\n"
 
mov eax, [IP_LIST + 4*edi]
cmp eax, [edx + ARP_header.TargetIP] ; Is it looking for my IP address?
jne .exit
 
push eax
push edi
 
; OK, it is a request for one of our MAC addresses.
; Build the frame and send it. We can reuse the buffer. (faster then using ARP_create_packet)
 
lea esi, [edx + ARP_header.SenderMAC]
lea edi, [edx + ARP_header.TargetMAC]
movsd ; Move Sender Mac to Dest MAC
movsw ;
movsd ; Move sender IP to Dest IP
 
pop esi
mov esi, [NET_DRV_LIST + 4*esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_header.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
movsw ;
pop eax
stosd ; Write our IP
 
mov [edx + ARP_header.Opcode], ARP_REP_OPCODE
 
; Now, Fill in ETHERNET header
 
mov edi, [esp]
lea esi, [edx + ARP_header.TargetMAC]
movsd
movsw
lea esi, [edx + ARP_header.SenderMAC]
movsd
movsw
; mov ax , ETHER_ARP ; It's already there, I'm sure of it!
; stosw
 
DEBUGF 1,"ARP_input: Sending reply\n"
 
call [ebx + NET_DEVICE.transmit]
ret
 
.collision:
inc [ARP_CONFLICTS + 4*edi]
DEBUGF 1,"ARP_input: IP address conflict detected!\n"
 
.exit:
call kernel_free
add esp, 4 ; pop (balance stack)
 
DEBUGF 1,"ARP_input: exiting\n"
ret
 
 
;---------------------------------------------------------------------------
;
; ARP_output_request
;
; IN: ip in eax
; device in edi
; OUT: /
;
;---------------------------------------------------------------------------
align 4
ARP_output_request:
 
push eax ; DestIP
pushd [IP_LIST + edi] ; SenderIP
inc [ARP_PACKETS_TX + edi] ; assume we will succeed
 
DEBUGF 1,"ARP_output_request: ip=%u.%u.%u.%u\n",\
[esp + 4]:1, [esp + 5]:1, [esp + 6]:1, [esp + 7]:1
 
mov ebx, [NET_DRV_LIST + edi] ; device ptr
 
lea eax, [ebx + ETH_DEVICE.mac] ; local device mac
mov edx, ETH_BROADCAST ; broadcast mac
mov ecx, sizeof.ARP_header
mov di, ETHER_ARP
call ETH_output
jz .exit
 
mov ecx, eax
 
mov [edi + ARP_header.HardwareType], 0x0100 ; Ethernet
mov [edi + ARP_header.ProtocolType], 0x0008 ; IP
mov [edi + ARP_header.HardwareSize], 6 ; MAC-addr length
mov [edi + ARP_header.ProtocolSize], 4 ; IP-addr length
mov [edi + ARP_header.Opcode], ARP_REQ_OPCODE ; Request
 
add edi, ARP_header.SenderMAC
 
lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac
movsw ;
movsd ;
pop eax ; SenderIP
stosd ;
 
mov eax, -1 ; DestMac
stosd ;
stosw ;
pop eax ; DestIP
stosd ;
 
DEBUGF 1,"ARP_output_request: device=%x\n", ebx
 
push edx ecx
call [ebx + NET_DEVICE.transmit]
ret
 
.exit:
add esp, 4 + 4
DEBUGF 1,"ARP_output_request: failed\n"
sub eax, eax
ret
 
 
;-----------------------------------------------------------------
;
; ARP_add_entry (or update)
;
; IN: esi = ptr to entry (can easily be made on the stack)
; OUT: eax = entry #, -1 on error
; edi = ptr to newly created entry
;
;----------------------------------------------------------------- ; TODO: use a mutex
align 4
ARP_add_entry:
 
DEBUGF 1,"ARP_add_entry: "
 
mov ecx, [NumARP]
cmp ecx, ARP_TABLE_SIZE ; list full ?
jae .error
 
xor eax, eax
mov edi, ARP_table
mov ecx, [esi + ARP_entry.IP]
.loop:
cmp [edi + ARP_entry.Status], ARP_NO_ENTRY ; is this slot empty?
je .add
 
cmp [edi + ARP_entry.IP], ecx ; if not, check if it doesnt collide
jne .maybe_next
 
cmp [edi + ARP_entry.TTL], ARP_STATIC_ENTRY ; ok, its the same IP, update it if not static
jne .add
 
.maybe_next: ; try the next slot
add edi, sizeof.ARP_entry
inc eax
cmp eax, ARP_TABLE_SIZE
jae .error
jmp .loop
 
.add:
mov ecx, sizeof.ARP_entry/2
rep movsw
inc [NumARP]
sub edi, sizeof.ARP_entry
DEBUGF 1,"entry=%u\n", eax
 
ret
 
.error:
DEBUGF 1,"failed\n"
mov eax, -1
ret
 
 
;-----------------------------------------------------------------
;
; ARP_del_entry
;
; IN: esi = ptr to arp entry
; OUT: /
;
;-----------------------------------------------------------------
align 4
ARP_del_entry:
 
DEBUGF 1,"ARP_del_entry: entry=%x entrys=%u\n", esi, [NumARP]
DEBUGF 1,"ARP_del_entry: IP=%u.%u.%u.%u\n", \
[esi + ARP_entry.IP]:1, [esi + ARP_entry.IP + 1]:1, [esi + ARP_entry.IP + 2]:1, [esi + ARP_entry.IP + 3]:1
 
mov ecx, ARP_table + (ARP_TABLE_SIZE - 1) * sizeof.ARP_entry
sub ecx, esi
shr ecx, 1
 
mov edi, esi
add esi, sizeof.ARP_entry
rep movsw
 
xor eax, eax
mov ecx, sizeof.ARP_entry/2
rep stosw
 
dec [NumARP]
DEBUGF 1,"ARP_del_entry: success\n"
 
ret
 
 
 
 
 
;-----------------------------------------------------------------
;
; ARP_IP_to_MAC
;
; This function translates an IP address to a MAC address
;
; IN: eax = IPv4 address
; edi = device number
; OUT: eax = -1 on error, -2 means request send
; else, ax = first two bytes of mac (high 16 bits of eax will be 0)
; ebx = last four bytes of mac
; edi = unchanged
;
;-----------------------------------------------------------------
align 4
ARP_IP_to_MAC:
 
DEBUGF 1,"ARP_IP_to_MAC: %u.%u", al, ah
rol eax, 16
DEBUGF 1,".%u.%u\n", al, ah
rol eax, 16
 
cmp eax, 0xffffffff
je .broadcast
 
;--------------------------------
; Try to find the IP in ARP_table
 
mov ecx, [NumARP]
test ecx, ecx
jz .not_in_list
mov esi, ARP_table + ARP_entry.IP
.scan_loop:
cmp [esi], eax
je .found_it
add esi, sizeof.ARP_entry
loop .scan_loop
 
.not_in_list:
DEBUGF 1,"ARP_IP_to_MAC: preparing for ARP request\n"
 
;--------------------
; Send an ARP request
 
push eax edi ; save IP for ARP_output_request
 
; Now create the ARP entry
pushw ARP_REQUEST_TTL ; TTL
pushw ARP_AWAITING_RESPONSE ; status
pushd 0 ; mac
pushw 0
pushd eax ; ip
mov esi, esp
call ARP_add_entry
add esp, sizeof.ARP_entry ; clear the entry from stack
 
cmp eax, -1 ; did ARP_add_entry fail?
je .full
 
mov esi, edi
pop edi eax ; IP in eax, device number in edi, for ARP_output_request
 
push esi edi
call ARP_output_request ; And send a request
pop edi esi
 
;-----------------------------------------------
; At this point, we got an ARP entry in the list
.found_it:
cmp [esi + ARP_entry.Status], ARP_VALID_MAPPING ; Does it have a MAC assigned?
je .valid
 
if ARP_BLOCK
 
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE ; Are we waiting for reply from remote end?
jne .give_up
push esi
mov esi, 10 ; wait 10 ms
call delay_ms
pop esi
jmp .found_it ; now check again
 
else
 
jmp .give_up
 
end if
 
.valid:
DEBUGF 1,"ARP_IP_to_MAC: found MAC\n"
movzx eax, word[esi + ARP_entry.MAC]
mov ebx, dword[esi + ARP_entry.MAC + 2]
ret
 
.full:
DEBUGF 1,"ARP_IP_to_MAC: table is full!\n"
add esp, 8
.give_up:
DEBUGF 1,"ARP_IP_to_MAC: entry has no valid mapping!\n"
mov eax, -1
ret
 
.broadcast:
mov eax, 0x0000ffff
mov ebx, 0xffffffff
ret
 
 
;-----------------------------------------------------------------
;
; ARP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT: ?
;
;-----------------------------------------------------------------
align 4
ARP_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .entries ; 2
dd .read ; 3
dd .write ; 4
dd .remove ; 5
dd .send_announce ; 6
dd .conflicts ; 7
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [ARP_PACKETS_TX + eax]
ret
 
.packets_rx:
mov eax, [ARP_PACKETS_RX + eax]
ret
 
.conflicts:
mov eax, [ARP_CONFLICTS + eax]
ret
 
.entries:
mov eax, [NumARP]
ret
 
.read:
cmp ecx, [NumARP]
jae .error
; edi = pointer to buffer
; ecx = # entry
imul ecx, sizeof.ARP_entry
add ecx, ARP_table
mov esi, ecx
mov ecx, sizeof.ARP_entry/2
rep movsw
 
xor eax, eax
ret
 
.write:
; esi = pointer to buffer
call ARP_add_entry ; out: eax = entry number, -1 on error
ret
 
.remove:
; ecx = # entry
cmp ecx, [NumARP]
jae .error
imul ecx, sizeof.ARP_entry
lea esi, [ARP_table + ecx]
call ARP_del_entry
ret
 
.send_announce:
mov edi, eax
mov eax, [IP_LIST + eax]
call ARP_output_request ; now send a gratuitous ARP
ret
 
/kernel/branches/Kolibri-acpi/network/IPv4.inc
0,0 → 1,1019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IPv4.INC ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3515 $
 
MAX_FRAGMENTS = 64
 
struct IPv4_header
 
VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits]
TypeOfService db ? ; precedence [7-5] minimize delay [4], maximize throughput [3], maximize riliability [2] minimize momentary cost [1] and zero [0]
TotalLength dw ?
Identification dw ?
FlagsAndFragmentOffset dw ? ; Flags[0-2] and FragmentOffset[3-15]
TimeToLive db ? ;
Protocol db ?
HeaderChecksum dw ?
SourceAddress dd ?
DestinationAddress dd ?
 
ends
 
struct FRAGMENT_slot
 
ttl dw ? ; Time to live for this entry, 0 for empty slot's
id dw ? ; Identification field from IP header
SrcIP dd ? ; .. from IP header
DstIP dd ? ; .. from IP header
ptr dd ? ; Pointer to first packet
 
ends
 
struct FRAGMENT_entry ; This structure will replace the ethernet header in fragmented ip packets
 
PrevPtr dd ? ; Pointer to previous fragment entry (-1 for first packet)
NextPtr dd ? ; Pointer to next fragment entry (-1 for last packet)
Owner dd ? ; Pointer to structure of driver
rb 2 ; to match ethernet header size ;;; FIXME
; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends
 
 
align 4
uglobal
 
IP_LIST rd MAX_NET_DEVICES
SUBNET_LIST rd MAX_NET_DEVICES
DNS_LIST rd MAX_NET_DEVICES
GATEWAY_LIST rd MAX_NET_DEVICES
BROADCAST_LIST rd MAX_NET_DEVICES
 
IP_packets_tx rd MAX_NET_DEVICES
IP_packets_rx rd MAX_NET_DEVICES
IP_packets_dumped rd MAX_NET_DEVICES
 
FRAGMENT_LIST rb MAX_FRAGMENTS * sizeof.FRAGMENT_slot
endg
 
 
;-----------------------------------------------------------------
;
; IPv4_init
;
; This function resets all IP variables
;
;-----------------------------------------------------------------
macro IPv4_init {
 
xor eax, eax
mov edi, IP_LIST
mov ecx, 7*MAX_NET_DEVICES + (sizeof.FRAGMENT_slot*MAX_FRAGMENTS)/4
rep stosd
 
}
 
 
;-----------------------------------------------------------------
;
; Decrease TimeToLive of all fragment slots
;
;-----------------------------------------------------------------
macro IPv4_decrease_fragment_ttls {
 
local .loop, .next
 
mov esi, FRAGMENT_LIST
mov ecx, MAX_FRAGMENTS
.loop:
cmp [esi + FRAGMENT_slot.ttl], 0
je .next
dec [esi + FRAGMENT_slot.ttl]
jz .died
.next:
add esi, sizeof.FRAGMENT_slot
dec ecx
jnz .loop
jmp .done
 
.died:
DEBUGF 2,"IPv4 Fragment slot timed-out!\n"
;;; TODO: clear all entry's of timed-out slot
jmp .next
 
.done:
}
 
 
 
macro IPv4_checksum ptr {
 
; This is the fast procedure to create or check an IP header without options
; To create a new checksum, the checksum field must be set to 0 before computation
; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct
 
push ebx
xor ebx, ebx
add bl, [ptr+1]
adc bh, [ptr+0]
 
adc bl, [ptr+3]
adc bh, [ptr+2]
 
adc bl, [ptr+5]
adc bh, [ptr+4]
 
adc bl, [ptr+7]
adc bh, [ptr+6]
 
adc bl, [ptr+9]
adc bh, [ptr+8]
 
; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation
 
adc bl, [ptr+13]
adc bh, [ptr+12]
 
adc bl, [ptr+15]
adc bh, [ptr+14]
 
adc bl, [ptr+17]
adc bh, [ptr+16]
 
adc bl, [ptr+19]
adc bh, [ptr+18]
 
adc ebx, 0
 
push ecx
mov ecx, ebx
shr ecx, 16
and ebx, 0xffff
add ebx, ecx
 
mov ecx, ebx
shr ecx, 16
add ebx, ecx
 
not bx
jnz .not_zero
dec bx
.not_zero:
xchg bl, bh
pop ecx
 
neg word [ptr+10] ; zero will stay zero so we just get the checksum
add word [ptr+10], bx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
pop ebx
 
}
 
 
 
;-----------------------------------------------------------------
;
; IPv4_input:
;
; Will check if IPv4 Packet isnt damaged
; and call appropriate handler. (TCP/UDP/ICMP/..)
;
; It will also re-construct fragmented packets
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; pointer to IPv4 header in edx
; size of IPv4 packet in ecx
; OUT: /
;
;-----------------------------------------------------------------
align 4
IPv4_input: ; TODO: add IPv4 raw sockets support
 
DEBUGF 2,"IPv4_input, packet from: %u.%u.%u.%u ",\
[edx + IPv4_header.SourceAddress + 0]:1,[edx + IPv4_header.SourceAddress + 1]:1,\
[edx + IPv4_header.SourceAddress + 2]:1,[edx + IPv4_header.SourceAddress + 3]:1
DEBUGF 2,"to: %u.%u.%u.%u\n",\
[edx + IPv4_header.DestinationAddress + 0]:1,[edx + IPv4_header.DestinationAddress + 1]:1,\
[edx + IPv4_header.DestinationAddress + 2]:1,[edx + IPv4_header.DestinationAddress + 3]:1
 
;-------------------------------
; re-calculate the checksum
 
IPv4_checksum edx
jnz .dump ; if checksum isn't valid then dump packet
 
DEBUGF 1,"IPv4_input: Checksum ok\n"
 
;-----------------------------------
; Check if destination IP is correct
 
call NET_ptr_to_num
shl edi, 2
 
; check if it matches local ip (Using RFC1122 strong end system model)
 
mov eax, [edx + IPv4_header.DestinationAddress]
cmp eax, [IP_LIST + edi]
je .ip_ok
 
; check for broadcast (IP or (not SUBNET))
 
cmp eax, [BROADCAST_LIST + edi]
je .ip_ok
 
; or a special broadcast (255.255.255.255)
 
cmp eax, 0xffffffff
je .ip_ok
 
; maybe it's a multicast (224.0.0.0/4)
 
and eax, 0x0fffffff
cmp eax, 224
je .ip_ok
 
; or a loopback address (127.0.0.0/8)
 
and eax, 0x00ffffff
cmp eax, 127
je .ip_ok
 
; or it's just not meant for us.. :(
 
DEBUGF 2,"IPv4_input: Destination address does not match!\n"
jmp .dump
 
;------------------------
; Now we can update stats
 
.ip_ok:
inc [IP_packets_rx + edi]
 
;----------------------------------
; Check if the packet is fragmented
 
test [edx + IPv4_header.FlagsAndFragmentOffset], 1 shl 5 ; Is 'more fragments' flag set ?
jnz .has_fragments ; If so, we definately have a fragmented packet
 
test [edx + IPv4_header.FlagsAndFragmentOffset], 0xff1f ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
jnz .is_last_fragment
 
;-------------------------------------------------------------------
; No, it's just a regular IP packet, pass it to the higher protocols
 
.handle_it: ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
 
movzx esi, [edx + IPv4_header.VersionAndIHL] ; Calculate Header length by using IHL field
and esi, 0x0000000f ;
shl esi, 2 ;
 
movzx ecx, [edx + IPv4_header.TotalLength] ; Calculate length of encapsulated Packet
xchg cl, ch ;
sub ecx, esi ;
 
lea edi, [edx + IPv4_header.SourceAddress] ; make edi ptr to source and dest IPv4 address
mov al, [edx + IPv4_header.Protocol]
add esi, edx ; make esi ptr to data
 
cmp al, IP_PROTO_TCP
je TCP_input
 
cmp al, IP_PROTO_UDP
je UDP_input
 
cmp al, IP_PROTO_ICMP
je ICMP_input
 
DEBUGF 2,"IPv4_input: unknown protocol %u\n", al
 
.dump:
DEBUGF 1,"IPv4_input: dumping\n"
inc [IP_packets_dumped] ; FIXME: use correct interface
call kernel_free
add esp, 4 ; pop (balance stack)
ret
 
 
;---------------------------
; Fragmented packet handler
 
 
.has_fragments:
movzx eax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
 
DEBUGF 1,"IPv4_input: fragmented packet offset=%u id=%x\n", ax, [edx + IPv4_header.Identification]:4
 
test ax, ax ; Is this the first packet of the fragment?
jz .is_first_fragment
 
 
;-------------------------------------------------------
; We have a fragmented IP packet, but it's not the first
 
DEBUGF 1,"IPv4_input: Middle fragment packet received!\n"
 
call IPv4_find_fragment_slot
cmp esi, -1
je .dump
 
mov [esi + FRAGMENT_slot.ttl], 15 ; Reset the ttl
mov esi, [esi + FRAGMENT_slot.ptr]
or edi, -1
.find_last_entry: ; The following routine will try to find the last entry
cmp edi, [esi + FRAGMENT_entry.PrevPtr]
jne .destroy_slot ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov edi, esi
mov esi, [esi + FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .find_last_entry
; We found the last entry (pointer is now in edi)
; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure
 
pop eax ; pointer to packet
mov [edi + FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry
mov [eax + FRAGMENT_entry.NextPtr], -1
mov [eax + FRAGMENT_entry.PrevPtr], edi
mov [eax + FRAGMENT_entry.Owner], ebx
 
add esp, 4
ret
 
 
;------------------------------------
; We have received the first fragment
 
.is_first_fragment:
DEBUGF 1,"IPv4_input: First fragment packet received!\n"
; try to locate a free slot..
mov ecx, MAX_FRAGMENTS
mov esi, FRAGMENT_LIST
.find_free_slot:
cmp word [esi + FRAGMENT_slot.ttl], 0
je .found_free_slot
add esi, sizeof.FRAGMENT_slot
loop .find_free_slot
jmp .dump ; If no free slot was found, dump the packet
 
.found_free_slot: ; We found a free slot, let's fill in the FRAGMENT_slot structure
mov [esi + FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl
mov ax, [edx + IPv4_header.Identification]
mov [esi + FRAGMENT_slot.id], ax
mov eax, [edx + IPv4_header.SourceAddress]
mov [esi + FRAGMENT_slot.SrcIP], eax
mov eax, [edx + IPv4_header.DestinationAddress]
mov [esi + FRAGMENT_slot.DstIP], eax
pop eax
mov [esi + FRAGMENT_slot.ptr], eax
; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
mov [eax + FRAGMENT_entry.NextPtr], -1
mov [eax + FRAGMENT_entry.PrevPtr], -1
mov [eax + FRAGMENT_entry.Owner], ebx
 
add esp, 4 ; balance stack and exit
ret
 
 
;-----------------------------------
; We have received the last fragment
 
.is_last_fragment:
DEBUGF 1,"IPv4_input: Last fragment packet received!\n"
 
call IPv4_find_fragment_slot
cmp esi, -1
je .dump
 
mov esi, [esi + FRAGMENT_slot.ptr] ; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer
push esi
xor eax, eax
or edi, -1
 
.count_bytes:
cmp [esi + FRAGMENT_entry.PrevPtr], edi
jne .destroy_slot_pop ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.TotalLength] ; Add total length
xchg cl, ch
DEBUGF 1,"IPv4_input: Packet size=%u\n", cx
add ax, cx
movzx cx, [esi + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Sub Header length
and cx, 0x000F
shl cx, 2
DEBUGF 1,"IPv4_input: Header size=%u\n", cx
sub ax, cx
mov edi, esi
mov esi, [esi + FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .count_bytes
 
mov esi, [esp+4]
mov [edi + FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code
mov [esi + FRAGMENT_entry.NextPtr], -1
mov [esi + FRAGMENT_entry.PrevPtr], edi
mov [esi + FRAGMENT_entry.Owner], ebx
 
mov cx, [edx + IPv4_header.TotalLength] ; Note: This time we dont substract Header length
xchg cl, ch
DEBUGF 1,"IPv4_input: Packet size=%u\n", cx
add ax, cx
DEBUGF 1,"IPv4_input: Total Received data size=%u\n", eax
 
push eax
mov ax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
add cx, ax
pop eax
DEBUGF 1,"IPv4_input: Total Fragment size=%u\n", ecx
 
cmp ax, cx
jne .destroy_slot_pop
 
push eax
push eax
call kernel_alloc
test eax, eax
je .destroy_slot_pop ; If we dont have enough space to allocate the buffer, discard all packets in slot
mov edx, [esp+4] ; Get pointer to first fragment entry back in edx
 
.rebuild_packet_loop:
movzx ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.FlagsAndFragmentOffset] ; Calculate the fragment offset
xchg cl, ch ; intel byte order
shl cx, 3 ; multiply by 8 and clear first 3 bits
DEBUGF 1,"IPv4_input: Fragment offset=%u\n", cx
 
lea edi, [eax + ecx] ; Notice that edi will be equal to eax for first fragment
movzx ebx, [edx + sizeof.FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Find header size (in ebx) of fragment
and bx, 0x000F ;
shl bx, 2 ;
 
lea esi, [edx + sizeof.FRAGMENT_entry] ; Set esi to the correct begin of fragment
movzx ecx, [edx + sizeof.FRAGMENT_entry + IPv4_header.TotalLength] ; Calculate total length of fragment
xchg cl, ch ; intel byte order
 
cmp edi, eax ; Is this packet the first fragment ?
je .first_fragment
sub cx, bx ; If not, dont copy the header
add esi, ebx ;
.first_fragment:
 
push cx ; First copy dword-wise, then byte-wise
shr cx, 2 ;
rep movsd ;
pop cx ;
and cx, 3 ;
rep movsb ;
 
push eax
push edx ; Push pointer to fragment onto stack
mov ebx, [edx + FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet
mov edx, [edx + FRAGMENT_entry.NextPtr] ; Set edx to the next pointer
call kernel_free ; free the previous fragment buffer (this uses the value from stack)
pop eax
cmp edx, -1 ; Check if it is last fragment in chain
jne .rebuild_packet_loop
 
pop ecx
xchg cl, ch
mov edx, eax
mov [edx + IPv4_header.TotalLength], cx
add esp, 8
xchg cl, ch
push ecx
 
push eax
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr
 
.destroy_slot_pop:
add esp, 4
.destroy_slot:
DEBUGF 1,"IPv4_input: Destroy fragment slot!\n"
; TODO!
jmp .dump
 
 
 
 
 
;-----------------------------------------------------------------
;
; find fragment slot
;
; IN: pointer to fragmented packet in edx
; OUT: pointer to slot in esi, -1 on error
;
;-----------------------------------------------------------------
align 4
IPv4_find_fragment_slot:
 
;;; TODO: the RFC says we should check protocol number too
 
push eax ebx ecx edx
mov ax, [edx + IPv4_header.Identification]
mov ecx, MAX_FRAGMENTS
mov esi, FRAGMENT_LIST
mov ebx, [edx + IPv4_header.SourceAddress]
mov edx, [edx + IPv4_header.DestinationAddress]
.find_slot:
cmp [esi + FRAGMENT_slot.id], ax
jne .try_next
cmp [esi + FRAGMENT_slot.SrcIP], ebx
jne .try_next
cmp [esi + FRAGMENT_slot.DstIP], edx
je .found_slot
.try_next:
add esi, sizeof.FRAGMENT_slot
loop .find_slot
 
or esi, -1
.found_slot:
pop edx ecx ebx eax
ret
 
 
;------------------------------------------------------------------
;
; IPv4_output
;
; IN: eax = dest ip
; ebx = output device ptr/0 for automatic choice
; ecx = data length
; edx = source ip
; di = TTL shl 8 + protocol
;
; OUT: eax = pointer to buffer start
; ebx = pointer to device struct (needed for sending procedure)
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
; edi = pointer to start of data (0 on error)
;
;------------------------------------------------------------------
align 4
IPv4_output:
 
DEBUGF 1,"IPv4_output: size=%u\n", ecx
 
cmp ecx, 65500 ; Max IPv4 packet size
ja .too_large
 
push ecx eax edx di
 
cmp eax, 1 shl 24 + 127
je .loopback
 
call IPv4_route ; outputs device number in edi, dest ip in eax
call ARP_IP_to_MAC
test eax, 0xffff0000 ; error bits
jnz .arp_error
push ebx ; push the mac onto the stack
push ax
 
inc [IP_packets_tx + edi] ; update stats
 
mov ebx, [NET_DRV_LIST + edi]
lea eax, [ebx + ETH_DEVICE.mac]
mov edx, esp
mov ecx, [esp + 10 + 6]
add ecx, sizeof.IPv4_header
mov di, ETHER_IPv4
call ETH_output
jz .eth_error
add esp, 6 ; pop the mac out of the stack
 
.continue:
xchg cl, ch ; internet byte order
mov [edi + IPv4_header.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_header.TypeOfService], 0 ; nothing special, just plain ip packet
mov [edi + IPv4_header.TotalLength], cx
mov [edi + IPv4_header.Identification], 0 ; fragment id: FIXME
mov [edi + IPv4_header.FlagsAndFragmentOffset], 0
pop word [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
mov [edi + IPv4_header.HeaderChecksum], 0
popd [edi + IPv4_header.SourceAddress]
popd [edi + IPv4_header.DestinationAddress]
 
pop ecx
 
IPv4_checksum edi
add edi, sizeof.IPv4_header
DEBUGF 2,"IPv4_output: success!\n"
ret
 
.eth_error:
DEBUGF 2,"IPv4_output: ethernet error\n"
add esp, 3*4+2+6
xor edi, edi
ret
 
.arp_error:
DEBUGF 2,"IPv4_output: ARP error=%x\n", eax
add esp, 3*4+2
xor edi, edi
ret
 
.too_large:
DEBUGF 2,"IPv4_output: Packet too large!\n"
xor edi, edi
ret
 
.loopback:
mov dword [esp + 2], eax
add ecx, sizeof.IPv4_header
mov di, ETHER_IPv4
call LOOP_output
jmp .continue
 
 
 
 
;------------------------------------------------------------------
;
; IPv4_output_raw
;
; IN: eax = socket ptr
; ecx = data length
; esi = data ptr
;
; OUT: /
;
;------------------------------------------------------------------
align 4
IPv4_output_raw:
 
DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax
 
cmp ecx, 1480 ;;;;; FIXME
ja .too_large
 
sub esp, 8
push esi eax
 
call IPv4_route
call ARP_IP_to_MAC
 
test eax, 0xffff0000 ; error bits
jnz .arp_error
 
push ebx ; push the mac
push ax
 
inc [IP_packets_tx + edi]
mov ebx, [NET_DRV_LIST + edi]
lea eax, [ebx + ETH_DEVICE.mac]
mov edx, esp
mov ecx, [esp + 6 + 4]
add ecx, sizeof.IPv4_header
mov di, ETHER_IPv4
call ETH_output
jz .error
 
add esp, 6 ; pop the mac
 
mov dword[esp+4+4], edx
mov dword[esp+4+4+4], eax
 
pop eax esi
;; todo: check socket options if we should add header, or just compute checksum
 
push edi ecx
rep movsb
pop ecx edi
 
; [edi + IPv4_header.VersionAndIHL] ; IPv4, normal length (no Optional header)
; [edi + IPv4_header.TypeOfService] ; nothing special, just plain ip packet
; [edi + IPv4_header.TotalLength]
; [edi + IPv4_header.TotalLength] ; internet byte order
; [edi + IPv4_header.FlagsAndFragmentOffset]
 
mov [edi + IPv4_header.HeaderChecksum], 0
 
; [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
; [edi + IPv4_header.Identification] ; fragment id
; [edi + IPv4_header.SourceAddress]
; [edi + IPv4_header.DestinationAddress]
 
IPv4_checksum edi ;;;; todo: checksum for IP packet with options!
add edi, sizeof.IPv4_header
DEBUGF 2,"IPv4_output_raw: device=%x\n", ebx
call [ebx + NET_DEVICE.transmit]
ret
 
.error:
add esp, 6
.arp_error:
add esp, 8+4+4
.too_large:
DEBUGF 2,"IPv4_output_raw: Failed\n"
sub edi, edi
ret
 
 
;--------------------------------------------------------
;
;
; IN: dword [esp] = pointer to buffer containing ipv4 packet to be fragmented
; dword [esp+4] = buffer size
; esi = pointer to ip header in that buffer
; ecx = max size of fragments
;
; OUT: /
;
;--------------------------------------------------------
 
align 4
IPv4_fragment:
 
DEBUGF 1,"IPv4_fragment\n"
 
and ecx, not 111b ; align 4
 
cmp ecx, sizeof.IPv4_header + 8 ; must be able to put at least 8 bytes
jb .err2
 
push esi ecx
mov eax, [esi + IPv4_header.DestinationAddress]
call ARP_IP_to_MAC
pop ecx esi
cmp eax, -1
jz .err2
 
push ebx
push ax
 
mov ebx, [NET_DRV_LIST]
lea eax, [ebx + ETH_DEVICE.mac]
push eax
 
 
push esi ; ptr to ip header
sub ecx, sizeof.IPv4_header ; substract header size
push ecx ; max data size
push dword 0 ; offset
 
.new_fragment:
DEBUGF 1,"Ipv4_fragment: new fragment"
 
 
mov eax, [esp + 3*4]
lea ebx, [esp + 4*4]
mov di , ETHER_IPv4
call ETH_output
 
cmp edi, -1
jz .err
 
; copy header
mov esi, [esp + 2*4]
mov ecx, 5 ; 5 dwords: TODO: use IHL field of the header!
rep movsd
 
; copy data
mov esi, [esp + 2*4]
add esi, sizeof.IPv4_header
add esi, [esp] ; offset
 
mov ecx, [esp + 1*4]
DEBUGF 1,"IPv4_fragment: copying %u bytes\n", ecx
rep movsb
 
; now, correct header
mov ecx, [esp + 1*4]
add ecx, sizeof.IPv4_header
xchg cl, ch
mov [edi + IPv4_header.TotalLength], cx
 
mov ecx, [esp] ; offset
xchg cl, ch
 
; cmp dword[esp + 4*4], 0 ; last fragment?;<<<<<<
; je .last_fragment
or cx, 1 shl 2 ; more fragments
; .last_fragment:
mov [edi + IPv4_header.FlagsAndFragmentOffset], cx
 
mov [edi + IPv4_header.HeaderChecksum], 0
 
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet
mov ecx, [esp + 1*4]
 
push edx eax
IPv4_checksum edi
 
call [ebx + NET_DEVICE.transmit]
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
mov ecx, [esp+4]
add [esp], ecx
 
mov ecx, [esp+3*4+6+4] ; ptr to begin of buff
add ecx, [esp+3*4+6+4+4] ; buff size
sub ecx, [esp+2*4] ; ptr to ip header
add ecx, [esp] ; offset
 
DEBUGF 1,"Ipv4_fragment: %u bytes remaining\n", ecx
 
cmp ecx, [esp+1*4]
jae .new_fragment
 
mov [esp+4], ecx ; set fragment size to remaining packet size
jmp .new_fragment
 
.err:
DEBUGF 1,"Ipv4_fragment: failed\n"
.done:
add esp, 12 + 4 + 6
.err2:
DEBUGF 1,"Ipv4_fragment: dumping\n"
call kernel_free
add esp, 4
 
ret
 
 
 
;---------------------------------------------------------------------------
;
; IPv4_route
;
; IN: eax = Destination IP
; OUT: edi = device id * 4
; eax = ip of gateway if nescessary, unchanged otherwise
;
;---------------------------------------------------------------------------
align 4
IPv4_route:
 
cmp eax, 0xffffffff
je .broadcast
 
xor edi, edi
mov ecx, MAX_NET_DEVICES
.loop:
mov ebx, [IP_LIST+edi]
and ebx, [SUBNET_LIST+edi]
jz .next
mov edx, eax
and edx, [SUBNET_LIST+edi]
 
cmp ebx, edx
je .found_it
.next:
add edi, 4
dec ecx
jnz .loop
 
.invalid:
xor edi, edi ; if none found, use device 0 as default
mov eax, [GATEWAY_LIST]
 
.found_it:
DEBUGF 1,"IPv4_dest_to_dev: %u\n", edi
ret
 
.broadcast:
xor edi, edi
ret
 
 
 
;---------------------------------------------------------------------------
;
; IPv4_get_frgmnt_num
;
; IN: /
; OUT: fragment number in ax
;
;---------------------------------------------------------------------------
align 4
IPv4_get_frgmnt_num:
xor ax, ax ;;; TODO: replace this with real code
 
ret
 
 
;---------------------------------------------------------------------------
;
; IPv4_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
align 4
IPv4_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0x000000ff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .read_ip ; 2
dd .write_ip ; 3
dd .read_dns ; 4
dd .write_dns ; 5
dd .read_subnet ; 6
dd .write_subnet ; 7
dd .read_gateway ; 8
dd .write_gateway ; 9
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [IP_packets_tx + eax]
ret
 
.packets_rx:
mov eax, [IP_packets_rx + eax]
ret
 
.read_ip:
mov eax, [IP_LIST + eax]
ret
 
.write_ip:
mov [IP_LIST + eax], ecx
mov edi, eax ; device number, we'll need it for ARP
 
; pre-calculate the local broadcast address
mov ebx, [SUBNET_LIST + eax]
not ebx
or ebx, ecx
mov [BROADCAST_LIST + eax], ebx
 
mov eax, ecx
call ARP_output_request ; now send a gratuitous ARP
 
call NET_send_event
xor eax, eax
ret
 
.read_dns:
mov eax, [DNS_LIST + eax]
ret
 
.write_dns:
mov [DNS_LIST + eax], ecx
call NET_send_event
xor eax, eax
ret
 
.read_subnet:
mov eax, [SUBNET_LIST + eax]
ret
 
.write_subnet:
mov [SUBNET_LIST + eax], ecx
 
; pre-calculate the local broadcast address
mov ebx, [IP_LIST + eax]
not ecx
or ecx, ebx
mov [BROADCAST_LIST + eax], ecx
 
call NET_send_event
xor eax, eax
ret
 
.read_gateway:
mov eax, [GATEWAY_LIST + eax]
ret
 
.write_gateway:
mov [GATEWAY_LIST + eax], ecx
 
call NET_send_event
xor eax, eax
ret
/kernel/branches/Kolibri-acpi/network/IPv6.inc
0,0 → 1,298
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2012-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IPv6.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3251 $
 
 
struct IPv6_header
 
VersionTrafficFlow dd ? ; Version[0-3], Traffic class[4-11], Flow Label [12-31]
PayloadLength dw ? ; 16 bits, unsigned length of payload (extension headers are part of this)
NextHeader db ? ; Values are same as in IPv4 'Protocol' field
HopLimit db ? ; Decremented by every node, packet is discarded when it reaches 0
SourceAddress rd 4 ; 128-bit addresses
DestinationAddress rd 4 ;
Payload rb 0
 
ends
 
 
align 4
uglobal
 
IPv6:
.addresses rd 4*MAX_NET_DEVICES
.subnet rd 4*MAX_NET_DEVICES
.dns rd 4*MAX_NET_DEVICES
.gateway rd 4*MAX_NET_DEVICES
 
.packets_tx rd MAX_NET_DEVICES
.packets_rx rd MAX_NET_DEVICES
 
endg
 
 
;-----------------------------------------------------------------
;
; IPv6_init
;
; This function resets all IP variables
;
;-----------------------------------------------------------------
macro IPv6_init {
 
xor eax, eax
mov edi, IPv6
mov ecx, (4*4*4+2*4)MAX_IP
rep stosd
 
}
 
 
 
;-----------------------------------------------------------------
;
; IPv6_input:
;
; Will check if IPv6 Packet isnt damaged
; and call appropriate handler. (TCP/UDP/ICMP/..)
;
; It will also re-construct fragmented packets
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; pointer to IPv6 header in edx
; size of IPv6 packet in ecx
; OUT: /
;
;-----------------------------------------------------------------
align 4
IPv6_input:
 
DEBUGF 2,"IPv6_input from: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\
[edx + IPv6_header.SourceAddress + 0]:2,[edx + IPv6_header.SourceAddress + 1]:2,\
[edx + IPv6_header.SourceAddress + 2]:2,[edx + IPv6_header.SourceAddress + 3]:2,\
[edx + IPv6_header.SourceAddress + 4]:2,[edx + IPv6_header.SourceAddress + 5]:2,\
[edx + IPv6_header.SourceAddress + 6]:2,[edx + IPv6_header.SourceAddress + 7]:2,\
[edx + IPv6_header.SourceAddress + 8]:2,[edx + IPv6_header.SourceAddress + 9]:2,\
[edx + IPv6_header.SourceAddress + 10]:2,[edx + IPv6_header.SourceAddress + 11]:2,\
[edx + IPv6_header.SourceAddress + 12]:2,[edx + IPv6_header.SourceAddress + 13]:2,\
[edx + IPv6_header.SourceAddress + 14]:2,[edx + IPv6_header.SourceAddress + 15]:2
 
DEBUGF 1,"IPv6_input to: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\
[edx + IPv6_header.DestinationAddress + 0]:2,[edx + IPv6_header.DestinationAddress + 1]:2,\
[edx + IPv6_header.DestinationAddress + 2]:2,[edx + IPv6_header.DestinationAddress + 3]:2,\
[edx + IPv6_header.DestinationAddress + 4]:2,[edx + IPv6_header.DestinationAddress + 5]:2,\
[edx + IPv6_header.DestinationAddress + 6]:2,[edx + IPv6_header.DestinationAddress + 7]:2,\
[edx + IPv6_header.DestinationAddress + 8]:2,[edx + IPv6_header.DestinationAddress + 9]:2,\
[edx + IPv6_header.DestinationAddress + 10]:2,[edx + IPv6_header.DestinationAddress + 11]:2,\
[edx + IPv6_header.DestinationAddress + 12]:2,[edx + IPv6_header.DestinationAddress + 13]:2,\
[edx + IPv6_header.DestinationAddress + 14]:2,[edx + IPv6_header.DestinationAddress + 15]:2
 
sub ecx, sizeof.IPv6_header
jb .dump
 
cmp cx, [edx + IPv6_header.PayloadLength]
jb .dump
 
;-------------------------------------------------------------------
; No, it's just a regular IP packet, pass it to the higher protocols
 
.handle_it:
movzx ecx, [edx + IPv6_header.PayloadLength]
lea edi, [edx + IPv6_header.SourceAddress] ; make edi ptr to source and dest IPv6 address
lea esi, [edx + IPv6_header.Payload] ; make esi ptr to data
mov al, [edx + IPv6_header.NextHeader]
 
.scan:
cmp al, 59 ; no next
je .dump
 
cmp al, 0
je .hop_by_hop
 
cmp al, 43
je .routing
 
cmp al, 44
je .fragment
 
cmp al, 60
je .dest_opts
 
; cmp al, IP_PROTO_TCP
; je TCP_input
 
; cmp al, IP_PROTO_UDP
; je UDP_input
 
; cmp al, 58
; je ICMP6_input
 
DEBUGF 2,"IPv6_input - unknown protocol: %u\n", al
 
.dump:
DEBUGF 1,"IPv6_input - dumping\n"
call kernel_free
add esp, 4
 
ret
 
.dump_options:
add esp, 2+4+4
jmp .dump
 
.nextheader:
pop esi
pop ecx
pop ax
jmp .scan
 
;-------------------------
; Hop-by-Hop
 
.hop_by_hop:
DEBUGF 1,"IPv6_input - hop by hop\n"
pushw [esi] ; 8 bit identifier for option type
movzx eax, byte[esi + 1] ; Hdr Ext Len
inc eax ; first 8 octets not counted
shl eax, 3 ; * 8
sub ecx, eax
push ecx
add eax, esi
push eax
inc esi
inc esi
 
mov al, [esi]
 
cmp al, 0
je .pad_1
 
cmp al, 1
je .pad_n
 
; TODO: check with other known options
 
; unknown option.. discard packet or not?
; check highest two bits
test al, 0xc0 ; discard packet
jnz .dump_options
 
.pad_n:
movzx eax, byte[esi + 1]
DEBUGF 1,"IPv6_input - pad %u\n", eax
inc esi
inc esi
add esi, eax
sub ecx, eax
jmp .hop_by_hop
 
.pad_1:
DEBUGF 1,"IPv6_input - pad 1\n"
inc esi
dec ecx
jmp .hop_by_hop
 
 
 
.dest_opts:
DEBUGF 1,"IPv6_input - dest opts\n"
jmp .nextheader
 
.routing:
DEBUGF 1,"IPv6_input - routing\n"
pushw [esi] ; 8 bit identifier for option type
movzx eax, byte[esi + 1] ; Hdr Ext Len
inc eax ; first 8 octets not counted
shl eax, 3 ; * 8
sub ecx, eax
push ecx
add eax, esi
push eax
inc esi
inc esi
 
cmp al, 0
je .pad_1
 
cmp al, 1
je .pad_n
 
mov al, [esi] ; routing type
 
jmp .nextheader
 
.fragment:
DEBUGF 1,"IPv6_input - fragment\n"
 
jmp .nextheader
 
 
 
 
 
 
;---------------------------------------------------------------------------
;
; IPv6_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
align 4
IPv6_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0x000000ff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
; dd .read_ip ; 2
; dd .write_ip ; 3
; dd .read_dns ; 4
; dd .write_dns ; 5
; dd .read_subnet ; 6
; dd .write_subnet ; 7
; dd .read_gateway ; 8
; dd .write_gateway ; 9
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [IPv6.packets_tx + eax]
ret
 
.packets_rx:
mov eax, [IPv6.packets_rx + eax]
ret
 
/kernel/branches/Kolibri-acpi/network/PPPoE.inc
0,0 → 1,340
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; PPPoE.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
struct PPPoE_frame
VersionAndType db ?
Code db ?
SessionID dw ?
Length dw ? ; Length of payload, does NOT include the length PPPoE header.
Payload rb 0
ends
 
uglobal
PPPoE_SID dw ?
PPPoE_MAC dp ?
endg
 
;-----------------------------------------------------------------
;
; PPPoE_init
;
; This function resets all IP variables
;
;-----------------------------------------------------------------
macro PPPoE_init {
 
call PPPoE_stop_connection
 
}
 
 
;-----------------------------------------------------------------
;
; PPPoE discovery input
;
; Handler of received Ethernet packet with type = Discovery
;
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; pointer to PPP header in edx
; size of PPP packet in ecx
; OUT: /
;
;-----------------------------------------------------------------
align 4
PPPoE_discovery_input:
 
DEBUGF 2,"PPPoE_discovery_input\n"
 
; First, find open PPPoE socket
 
mov eax, net_sockets
 
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump
 
cmp [eax + SOCKET.Domain], AF_PPP
jne .next_socket
 
cmp [eax + SOCKET.Protocol], PPP_PROTO_ETHERNET
jne .next_socket
 
; Now, send it to the this socket
 
mov ecx, [esp + 4]
mov esi, [esp]
 
jmp SOCKET_input
 
.dump:
DEBUGF 1,'PPPoE_discovery_input: dumping\n'
call kernel_free
add esp, 4
ret
 
 
;--------------------------------------
;
; Send discovery packet
;
; IN: eax = socket pointer
; ecx = number of bytes to send
; esi = pointer to data
;
;--------------------------------------
 
align 4
PPPoE_discovery_output:
 
DEBUGF 2,"PPPoE_discovery_output: socket=%x buffer=%x size=%d\n", eax, esi, ecx
 
; RFC2516: An entire PADI packet (including the PPPoE header) MUST NOT
; exceed 1484 octets.
cmp ecx, 1484 + 14
ja .bad
 
; Check that device exists and is ethernet device
mov ebx, [eax + SOCKET.device]
 
cmp ebx, MAX_NET_DEVICES
ja .bad
 
mov ebx, [NET_DRV_LIST + 4*ebx]
test ebx, ebx
jz .bad
 
cmp [ebx + NET_DEVICE.type], NET_TYPE_ETH
jne .bad
 
DEBUGF 2,"PPPoE_discovery_output: device=%x\n", ebx
 
; Create packet.
push ecx esi
stdcall kernel_alloc, 1500
pop esi ecx
test eax, eax
jz .bad
 
mov edx, ecx
mov edi, eax
rep movsb
 
cmp edx, 60 ; Min ETH size
ja @f
mov edx, 60
@@:
 
push edx eax ; size and packet ptr for driver send proc
 
; Overwrite source MAC and protocol type
lea edi, [eax + ETH_header.SrcMAC]
lea esi, [ebx + ETH_DEVICE.mac]
movsd
movsw
cmp word[edi], ETHER_PPP_SESSION ; Allow only PPP_discovery, or LCP
je @f
mov ax, ETHER_PPP_DISCOVERY
stosw
@@:
 
; And send the packet
call [ebx + NET_DEVICE.transmit]
 
xor eax, eax
ret
 
.bad:
or eax, -1
ret
 
 
;-----------------------------------------------------------------
;
; PPPoE session input
;
; Handler of received Ethernet packet with type = Session
;
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; pointer to PPP header in edx
; size of PPP packet in ecx
; OUT: /
;
;-----------------------------------------------------------------
align 4
PPPoE_session_input:
 
cmp [edx + PPPoE_frame.VersionAndType], 0x11
jne .dump
 
cmp [edx + PPPoE_frame.Code], 0x00
jne .dump
 
movzx ecx, [edx + PPPoE_frame.Length]
xchg cl, ch
 
mov ax, [edx + PPPoE_frame.SessionID]
DEBUGF 2,"PPPoE_input: session ID=%x, length=%u\n", ax, cx
cmp ax, [PPPoE_SID]
jne .dump
 
mov ax, word [edx + PPPoE_frame.Payload]
add edx, PPPoE_frame.Payload + 2
 
cmp ax, PPP_IPv4
je IPv4_input
 
; cmp ax, PPP_IPv6
; je IPv6_input
 
jmp PPPoE_discovery_input ; Send LCP,CHAP,CBCP,... packets to the PPP dialer
DEBUGF 2,"PPPoE_input: Unknown protocol=%x\n", ax
 
.dump:
DEBUGF 2,"PPPoE_input: dumping\n"
call kernel_free
add esp, 4
ret
 
 
 
 
;-----------------------------------------------------------------
;
; PPPoE_output
;
; IN:
; ebx = device ptr
; ecx = packet size
;
; di = protocol
;
; OUT: edi = 0 on error, pointer to buffer otherwise
; eax = buffer start
; ebx = to device structure
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
;
;-----------------------------------------------------------------
align 4
PPPoE_output:
 
DEBUGF 1,"PPPoE_output: size=%u device=%x\n", ecx, ebx
 
pushw di
pushw [PPPoE_SID]
 
lea eax, [ebx + ETH_DEVICE.mac]
lea edx, [PPPoE_MAC]
add ecx, PPPoE_frame.Payload + 2
mov di, ETHER_PPP_SESSION
call ETH_output
jz .eth_error
 
sub ecx, PPPoE_frame.Payload
mov [edi + PPPoE_frame.VersionAndType], 0x11
mov [edi + PPPoE_frame.Code], 0
popw [edi + PPPoE_frame.SessionID]
xchg cl, ch
mov [edi + PPPoE_frame.Length], cx
xchg cl, ch
 
pop word [edi + PPPoE_frame.Payload]
 
sub ecx, 2
add edi, PPPoE_frame.Payload + 2
 
DEBUGF 1,"PPPoE_output: success!\n"
ret
 
 
.eth_error:
add esp, 4
xor edi, edi
 
ret
 
 
PPPoE_start_connection:
 
DEBUGF 2,"PPPoE_start_connection: %x\n", cx
 
cmp [PPPoE_SID], 0
jne .fail
 
mov [PPPoE_SID], cx
mov dword [PPPoE_MAC], edx
mov word [PPPoE_MAC + 4], si
 
xor eax, eax
ret
 
.fail:
or eax, -1
ret
 
 
align 4
PPPoE_stop_connection:
 
DEBUGF 2,"PPPoE_stop_connection\n"
 
xor eax, eax
mov [PPPoE_SID], ax
mov dword [PPPoE_MAC], eax
mov word [PPPoE_MAC + 4], ax
 
ret
 
 
;---------------------------------------------------------------------------
;
; PPPoE API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
align 4
PPPoE_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd PPPoE_start_connection ; 0
dd PPPoE_stop_connection ; 1
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
/kernel/branches/Kolibri-acpi/network/ethernet.inc
0,0 → 1,233
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ETHERNET.INC ;;
;; ;;
;; Ethernet network layer for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3346 $
 
ETH_FRAME_MINIMUM = 60
 
struct ETH_header
 
DstMAC dp ? ; destination MAC-address
SrcMAC dp ? ; source MAC-address
Type dw ? ; type of the upper-layer protocol
 
ends
 
struct ETH_DEVICE NET_DEVICE
 
mac dp ?
 
ends
 
align 4
iglobal
 
ETH_BROADCAST dp 0xffffffffffff
endg
 
;-----------------------------------------------------------------
;
; ETH_input
;
; This function is called by ethernet drivers,
; It pushes the received ethernet packets onto the eth_in_queue
;
; IN: [esp] = Pointer to buffer
; [esp+4] = size of buffer
; ebx = pointer to eth_device
; OUT: /
;
;-----------------------------------------------------------------
align 4
ETH_input:
mov eax, [esp]
mov ecx, [esp+4]
 
DEBUGF 1,"ETH_input: size=%u\n", ecx
cmp ecx, ETH_FRAME_MINIMUM
jb .dump
sub ecx, sizeof.ETH_header
 
lea edx, [eax + sizeof.ETH_header]
mov ax, [eax + ETH_header.Type]
 
cmp ax, ETHER_IPv4
je IPv4_input
 
cmp ax, ETHER_ARP
je ARP_input
 
cmp ax, ETHER_IPv6
je IPv6_input
 
cmp ax, ETHER_PPP_DISCOVERY
je PPPoE_discovery_input
 
cmp ax, ETHER_PPP_SESSION
je PPPoE_session_input
 
DEBUGF 2,"ETH_input: Unknown packet type=%x\n", ax
 
.dump:
DEBUGF 2,"ETH_input: dumping\n"
call kernel_free
add esp, 4
ret
 
;-----------------------------------------------------------------
;
; ETH_output
;
; IN: eax = pointer to source mac
; ebx = device ptr
; ecx = packet size
; edx = pointer to destination mac
; di = protocol
;
; OUT: edi = 0 on error, pointer to buffer otherwise
; eax = buffer start
; ebx = to device structure
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
;
;-----------------------------------------------------------------
align 4
ETH_output:
 
DEBUGF 1,"ETH_output: size=%u device=%x\n", ecx, ebx
 
cmp ecx, [ebx + NET_DEVICE.mtu]
ja .exit
 
push ecx
push di eax edx
 
add ecx, sizeof.ETH_header
stdcall kernel_alloc, ecx
test eax, eax
jz .out_of_ram
mov edi, eax
 
pop esi
movsd
movsw
pop esi
movsd
movsw
pop ax
stosw
 
lea eax, [edi - sizeof.ETH_header] ; Set eax to buffer start
pop ecx
lea edx, [ecx + sizeof.ETH_header] ; Set edx to complete buffer size
 
cmp edx, ETH_FRAME_MINIMUM
jbe .adjust_size
.done:
DEBUGF 1,"ETH_output: ptr=%x size=%u\n", eax, edx
ret
 
.adjust_size:
mov edx, ETH_FRAME_MINIMUM
test edx, edx ; clear zero flag
jmp .done
 
.out_of_ram:
DEBUGF 2,"ETH_output: Out of ram!\n"
add esp, 4+4+2+4
sub edi, edi
ret
 
.exit:
DEBUGF 2,"ETH_output: Packet too large!\n"
sub edi, edi
ret
 
 
 
;-----------------------------------------------------------------
;
; ETH_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;-----------------------------------------------------------------
align 4
ETH_api:
 
cmp bh, MAX_NET_DEVICES
ja .error
movzx eax, bh
mov eax, dword [NET_DRV_LIST + 4*eax]
cmp [eax + NET_DEVICE.type], NET_TYPE_ETH
jne .error
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .bytes_tx ; 2
dd .bytes_rx ; 3
dd .read_mac ; 4
dd .state ; 5
.number = ($ - .table) / 4 - 1
 
.error:
or eax, -1
ret
 
.packets_tx:
mov eax, [eax + NET_DEVICE.packets_tx]
 
ret
 
.packets_rx:
mov eax, [eax + NET_DEVICE.packets_rx]
ret
 
.bytes_tx:
mov ebx, dword [eax + NET_DEVICE.bytes_tx + 4]
mov eax, dword [eax + NET_DEVICE.bytes_tx]
mov [esp+20+4], ebx ; TODO: fix this ugly code
ret
 
.bytes_rx:
mov ebx, dword [eax + NET_DEVICE.bytes_rx + 4]
mov eax, dword [eax + NET_DEVICE.bytes_rx]
mov [esp+20+4], ebx ; TODO: fix this ugly code
ret
 
 
.read_mac:
movzx ebx, word [eax + ETH_DEVICE.mac]
mov eax, dword [eax + ETH_DEVICE.mac + 2]
mov [esp+20+4], ebx ; TODO: fix this ugly code
ret
 
.state:
mov eax, [eax + NET_DEVICE.state]
ret
 
/kernel/branches/Kolibri-acpi/network/icmp.inc
1,193 → 1,431
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ICMP.INC ;;
;; ;;
;; Internet Control Message Protocol ( RFC 792 ) ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Last revision: 11.11.2006 ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; This file contains the following: ;;
;; icmp_rx - processes ICMP-packets received by the IP layer ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Changes history: ;;
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;;
;; 11.11.2006 - [Johnny_B] and [smb] ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Current status: ;;
;; This implemetation of ICMP proto supports message of ECHO type.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
$Revision$
 
; ICMP types & codes
 
struc ICMP_PACKET
{ .Type db ? ;+00
.Code db ? ;+01
.Checksum dw ? ;+02
.Identifier dw ? ;+04
.SequenceNumber dw ? ;+06
.Data db ? ;+08
}
ICMP_ECHOREPLY = 0 ; echo reply message
 
virtual at 0
ICMP_PACKET ICMP_PACKET
end virtual
ICMP_UNREACH = 3
ICMP_UNREACH_NET = 0 ; bad net
ICMP_UNREACH_HOST = 1 ; bad host
ICMP_UNREACH_PROTOCOL = 2 ; bad protocol
ICMP_UNREACH_PORT = 3 ; bad port
ICMP_UNREACH_NEEDFRAG = 4 ; IP_DF caused drop
ICMP_UNREACH_SRCFAIL = 5 ; src route failed
ICMP_UNREACH_NET_UNKNOWN = 6 ; unknown net
ICMP_UNREACH_HOST_UNKNOWN = 7 ; unknown host
ICMP_UNREACH_ISOLATED = 8 ; src host isolated
ICMP_UNREACH_NET_PROHIB = 9 ; prohibited access
ICMP_UNREACH_HOST_PROHIB = 10 ; ditto
ICMP_UNREACH_TOSNET = 11 ; bad tos for net
ICMP_UNREACH_TOSHOST = 12 ; bad tos for host
ICMP_UNREACH_FILTER_PROHIB = 13 ; admin prohib
ICMP_UNREACH_HOST_PRECEDENCE = 14 ; host prec vio.
ICMP_UNREACH_PRECEDENCE_CUTOFF = 15 ; prec cutoff
 
ICMP_SOURCEQUENCH = 4 ; Packet lost, slow down
 
; Example:
; ECHO message format
ICMP_REDIRECT = 5 ; shorter route, codes:
ICMP_REDIRECT_NET = 0 ; for network
ICMP_REDIRECT_HOST = 1 ; for host
ICMP_REDIRECT_TOSNET = 2 ; for tos and net
ICMP_REDIRECT_TOSHOST = 3 ; for tos and host
 
ICMP_ALTHOSTADDR = 6 ; alternate host address
ICMP_ECHO = 8 ; echo service
ICMP_ROUTERADVERT = 9 ; router advertisement
ICMP_ROUTERADVERT_NORMAL = 0 ; normal advertisement
ICMP_ROUTERADVERT_NOROUTE_COMMON= 16 ; selective routing
 
ICMP_ROUTERSOLICIT = 10 ; router solicitation
ICMP_TIMXCEED = 11 ; time exceeded, code:
ICMP_TIMXCEED_INTRANS = 0 ; ttl==0 in transit
ICMP_TIMXCEED_REASS = 1 ; ttl==0 in reass
 
ICMP_PARAMPROB = 12 ; ip header bad
ICMP_PARAMPROB_ERRATPTR = 0 ; error at param ptr
ICMP_PARAMPROB_OPTABSENT = 1 ; req. opt. absent
ICMP_PARAMPROB_LENGTH = 2 ; bad length
 
ICMP_TSTAMP = 13 ; timestamp request
ICMP_TSTAMPREPLY = 14 ; timestamp reply
ICMP_IREQ = 15 ; information request
ICMP_IREQREPLY = 16 ; information reply
ICMP_MASKREQ = 17 ; address mask request
ICMP_MASKREPLY = 18 ; address mask reply
ICMP_TRACEROUTE = 30 ; traceroute
ICMP_DATACONVERR = 31 ; data conversion error
ICMP_MOBILE_REDIRECT = 32 ; mobile host redirect
ICMP_IPV6_WHEREAREYOU = 33 ; IPv6 where-are-you
ICMP_IPV6_IAMHERE = 34 ; IPv6 i-am-here
ICMP_MOBILE_REGREQUEST = 35 ; mobile registration req
ICMP_MOBILE_REGREPLY = 36 ; mobile registreation reply
ICMP_SKIP = 39 ; SKIP
 
ICMP_PHOTURIS = 40 ; Photuris
ICMP_PHOTURIS_UNKNOWN_INDEX = 1 ; unknown sec index
ICMP_PHOTURIS_AUTH_FAILED = 2 ; auth failed
ICMP_PHOTURIS_DECRYPT_FAILED = 3 ; decrypt failed
 
 
 
struct ICMP_header
 
Type db ?
Code db ?
Checksum dw ?
Identifier dw ?
SequenceNumber dw ?
 
ends
 
 
align 4
uglobal
ICMP_PACKETS_TX rd MAX_NET_DEVICES
ICMP_PACKETS_RX rd MAX_NET_DEVICES
endg
 
 
 
;-----------------------------------------------------------------
;
; ICMP_init
;
; 0 1 2 3
; 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | Type | Code | Checksum |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | Identifier | Sequence Number |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | Data ...
; +-+-+-+-+-
;
;-----------------------------------------------------------------
 
macro ICMP_init {
 
xor eax, eax
mov edi, ICMP_PACKETS_TX
mov ecx, 2*MAX_NET_DEVICES
rep stosd
 
}
 
 
;-----------------------------------------------------------------
;
; ICMP types & codes, RFC 792 and FreeBSD's ICMP sources
; ICMP_input:
;
; This procedure will send reply's to ICMP echo's
; and insert packets into sockets when needed
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; ebx = pointer to device struct
; ecx = ICMP Packet size
; esi = ptr to ICMP Packet data
; edi = ptr to ipv4 source and dest address
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
ICMP_input:
 
ICMP_ECHOREPLY equ 0 ; echo reply message
DEBUGF 1,"ICMP_input:\n"
 
ICMP_UNREACH equ 3
ICMP_UNREACH_NET equ 0 ; bad net
ICMP_UNREACH_HOST equ 1 ; bad host
ICMP_UNREACH_PROTOCOL equ 2 ; bad protocol
ICMP_UNREACH_PORT equ 3 ; bad port
ICMP_UNREACH_NEEDFRAG equ 4 ; IP_DF caused drop
ICMP_UNREACH_SRCFAIL equ 5 ; src route failed
ICMP_UNREACH_NET_UNKNOWN equ 6 ; unknown net
ICMP_UNREACH_HOST_UNKNOWN equ 7 ; unknown host
ICMP_UNREACH_ISOLATED equ 8 ; src host isolated
ICMP_UNREACH_NET_PROHIB equ 9 ; prohibited access
ICMP_UNREACH_HOST_PROHIB equ 10 ; ditto
ICMP_UNREACH_TOSNET equ 11 ; bad tos for net
ICMP_UNREACH_TOSHOST equ 12 ; bad tos for host
ICMP_UNREACH_FILTER_PROHIB equ 13 ; admin prohib
ICMP_UNREACH_HOST_PRECEDENCE equ 14 ; host prec vio.
ICMP_UNREACH_PRECEDENCE_CUTOFF equ 15 ; prec cutoff
; First, check the checksum (altough some implementations ignore it)
 
ICMP_SOURCEQUENCH equ 4 ; packet lost, slow down
push esi ecx
push [esi + ICMP_header.Checksum]
mov [esi + ICMP_header.Checksum], 0
xor edx, edx
call checksum_1
call checksum_2
pop si
cmp dx, si
pop ecx edx
jne .checksum_mismatch
 
ICMP_REDIRECT equ 5 ; shorter route, codes:
ICMP_REDIRECT_NET equ 0 ; for network
ICMP_REDIRECT_HOST equ 1 ; for host
ICMP_REDIRECT_TOSNET equ 2 ; for tos and net
ICMP_REDIRECT_TOSHOST equ 3 ; for tos and host
cmp [edx + ICMP_header.Type], ICMP_ECHO ; Is this an echo request?
jne .check_sockets
 
ICMP_ALTHOSTADDR equ 6 ; alternate host address
ICMP_ECHO equ 8 ; echo service
ICMP_ROUTERADVERT equ 9 ; router advertisement
ICMP_ROUTERADVERT_NORMAL equ 0 ; normal advertisement
ICMP_ROUTERADVERT_NOROUTE_COMMON equ 16 ; selective routing
; We well re-use the packet so we can create the response as fast as possible
; Notice: this only works on pure ethernet
 
ICMP_ROUTERSOLICIT equ 10 ; router solicitation
ICMP_TIMXCEED equ 11 ; time exceeded, code:
ICMP_TIMXCEED_INTRANS equ 0 ; ttl==0 in transit
ICMP_TIMXCEED_REASS equ 1 ; ttl==0 in reass
DEBUGF 1,"got echo request\n"
mov [edx + ICMP_header.Type], ICMP_ECHOREPLY ; Change Packet type to reply
 
ICMP_PARAMPROB equ 12 ; ip header bad
ICMP_PARAMPROB_ERRATPTR equ 0 ; error at param ptr
ICMP_PARAMPROB_OPTABSENT equ 1 ; req. opt. absent
ICMP_PARAMPROB_LENGTH equ 2 ; bad length
mov esi, [esp] ; Start of buffer
 
ICMP_TSTAMP equ 13 ; timestamp request
ICMP_TSTAMPREPLY equ 14 ; timestamp reply
ICMP_IREQ equ 15 ; information request
ICMP_IREQREPLY equ 16 ; information reply
ICMP_MASKREQ equ 17 ; address mask request
ICMP_MASKREPLY equ 18 ; address mask reply
ICMP_TRACEROUTE equ 30 ; traceroute
ICMP_DATACONVERR equ 31 ; data conversion error
ICMP_MOBILE_REDIRECT equ 32 ; mobile host redirect
ICMP_IPV6_WHEREAREYOU equ 33 ; IPv6 where-are-you
ICMP_IPV6_IAMHERE equ 34 ; IPv6 i-am-here
ICMP_MOBILE_REGREQUEST equ 35 ; mobile registration req
ICMP_MOBILE_REGREPLY equ 36 ; mobile registreation reply
ICMP_SKIP equ 39 ; SKIP
cmp dword[edi + 4], 1 shl 24 + 127
je .loopback
 
ICMP_PHOTURIS equ 40 ; Photuris
ICMP_PHOTURIS_UNKNOWN_INDEX equ 1 ; unknown sec index
ICMP_PHOTURIS_AUTH_FAILED equ 2 ; auth failed
ICMP_PHOTURIS_DECRYPT_FAILED equ 3 ; decrypt failed
; Update stats (and validate device ptr)
call NET_ptr_to_num
cmp edi,-1
je .dump
inc [ICMP_PACKETS_RX + 4*edi]
inc [ICMP_PACKETS_TX + 4*edi]
 
; exchange dest and source address in IP header
; exchange dest and source MAC in ETH header
push dword [esi + ETH_header.DstMAC]
push dword [esi + ETH_header.SrcMAC]
pop dword [esi + ETH_header.DstMAC]
pop dword [esi + ETH_header.SrcMAC]
push word [esi + ETH_header.DstMAC + 4]
push word [esi + ETH_header.SrcMAC + 4]
pop word [esi + ETH_header.DstMAC + 4]
pop word [esi + ETH_header.SrcMAC + 4]
add esi, sizeof.ETH_header-2
 
;***************************************************************************
; Function
; icmp_rx [by Johnny_B]
.loopback:
add esi, 2
push [esi + IPv4_header.SourceAddress]
push [esi + IPv4_header.DestinationAddress]
pop [esi + IPv4_header.SourceAddress]
pop [esi + IPv4_header.DestinationAddress]
 
; Recalculate ip header checksum
movzx ecx, [esi + IPv4_header.VersionAndIHL] ; Calculate IP Header length by using IHL field
and ecx, 0x0f
shl cx, 2
mov edi, ecx ; IP header length
mov eax, edx ; ICMP packet start addr
 
push esi ; Calculate the IP checksum
xor edx, edx ;
call checksum_1 ;
call checksum_2 ;
pop esi ;
mov [esi + IPv4_header.HeaderChecksum], dx ;
 
; Recalculate ICMP CheckSum
movzx ecx, [esi + IPv4_header.TotalLength] ; Find length of IP Packet
xchg ch, cl ;
sub ecx, edi ; IP packet length - IP header length = ICMP packet length
 
mov esi, eax ; Calculate ICMP checksum
xor edx, edx ;
call checksum_1 ;
call checksum_2 ;
mov [eax + ICMP_header.Checksum], dx ;
 
; Transmit the packet (notice that packet ptr and packet size have been on stack since start of the procedure!)
call [ebx + NET_DEVICE.transmit]
ret
 
 
 
 
.check_sockets:
; Look for an open ICMP socket
 
mov esi, [edi] ; ipv4 source address
mov eax, net_sockets
.try_more:
; mov , [edx + ICMP_header.Identifier]
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
 
cmp [eax + SOCKET.Protocol], IP_PROTO_ICMP
jne .next_socket
 
cmp [eax + IP_SOCKET.RemoteIP], esi
jne .next_socket
 
; cmp [eax + ICMP_SOCKET.Identifier],
; jne .next_socket
 
; call IPv4_dest_to_dev
; cmp edi,-1
; je .dump
; inc [ICMP_PACKETS_RX+edi]
 
DEBUGF 1,"socket=%x\n", eax
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
mov esi, edx
jmp SOCKET_input
 
 
.checksum_mismatch:
DEBUGF 1,"checksum mismatch\n"
 
.dump:
DEBUGF 1,"ICMP_input: dumping\n"
 
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
 
;-----------------------------------------------------------------
;
; Description
; ICMP protocol handler
; This is a kernel function, called by ip_rx
; ICMP_output
;
; IN:
; buffer_number - # of IP-buffer. This buffer must be reused or marked as empty afterwards
; IPPacketBase - IP_PACKET base address
; IPHeaderLength - Header length of IP_PACKET
; IN: eax = dest ip
; ebx = source ip
; ecx = data length
; dh = type
; dl = code
; esi = data offset
; edi = identifier shl 16 + sequence number
;
; OUT:
; EAX=not defined
;-----------------------------------------------------------------
align 4
ICMP_output:
 
DEBUGF 1,"Creating ICMP Packet\n"
 
push esi edi dx
 
mov edx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
add ecx, sizeof.ICMP_header
mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL
call IPv4_output
jz .exit
 
DEBUGF 1,"full icmp packet size: %u\n", edx
 
pop word [edi + ICMP_header.Type] ; Write both type and code bytes at once
pop dword [edi + ICMP_header.Identifier] ; identifier and sequence number
mov [edi + ICMP_header.Checksum], 0
 
push ebx ecx edx
mov esi, edi
xor edx, edx
call checksum_1
call checksum_2
mov [edi + ICMP_header.Checksum], dx
pop edx ecx ebx esi
 
sub ecx, sizeof.ICMP_header
add edi, sizeof.ICMP_header
push cx
shr cx, 2
rep movsd
pop cx
and cx, 3
rep movsb
 
sub edi, edx ;;; TODO: find a better way to remember start of packet
push edx edi
DEBUGF 1,"Sending ICMP Packet\n"
call [ebx + NET_DEVICE.transmit]
ret
.exit:
DEBUGF 1,"Creating ICMP Packet failed\n"
add esp, 2*4 + 2
ret
 
 
 
 
;-----------------------------------------------------------------
;
; All used registers will be saved
; ICMP_output
;
;***************************************************************************
proc icmp_rx stdcall uses ebx esi edi,\
buffer_number:DWORD,IPPacketBase:DWORD,IPHeaderLength:DWORD
; IN: eax = socket ptr
; ecx = data length
; esi = data offset
;
;-----------------------------------------------------------------
align 4
ICMP_output_raw:
 
mov esi, [IPPacketBase];esi=IP_PACKET base address
mov edi, esi
add edi, [IPHeaderLength];edi=ICMP_PACKET base address
DEBUGF 1,"Creating ICMP Packet for socket %x, data ptr=%x\n", eax, edx
 
cmp byte[edi + ICMP_PACKET.Type], ICMP_ECHO; Is this an echo request? discard if not
jz .icmp_echo
push edx
 
mov eax, [buffer_number]
call freeBuff
jmp .exit
mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL
mov edx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
call IPv4_output
jz .exit
 
.icmp_echo:
pop esi
push edx
push eax
 
; swap the source and destination addresses
mov ecx, [esi + IP_PACKET.DestinationAddress]
mov ebx, [esi + IP_PACKET.SourceAddress]
mov [esi + IP_PACKET.DestinationAddress], ebx
mov [esi + IP_PACKET.SourceAddress], ecx
push edi ecx
DEBUGF 1,"copying %u bytes from %x to %x\n", ecx, esi, edi
rep movsb
pop ecx edi
 
; recalculate the IP header checksum
mov eax, [IPHeaderLength]
stdcall checksum_jb, esi, eax;buf_ptr,buf_size
mov [edi + ICMP_header.Checksum], 0
 
mov byte[esi + IP_PACKET.HeaderChecksum], ah
mov byte[esi + IP_PACKET.HeaderChecksum + 1], al ; ?? correct byte order?
mov esi, edi
xor edx, edx
call checksum_1
call checksum_2
mov [edi + ICMP_header.Checksum], dx
 
mov byte[edi + ICMP_PACKET.Type], ICMP_ECHOREPLY; change the request to a response
mov word[edi + ICMP_PACKET.Checksum], 0; clear ICMP checksum prior to re-calc
DEBUGF 1,"Sending ICMP Packet\n"
call [ebx + NET_DEVICE.transmit]
ret
.exit:
DEBUGF 1,"Creating ICMP Packet failed\n"
add esp, 4
ret
 
; Calculate the length of the ICMP data ( IP payload)
xor eax, eax
mov ah, byte[esi + IP_PACKET.TotalLength]
mov al, byte[esi + IP_PACKET.TotalLength + 1]
sub ax, word[IPHeaderLength];ax=ICMP-packet length
 
stdcall checksum_jb, edi, eax;buf_ptr,buf_size
 
mov byte[edi + ICMP_PACKET.Checksum], ah
mov byte[edi + ICMP_PACKET.Checksum + 1], al
 
; Queue packet for transmission
mov ebx, [buffer_number]
mov eax, NET1OUT_QUEUE
call queue
;-----------------------------------------------------------------
;
; ICMP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;-----------------------------------------------------------------
align 4
ICMP_api:
 
.exit:
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
endp
 
.packets_tx:
mov eax, [ICMP_PACKETS_TX + eax]
ret
 
.packets_rx:
mov eax, [ICMP_PACKETS_RX + eax]
ret
/kernel/branches/Kolibri-acpi/network/loopback.inc
0,0 → 1,126
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; loopback.inc ;;
;; ;;
;; LoopBack device for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 2891 $
 
iglobal
 
LOOPBACK_DEVICE:
.type dd NET_TYPE_LOOPBACK
.mtu dd 4096
.name dd .namestr
 
.unload dd .dummy_fn
.reset dd .dummy_fn
.transmit dd LOOP_input
 
.bytes_tx dq 0
.bytes_rx dq 0
.packets_tx dd 0
.packets_rx dd 0
 
.namestr db 'loopback', 0
 
.dummy_fn:
ret
 
endg
 
;-----------------------------------------------------------------
;
; LOOP_input
;
; IN: [esp+4] = Pointer to buffer
; [esp+8] = size of buffer
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
LOOP_input:
pop ebx
pop eax
pop ecx
 
push ebx
push ecx
push eax
 
DEBUGF 1,"LOOP_input: size=%u\n", ecx
lea edx, [eax + 2]
mov ax, word[eax]
mov ebx, LOOPBACK_DEVICE
 
cmp ax, ETHER_IPv4
je IPv4_input
 
DEBUGF 2,"LOOP_input: Unknown packet type=%x\n", ax
 
.dump:
DEBUGF 2,"LOOP_input: dumping\n"
call kernel_free
add esp, 4
ret
 
;-----------------------------------------------------------------
;
; LOOP_output
;
; IN:
; ecx = packet size
; di = protocol
;
; OUT: edi = 0 on error, pointer to buffer otherwise
; eax = buffer start
; ebx = to device structure
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
;
;-----------------------------------------------------------------
align 4
LOOP_output:
 
DEBUGF 1,"LOOP_output\n"
 
push ecx
push di
 
add ecx, 2
cmp ecx, [LOOPBACK_DEVICE.mtu]
ja .out_of_ram
stdcall kernel_alloc, ecx
test eax, eax
jz .out_of_ram
mov edi, eax
pop ax
stosw
 
lea eax, [edi - 2] ; Set eax to buffer start
pop ecx
lea edx, [ecx + 2] ; Set edx to complete buffer size
mov ebx, LOOPBACK_DEVICE
 
.done:
DEBUGF 2,"LOOP_output: ptr=%x size=%u\n", eax, edx
ret
 
.out_of_ram:
DEBUGF 2,"LOOP_output: failed\n"
add esp, 2+4
sub edi, edi
ret
 
 
/kernel/branches/Kolibri-acpi/network/queue.inc
1,229 → 1,100
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; queue.inc ;;
;; ;;
;; QUEUE.INC ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Buffer queue management for Menuet OS TCP/IP Stack ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; ;;
;; See file COPYING for details ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
;*******************************************************************
; Interface
; The Queues implemented by these macros form a ring-buffer.
; The data to these queue's always looks like this:
;
; queueInit Configures the queues to empty
; dequeue Removes a buffer pointer from a queue
; queue Inserts a buffer pointer into a queue
; freeBuff Adds the buffer pointer to the list of free buffers
; queueSize Returns the number of entries in a queue
;
; The various defines for queue names can be found in stack.inc
;
;*******************************************************************
; At top, you have the queue struct, wich has the size (number of currently queued packets, read and write pointers.
; This struct is followed by a number of slots wich you can read and write to using the macros.
; How these slots look like is up to you to chose, normally they should have at least a pointer to where the real data is.
; (you can see some examples below)
 
 
;***************************************************************************
; Function
; freeBuff
;
; Description
; Adds a buffer number to the beginning of the free list.
; buffer number in eax ( ms word zeroed )
; all other registers preserved
; This always works, so no error returned
;***************************************************************************
;uglobal
; freeBuff_cnt dd ?
;endg
freeBuff:
; inc [freeBuff_cnt]
; DEBUGF 1, "K : freeBuff (%u)\n", [freeBuff_cnt]
push ebx
push ecx
mov ebx, queues + EMPTY_QUEUE * 2
cli ; Ensure that another process does not interfer
mov cx, [ebx]
mov [ebx], ax
mov [queueList + eax * 2], cx
sti
pop ecx
pop ebx
struct queue
 
ret
size dd ? ; number of queued packets in this queue
w_ptr dd ? ; current writing pointer in queue
r_ptr dd ? ; current reading pointer
 
ends
 
;***************************************************************************
; Function
; queueSize
;
; Description
; Counts the number of entries in a queue
; queue number in ebx ( ms word zeroed )
; Queue size returned in eax
; This always works, so no error returned
;***************************************************************************
queueSize:
xor eax, eax
shl ebx, 1
add ebx, queues
movzx ecx, word [ebx]
cmp cx, NO_BUFFER
je qs_exit
; The following macros share these inputs:
 
qs_001:
inc eax
shl ecx, 1
add ecx, queueList
movzx ecx, word [ecx]
cmp cx, NO_BUFFER
je qs_exit
jmp qs_001
; ptr = pointer to where the queue data is located
; size = number of slots/entrys in the queue
; entry_size = size of one slot, in bytes
; failaddr = the address where macro will jump to when there is no data in the queue
 
qs_exit:
ret
; additionally, add_to_queue requires you to set esi to the data wich you want to queue
; get_from_queue on the other hand will return a pointer in esi, to the entry you're interessed in
; PS: macros WILL destroy ecx and edi
 
macro add_to_queue ptr, size, entry_size, failaddr {
 
;***************************************************************************
; Function
; queue
;
; Description
; Adds a buffer number to the *end* of a queue
; This is quite quick because these queues will be short
; queue number in eax ( ms word zeroed )
; buffer number in ebx ( ms word zeroed )
; all other registers preserved
; This always works, so no error returned
;***************************************************************************
;uglobal
; queue_cnt dd ?
;endg
queue:
; inc [queue_cnt]
; DEBUGF 1, "K : queue (%u)\n", [queue_cnt]
push ebx
shl ebx, 1
add ebx, queueList ; eax now holds address of queue entry
mov [ebx], word NO_BUFFER; This buffer will be the last
cmp [ptr + queue.size], size ; Check if queue isnt full
jae failaddr
 
cli
shl eax, 1
add eax, queues ; eax now holds address of queue
movzx ebx, word [eax]
inc [ptr + queue.size] ; if not full, queue one more
 
cmp bx, NO_BUFFER
jne qu_001
mov edi, [ptr + queue.w_ptr] ; Current write pointer (FIFO!)
mov ecx, entry_size/4 ; Write the queue entry
rep movsd ;
 
pop ebx
; The list is empty, so add this to the head
mov [eax], bx
jmp qu_exit
lea ecx, [size*entry_size+ptr+sizeof.queue]
cmp edi, ecx ; entry size
jb .no_wrap
 
qu_001:
; Find the last entry
shl ebx, 1
add ebx, queueList
mov eax, ebx
movzx ebx, word [ebx]
cmp bx, NO_BUFFER
jne qu_001
sub edi, size*entry_size
 
mov ebx, eax
pop eax
mov [ebx], ax
.no_wrap:
mov [ptr + queue.w_ptr], edi
 
qu_exit:
sti
ret
}
 
 
 
;***************************************************************************
; Function
; dequeue
;
; Description
; removes a buffer number from the head of a queue
; This is fast, as it unlinks the first entry in the list
; queue number in eax ( ms word zeroed )
; buffer number returned in eax ( ms word zeroed )
; all other registers preserved
;
;***************************************************************************
;uglobal
; dequeue_cnt dd ?
;endg
dequeue:
push ebx
shl eax, 1
add eax, queues ; eax now holds address of queue
mov ebx, eax
cli
movzx eax, word [eax]
cmp ax, NO_BUFFER
je dq_exit
; inc [dequeue_cnt]
; DEBUGF 1, "K : dequeue (%u)\n", [dequeue_cnt]
push eax
shl eax, 1
add eax, queueList ; eax now holds address of queue entry
mov ax, [eax]
mov [ebx], ax
pop eax
macro get_from_queue ptr, size, entry_size, failaddr {
 
dq_exit:
sti
pop ebx
ret
cmp [ptr + queue.size], 0 ; any packets queued?
je failaddr
 
dec [ptr + queue.size] ; if so, dequeue one
 
;***************************************************************************
; Function
; queueInit
;
; Description
; Initialises the queues to empty, and creates the free queue
; list.
;
;***************************************************************************
queueInit:
mov esi, queues
mov ecx, NUMQUEUES
mov ax, NO_BUFFER
mov esi, [ptr + queue.r_ptr]
push esi
 
qi001:
mov [esi], ax
inc esi
inc esi
loop qi001
add esi, entry_size
 
mov esi, queues + ( 2 * EMPTY_QUEUE )
lea ecx, [size*entry_size+ptr+sizeof.queue]
cmp esi, ecx ; entry size
jb .no_wrap
 
; Initialise empty queue list
sub esi, size*entry_size
 
xor ax, ax
mov [esi], ax
.no_wrap:
mov dword [ptr + queue.r_ptr], esi
 
mov ecx, NUMQUEUEENTRIES - 1
mov esi, queueList
pop esi
 
qi002:
inc ax
mov [esi], ax
inc esi
inc esi
loop qi002
}
 
mov ax, NO_BUFFER
mov [esi], ax
macro init_queue ptr {
 
ret
mov [ptr + queue.size] , 0
lea edi, [ptr + sizeof.queue]
mov [ptr + queue.w_ptr], edi
mov [ptr + queue.r_ptr], edi
}
/kernel/branches/Kolibri-acpi/network/socket.inc
1,1129 → 1,2284
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; SOCKET.INC ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Sockets constants, structures and functions ;;
;; Written by hidnplayr@kolibrios.org, ;;
;; and Clevermouse. ;;
;; ;;
;; This file contains the following: ;;
;; is_localport_unused ;;
;; get_free_socket ;;
;; socket_open ;;
;; socket_open_tcp ;;
;; socket_close ;;
;; socket_close_tcp ;;
;; socket_poll ;;
;; socket_status ;;
;; socket_read ;;
;; socket_write ;;
;; socket_write_tcp ;;
;; Based on code by mike.dld ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; Changes history: ;;
;; 22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net ;;
;; 11.11.2006 - [Johnny_B] and [smb] ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
; socket data structure
 
struct SOCKET
 
NextPtr dd ? ; pointer to next socket in list
PrevPtr dd ? ; pointer to previous socket in list
NextPtr dd ? ; pointer to next socket in list
Number dd ? ; socket number (unique within single process)
PID dd ? ; application process id
LocalIP dd ? ; local IP address
LocalPort dw ? ; local port
RemoteIP dd ? ; remote IP address
RemotePort dw ? ; remote port
OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state)
OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
rxDataCount dd ? ; rx data count
TCBState dd ? ; TCB state
TCBTimer dd ? ; TCB timer (seconds)
ISS dd ? ; initial send sequence
IRS dd ? ; initial receive sequence
SND_UNA dd ? ; sequence number of unack'ed sent packets
SND_NXT dd ? ; bext send sequence number to use
Number dd ? ; socket number
 
mutex MUTEX
 
PID dd ? ; process ID
TID dd ? ; thread ID
Domain dd ? ; INET/LOCAL/..
Type dd ? ; RAW/STREAM/DGRAP
Protocol dd ? ; ICMP/IPv4/ARP/TCP/UDP
errorcode dd ?
device dd ? ; driver pointer, socket pointer if it's an LOCAL socket
 
options dd ?
state dd ?
backlog dw ? ; how many incoming connections that can be queued
 
snd_proc dd ?
rcv_proc dd ?
 
ends
 
struct IP_SOCKET SOCKET
 
LocalIP rd 4 ; network byte order
RemoteIP rd 4 ; network byte order
 
ends
 
struct TCP_SOCKET IP_SOCKET
 
LocalPort dw ? ; network byte order
RemotePort dw ? ; network byte order
 
t_state dd ? ; TCB state
t_rxtshift db ?
rb 3 ; align
t_rxtcur dd ?
t_dupacks dd ?
t_maxseg dd ?
t_force dd ?
t_flags dd ?
 
;---------------
; RFC783 page 21
 
; send sequence
SND_UNA dd ? ; sequence number of unack'ed sent Packets
SND_NXT dd ? ; next send sequence number to use
SND_UP dd ? ; urgent pointer
SND_WL1 dd ? ; window minus one
SND_WL2 dd ? ;
ISS dd ? ; initial send sequence number
SND_WND dd ? ; send window
 
; receive sequence
RCV_WND dd ? ; receive window
RCV_NXT dd ? ; next receive sequence number to use
RCV_WND dd ? ; receive window
SEG_LEN dd ? ; segment length
SEG_WND dd ? ; segment window
wndsizeTimer dd ? ; window size timer
mutex MUTEX ; lock mutex
rxData dd ? ; receive data buffer here
RCV_UP dd ? ; urgent pointer
IRS dd ? ; initial receive sequence number
 
;---------------------
; Additional variables
 
; receive variables
RCV_ADV dd ?
 
; retransmit variables
SND_MAX dd ?
 
; congestion control
SND_CWND dd ?
SND_SSTHRESH dd ?
 
;----------------------
; Transmit timing stuff
t_idle dd ?
t_rtt dd ?
t_rtseq dd ?
t_srtt dd ?
t_rttvar dd ?
t_rttmin dd ?
max_sndwnd dd ?
 
;-----------------
; Out-of-band data
t_oobflags dd ?
t_iobc dd ?
t_softerror dd ?
 
 
;---------
; RFC 1323 ; the order of next 4 elements may not change
 
SND_SCALE db ?
RCV_SCALE db ?
requested_s_scale db ?
request_r_scale db ?
 
ts_recent dd ? ; a copy of the most-recent valid timestamp from the other end
ts_recent_age dd ?
last_ack_sent dd ?
 
 
;-------
; Timers
timer_retransmission dd ? ; rexmt
timer_persist dd ?
timer_keepalive dd ? ; keepalive/syn timeout
timer_timed_wait dd ? ; also used as 2msl timer
 
; extra
 
ts_ecr dd ? ; timestamp echo reply
ts_val dd ?
 
seg_next dd ? ; re-assembly queue
 
temp_bits db ?
rb 3 ; align
 
ends
 
; TCP opening modes
SOCKET_PASSIVE = 0
SOCKET_ACTIVE = 1
struct UDP_SOCKET IP_SOCKET
 
; socket types
SOCK_STREAM = 1
SOCK_DGRAM = 2
LocalPort dw ? ; network byte order
RemotePort dw ? ; network byte order
firstpacket db ?
 
; pointer to bitmap of free ports (1=free, 0=used)
ends
 
 
struct ICMP_SOCKET IP_SOCKET
 
Identifier dw ?
 
ends
 
 
struct RING_BUFFER
 
mutex MUTEX
start_ptr dd ? ; Pointer to start of buffer
end_ptr dd ? ; pointer to end of buffer
read_ptr dd ? ; Read pointer
write_ptr dd ? ; Write pointer
size dd ? ; Number of bytes buffered
 
ends
 
struct STREAM_SOCKET TCP_SOCKET
 
rcv RING_BUFFER
snd RING_BUFFER
 
ends
 
struct socket_queue_entry
 
data_ptr dd ?
buf_ptr dd ?
data_size dd ?
 
ends
 
 
SOCKETBUFFSIZE = 4096 ; in bytes
 
SOCKET_QUEUE_SIZE = 10 ; maximum number of incoming packets queued for 1 socket
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
SOCKET_QUEUE_LOCATION = (SOCKETBUFFSIZE - SOCKET_QUEUE_SIZE*sizeof.socket_queue_entry - sizeof.queue)
 
uglobal
align 4
network_free_ports dd ?
net_sockets rd 4
last_socket_num dd ?
last_UDP_port dw ? ; These values give the number of the last used ephemeral port
last_TCP_port dw ? ;
endg
 
iglobal
 
;-----------------------------------------------------------------
;
; SOCKET_init
;
;-----------------------------------------------------------------
macro SOCKET_init {
 
xor eax, eax
mov edi, net_sockets
mov ecx, 5
rep stosd
 
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jb @r
cmp ax, MAX_EPHEMERAL_PORT
ja @r
xchg al, ah
mov [last_UDP_port], ax
 
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jb @r
cmp ax, MAX_EPHEMERAL_PORT
ja @r
xchg al, ah
mov [last_TCP_port], ax
 
}
 
;-----------------------------------------------------------------
;
; Socket API (function 74)
;
;-----------------------------------------------------------------
align 4
network_free_hint dd 1024/8
endg
sys_socket:
 
;; Allocate memory for socket data and put new socket into the list
; Newly created socket is initialized with calling PID and number and
; put into beginning of list (which is a fastest way).
cmp ebx, 255
jz SOCKET_debug
 
cmp ebx, .number
ja s_error
jmp dword [.table + 4*ebx]
 
.table:
dd SOCKET_open ; 0
dd SOCKET_close ; 1
dd SOCKET_bind ; 2
dd SOCKET_listen ; 3
dd SOCKET_connect ; 4
dd SOCKET_accept ; 5
dd SOCKET_send ; 6
dd SOCKET_receive ; 7
dd SOCKET_set_opt ; 8
dd SOCKET_get_opt ; 9
dd SOCKET_pair ; 10
.number = ($ - .table) / 4 - 1
 
s_error:
DEBUGF 2,"SOCKET: error\n"
mov dword [esp+32], -1
 
ret
 
;-----------------------------------------------------------------
;
; @return socket structure address in EAX
;;
proc net_socket_alloc stdcall uses ebx ecx edx edi
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "K : net_socket_alloc (0x%x)\n", eax
; check if we can allocate needed amount of memory
or eax, eax
jz .exit
; SOCKET_open
;
; IN: domain in ecx
; type in edx
; protocol in esi
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_open:
 
; zero-initialize allocated memory
DEBUGF 2,"SOCKET_open: domain=%u type=%u protocol=%x ", ecx, edx, esi
 
push ecx edx esi
call SOCKET_alloc
pop esi edx ecx
jz s_error
 
mov [esp+32], edi ; return socketnumber
DEBUGF 2,"socknum=%u\n", edi
 
; push edx
; and edx, SO_NONBLOCK
or [eax + SOCKET.options], SO_NONBLOCK ;edx
; pop edx
; and edx, not SO_NONBLOCK
 
mov [eax + SOCKET.Domain], ecx
mov [eax + SOCKET.Type], edx
mov [eax + SOCKET.Protocol], esi
 
cmp ecx, AF_INET4
jne .no_inet4
 
cmp edx, SOCK_DGRAM
je .udp
 
cmp edx, SOCK_STREAM
je .tcp
 
cmp edx, SOCK_RAW
je .raw
 
.no_inet4:
cmp ecx, AF_PPP
jne .no_ppp
 
cmp esi, PPP_PROTO_ETHERNET
je .pppoe
 
.no_ppp:
DEBUGF 2,"Unknown socket family/protocol\n"
ret
 
align 4
.raw:
test esi, esi ; IP_PROTO_IP
jz .ip
 
cmp esi, IP_PROTO_ICMP
je .icmp
 
cmp esi, IP_PROTO_UDP
je .udp
 
cmp esi, IP_PROTO_TCP
je .tcp
 
ret
 
align 4
.udp:
mov [eax + SOCKET.Protocol], IP_PROTO_UDP
mov [eax + SOCKET.snd_proc], SOCKET_send_udp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
 
align 4
.tcp:
mov [eax + SOCKET.Protocol], IP_PROTO_TCP
mov [eax + SOCKET.snd_proc], SOCKET_send_tcp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_stream
 
TCP_init_socket eax
ret
 
 
align 4
.ip:
mov [eax + SOCKET.snd_proc], SOCKET_send_ip
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
 
 
align 4
.icmp:
mov [eax + SOCKET.snd_proc], SOCKET_send_icmp
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
 
align 4
.pppoe:
push eax
mov edi, eax
mov ecx, SOCKETBUFFSIZE / 4
cld
xor eax, eax
rep stosd
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
mov [eax + SOCKET.snd_proc], SOCKET_send_pppoe
mov [eax + SOCKET.rcv_proc], SOCKET_receive_dgram
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_bind
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------------------------
align 4
SOCKET_bind:
 
DEBUGF 2,"SOCKET_bind: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call SOCKET_num_to_ptr
jz s_error
 
cmp esi, 2
jb s_error
 
cmp word [edx], AF_INET4
je .af_inet4
 
cmp word [edx], AF_LOCAL
je .af_local
 
jmp s_error
 
.af_local:
; TODO: write code here
 
mov dword [esp+32], 0
ret
 
.af_inet4:
 
cmp esi, 6
jb s_error
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
jmp s_error
 
.tcp:
.udp:
 
mov ebx, [edx + 4] ; First, fill in the IP
test ebx, ebx ; If IP is 0, use default
jnz @f
mov ebx, [NET_DEFAULT]
mov ebx, [IP_LIST + 4*ebx]
@@:
mov [eax + IP_SOCKET.LocalIP], ebx
 
mov bx, [edx + 2] ; Now fill in the local port if it's still available
call SOCKET_check_port
jz s_error ; ZF is set by socket_check_port, on error
 
DEBUGF 1,"SOCKET_bind: local ip=%u.%u.%u.%u\n",\
[eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\
[eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1
 
mov dword [esp+32], 0
ret
 
 
 
 
;-----------------------------------------------------------------
;
; SOCKET_connect
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
; OUT: 0 on success
;
;-----------------------------------------------------------------
align 4
SOCKET_connect:
 
DEBUGF 2,"SOCKET_connect: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call SOCKET_num_to_ptr
jz s_error
 
cmp esi, 8
jb s_error
 
cmp word [edx], AF_INET4
je .af_inet4
 
jmp s_error
 
.af_inet4:
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IP_LIST] ; FIXME
pop [eax + IP_SOCKET.LocalIP]
@@:
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_IP
je .ip
 
cmp [eax + SOCKET.Protocol], IP_PROTO_ICMP
je .ip
 
jmp s_error
 
align 4
.udp:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
pushw [edx + 2]
pop [eax + UDP_SOCKET.RemotePort]
 
pushd [edx + 4]
pop [eax + IP_SOCKET.RemoteIP]
 
cmp [eax + UDP_SOCKET.LocalPort], 0
jne @f
call SOCKET_find_port
@@:
 
mov [eax + UDP_SOCKET.firstpacket], 0
 
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
mov dword [esp+32], 0
ret
 
align 4
.tcp:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
pushw [edx + 2]
pop [eax + TCP_SOCKET.RemotePort]
 
pushd [edx + 4]
pop [eax + IP_SOCKET.RemoteIP]
 
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
call SOCKET_find_port
@@:
 
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_state], TCPS_SYN_SENT
 
push [TCP_sequence_num]
add [TCP_sequence_num], 6400
pop [eax + TCP_SOCKET.ISS]
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_init
 
 
TCP_sendseqinit eax
 
; mov [ebx + TCP_SOCKET.timer_retransmission], ;; todo: create macro to set retransmission timer
 
mov ebx, eax
lea ecx, [eax+SOCKET.mutex]
call mutex_init
 
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_create
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
mov eax, ebx
call TCP_output
 
; add socket to the list by changing pointers
mov ebx, net_sockets
push [ebx + SOCKET.NextPtr]
mov [ebx + SOCKET.NextPtr], eax
mov [eax + SOCKET.PrevPtr], ebx
pop ebx
mov [eax + SOCKET.NextPtr], ebx
or ebx, ebx
;;; TODO: wait for successfull connection if blocking socket
 
mov dword [esp+32], 0
ret
 
align 4
.ip:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
pushd [edx + 4]
pop [eax + IP_SOCKET.RemoteIP]
 
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
mov dword [esp+32], 0
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_listen
;
; IN: socket number in ecx
; backlog in edx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_listen:
 
DEBUGF 2,"SOCKET_listen: socknum=%u backlog=%u\n", ecx, edx
 
call SOCKET_num_to_ptr
jz s_error
 
cmp [eax + SOCKET.Domain], AF_INET4
jne s_error
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne s_error
 
cmp [eax + TCP_SOCKET.LocalPort], 0
je s_error
 
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IP_LIST]
pop [eax + IP_SOCKET.LocalIP]
@@:
 
cmp edx, MAX_backlog
jbe @f
mov edx, MAX_backlog
@@:
 
mov [eax + SOCKET.backlog], dx
or [eax + SOCKET.options], SO_ACCEPTCON
mov [eax + TCP_SOCKET.t_state], TCPS_LISTEN
mov [eax + TCP_SOCKET.timer_keepalive], 0 ; disable keepalive timer
 
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up sockets queue
pop eax
 
mov dword [esp+32], 0
 
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_accept
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_accept:
 
DEBUGF 2,"SOCKET_accept: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call SOCKET_num_to_ptr
jz s_error
 
test [eax + SOCKET.options], SO_ACCEPTCON
jz s_error
 
cmp [eax + SOCKET.Domain], AF_INET4
jne s_error
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne s_error
 
.loop:
get_from_queue (eax + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .block
 
; Ok, we got a socket ptr
mov eax, [esi]
 
; Change thread ID to that of the current thread
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.TID], ebx
 
; Convert it to a socket number
call SOCKET_ptr_to_num
jz s_error
; and return it to caller
mov [esp+32], eax
ret
 
.block:
test [eax + SOCKET.options], SO_NONBLOCK
jnz s_error
 
call SOCKET_block
jmp .loop
 
;-----------------------------------------------------------------
;
; SOCKET_close
;
; IN: socket number in ecx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_close:
 
DEBUGF 2,"SOCKET_close: socknum=%u\n", ecx
 
call SOCKET_num_to_ptr
jz s_error
 
mov dword [esp+32], 0 ; The socket exists, so we will succeed in closing it.
 
.socket:
or [eax + SOCKET.options], SO_NONBLOCK ; Mark the socket as non blocking, we dont want it to block any longer!
 
test [eax + SOCKET.state], SS_BLOCKED ; Is the socket still in blocked state?
jz @f
mov [ebx + SOCKET.PrevPtr], eax
call SOCKET_notify.unblock ; Unblock it.
@@:
 
@@: ; set socket owner PID to the one of calling process
cmp [eax + SOCKET.Domain], AF_INET4
jne .free
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
.free:
call SOCKET_free
ret
 
.tcp:
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED ; state must be LISTEN, SYN_SENT or CLOSED
jb .free
 
call TCP_usrclosed
call TCP_output ;;;; Fixme: is this nescessary??
 
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_receive
;
; IN: socket number in ecx
; addr to buffer in edx
; length of buffer in esi
; flags in edi
; OUT: eax is number of bytes copied, -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_receive:
 
DEBUGF 2,"SOCKET_receive: socknum=%u bufaddr=%x buflength=%u flags=%x\n", ecx, edx, esi, edi
 
call SOCKET_num_to_ptr
jz s_error
 
jmp [eax + SOCKET.rcv_proc]
 
 
align 4
SOCKET_receive_dgram:
 
DEBUGF 1,"SOCKET_receive: DGRAM\n"
 
mov ebx, esi
mov edi, edx ; addr to buffer
 
.loop:
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, .block ; destroys esi and ecx
 
mov ecx, [esi + socket_queue_entry.data_size]
DEBUGF 1,"SOCKET_receive: %u bytes data\n", ecx
 
cmp ecx, ebx
ja .too_small
 
push [esi + socket_queue_entry.buf_ptr] ; save the buffer addr so we can clear it later
mov esi, [esi + socket_queue_entry.data_ptr]
DEBUGF 1,"SOCKET_receive: Source buffer=%x real addr=%x\n", [esp], esi
mov [esp+32+4], ecx ; return number of bytes copied
 
; copy the data
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
 
call kernel_free ; remove the packet
ret
 
.too_small:
 
DEBUGF 2,"SOCKET_receive: Buffer too small\n"
jmp s_error
 
.block:
test [eax + SOCKET.options], SO_NONBLOCK
jnz s_error
 
call SOCKET_block
jmp .loop
 
 
align 4
SOCKET_receive_local:
 
; does this socket have a PID yet?
cmp [eax + SOCKET.PID], 0
jne @f
 
; Change PID to that of current process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
@@:
 
; find first free socket number and use it
;mov edx, ebx
mov ebx, net_sockets
xor ecx, ecx
.next_socket_number:
inc ecx
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .last_socket_number
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
;cmp [ebx + SOCKET.PID], edx
;jne .next_socket
mov ebx, net_sockets
jmp .next_socket_number
mov [eax + SOCKET.rcv_proc], SOCKET_receive_stream
 
.last_socket_number:
mov [eax + SOCKET.Number], ecx
align 4
SOCKET_receive_stream:
 
.exit:
DEBUGF 1,"SOCKET_receive: STREAM\n"
 
mov ebx, edi
mov ecx, esi
mov edi, edx
xor edx, edx
 
test ebx, MSG_DONTWAIT
jnz .dontwait
.loop:
cmp [eax + STREAM_SOCKET.rcv + RING_BUFFER.size], 0
je .block
.dontwait:
test ebx, MSG_PEEK
jnz .peek
 
add eax, STREAM_SOCKET.rcv
call SOCKET_ring_read
call SOCKET_ring_free
 
mov [esp+32], ecx ; return number of bytes copied
ret
endp
 
;; Free socket data memory and pop socket off the list
.peek:
mov ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size]
mov [esp+32], ecx ; return number of bytes available
ret
 
.block:
test [eax + SOCKET.options], SO_NONBLOCK
jnz .return0
 
call SOCKET_block
jmp .loop
 
.return0:
xor ecx, ecx
mov [esp+32], ecx
ret
 
 
;-----------------------------------------------------------------
;
; @param sockAddr is a socket structure address
;;
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
mov eax, [sockAddr]
DEBUGF 1, "K : net_socket_free (0x%x)\n", eax
; check if we got something similar to socket structure address
or eax, eax
jz .error
; SOCKET_send
;
;
; IN: socket number in ecx
; pointer to data in edx
; datalength in esi
; flags in edi
; OUT: -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_send:
 
; make sure sockAddr is one of the socket addresses in the list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
DEBUGF 2,"SOCKET_send: socknum=%u data ptr=%x length=%u flags=%x\n", ecx, edx, esi, edi
 
; okay, we found the correct one
; mark local port as unused
movzx ebx, [eax + SOCKET.LocalPort]
call SOCKET_num_to_ptr
jz s_error
 
mov ecx, esi
mov esi, edx
 
jmp [eax + SOCKET.snd_proc]
 
 
align 4
SOCKET_send_udp:
 
DEBUGF 1,"SOCKET_send: UDP\n"
 
mov [esp+32], ecx
call UDP_output
cmp eax, -1
je s_error
ret
 
 
align 4
SOCKET_send_tcp:
 
DEBUGF 1,"SOCKET_send: TCP\n"
 
push eax
mov eax, [network_free_ports]
xchg bl, bh
lock bts [eax], ebx
add eax, STREAM_SOCKET.snd
call SOCKET_ring_write
pop eax
; remove it from the list first, changing pointers
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
mov [eax + SOCKET.NextPtr], ebx
or ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
 
@@: ; and finally free the memory structure used
stdcall kernel_free, [sockAddr]
mov [esp+32], ecx
 
call TCP_output
ret
 
.error:
DEBUGF 1, "K : failed\n"
 
align 4
SOCKET_send_ip:
 
DEBUGF 1,"SOCKET_send: IPv4\n"
 
mov [esp+32], ecx
call IPv4_output_raw
cmp eax, -1
je s_error
ret
endp
 
;; Get socket structure address by its number
; Scan through sockets list to find the socket with specified number.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
 
align 4
SOCKET_send_icmp:
 
DEBUGF 1,"SOCKET_send: ICMP\n"
 
mov [esp+32], ecx
call ICMP_output_raw
cmp eax, -1
je s_error
ret
 
 
align 4
SOCKET_send_pppoe:
 
DEBUGF 1,"SOCKET_send: PPPoE\n"
 
mov [esp+32], ecx
mov ebx, [eax + SOCKET.device]
 
call PPPoE_discovery_output
cmp eax, -1
je s_error
ret
 
 
 
align 4
SOCKET_send_local:
 
; does this socket have a PID yet?
cmp [eax + SOCKET.PID], 0
jne @f
 
; Change PID to that of current process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
@@:
mov [eax + SOCKET.snd_proc], SOCKET_send_local_
 
align 4
SOCKET_send_local_:
 
DEBUGF 1,"SOCKET_send: LOCAL\n"
 
; get the other side's socket and check if it still exists
mov eax, [eax + SOCKET.device]
call SOCKET_check
jz s_error
 
; allright, shove in the data!
push eax
add eax, STREAM_SOCKET.rcv
call SOCKET_ring_write
pop eax
 
; return the number of written bytes (or errorcode) to application
mov [esp+32], ecx
 
; and notify the other end
call SOCKET_notify
 
ret
 
 
;-----------------------------------------------------------------
;
; @param sockNum is a socket number
; @return socket structure address or 0 (not found) in EAX
;;
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
mov eax, [sockNum]
; check if we got something similar to socket number
or eax, eax
jz .error
; SOCKET_get_options
;
; IN: ecx = socket number
; edx = pointer to the options:
; dd level, optname, optval, optlen
; OUT: -1 on error
;
; At moment, uses only pseudo-optname -2 for get last_ack_number for TCP.
; TODO: find best way to notify that send()'ed data were acknowledged
; Also pseudo-optname -3 is valid and returns socket state, one of TCPS_*.
;
;-----------------------------------------------------------------
align 4
SOCKET_get_opt:
 
; scan through sockets list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
cmp [ebx + SOCKET.Number], eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
DEBUGF 2,"SOCKET_get_opt\n"
 
; okay, we found the correct one
mov eax, ebx
call SOCKET_num_to_ptr
jz s_error
 
cmp dword [edx], IP_PROTO_TCP
jne s_error
cmp dword [edx+4], -2
je @f
cmp dword [edx+4], -3
jne s_error
@@:
; mov eax, [edx+12]
; test eax, eax
; jz .fail
; cmp dword [eax], 4
; mov dword [eax], 4
; jb .fail
; stdcall net_socket_num_to_addr, ecx
; test eax, eax
; jz .fail
; ; todo: check that eax is really TCP socket
; mov ecx, [eax + TCP_SOCKET.last_ack_number]
; cmp dword [edx+4], -2
; jz @f
; mov ecx, [eax + TCP_SOCKET.state]
@@:
mov eax, [edx+8]
test eax, eax
jz @f
mov [eax], ecx
@@:
mov dword [esp+32], 0
ret
 
.error:
xor eax, eax
 
 
;-----------------------------------------------------------------
;
; SOCKET_set_options
;
; IN: ecx = socket number
; edx = pointer to the options:
; dd level, optname, optlen, optval
; OUT: -1 on error
;
;-----------------------------------------------------------------
align 4
SOCKET_set_opt:
 
DEBUGF 2,"SOCKET_set_opt\n"
 
call SOCKET_num_to_ptr
jz s_error
 
cmp dword [edx], SOL_SOCKET
jne s_error
 
cmp dword [edx+4], SO_BINDTODEVICE
je .bind
 
cmp dword [edx+4], SO_BLOCK
je .block
 
jmp s_error
 
.bind:
cmp dword [edx+8], 0
je .unbind
 
movzx edx, byte [edx + 9]
cmp edx, MAX_NET_DEVICES
ja s_error
 
mov edx, [NET_DRV_LIST + 4*edx]
test edx, edx
jz s_error
mov [eax + SOCKET.device], edx
 
DEBUGF 1,"SOCKET_set_opt: Bound socket %x to device %x\n",eax, edx
 
mov dword [esp+32], 0 ; success!
ret
endp
 
;; Get socket number by its structure address
; Scan through sockets list to find the socket with specified address.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
.unbind:
mov [eax + SOCKET.device], 0
 
mov dword [esp+32], 0 ; success!
ret
 
.block:
cmp dword [edx+8], 0
je .unblock
 
and [eax + SOCKET.options], not SO_NONBLOCK
 
mov dword [esp+32], 0 ; success!
ret
 
.unblock:
or [eax + SOCKET.options], SO_NONBLOCK
 
mov dword [esp+32], 0 ; success!
ret
 
 
 
;-----------------------------------------------------------------
;
; @param sockAddr is a socket structure address
; @return socket number (SOCKET.Number) or 0 (not found) in EAX
;;
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
mov eax, [sockAddr]
; check if we got something similar to socket structure address
or eax, eax
jz .error
; SOCKET_pair
;
; Allocates a pair of linked LOCAL domain sockets
;
; IN: /
; OUT: eax is socket1 num, -1 on error
; ebx is socket2 num
;
;-----------------------------------------------------------------
align 4
SOCKET_pair:
 
; scan through sockets list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
DEBUGF 2,"SOCKET_pair\n"
 
call SOCKET_alloc
jz s_error
mov [esp+32], edi ; application's eax
 
mov [eax + SOCKET.Domain], AF_LOCAL
mov [eax + SOCKET.Type], SOCK_STREAM
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME
mov [eax + SOCKET.snd_proc], SOCKET_send_local
mov [eax + SOCKET.rcv_proc], SOCKET_receive_local
mov [eax + SOCKET.PID], 0
mov ebx, eax
 
call SOCKET_alloc
jz .error
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
mov [esp+24], edi ; application's ebx
 
; okay, we found the correct one
mov eax, [ebx + SOCKET.Number]
mov [eax + SOCKET.Domain], AF_LOCAL
mov [eax + SOCKET.Type], SOCK_STREAM
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME
mov [eax + SOCKET.snd_proc], SOCKET_send_local
mov [eax + SOCKET.rcv_proc], SOCKET_receive_local
mov [eax + SOCKET.PID], 0
 
; Link the two sockets to eachother
mov [eax + SOCKET.device], ebx
mov [ebx + SOCKET.device], eax
 
lea eax, [eax + STREAM_SOCKET.rcv]
call SOCKET_ring_create
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create
pop eax
 
ret
 
.error:
xor eax, eax
ret
endp
mov eax, ebx
call SOCKET_free
jmp s_error
 
;; [53.9] Check if local port is used by any socket in the system.
; Scan through sockets list, checking SOCKET.LocalPort.
; Useful when you want a to generate a unique local port number.
; This proc doesn't guarantee that after calling it and trying to use
; the port reported being free in calls to socket_open/socket_open_tcp it'll
; still be free or otherwise it'll still be used if reported being in use.
 
 
;-----------------------------------------------------------------
;
; @param BX is a port number
; @return 1 (port is free) or 0 (port is in use) in EAX
;;
proc is_localport_unused stdcall
movzx ebx, bx
mov eax, [network_free_ports]
bt [eax], ebx
setc al
movzx eax, al
; SOCKET_debug
;
; Copies socket variables to application buffer
;
; IN: ecx = socket number
; edx = pointer to buffer
;
; OUT: -1 on error
;-----------------------------------------------------------------
align 4
SOCKET_debug:
 
DEBUGF 1,"SOCKET_debug\n"
 
mov edi, edx
 
test ecx, ecx
jz .returnall
 
call SOCKET_num_to_ptr
jz s_error
 
mov esi, eax
mov ecx, SOCKETBUFFSIZE/4
rep movsd
 
mov dword [esp+32], 0
ret
endp
 
;======================================
set_local_port:
;--------------------------------------
;? Set local port in socket structure.
;--------------------------------------
;> eax -> struct SOCKET
;> bx = local port, or 0 if the kernel must select it itself
;--------------------------------------
;< CF set on error / cleared on success
;< [eax+SOCKET.LocalPort] filled on success
;======================================
; 0. Prepare: save registers, make eax point to ports table, expand port to ebx.
push eax ecx
mov eax, [network_free_ports]
movzx ebx, bx
; 1. Test, whether the kernel should choose port itself. If no, proceed to 5.
.returnall:
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jnz .given
; 2. Yes, it should. Set ecx = limit of table, eax = start value
lea ecx, [eax+0x10000/8]
add eax, [network_free_hint]
; 3. First scan loop: from free hint to end of table.
.scan1:
; 3a. For each dword, find bit set to 1
bsf ebx, [eax]
jz .next1
; 3b. If such bit has been found, atomically test again and clear it.
lock btr [eax], ebx
; 3c. If the bit was still set (usual case), we have found and reserved one port.
; Proceed to 6.
jc .found
; 3d. Otherwise, someone has reserved it between bsf and btr, so retry search.
jmp .scan1
.next1:
; 3e. All bits are cleared, so advance to next dword.
add eax, 4
; 3f. Check limit and continue loop.
cmp eax, ecx
jb .scan1
; 4. Second scan loop: from port 1024 (start of non-system ports) to free hint.
mov eax, [network_free_ports]
mov ecx, eax
add ecx, [network_free_hint]
add eax, 1024/8
; 4a. Test whether there is something to scan.
cmp eax, ecx
jae .fail
; 4b. Enter the loop, the process is same as for 3.
.scan2:
bsf ebx, [eax]
jz .next2
lock btr [eax], ebx
jc .found
jmp .scan2
.next2:
add eax, 4
cmp eax, ecx
jb .scan2
; 4c. None found. Fail.
.fail:
pop ecx eax
stc
jz .done
mov eax, [ebx + SOCKET.Number]
stosd
jmp .next_socket
.done:
xor eax, eax
stosd
 
mov dword [esp+32], 0
ret
; 5. No, the kernel should reserve selected port.
.given:
; 5a. Atomically test old value and clear bit.
lock btr [eax], ebx
; 5b. If the bit was set, reservation is successful. Proceed to 8.
jc .set
; 5c. Otherwise, fail.
jmp .fail
.found:
; 6. We have found the bit set to 1, convert the position to port number.
sub eax, [network_free_ports]
lea ebx, [ebx+eax*8]
; 7. Update free hint.
add eax, 4
cmp eax, 65536/8
jb @f
mov eax, 1024/8
@@:
mov [network_free_hint], eax
.set:
; 8. Restore eax, set SOCKET.LocalPort and return.
pop ecx eax
xchg bl, bh ; Intel -> network byte order
mov [eax + SOCKET.LocalPort], bx
clc
ret
 
;; [53.0] Open DGRAM socket (connectionless, unreliable)
 
;-----------------------------------------------------------------
;
; @param BX is local port number
; @param CX is remote port number
; @param EDX is remote IP address
; @return socket number or -1 (error) in EAX
;;
proc socket_open stdcall
call net_socket_alloc
or eax, eax
jz .error
; SOCKET_find_port
;
; Fills in the local port number for TCP and UDP sockets
; This procedure always works because the number of sockets is
; limited to a smaller number then the number of possible ports
;
; IN: eax = socket pointer
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_find_port:
 
DEBUGF 1, "K : socket_open (0x%x)\n", eax
DEBUGF 2,"SOCKET_find_port\n"
 
push eax
push ebx esi ecx
 
call set_local_port
jc .error.free
xchg ch, cl
mov [eax + SOCKET.RemotePort], cx
mov ebx, [stack_ip]
mov [eax + SOCKET.LocalIP], ebx
mov [eax + SOCKET.RemoteIP], edx
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
;pop eax ; Get the socket number back, so we can return it
stdcall net_socket_addr_to_num
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
pop ecx esi ebx
ret
 
.error.free:
stdcall net_socket_free;, eax
.udp:
mov bx, [last_UDP_port]
call .findit
mov [last_UDP_port], bx
 
.error:
DEBUGF 1, "K : socket_open (fail)\n"
or eax, -1
pop ecx esi ebx
ret
endp
 
;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way)
.tcp:
mov bx, [last_TCP_port]
call .findit
mov [last_TCP_port], bx
 
pop ecx esi ebx
ret
 
 
.restart:
mov bx, MIN_EPHEMERAL_PORT_N
.findit:
cmp bx, MAX_EPHEMERAL_PORT_N
je .restart
 
add bh, 1
adc bl, 0
 
call SOCKET_check_port
jz .findit
ret
 
 
 
;-----------------------------------------------------------------
;
; @param BX is local port number
; @param CX is remote port number
; @param EDX is remote IP address
; @param ESI is open mode (SOCKET_ACTIVE, SOCKET_PASSIVE)
; @return socket number or -1 (error) in EAX
;;
proc socket_open_tcp stdcall
local sockAddr dd ?
; SOCKET_check_port (to be used with AF_INET only!)
;
; Checks if a local port number is unused
; If the proposed port number is unused, it is filled in in the socket structure
;
; IN: eax = socket ptr (to find out if its a TCP/UDP socket)
; bx = proposed socket number (network byte order)
;
; OUT: ZF = set on error
;
;-----------------------------------------------------------------
align 4
SOCKET_check_port:
 
cmp esi, SOCKET_PASSIVE
jne .skip_port_check
DEBUGF 2,"SOCKET_check_port: "
 
push ebx
mov eax, ebx
xchg al, ah
mov ebx, net_sockets
mov ecx, [eax + SOCKET.Protocol]
mov edx, [eax + IP_SOCKET.LocalIP]
mov esi, net_sockets
 
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .last_socket
cmp [ebx + SOCKET.TCBState], TCB_LISTEN
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .port_ok
 
cmp [esi + SOCKET.Protocol], ecx
jne .next_socket
cmp [ebx + SOCKET.LocalPort], ax
 
cmp [esi + IP_SOCKET.LocalIP], edx
jne .next_socket
 
xchg al, ah
DEBUGF 1, "K : error: port %u is listened by 0x%x\n", ax, ebx
pop ebx
jmp .error
cmp [esi + UDP_SOCKET.LocalPort], bx
jne .next_socket
 
.last_socket:
pop ebx
DEBUGF 2,"local port %x already in use\n", bx ; FIXME: find a way to print big endian values with debugf
ret
 
.skip_port_check:
call net_socket_alloc
or eax, eax
jz .error
.port_ok:
DEBUGF 2,"local port %x is free\n", bx ; FIXME: find a way to print big endian values with debugf
mov [eax + UDP_SOCKET.LocalPort], bx
or bx, bx ; clear the zero-flag
ret
 
DEBUGF 1, "K : socket_open_tcp (0x%x)\n", eax
 
mov [sockAddr], eax
 
; TODO - check this works!
;mov [eax + SOCKET.wndsizeTimer], 0 ; Reset the window timer.
;-----------------------------------------------------------------
;
; SOCKET_input
;
; Updates a (stateless) socket with received data
;
; Note: the mutex should already be set !
;
; IN: eax = socket ptr
; ecx = data size
; esi = ptr to data
; [esp] = ptr to buf
; [esp + 4] = buf size
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_input:
 
call set_local_port
jc .error.free
xchg ch, cl
mov [eax + SOCKET.RemotePort], cx
mov [eax + SOCKET.OrigRemotePort], cx
mov ebx, [stack_ip]
mov [eax + SOCKET.LocalIP], ebx
mov [eax + SOCKET.RemoteIP], edx
mov [eax + SOCKET.OrigRemoteIP], edx
DEBUGF 2,"SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
 
mov ebx, TCB_LISTEN
cmp esi, SOCKET_PASSIVE
je @f
mov ebx, TCB_SYN_SENT
@@:
mov [eax + SOCKET.TCBState], ebx ; Indicate the state of the TCB
mov [esp+4], ecx
push esi
mov esi, esp
 
cmp ebx, TCB_LISTEN
je .exit
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, SOCKET_input.full
 
; Now, if we are in active mode, then we have to send a SYN to the specified remote port
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .exit
DEBUGF 1,"SOCKET_input: success\n"
add esp, sizeof.socket_queue_entry
 
push eax
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
mov bl, TH_SYN
xor ecx, ecx
stdcall build_tcp_packet, [sockAddr]
jmp SOCKET_notify
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
.full:
DEBUGF 2,"SOCKET_input: socket %x is full!\n", eax
 
.not_local:
; Send it.
pop ebx
call queue
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
mov esi, [sockAddr]
call kernel_free
add esp, 8
 
; increment SND.NXT in socket
add esi, SOCKET.SND_NXT
call inc_inet_esi
 
.exit:
; Get the socket number back, so we can return it
stdcall net_socket_addr_to_num, [sockAddr]
ret
 
.error.free:
stdcall net_socket_free, eax
 
.error:
DEBUGF 1, "K : socket_open_tcp (fail)\n"
or eax, -1
;--------------------------
;
; eax = ptr to ring struct (just a buffer of the right size)
;
align 4
SOCKET_ring_create:
 
push esi
mov esi, eax
 
push edx
stdcall create_ring_buffer, SOCKET_MAXDATA, PG_SW
pop edx
 
DEBUGF 1,"SOCKET_ring_created: %x\n", eax
 
pusha
lea ecx, [esi + RING_BUFFER.mutex]
call mutex_init
popa
 
mov [esi + RING_BUFFER.start_ptr], eax
mov [esi + RING_BUFFER.write_ptr], eax
mov [esi + RING_BUFFER.read_ptr], eax
mov [esi + RING_BUFFER.size], 0
add eax, SOCKET_MAXDATA
mov [esi + RING_BUFFER.end_ptr], eax
mov eax, esi
pop esi
 
ret
endp
 
;; [53.1] Close DGRAM socket
;-----------------------------------------------------------------
;
; @param EBX is socket number
; @return 0 (closed successfully) or -1 (error) in EAX
;;
proc socket_close stdcall
DEBUGF 1, "K : socket_close (0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx
or eax, eax
jz .error
; SOCKET_ring_write
;
; Adds data to a stream socket, and updates write pointer and size
;
; IN: eax = ptr to ring struct
; ecx = data size
; esi = ptr to data
;
; OUT: ecx = number of bytes stored
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_write:
 
stdcall net_socket_free, eax
DEBUGF 1,"SOCKET_ring_write: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx
 
xor eax, eax
ret
; lock mutex
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
popa
 
.error:
DEBUGF 1, "K : socket_close (fail)\n"
or eax, -1
; calculate available size
mov edi, SOCKET_MAXDATA
sub edi, [eax + RING_BUFFER.size] ; available buffer size in edi
cmp ecx, edi
jbe .copy
mov ecx, edi
.copy:
mov edi, [eax + RING_BUFFER.write_ptr]
DEBUGF 2,"SOCKET_ring_write: %u bytes from %x to %x\n", ecx, esi, edi
 
; update write ptr
push edi
add edi, ecx
cmp edi, [eax + RING_BUFFER.end_ptr]
jb @f
sub edi, SOCKET_MAXDATA ; WRAP
@@:
mov [eax + RING_BUFFER.write_ptr], edi
pop edi
 
; update size
add [eax + RING_BUFFER.size], ecx
 
; copy the data
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
 
; unlock mutex
push eax ecx
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
pop ecx eax
 
ret
endp
 
;; [53.8] Close STREAM socket
; Closing TCP sockets takes time, so when you get successful return code
; from this function doesn't always mean that socket is actually closed.
;-----------------------------------------------------------------
;
; @param EBX is socket number
; @return 0 (closed successfully) or -1 (error) in EAX
;;
proc socket_close_tcp stdcall
local sockAddr dd ?
; SOCKET_ring_read
;
; IN: eax = ring struct ptr
; ecx = bytes to read
; edx = offset
; edi = ptr to buffer start
;
; OUT: eax = unchanged
; ecx = number of bytes read (0 on error)
; edx = destroyed
; esi = destroyed
; edi = ptr to buffer end
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_read:
 
DEBUGF 1, "K : socket_close_tcp (0x%x)\n", ebx
; first, remove any resend entries
DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u offset=%x\n", eax, edi, ecx, edx
 
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
popa
 
mov esi, resendQ
mov ecx, 0
mov esi, [eax + RING_BUFFER.read_ptr]
add esi, edx ; esi = start_ptr + offset
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .last_resendq ; None left
cmp [esi + 4], ebx
je @f ; found one
inc ecx
add esi, 8
jmp .next_resendq
neg edx
add edx, [eax + RING_BUFFER.size] ; edx = snd.size - offset
jle .no_data_at_all
 
@@:
mov dword[esi + 4], 0
inc ecx
add esi, 8
jmp .next_resendq
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
popa
 
.last_resendq:
cmp ecx, edx
ja .less_data
 
.copy:
DEBUGF 2,"SOCKET_ring_read: %u bytes from %x to %x\n", ecx, esi, edi
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
ret
 
.no_data_at_all:
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
popa
 
stdcall net_socket_num_to_addr, ebx
or eax, eax
jz .error
DEBUGF 1,"SOCKET_ring_read: no data at all!\n"
xor ecx, ecx
ret
 
mov ebx, eax
mov [sockAddr], eax
.less_data:
mov ecx, edx
jmp .copy
 
cmp [ebx + SOCKET.TCBState], TCB_LISTEN
je .destroy_tcb
cmp [ebx + SOCKET.TCBState], TCB_SYN_SENT
je .destroy_tcb
cmp [ebx + SOCKET.TCBState], TCB_CLOSED
je .destroy_tcb
 
; Now construct the response, and queue for sending by IP
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .error
;-----------------------------------------------------------------
;
; SOCKET_ring_free
;
; Free's some bytes from the ringbuffer
;
; IN: eax = ptr to ring struct
; ecx = data size
;
; OUT: ecx = number of bytes free-ed
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_free:
 
DEBUGF 1,"SOCKET_ring_free: %u bytes from ring %x\n", ecx, eax
 
push eax ecx
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
pop ecx eax
 
sub [eax + RING_BUFFER.size], ecx
jb .error
add [eax + RING_BUFFER.read_ptr], ecx
 
mov edx, [eax + RING_BUFFER.end_ptr]
cmp [eax + RING_BUFFER.read_ptr], edx
jb @f
sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA
@@:
 
push eax ecx
lea ecx, [eax + RING_BUFFER.mutex] ; TODO: check what registers this function actually destroys
call mutex_unlock
pop ecx eax
 
ret
 
.error: ; we could free all available bytes, but that would be stupid, i guess..
DEBUGF 1,"SOCKET_ring_free: buffer=%x error!\n", eax
add [eax + RING_BUFFER.size], ecx
 
push eax
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
pop eax
 
mov bl, TH_FIN+TH_ACK
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
ret
 
mov ebx, [sockAddr]
; increament SND.NXT in socket
lea esi, [ebx + SOCKET.SND_NXT]
call inc_inet_esi
 
; Get the socket state
mov eax, [ebx + SOCKET.TCBState]
cmp eax, TCB_SYN_RECEIVED
je .fin_wait_1
cmp eax, TCB_ESTABLISHED
je .fin_wait_1
;-----------------------------------------------------------------
;
; SOCKET_block
;
; Suspends the thread attached to a socket
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_block:
 
; assume CLOSE WAIT
; Send a fin, then enter last-ack state
mov [ebx + SOCKET.TCBState], TCB_LAST_ACK
jmp .send
DEBUGF 1,"SOCKET_block: %x\n", eax
 
.fin_wait_1:
; Send a fin, then enter finwait2 state
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
pushf
cli
 
.send:
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
; Set the 'socket is blocked' flag
or [eax + SOCKET.state], SS_BLOCKED
 
.not_local:
; Send it.
pop ebx
call queue
jmp .exit
; Suspend the thread
push edx
mov edx, [TASK_BASE]
mov [edx + TASKDATA.state], 1 ; Suspended
 
.destroy_tcb:
; Remember the thread ID so we can wake it up again
mov edx, [edx + TASKDATA.pid]
DEBUGF 1,"SOCKET_block: suspending thread: %u\n", edx
mov [eax + SOCKET.TID], edx
pop edx
 
; Clear the socket variables
stdcall net_socket_free, ebx
call change_task
popf
 
.exit:
xor eax, eax
ret
DEBUGF 1,"SOCKET_block: continueing\n"
 
.error:
DEBUGF 1, "K : socket_close_tcp (fail)\n"
or eax, -1
ret
endp
 
;; [53.2] Poll socket
 
;-----------------------------------------------------------------
;
; @param EBX is socket number
; @return count or bytes in rx buffer or 0 (error) in EAX
;;
proc socket_poll stdcall
; DEBUGF 1, "socket_poll(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx
or eax, eax
; SOCKET_notify
;
; notify's the owner of a socket that something happened
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_notify:
 
DEBUGF 1,"SOCKET_notify: %x\n", eax
 
call SOCKET_check
jz .error
 
mov eax, [eax + SOCKET.rxDataCount]
ret
test [eax + SOCKET.state], SS_BLOCKED
jnz .unblock
 
.error:
xor eax, eax
ret
endp
test [eax + SOCKET.options], SO_NONBLOCK
jz .error
 
;; [53.6] Get socket TCB state
;
; @param EBX is socket number
; @return socket TCB state or 0 (error) in EAX
;;
proc socket_status stdcall
;; DEBUGF 1, "socket_status(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx
or eax, eax
push eax ecx esi
 
; socket exists and is of non blocking type.
; We'll try to flag an event to the thread
 
mov eax, [eax + SOCKET.TID]
test eax, eax
jz .done
mov ecx, 1
mov esi, TASK_DATA + TASKDATA.pid
 
.next_pid:
cmp [esi], eax
je .found_pid
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next_pid
; PID not found, TODO: close socket!
jmp .done
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK
 
DEBUGF 1,"SOCKET_notify: Raised a network event!\n"
 
jmp .done
 
.unblock:
push eax ecx esi
; Clear the 'socket is blocked' flag
and [eax + SOCKET.state], not SS_BLOCKED
 
; Find the thread's TASK_DATA
mov eax, [eax + SOCKET.TID]
test eax, eax
jz .error
xor ecx, ecx
inc ecx
mov esi, TASK_DATA
.next:
cmp [esi + TASKDATA.pid], eax
je .found
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next
jmp .error
.found:
 
mov eax, [eax + SOCKET.TCBState]
ret
; Run the thread
mov [esi + TASKDATA.state], 0 ; Running
DEBUGF 1,"SOCKET_notify: Unblocked socket!\n"
 
.done:
pop esi ecx eax
 
.error:
xor eax, eax
ret
endp
 
;; [53.3] Get one byte from rx buffer
; This function can return 0 in two cases: if there's one byte read and
; non left, and if an error occured. Behavior should be changed and function
; shouldn't be used for now. Consider using [53.11] instead.
 
;--------------------------------------------------------------------
;
; @param EBX is socket number
; @return number of bytes left in rx buffer or 0 (error) in EAX
; @return byte read in BL
;;
proc socket_read stdcall
; DEBUGF 1, "socket_read(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx
; SOCKET_alloc
;
; Allocate memory for socket data and put new socket into the list
; Newly created socket is initialized with calling PID and number and
; put into beginning of list (which is a fastest way).
;
; IN: /
; OUT: eax = 0 on error, socket ptr otherwise
; edi = socket number
; ZF = cleared on error
;
;--------------------------------------------------------------------
align 4
SOCKET_alloc:
 
push ebx
 
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax
or eax, eax
jz .error
jz .exit
 
mov ebx, eax
; zero-initialize allocated memory
push eax
mov edi, eax
mov ecx, SOCKETBUFFSIZE / 4
xor eax, eax
rep stosd
pop eax
 
; set send-and receive procedures to return -1
mov [eax + SOCKET.snd_proc], s_error
mov [eax + SOCKET.rcv_proc], s_error
 
; find first free socket number and use it
mov edi, [last_socket_num]
.next_socket_number:
inc edi
jz .next_socket_number ; avoid socket nr 0
cmp edi, -1
je .next_socket_number ; avoid socket nr -1
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .last_socket
 
cmp [ebx + SOCKET.Number], edi
jne .next_socket
jmp .next_socket_number
 
.last_socket:
mov [last_socket_num], edi
mov [eax + SOCKET.Number], edi
DEBUGF 1, "SOCKET_alloc: number=%u\n", edi
 
; Fill in PID
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
 
; init mutex
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
call mutex_init
popa
 
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes
test eax, eax
jz .error_release
; add socket to the list by re-arranging some pointers
mov ebx, [net_sockets + SOCKET.NextPtr]
 
dec eax
mov esi, ebx ; esi is address of socket
mov [ebx + SOCKET.rxDataCount], eax ; store new count
movzx eax, byte[ebx + SOCKET.rxData] ; get the byte
mov [eax + SOCKET.PrevPtr], net_sockets
mov [eax + SOCKET.NextPtr], ebx
 
mov ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
lea edi, [esi + SOCKET.rxData]
lea esi, [edi + 1]
cld
push ecx
shr ecx, 2
rep movsd
pop ecx
and ecx, 3
rep movsb
test ebx, ebx
jz @f
 
pusha
lea ecx, [ebx + SOCKET.mutex]
mov ebx, eax
call mutex_unlock
mov eax, ebx
ret
call mutex_lock
popa
 
.error_release:
mov [ebx + SOCKET.PrevPtr], eax
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
.error:
xor ebx, ebx
xor eax, eax
popa
@@:
 
mov [net_sockets + SOCKET.NextPtr], eax
or eax, eax ; used to clear zero flag
.exit:
pop ebx
 
ret
endp
 
;; [53.11] Get specified number of bytes from rx buffer
; Number of bytes in rx buffer can be less than requested size. In this case,
; only available number of bytes is read.
; This function can return 0 in two cases: if there's no data to read, and if
; an error occured. Behavior should be changed.
 
;----------------------------------------------------
;
; @param EBX is socket number
; @param ECX is pointer to application buffer
; @param EDX is application buffer size (number of bytes to read)
; @return number of bytes read or 0 (error) in EAX
;;
proc socket_read_packet stdcall
; DEBUGF 1, "socket_read_packet(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx ; get real socket address
or eax, eax
; SOCKET_free
;
; Free socket data memory and remove socket from the list
;
; IN: eax = socket ptr
; OUT: /
;
;----------------------------------------------------
align 4
SOCKET_free:
 
DEBUGF 1, "SOCKET_free: %x\n", eax
 
call SOCKET_check
jz .error
 
mov ebx, eax
push ebx
 
push ecx edx
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop edx ecx
popa
 
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes
test eax, eax ; if count of bytes is zero..
jz .exit ; exit function (eax will be zero)
cmp [eax + SOCKET.Domain], AF_INET4
jnz .no_tcp
 
test edx, edx ; if buffer size is zero, copy all data
jz .copy_all_bytes
cmp edx, eax ; if buffer size is larger then the bytes of data, copy all data
jge .copy_all_bytes
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jnz .no_tcp
 
sub eax, edx ; store new count (data bytes in buffer - bytes we're about to copy)
mov [ebx + SOCKET.rxDataCount], eax ;
push eax
mov eax, edx ; number of bytes we want to copy must be in eax
call .start_copy ; copy to the application
 
mov esi, ebx ; now we're going to copy the remaining bytes to the beginning
add esi, SOCKET.rxData ; we dont need to copy the header
mov edi, esi ; edi is where we're going to copy to
add esi, edx ; esi is from where we copy
pop ecx ; count of bytes we have left
push ecx ; push it again so we can re-use it later
shr ecx, 2 ; divide eax by 4
cld
rep movsd ; copy all full dwords
pop ecx
and ecx, 3
rep movsb ; copy remaining bytes
 
.exit:
lea ecx, [ebx + SOCKET.mutex]
mov ebx, eax
call mutex_unlock
stdcall kernel_free, [ebx + STREAM_SOCKET.rcv.start_ptr]
stdcall kernel_free, [ebx + STREAM_SOCKET.snd.start_ptr]
mov eax, ebx
ret ; at last, exit
.no_tcp:
 
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
 
DEBUGF 1, "SOCKET_free: linking socket %x to socket %x\n", eax, ebx
 
test eax, eax
jz @f
mov [eax + SOCKET.NextPtr], ebx
@@:
 
test ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
@@:
 
call kernel_free
pop ebx
 
DEBUGF 1, "SOCKET_free: success!\n"
 
.error:
xor eax, eax
ret
 
.copy_all_bytes:
xor esi, esi
mov [ebx + SOCKET.rxDataCount], esi ; store new count (zero)
call .start_copy
lea ecx, [ebx + SOCKET.mutex]
mov ebx, eax
call mutex_unlock
mov eax, ebx
ret
;------------------------------------
;
; SOCKET_fork
;
; Create a child socket
;
; IN: socket nr in ebx
; OUT: child socket nr in eax
;
;-----------------------------------
align 4
SOCKET_fork:
 
.start_copy:
mov edi, ecx
mov esi, ebx
add esi, SOCKET.rxData ; we dont need to copy the header
mov ecx, eax ; eax is count of bytes
push ecx
shr ecx, 2 ; divide eax by 4
cld ; copy all full dwords
DEBUGF 1,"SOCKET_fork: %x\n", ebx
 
; Exit if backlog queue is full
mov eax, [ebx + SOCKET_QUEUE_LOCATION + queue.size]
cmp ax, [ebx + SOCKET.backlog]
jae .fail
 
; Allocate new socket
push ebx
call SOCKET_alloc
pop ebx
jz .fail
 
push eax
mov esi, esp
add_to_queue (ebx + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .fail2
pop eax
 
; Copy structure from current socket to new
; We start at PID to preserve the socket num, and the 2 pointers at beginning of socket
lea esi, [ebx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (SOCKET_QUEUE_LOCATION - SOCKET.PID + 3)/4
rep movsd
pop ecx
and ecx, 3
rep movsb ; copy the rest bytes
retn ; exit, or go back to shift remaining bytes if any
endp
 
;; [53.4] Send data through DGRAM socket
and [eax + SOCKET.options], not SO_ACCEPTCON
 
ret
 
.fail2:
add esp, 4+4+4
.fail:
DEBUGF 1,"SOCKET_fork: failed\n"
xor eax, eax
ret
 
 
;---------------------------------------------------
;
; @param EBX is socket number
; @param ECX is application data size (number of bytes to send)
; @param EDX is pointer to application data buffer
; @return 0 (sent successfully) or -1 (error) in EAX
;;
proc socket_write stdcall
; DEBUGF 1, "socket_write(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx ; get real socket address
; SOCKET_num_to_ptr
;
; Get socket structure address by its number
;
; IN: ecx = socket number
; OUT: eax = 0 on error, socket ptr otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_num_to_ptr:
 
DEBUGF 1,"SOCKET_num_to_ptr: num=%u ", ecx
 
mov eax, net_sockets
 
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .error
cmp [eax + SOCKET.Number], ecx
jne .next_socket
 
mov ebx, eax
test eax, eax
 
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .error
DEBUGF 1,"ptr=%x\n", eax
ret
 
; Save the queue entry number
push eax
.error:
DEBUGF 1,"not found\n", eax
ret
 
; save the pointers to the data buffer & size
push edx
push ecx
 
; convert buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
;---------------------------------------------------
;
; SOCKET_ptr_to_num
;
; Get socket number by its address
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, socket num otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_ptr_to_num:
 
mov edx, eax
DEBUGF 1,"SOCKET_ptr_to_num: ptr=%x ", eax
 
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
call SOCKET_check
jz .error
 
; Fill in the IP header (some data is in the socket descriptor)
mov eax, [ebx + SOCKET.LocalIP]
mov [edx + IP_PACKET.SourceAddress], eax
mov eax, [ebx + SOCKET.RemoteIP]
mov [edx + IP_PACKET.DestinationAddress], eax
mov eax, [eax + SOCKET.Number]
 
mov [edx + IP_PACKET.VersionAndIHL], 0x45
mov [edx + IP_PACKET.TypeOfService], 0
DEBUGF 1,"num=%u\n", eax
ret
 
pop eax ; Get the UDP data length
push eax
.error:
DEBUGF 1,"not found\n", eax
ret
 
add eax, 20 + 8 ; add IP header and UDP header lengths
xchg al, ah
mov [edx + IP_PACKET.TotalLength], ax
xor eax, eax
mov [edx + IP_PACKET.Identification], ax
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
mov [edx + IP_PACKET.TimeToLive], 0x20
mov [edx + IP_PACKET.Protocol], PROTOCOL_UDP
 
; Checksum left unfilled
mov [edx + IP_PACKET.HeaderChecksum], ax
;---------------------------------------------------
;
; SOCKET_check
;
; checks if the given value is really a socket ptr
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, unchanged otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_check:
 
; Fill in the UDP header (some data is in the socket descriptor)
mov ax, [ebx + SOCKET.LocalPort]
mov [edx + 20 + UDP_PACKET.SourcePort], ax
DEBUGF 1,"SOCKET_check: %x\n", eax
 
mov ax, [ebx + SOCKET.RemotePort]
mov [edx + 20 + UDP_PACKET.DestinationPort], ax
push ebx
mov ebx, net_sockets
 
pop eax
push eax
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .done
cmp ebx, eax
jnz .next_socket
 
add eax, 8
xchg al, ah
mov [edx + 20 + UDP_PACKET.Length], ax
.done:
mov eax, ebx
test eax, eax
pop ebx
 
; Checksum left unfilled
xor eax, eax
mov [edx + 20 + UDP_PACKET.Checksum], ax
ret
 
pop ecx ; count of bytes to send
mov ebx, ecx ; need the length later
pop eax ; get callers ptr to data to send
 
; Get the address of the callers data
mov edi, [TASK_BASE]
add edi, TASKDATA.mem_start
add eax, [edi]
mov esi, eax
 
mov edi, edx
add edi, 28
cld
rep movsb ; copy the data across
;---------------------------------------------------
;
; SOCKET_check_owner
;
; checks if the caller application owns the socket
;
; IN: eax = socket ptr
; OUT: ZF = true/false
;
;---------------------------------------------------
align 4
SOCKET_check_owner:
 
; we have edx as IPbuffer ptr.
; Fill in the UDP checksum
; First, fill in pseudoheader
mov eax, [edx + IP_PACKET.SourceAddress]
mov [pseudoHeader], eax
mov eax, [edx + IP_PACKET.DestinationAddress]
mov [pseudoHeader + 4], eax
mov word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0 ; 0 + protocol
add ebx, 8
mov eax, ebx
xchg al, ah
mov [pseudoHeader + 10], ax
DEBUGF 1,"SOCKET_check_owner: %x\n", eax
 
mov eax, pseudoHeader
mov [checkAdd1], eax
mov [checkSize1], word 12
mov eax, edx
add eax, 20
mov [checkAdd2], eax
mov eax, ebx
mov [checkSize2], ax ; was eax!! mjh 8/7/02
push ebx
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
cmp [eax + SOCKET.PID], ebx
pop ebx
 
call checksum
ret
 
; store it in the UDP checksum ( in the correct order! )
mov ax, [checkResult]
 
; If the UDP checksum computes to 0, we must make it 0xffff
; (0 is reserved for 'not used')
test ax, ax
jnz @f
mov ax, 0xffff
 
@@:
xchg al, ah
mov [edx + 20 + UDP_PACKET.Checksum], ax
 
; Fill in the IP header checksum
GET_IHL ecx,edx ; get IP-Header length
stdcall checksum_jb, edx, ecx; buf_ptr, buf_size
xchg al, ah
mov [edx + IP_PACKET.HeaderChecksum], ax
;------------------------------------------------------
;
; SOCKET_process_end
;
; Kernel calls this function when a certain process ends
; This function will check if the process had any open sockets
; And update them accordingly
;
; IN: edx = pid
; OUT: /
;
;------------------------------------------------------
align 4
SOCKET_process_end:
 
; Check destination IP address.
; If it is the local host IP, route it back to IP_RX
DEBUGF 1, "SOCKET_process_end: %x\n", edx
 
pop ebx
push ebx
mov ebx, net_sockets
 
mov eax, NET1OUT_QUEUE
mov ecx, [edx + SOCKET.RemoteIP]
mov edx, [stack_ip]
cmp edx, ecx
jne .not_local
mov eax, IPIN_QUEUE
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
.next_socket_test:
test ebx, ebx
jz .done
 
.not_local:
; Send it.
call queue
cmp [ebx + SOCKET.PID], edx
jne .next_socket
 
xor eax, eax
ret
DEBUGF 1, "SOCKET_process_end: killing socket %x\n", ebx
 
.error:
or eax, -1
mov [ebx + SOCKET.PID], 0
mov eax, ebx
mov ebx, [ebx + SOCKET.NextPtr]
pusha
call SOCKET_close.socket
popa
jmp .next_socket_test
 
.done:
pop ebx
 
ret
endp
 
;; [53.7] Send data through STREAM socket
 
 
 
;-----------------------------------------------------------------
;
; @param EBX is socket number
; @param ECX is application data size (number of bytes to send)
; @param EDX is pointer to application data buffer
; @return 0 (sent successfully) or -1 (error) in EAX
;;
proc socket_write_tcp stdcall
local sockAddr dd ?
; SOCKET_is_connecting
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx
stdcall net_socket_num_to_addr, ebx
or eax, eax
jz .error
align 4
SOCKET_is_connecting:
 
mov ebx, eax
mov [sockAddr], ebx
DEBUGF 1,"SOCKET_is_connecting: %x\n", eax
 
; If the sockets window timer is nonzero, do not queue packet
cmp [ebx + SOCKET.wndsizeTimer], 0
jne .error
and [eax + SOCKET.options], not (SS_ISCONNECTED + SS_ISDISCONNECTING + SS_ISCONFIRMING)
or [eax + SOCKET.options], SS_ISCONNECTING
 
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .error
jmp SOCKET_notify
 
push eax
 
; Get the address of the callers data
mov edi, [TASK_BASE]
add edi, TASKDATA.mem_start
add edx, [edi]
mov esi, edx
 
pop eax
push eax
;-----------------------------------------------------------------
;
; SOCKET_is_connected
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
push ecx
mov bl, TH_ACK
stdcall build_tcp_packet, [sockAddr]
pop ecx
align 4
SOCKET_is_connected:
 
; Check destination IP address.
; If it is the local host IP, route it back to IP_RX
DEBUGF 1,"SOCKET_is_connected: %x\n", eax
 
pop ebx
push ecx
and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISDISCONNECTING + SS_ISCONFIRMING)
or [eax + SOCKET.options], SS_ISCONNECTED
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
jmp SOCKET_notify
 
.not_local:
pop ecx
push ebx ; save ipbuffer number
 
call queue
 
mov esi, [sockAddr]
 
; increament SND.NXT in socket
; Amount to increment by is in ecx
add esi, SOCKET.SND_NXT
call add_inet_esi
;-----------------------------------------------------------------
;
; SOCKET_is_disconnecting
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
pop ebx
align 4
SOCKET_is_disconnecting:
 
; Copy the IP buffer to a resend queue
; If there isn't one, dont worry about it for now
mov esi, resendQ
mov ecx, 0
DEBUGF 1,"SOCKET_is_disconnecting: %x\n", eax
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .exit ; None found
cmp dword[esi + 4], 0
je @f ; found one
inc ecx
add esi, 8
jmp .next_resendq
and [eax + SOCKET.options], not (SS_ISCONNECTING)
or [eax + SOCKET.options], SS_ISDISCONNECTING + SS_CANTRCVMORE + SS_CANTSENDMORE
 
@@:
push ebx
jmp SOCKET_notify
 
; OK, we have a buffer descriptor ptr in esi.
; resend entry # in ecx
; Populate it
; socket #
; retries count
; retry time
; fill IP buffer associated with this descriptor
 
stdcall net_socket_addr_to_num, [sockAddr]
mov [esi + 4], eax
mov byte[esi + 1], TCP_RETRIES
mov word[esi + 2], TCP_TIMEOUT
 
inc ecx
; Now get buffer location, and copy buffer across. argh! more copying,,
mov edi, resendBuffer - IPBUFFSIZE
;-----------------------------------------------------------------
;
; SOCKET_is_disconnected
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
@@:
add edi, IPBUFFSIZE
loop @b
align 4
SOCKET_is_disconnected:
 
; we have dest buffer location in edi
pop eax
; convert source buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
mov esi, eax
DEBUGF 1,"SOCKET_is_disconnected: %x\n", eax
 
; do copy
mov ecx, IPBUFFSIZE
cld
rep movsb
and [eax + SOCKET.options], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING)
or [eax + SOCKET.options], SS_CANTRCVMORE + SS_CANTSENDMORE
 
.exit:
xor eax, eax
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
jmp SOCKET_notify
 
.tcp:
.udp:
mov [eax + UDP_SOCKET.LocalPort], 0 ; UDP and TCP structs store localport at the same offset
mov [eax + UDP_SOCKET.RemotePort], 0
 
jmp SOCKET_notify
 
 
;-----------------------------------------------------------------
;
; SOCKET_cant_recv_more
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
SOCKET_cant_recv_more:
 
DEBUGF 1,"SOCKET_cant_recv_more: %x\n", eax
 
or [eax + SOCKET.options], SS_CANTRCVMORE
 
ret
 
.error:
or eax, -1
ret
endp
 
 
;-----------------------------------------------------------------
;
; SOCKET_cant_send_more
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
SOCKET_cant_send_more:
 
DEBUGF 1,"SOCKET_cant_send_more: %x\n", eax
 
or [eax + SOCKET.options], SS_CANTSENDMORE
 
ret
/kernel/branches/Kolibri-acpi/network/stack.inc
1,912 → 1,789
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; STACK.INC ;;
;; ;;
;; TCP/IP stack for Menuet OS ;;
;; TCP/IP stack for KolibriOS ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; See file COPYING for details ;;
;; Some parts of code are based on the work of: ;;
;; Mike Hibbett (menuetos network stack) ;;
;; Eugen Brasoveanu (solar os network stack and drivers) ;;
;; mike.dld (kolibrios socket code) ;;
;; ;;
;; Version 0.7 ;;
;; Added a timer per socket to allow delays when rx window ;;
;; gets below 1KB ;;
;; TCP part is based on 4.4BSD ;;
;; ;;
;;10.01.2007 Bugfix for checksum function from Paolo Franchetti ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
;*******************************************************************
; Interface
; The interfaces defined in ETHERNET.INC plus:
; stack_init
; stack_handler
; app_stack_handler
; app_socket_handler
; checksum
;
;*******************************************************************
 
uglobal
StackCounters:
dumped_rx_count dd 0
arp_tx_count:
dd 0
arp_rx_count:
dd 0
ip_rx_count:
dd 0
ip_tx_count:
dd 0
net_10ms dd ?
net_tmr_count dw ?
endg
 
; socket buffers
SOCKETBUFFSIZE equ 4096 ; state + config + buffer.
SOCKETHEADERSIZE equ SOCKET.rxData ; thus 4096 - SOCKETHEADERSIZE bytes data
MAX_NET_DEVICES = 16
ARP_BLOCK = 1 ; true or false
 
;NUM_SOCKETS equ 16 ; Number of open sockets supported. Was 20
MIN_EPHEMERAL_PORT = 49152
MIN_EPHEMERAL_PORT_N = 0x00C0 ; same in Network byte order (FIXME)
MAX_EPHEMERAL_PORT = 61000
MAX_EPHEMERAL_PORT_N = 0x48EE ; same in Network byte order (FIXME)
 
; IPBUFF status values
BUFF_EMPTY equ 0
BUFF_RX_FULL equ 1
BUFF_ALLOCATED equ 2
BUFF_TX_FULL equ 3
; Ethernet protocol numbers
ETHER_ARP = 0x0608
ETHER_IPv4 = 0x0008
ETHER_IPv6 = 0xDD86
ETHER_PPP_DISCOVERY = 0x6388
ETHER_PPP_SESSION = 0x6488
 
NUM_IPBUFFERS equ 20 ; buffers allocated for TX/RX
; PPP protocol numbers
PPP_IPv4 = 0x2100
PPP_IPV6 = 0x5780
 
NUMQUEUES equ 4
;Protocol family
AF_UNSPEC = 0
AF_LOCAL = 1
AF_INET4 = 2
AF_INET6 = 10
AF_PPP = 777
 
EMPTY_QUEUE equ 0
IPIN_QUEUE equ 1
IPOUT_QUEUE equ 2
NET1OUT_QUEUE equ 3
; Internet protocol numbers
IP_PROTO_IP = 0
IP_PROTO_ICMP = 1
IP_PROTO_TCP = 6
IP_PROTO_UDP = 17
 
NO_BUFFER equ 0xFFFF
IPBUFFSIZE equ 1500 ; MTU of an ethernet packet
NUMQUEUEENTRIES equ NUM_IPBUFFERS
NUMRESENDENTRIES equ 18 ; Buffers for TCP resend packets
; PPP protocol number
PPP_PROTO_ETHERNET = 666
 
; These are the 0x40 function codes for application access to the stack
STACK_DRIVER_STATUS equ 52
SOCKET_INTERFACE equ 53
; Socket types
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
 
; Socket options
SO_ACCEPTCON = 1 shl 0
SO_BROADCAST = 1 shl 1
SO_DEBUG = 1 shl 2
SO_DONTROUTE = 1 shl 3
SO_KEEPALIVE = 1 shl 4
SO_OOBINLINE = 1 shl 5
SO_REUSEADDR = 1 shl 6
SO_REUSEPORT = 1 shl 7
SO_USELOOPBACK = 1 shl 8
SO_BINDTODEVICE = 1 shl 9
 
; 128KB allocated for the stack and network driver buffers and other
; data requirements
;stack_data_start equ 0x700000
;eth_data_start equ 0x700000
;stack_data equ 0x704000
;stack_data_end equ 0x71ffff
SO_BLOCK = 1 shl 10 ; TO BE REMOVED
SO_NONBLOCK = 1 shl 31
 
; 32 bit word
stack_config equ stack_data
; Socket flags for user calls
MSG_PEEK = 0x02
MSG_DONTWAIT = 0x40
 
; 32 bit word - IP Address in network format
stack_ip equ stack_data + 4
; Socket level
SOL_SOCKET = 0
 
; 1 byte. 0 == inactive, 1 = active
ethernet_active equ stack_data + 9
 
; Socket States
SS_NOFDREF = 0x0001 ; no file table ref any more
SS_ISCONNECTED = 0x0002 ; socket connected to a peer
SS_ISCONNECTING = 0x0004 ; in process of connecting to peer
SS_ISDISCONNECTING = 0x0008 ; in process of disconnecting
SS_CANTSENDMORE = 0x0010 ; can't send more data to peer
SS_CANTRCVMORE = 0x0020 ; can't receive more data from peer
SS_RCVATMARK = 0x0040 ; at mark on input
SS_ISABORTING = 0x0080 ; aborting fd references - close()
SS_RESTARTSYS = 0x0100 ; restart blocked system calls
SS_ISDISCONNECTED = 0x0800 ; socket disconnected from peer
 
; TODO :: empty memory area
SS_ASYNC = 0x0100 ; async i/o notify
SS_ISCONFIRMING = 0x0200 ; deciding to accept connection req
SS_MORETOCOME = 0x0400
 
; Address of selected socket
;sktAddr equ stack_data + 32
; Parameter to checksum routine - data ptr
checkAdd1 equ stack_data + 36
; Parameter to checksum routine - 2nd data ptr
checkAdd2 equ stack_data + 40
; Parameter to checksum routine - data size
checkSize1 equ stack_data + 44
; Parameter to checksum routine - 2nd data size
checkSize2 equ stack_data + 46
; result of checksum routine
checkResult equ stack_data + 48
SS_BLOCKED = 0x8000
 
; holds the TCP/UDP pseudo header. SA|DA|0|prot|UDP len|
pseudoHeader equ stack_data + 50
 
; receive and transmit IP buffer allocation
;sockets equ stack_data + 62
Next_free2 equ stack_data + 62;Next_free2 equ sockets + (SOCKETBUFFSIZE * NUM_SOCKETS)
; 1560 byte buffer for rx / tx ethernet packets
Ether_buffer equ Next_free2
Next_free3 equ Ether_buffer + 1518
last_1sTick equ Next_free3
IPbuffs equ Next_free3 + 1
queues equ IPbuffs + ( NUM_IPBUFFERS * IPBUFFSIZE )
queueList equ queues + (2 * NUMQUEUES)
last_1hsTick equ queueList + ( 2 * NUMQUEUEENTRIES )
SOCKET_MAXDATA = 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8
 
;resendQ equ queueList + ( 2 * NUMQUEUEENTRIES )
;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP
; equ resendBuffer + ( IPBUFFSIZE * NUMRESENDENTRIES )
; Network driver types
NET_TYPE_LOOPBACK = 0
NET_TYPE_ETH = 1
NET_TYPE_SLIP = 2
 
MAX_backlog = 20 ; maximum backlog for stream sockets
 
; Error Codes
ENOBUFS = 55
ECONNREFUSED = 61
ECONNRESET = 52
ETIMEDOUT = 60
ECONNABORTED = 53
 
;resendQ equ 0x770000
;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP ; XTODO: validate size
resendBuffer equ resendQ + ( 8 * NUMRESENDENTRIES ) ; for TCP
; Api protocol numbers
API_ETH = 0
API_IPv4 = 1
API_ICMP = 2
API_UDP = 3
API_TCP = 4
API_ARP = 5
API_PPPOE = 6
API_IPv6 = 7
 
HWACC_TCP_IPv4 = 1 shl 0
 
uglobal
net_sockets rd 2
endg
struct NET_DEVICE
 
; simple macro for memory set operation
macro _memset_dw adr,value,amount
{
mov edi, adr
mov ecx, amount
if value = 0
xor eax, eax
else
mov eax, value
end if
cld
rep stosd
type dd ? ; Type field
mtu dd ? ; Maximal Transmission Unit
name dd ? ; Ptr to 0 terminated string
 
unload dd ? ; Ptrs to driver functions
reset dd ? ;
transmit dd ? ;
 
bytes_tx dq ? ; Statistics, updated by the driver
bytes_rx dq ? ;
packets_tx dd ? ;
packets_rx dd ? ;
 
state dd ? ; link state (0 = no link)
hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines)
 
ends
 
 
; Exactly as it says..
macro pseudo_random reg {
add reg, [esp]
rol reg, 5
xor reg, [timer_ticks]
; add reg, [CPU_FREQ]
imul reg, 214013
xor reg, 0xdeadbeef
rol reg, 9
}
 
; Network to Hardware byte order (dword)
macro ntohd reg {
 
; Below, the main network layer source code is included
;
rol word reg, 8
rol dword reg, 16
rol word reg , 8
 
}
 
; Network to Hardware byte order (word)
macro ntohw reg {
 
rol word reg, 8
 
}
 
 
include "queue.inc"
include "eth_drv/ethernet.inc"
include "ip.inc"
 
include "loopback.inc"
include "ethernet.inc"
 
include "PPPoE.inc"
 
include "ARP.inc"
include "IPv4.inc"
include "IPv6.inc"
 
include "icmp.inc"
include "udp.inc"
include "tcp.inc"
 
include "socket.inc"
 
;***************************************************************************
; Function
; stack_init
;
; Description
; Clear all allocated memory to zero. This ensures that
; on startup, the stack is inactive, and consumes no resources
; This is a kernel function, called prior to the OS main loop
; in set_variables
;
;***************************************************************************
 
stack_init:
; Init two address spaces with default values
_memset_dw stack_data_start, 0, 0x20000/4
_memset_dw resendQ, 0, NUMRESENDENTRIES * 2
 
mov [net_sockets], 0
mov [net_sockets + 4], 0
align 4
uglobal
 
; Queries initialization
call queueInit
NET_RUNNING dd ?
NET_DEFAULT dd ?
NET_DRV_LIST rd MAX_NET_DEVICES
 
; The following block sets up the 1s timer
mov al, 0x0
out 0x70, al
in al, 0x71
mov [last_1sTick], al
ret
endg
 
 
 
;***************************************************************************
; Function
; stack_handler
;-----------------------------------------------------------------
;
; Description
; The kernel loop routine for the stack
; This is a kernel function, called in the main loop
; stack_init
;
;***************************************************************************
; This function calls all network init procedures
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
stack_handler:
stack_init:
 
call ethernet_driver
call ip_rx
; Init the network drivers list
xor eax, eax
mov edi, NET_RUNNING
mov ecx, (MAX_NET_DEVICES + 2)
rep stosd
 
PPPoE_init
 
; Test for 10ms tick, call tcp timer
mov eax, [timer_ticks];[0xfdf0]
cmp eax, [last_1hsTick]
je sh_001
IPv4_init
; IPv6_init
ICMP_init
 
mov [last_1hsTick], eax
call tcp_tx_handler
ARP_init
UDP_init
TCP_init
 
sh_001:
SOCKET_init
 
; Test for 1 second event, call 1s timer functions
mov al, 0x0;second
out 0x70, al
in al, 0x71
cmp al, [last_1sTick]
je sh_exit
mov [net_tmr_count], 0
 
mov [last_1sTick], al
 
stdcall arp_table_manager, ARP_TABLE_TIMER, 0, 0
call tcp_tcb_handler
 
sh_exit:
ret
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Checksum [by Johnny_B]
;; IN:
;; buf_ptr=POINTER to buffer
;; buf_size=SIZE of buffer
;; OUT:
;; AX=16-bit checksum
;; Saves all used registers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc checksum_jb stdcall uses ebx esi ecx,\
buf_ptr:DWORD, buf_size:DWORD
 
xor eax, eax
xor ebx, ebx;accumulator
mov esi, dword[buf_ptr]
mov ecx, dword[buf_size]
shr ecx, 1; ecx=ecx/2
jnc @f ; if CF==0 then size is even number
mov bh, byte[esi + ecx*2]
@@:
cld
 
.loop:
lodsw ;eax=word[esi],esi=esi+2
xchg ah, al;cause must be a net byte-order
add ebx, eax
loop .loop
; Wakeup every tick.
proc stack_handler_has_work?
 
mov eax, ebx
shr eax, 16
add ax, bx
not ax
mov eax, [timer_ticks]
cmp eax, [net_10ms]
 
ret
endp
 
;***************************************************************************
; Function
; checksum
 
;-----------------------------------------------------------------
;
; Description
; checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult
; Dont break anything; Most registers are used by the caller
; This code is derived from the 'C' source, cksum.c, in the book
; Internetworking with TCP/IP Volume II by D.E. Comer
; stack_handler
;
;***************************************************************************
; This function is called in kernel loop
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
stack_handler:
 
; Test for 10ms tick
mov eax, [timer_ticks]
cmp eax, [net_10ms]
je .exit
mov [net_10ms], eax
 
checksum:
pusha
mov eax, [checkAdd1]
xor edx, edx ; edx is the accumulative checksum
xor ebx, ebx
mov cx, [checkSize1]
shr cx, 1
jz cs1_1
cmp [NET_RUNNING], 0
je .exit
 
cs1:
mov bh, [eax]
mov bl, [eax + 1]
test [net_10ms], 0x0f ; 160ms
jnz .exit
 
add eax, 2
add edx, ebx
TCP_timer_160ms
 
loopw cs1
test [net_10ms], 0x3f ; 640ms
jnz .exit
 
cs1_1:
and word [checkSize1], 0x01
jz cs_test2
TCP_timer_640ms
ARP_decrease_entry_ttls
IPv4_decrease_fragment_ttls
 
mov bh, [eax]
xor bl, bl
.exit:
ret
 
add edx, ebx
 
cs_test2:
mov cx, [checkSize2]
cmp cx, 0
jz cs_exit ; Finished if no 2nd buffer
 
mov eax, [checkAdd2]
align 4
NET_link_changed:
 
shr cx, 1
jz cs2_1
DEBUGF 1,"NET_link_changed device=0x%x status=0x%x\n", ebx, [ebx + NET_DEVICE.state]
 
cs2:
mov bh, [eax]
mov bl, [eax + 1]
align 4
NET_send_event:
 
add eax, 2
add edx, ebx
DEBUGF 1,"NET_send_event\n"
 
loopw cs2
; Send event to all applications
push edi ecx
mov edi, SLOT_BASE
mov ecx, [TASK_COUNT]
.loop:
add edi, 256
or [edi + APPDATA.event_mask], EVENT_NETWORK2
loop .loop
pop ecx edi
 
cs2_1:
and word [checkSize2], 0x01
jz cs_exit
ret
 
mov bh, [eax]
xor bl, bl
 
add edx, ebx
 
cs_exit:
mov ebx, edx
;-----------------------------------------------------------------
;
; NET_add_device:
;
; This function is called by the network drivers,
; to register each running NIC to the kernel
;
; IN: Pointer to device structure in ebx
; OUT: Device num in eax, -1 on error
;
;-----------------------------------------------------------------
align 4
NET_add_device:
 
shr ebx, 16
and edx, 0xffff
add edx, ebx
mov eax, edx
shr eax, 16
add edx, eax
not dx
DEBUGF 1,"NET_Add_Device: %x\n", ebx ;;; TODO: use mutex to lock net device list
 
mov [checkResult], dx
popa
ret
cmp [NET_RUNNING], MAX_NET_DEVICES
jae .error
 
;----------------------------------
; Check if device is already listed
mov eax, ebx
mov ecx, MAX_NET_DEVICES ; We need to check whole list because a device may be removed without re-organizing list
mov edi, NET_DRV_LIST
 
repne scasd ; See if device is already in the list
jz .error
 
;----------------------------
; Find empty slot in the list
xor eax, eax
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
 
;***************************************************************************
; Function
; app_stack_handler
;
; Description
; This is an application service, called by int 0x40, function 52
; It provides application access to the network interface layer
;
;***************************************************************************
iglobal
align 4
f52call:
dd app_stack_handler.00
dd app_stack_handler.01
dd app_stack_handler.02
dd app_stack_handler.03
dd app_stack_handler.fail ;04
dd app_stack_handler.fail ;05
dd stack_insert_packet ;app_stack_handler.06
dd app_stack_handler.fail ;07
dd stack_get_packet ;app_stack_handler.08
dd app_stack_handler.09
dd app_stack_handler.10
dd app_stack_handler.11
dd app_stack_handler.12
dd app_stack_handler.13
dd app_stack_handler.14
dd app_stack_handler.15
endg
app_stack_handler:
;in ebx,ecx
;out eax
cmp ebx, 15
ja .fail ;if more than 15 then exit
repne scasd
jnz .error
 
jmp dword [f52call+ebx*4]
sub edi, 4
 
;-----------------------------
; Add device to the found slot
mov [edi], ebx ; add device to list
 
.00:
; Read the configuration word
mov eax, [stack_config]
ret
mov eax, edi ; Calculate device number in eax
sub eax, NET_DRV_LIST
shr eax, 2
 
.01:
; read the IP address
mov eax, [stack_ip]
ret
inc [NET_RUNNING] ; Indicate that one more network device is up and running
 
.02:
; write the configuration word
mov [stack_config], ecx
cmp eax, 1 ; If it's the first network device, try to set it as default
jne @f
push eax
call NET_set_default
pop eax
@@:
 
; <Slip shouldn't be active anyway - thats an operational issue.>
; If ethernet now enabled, probe for the card, reset it and empty
; the packet buffer
; If all successfull, enable the card.
; If ethernet now disabled, set it as disabled. Should really
; empty the tcpip data area too.
call NET_send_event
 
; ethernet interface is '3' in ls 7 bits
and cl, 0x7f
cmp cl, 3
je ash_eth_enable
; Ethernet isn't enabled, so make sure that the card is disabled
mov [ethernet_active], byte 0
DEBUGF 1,"Device number: %u\n", eax
ret
 
.03:
; write the IP Address
mov [stack_ip], ecx
.error:
or eax, -1
DEBUGF 2,"Adding network device failed\n"
ret
;old functions was deleted
;.06:
; Insert an IP packet into the stacks received packet queue
; call stack_insert_packet
; ret
 
; Test for any packets queued for transmission over the network
 
;.08:
; call stack_get_packet
; Extract a packet queued for transmission by the network
; ret
 
.09:
; read the gateway IP address
mov eax, [gateway_ip]
ret
;-----------------------------------------------------------------
;
; NET_set_default
;
; API to set the default interface
;
; IN: Device num in eax
; OUT: Device num in eax, -1 on error
;
;-----------------------------------------------------------------
align 4
NET_set_default:
 
.10:
; read the subnet mask
mov eax, [subnet_mask]
ret
.11:
; write the gateway IP Address
mov [gateway_ip], ecx
ret
DEBUGF 1,"NET_set_default: device=%x\n", eax
 
.12:
; write the subnet mask
mov [subnet_mask], ecx
ret
cmp eax, MAX_NET_DEVICES
jae .error
 
.13:
; read the dns
mov eax, [dns_ip]
cmp [NET_DRV_LIST+eax*4], 0
je .error
 
mov [NET_DEFAULT], eax
 
DEBUGF 1,"NET_set_default: succes\n"
ret
 
.14:
; write the dns IP Address
mov [dns_ip], ecx
.error:
or eax, -1
DEBUGF 1,"NET_set_default: failed\n"
ret
 
.15:
;<added by Frank Sommer>
; in ecx we need 4 to read the last 2 bytes
; or we need 0 to read the first 4 bytes
cmp ecx, 4
ja .param_error
 
; read MAC, returned (in mirrored byte order) in eax
mov eax, [node_addr + ecx]
ret
;-----------------------------------------------------------------
;
; NET_Remove_Device:
;
; This function is called by etwork drivers,
; to unregister network devices from the kernel
;
; IN: Pointer to device structure in ebx
; OUT: eax: -1 on error
;
;-----------------------------------------------------------------
align 4
NET_remove_device:
 
.param_error:
or eax, -1 ; params not accepted
ret
cmp [NET_RUNNING], 0
je .error
 
.16:
; 0 -> arp_probe
; 1 -> arp_announce
; 2 -> arp_responce (not supported yet)
test ecx, ecx
je a_probe
cmp [NET_DRV_LIST], ebx
jne @f
mov [NET_DRV_LIST], 0
cmp [NET_RUNNING], 1
je @f
; there are still active devices, find one and make it default
xor eax, eax
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
repe scasd
je @f
shr edi, 2
dec edi
mov [NET_DEFAULT], edi
@@:
 
dec ebx
jz a_ann ; arp announce
.fail:
or eax, -1
ret
;----------------------------
; Find the driver in the list
 
; cmp ebx,2
; jne a_resp ; arp response
mov eax, ebx
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST+4
 
; arp probe, sender IP must be set to 0.0.0.0, target IP is set to address being probed
; ecx: pointer to target MAC, MAC should set to 0 by application
; edx: target IP
a_probe:
push dword [stack_ip]
repne scasd
jnz .error
 
mov edx, [stack_ip]
and [stack_ip], dword 0
mov esi, ecx ; pointer to target MAC address
call arp_request
;------------------------
; Remove it from the list
 
pop dword [stack_ip]
xor eax, eax
mov dword [edi-4], eax
 
call NET_send_event
 
dec [NET_RUNNING]
ret
 
; arp announce, sender IP must be set to target IP
; ecx: pointer to target MAC
a_ann:
mov edx, [stack_ip]
mov esi, ecx ; pointer to target MAC address
call arp_request
.error:
or eax, -1
ret
 
.17:
;</added by Frank Sommer>
; modified by [smb]
 
;<added by Johnny_B>
; ARPTable manager interface
;see "proc arp_table_manager" for more details
stdcall arp_table_manager, ecx, edx, esi;Opcode,Index,Extra
ret
;</added by Johnny_B>
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ash_eth_enable:
; Probe for the card. This will reset it and enable the interface
; if found
call eth_probe
test eax, eax
jz ash_eth_done ; Abort if no hardware found
 
mov [ethernet_active], byte 1
ash_eth_done:
ret
;***************************************************************************
; Function
; app_socket_handler
;-----------------------------------------------------------------
;
; Description
; This is an application service, called by int 0x40, function 53
; It provides application access to stack socket services
; such as opening sockets
; NET_ptr_to_num
;
;***************************************************************************
iglobal
; IN: ebx = ptr to device struct
; OUT: edi = -1 on error, device number otherwise
;
;-----------------------------------------------------------------
align 4
f53call:
dd socket_open ;00
dd socket_close ;01
dd socket_poll ;02
dd socket_read ;03
dd socket_write ;04
dd socket_open_tcp ;05
dd socket_status ;06
dd socket_write_tcp ;07
dd socket_close_tcp ;08
dd is_localport_unused ;09
dd app_socket_handler.10
dd socket_read_packet ;11
endg
NET_ptr_to_num:
push ecx
 
app_socket_handler:
;in ebx,ecx,edx,wsi
;out eax
cmp eax, 255
je stack_internal_status
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
 
cmp eax, 11
ja .fail ;if more than 15 then exit
.loop:
cmp ebx, [edi]
jz .found
add edi, 4
dec ecx
jnz .loop
 
jmp dword [f53call+eax*4]
; repnz scasd could work too if eax is used instead of ebx!
 
.10:
mov eax, dword[drvr_cable]
test eax, eax
jnz @f ; if function is not implented, return -1
or al, -1
or edi, -1
 
pop ecx
ret
@@:
jmp dword[drvr_cable]
 
.fail:
or eax, -1
.found:
sub edi, NET_DRV_LIST
shr edi, 2
 
pop ecx
ret
uglobal
ARPTmp:
times 14 db 0
endg
 
;***************************************************************************
; Function
; stack_internal_status
;-----------------------------------------------------------------
;
; Description
; Returns information about the internal status of the stack
; This is only useful for debugging
; It works with the ethernet driver
; sub function in ebx
; return requested data in eax
; checksum_1
;
;***************************************************************************
; This sub function allows access to debugging information on the stack
; ecx holds the request:
; 100 : return length of empty queue
; 101 : return length of IPOUT QUEUE
; 102 : return length of IPIN QUEUE
; 103 : return length of NET1OUT QUEUE
; 200 : return # of ARP entries
; 201 : return size of ARP table ( max # entries )
; 202 : select ARP table entry #
; 203 : return IP of selected table entry
; 204 : return High 4 bytes of MAC address of selected table entry
; 205 : return low 2 bytes of MAC address of selected table entry
; 206 : return status word of selected table entry
; 207 : return Time to live of selected table entry
; This is the first of two functions needed to calculate a checksum.
;
; IN: edx = start offset for semi-checksum
; esi = pointer to data
; ecx = data size
; OUT: edx = semi-checksum
;
;
; Code was optimized by diamond
;
;-----------------------------------------------------------------
align 4
checksum_1:
 
shr ecx, 1
pushf
jz .no_2
 
; 2 : return number of IP packets received
; 3 : return number of packets transmitted
; 4 : return number of received packets dumped
; 5 : return number of arp packets received
; 6 : return status of packet driver
; ( 0 == not active, FFFFFFFF = successful )
shr ecx, 1
pushf
jz .no_4
 
shr ecx, 1
pushf
jz .no_8
 
stack_internal_status:
cmp ebx, 100
jnz notsis100
.loop:
add dl, [esi+1]
adc dh, [esi+0]
 
; 100 : return length of EMPTY QUEUE
mov ebx, EMPTY_QUEUE
call queueSize
ret
adc dl, [esi+3]
adc dh, [esi+2]
 
notsis100:
cmp ebx, 101
jnz notsis101
adc dl, [esi+5]
adc dh, [esi+4]
 
; 101 : return length of IPOUT QUEUE
mov ebx, IPOUT_QUEUE
call queueSize
ret
adc dl, [esi+7]
adc dh, [esi+6]
 
notsis101:
cmp ebx, 102
jnz notsis102
adc edx, 0
add esi, 8
 
; 102 : return length of IPIN QUEUE
mov ebx, IPIN_QUEUE
call queueSize
ret
dec ecx
jnz .loop
 
notsis102:
cmp ebx, 103
jnz notsis103
adc edx, 0
 
; 103 : return length of NET1OUT QUEUE
mov ebx, NET1OUT_QUEUE
call queueSize
ret
.no_8:
popf
jnc .no_4
 
notsis103:
cmp ebx, 200
jnz notsis200
add dl, [esi+1]
adc dh, [esi+0]
 
; 200 : return num entries in arp table
movzx eax, byte [NumARP]
ret
adc dl, [esi+3]
adc dh, [esi+2]
 
notsis200:
cmp ebx, 201
jnz notsis201
adc edx, 0
add esi, 4
 
; 201 : return arp table size
mov eax, 20; ARP_TABLE_SIZE
ret
.no_4:
popf
jnc .no_2
 
notsis201:
cmp ebx, 202
jnz notsis202
add dl, [esi+1]
adc dh, [esi+0]
 
; 202 - read the requested table entry
; into a temporary buffer
; ecx holds the entry number
adc edx, 0
inc esi
inc esi
 
mov eax, ecx
mov ecx, 14; ARP_ENTRY_SIZE
mul ecx
.no_2:
popf
jnc .end
 
mov ecx, [eax + ARPTable]
mov [ARPTmp], ecx
mov ecx, [eax + ARPTable+4]
mov [ARPTmp+4], ecx
mov ecx, [eax + ARPTable+8]
mov [ARPTmp+8], ecx
mov cx, [eax + ARPTable+12]
mov [ARPTmp+12], cx
add dh, [esi+0]
adc edx, 0
.end:
ret
 
notsis202:
cmp ebx, 203
jnz notsis203
;-----------------------------------------------------------------
;
; checksum_2
;
; This function calculates the final ip/tcp/udp checksum for you
;
; IN: edx = semi-checksum
; OUT: dx = checksum (in INET byte order)
;
;-----------------------------------------------------------------
align 4
checksum_2:
 
; 203 - return IP address
mov eax, [ARPTmp]
ret
mov ecx, edx
shr ecx, 16
and edx, 0xffff
add edx, ecx
 
notsis203:
cmp ebx, 204
jnz notsis204
mov ecx, edx
shr ecx, 16
add dx, cx
test dx, dx ; it seems that ZF is not set when CF is set :(
not dx
jnz .not_zero
dec dx
.not_zero:
xchg dl, dh
 
; 204 - return MAC high dword
mov eax, [ARPTmp+4]
DEBUGF 1,"Checksum: %x\n", dx
 
ret
 
notsis204:
cmp ebx, 205
jnz notsis205
 
; 205 - return MAC ls word
movzx eax, word [ARPTmp+8]
ret
 
notsis205:
cmp ebx, 206
jnz notsis206
;----------------------------------------------------------------
;
; System function to work with network devices (75)
;
;----------------------------------------------------------------
align 4
sys_network: ; FIXME: make default device easily accessible
 
; 206 - return status word
movzx eax, word [ARPTmp+10]
ret
cmp ebx, -1
jne @f
 
notsis206:
cmp ebx, 207
jnz notsis207
mov eax, [NET_RUNNING]
jmp .return
 
; 207 - return ttl word
movzx eax, word [ARPTmp+12]
ret
@@:
cmp bh, MAX_NET_DEVICES ; Check if device number exists
jae .doesnt_exist
 
notsis207:
cmp ebx, 2
jnz notsis2
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6
 
; 2 : return number of IP packets received
mov eax, [ip_rx_count]
ret
cmp dword [esi + NET_DRV_LIST], 0 ; check if driver is running
je .doesnt_exist
 
notsis2:
cmp ebx, 3
jnz notsis3
mov eax, [esi + NET_DRV_LIST]
 
; 3 : return number of packets transmitted
mov eax, [ip_tx_count]
ret
and ebx, 0x000000ff
cmp ebx, .number
ja .doesnt_exist
jmp dword [.table + 4*ebx]
 
notsis3:
cmp ebx, 4
jnz notsis4
.table:
dd .get_type ; 0
dd .get_dev_name ; 1
dd .reset ; 2
dd .stop ; 3
dd .get_ptr ; 4
dd .get_drv_name ; 5
dd .set_default ; 6
.number = ($ - .table) / 4 - 1
 
; 4 : return number of received packets dumped
mov eax, [dumped_rx_count]
ret
.get_type: ; 0 = Get device type (ethernet/token ring/...)
 
notsis4:
cmp ebx, 5
jnz notsis5
mov eax, [eax + NET_DEVICE.type]
jmp .return
 
; 5 : return number of arp packets received
mov eax, [arp_rx_count]
ret
 
notsis5:
cmp ebx, 6
jnz notsis6
.get_dev_name: ; 1 = Get device name
 
; 6 : return status of packet driver
; ( 0 == not active, FFFFFFFF = successful )
mov eax, [eth_status]
ret
mov esi, [eax + NET_DEVICE.name]
mov edi, ecx
 
notsis6:
mov ecx, 64/4 ; max length
rep movsd
 
xor eax, eax
ret
jmp .return
 
.reset: ; 2 = Reset the device
 
call [eax + NET_DEVICE.reset]
jmp .return
 
;***************************************************************************
; Function
; stack_get_packet
;
; Description
; extracts an IP packet from the NET1 output queue
; and sends the data to the calling process
; pointer to data in edx
; returns number of bytes read in eax
;
;***************************************************************************
stack_get_packet:
; Look for a buffer to tx
mov eax, NET1OUT_QUEUE
call dequeue
cmp ax, NO_BUFFER
je sgp_non_exit ; Exit if no buffer available
.stop: ; 3 = Stop driver for this device
 
push eax ; Save buffer number for freeing at end
call [eax + NET_DEVICE.unload]
jmp .return
 
push edx
; convert buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
pop edx
 
push eax ; save address of IP data
; Get the address of the callers data
mov edi, [TASK_BASE]
add edi, TASKDATA.mem_start
add edx, [edi]
mov edi, edx
pop eax
.get_ptr: ; 4 = Get driver pointer
 
mov ecx, 1500 ; should get the actual number of bytes to write
mov esi, eax
cld
rep movsb ; copy the data across
jmp .return
 
; And finally, return the buffer to the free queue
pop eax
call freeBuff
 
mov eax, 1500
ret
.get_drv_name: ; 5 = Get driver name
 
sgp_non_exit:
xor eax, eax
jmp .return
 
 
.set_default: ; 6 = Set default device
 
call NET_set_default
jmp .return
 
.doesnt_exist:
mov eax, -1
 
.return:
mov [esp+32], eax
ret
 
 
 
;***************************************************************************
; Function
; stack_insert_packet
;----------------------------------------------------------------
;
; Description
; writes an IP packet into the stacks receive queue
; # of bytes to write in ecx
; pointer to data in edx
; returns 0 in eax ok, -1 == failed
; System function to work with protocols (76)
;
;***************************************************************************
stack_insert_packet:
;----------------------------------------------------------------
align 4
sys_protocols:
cmp bh, MAX_NET_DEVICES ; Check if device number exists
jae .doesnt_exist
 
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je sip_err_exit
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6 ; now we have the device num * 4 in esi
cmp [esi + NET_DRV_LIST], 0 ; check if driver is running
je .doesnt_exist
 
push eax
push .return ; return address (we will be using jumps instead of calls)
 
; save the pointers to the data buffer & size
push edx
push ecx
mov eax, ebx ; set ax to protocol number
shr eax, 16 ;
 
; convert buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
cmp ax, API_ETH
je ETH_api
 
mov edx, eax
cmp ax, API_IPv4
je IPv4_api
 
; So, edx holds the IPbuffer ptr
cmp ax, API_ICMP
je ICMP_api
 
pop ecx ; count of bytes to send
mov ebx, ecx ; need the length later
pop eax ; get callers ptr to data to send
cmp ax, API_UDP
je UDP_api
 
; Get the address of the callers data
mov edi, [TASK_BASE]
add edi, TASKDATA.mem_start
add eax, [edi]
mov esi, eax
cmp ax, API_TCP
je TCP_api
 
mov edi, edx
cld
rep movsb ; copy the data across
cmp ax, API_ARP
je ARP_api
 
pop ebx
cmp ax, API_PPPOE
je PPPoE_api
 
mov eax, IPIN_QUEUE
call queue
cmp ax, API_IPv6
je IPv6_api
 
inc dword [ip_rx_count]
add esp, 4 ; if we reached here, no function was called, so we need to balance stack
 
mov eax, 0
ret
.doesnt_exist:
mov eax, -1
 
sip_err_exit:
mov eax, 0xFFFFFFFF
.return:
mov [esp+28+4], eax ; return eax value to the program
ret
 
/kernel/branches/Kolibri-acpi/network/tcp.inc
1,1176 → 1,224
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; TCP.INC ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; TCP Processes for Menuet OS TCP/IP stack ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; See file COPYING for details ;;
;; v0.6 : Added reset handling in the established state ;;
;; Added a timer per socket to allow delays when ;;
;; rx window gets below 1KB ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
; Socket states
TCPS_CLOSED = 0
TCPS_LISTEN = 1
TCPS_SYN_SENT = 2
TCPS_SYN_RECEIVED = 3
TCPS_ESTABLISHED = 4
TCPS_CLOSE_WAIT = 5
TCPS_FIN_WAIT_1 = 6
TCPS_CLOSING = 7
TCPS_LAST_ACK = 8
TCPS_FIN_WAIT_2 = 9
TCPS_TIMED_WAIT = 10
 
; TCP TCB states
TCB_LISTEN equ 1
TCB_SYN_SENT equ 2
TCB_SYN_RECEIVED equ 3
TCB_ESTABLISHED equ 4
TCB_FIN_WAIT_1 equ 5
TCB_FIN_WAIT_2 equ 6
TCB_CLOSE_WAIT equ 7
TCB_CLOSING equ 8
TCB_LAST_ACK equ 9
TCB_TIMED_WAIT equ 10
TCB_CLOSED equ 11
; Socket Flags
TF_ACKNOW = 1 shl 0 ; ack peer immediately
TF_DELACK = 1 shl 1 ; ack, but try to delay it
TF_NODELAY = 1 shl 2 ; don't delay packets to coalesce
TF_NOOPT = 1 shl 3 ; don't use tcp options
TF_SENTFIN = 1 shl 4 ; have sent FIN
TF_REQ_SCALE = 1 shl 5 ; have/will request window scaling
TF_RCVD_SCALE = 1 shl 6 ; other side has requested scaling
TF_REQ_TSTMP = 1 shl 7 ; have/will request timestamps
TF_RCVD_TSTMP = 1 shl 8 ; a timestamp was received in SYN
TF_SACK_PERMIT = 1 shl 9 ; other side said I could SACK
 
TH_FIN = 0x01
TH_SYN = 0x02
TH_RST = 0x04
TH_PUSH = 0x08
TH_ACK = 0x10
TH_URG = 0x20
; Segment flags
TH_FIN = 1 shl 0
TH_SYN = 1 shl 1
TH_RST = 1 shl 2
TH_PUSH = 1 shl 3
TH_ACK = 1 shl 4
TH_URG = 1 shl 5
 
TWOMSL equ 10 ; # of secs to wait before closing socket
; Segment header options
TCP_OPT_EOL = 0 ; End of option list.
TCP_OPT_NOP = 1 ; No-Operation.
TCP_OPT_MAXSEG = 2 ; Maximum Segment Size.
TCP_OPT_WINDOW = 3 ; window scale
TCP_OPT_SACK_PERMIT = 4 ; Selective Acknowledgement
TCP_OPT_SACK = 5
TCP_OPT_TIMESTAMP = 8
 
TCP_RETRIES equ 5 ; Number of times to resend a packet
TCP_TIMEOUT equ 20 ; resend if not replied to in x hs
; Fundamental timer values
TCP_time_MSL = 47 ; max segment lifetime (30s)
TCP_time_re_min = 2 ; min retransmission (1,28s)
TCP_time_re_max = 100 ; max retransmission (64s)
TCP_time_pers_min = 8 ; min persist (5,12s)
TCP_time_pers_max = 94 ; max persist (60,16s)
TCP_time_keep_init = 118 ; connection establishment (75,52s)
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h)
TCP_time_keep_interval = 118 ; between probes when no response (75,52s)
TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s)
TCP_time_srtt_default = 0 ;
TCP_time_max_idle = 8*TCP_time_keep_interval ; FIXME
 
;*******************************************************************
; Interface
;
; tcp_tx_handler Handles the TCP transmit queue
; tcp_rx The protocol handler for received data
; buildTCPPacket fills in the packet headers and data
; tcpStateMachine Main state machine for received TCP packets
; tcp_tcb_handler 1s timer, to erase tcb's in TIME_WAIT state
;
;*******************************************************************
; timer constants
TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK
TCP_max_keepcnt = 8 ; max keepalive probes
 
 
; TCP Payload ( Data field in IP datagram )
;
; 0 1 2 3
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;20 | Source Port | Destination Port |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;24 | Sequence Number |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;28 | Acknowledgment Number |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;32 | Data | |U|A|P|R|S|F| |
; | Offset| Reserved |R|C|S|S|Y|I| Window |
; | | |G|K|H|T|N|N| |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;36 | Checksum | Urgent Pointer |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;40 | Options | Padding |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | data
TCP_max_winshift = 14
TCP_max_win = 65535
 
TCP_re_xmit_thresh = 3
 
struc TCP_PACKET
{ .SourcePort dw ? ;+00
.DestinationPort dw ? ;+02
.SequenceNumber dd ? ;+04
.AckNumber dd ? ;+08
.DataOffset db ? ;+12 - DataOffset[0-3 bits] and Reserved[4-7]
.Flags db ? ;+13 - Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN
.Window dw ? ;+14
.Checksum dw ? ;+16
.UrgentPointer dw ? ;+18
.Options rb 3 ;+20
.Padding db ? ;+23
.Data db ? ;+24
}
TCP_mss_default = 1480 ; default max segment size
 
virtual at 0
TCP_PACKET TCP_PACKET
end virtual
; smoothed round trip time and estimated variance are stored as fixed point numbers,
; shifted by the value below.
; With these scales, srtt has 3 bits to the right of the binary point, and thus an "alpha"
; of .875. rttvar has 2 bits to the right and thus "alpha" of 0.75
TCP_RTT_SHIFT = 3
TCP_RTTVAR_SHIFT = 2
 
; bits used by tcp_input and tcp_output
TCP_BIT_NEEDOUTPUT = 1 shl 0
TCP_BIT_TIMESTAMP = 1 shl 1
TCP_BIT_DROPSOCKET = 1 shl 2
 
TCP_BIT_SENDALOT = 1 shl 0
 
;***************************************************************************
; Function
; tcp_tcb_handler
;
; Description
; Handles sockets in the timewait state, closing them
; when the TCB timer expires
;
;***************************************************************************
TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds
 
proc tcp_tcb_handler stdcall uses ebx
; scan through all the sockets, decrementing active timers
TCP_QUEUE_SIZE = 50
 
mov ebx, net_sockets
struct TCP_header
 
cmp [ebx + SOCKET.NextPtr], 0
je .exit
;DEBUGF 1, "K : sockets:\n"
SourcePort dw ?
DestinationPort dw ?
SequenceNumber dd ?
AckNumber dd ?
DataOffset db ? ; DataOffset[0-3 bits] and Reserved[4-7]
Flags db ? ; Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN
Window dw ?
Checksum dw ?
UrgentPointer dw ?
 
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .exit
ends
 
;DEBUGF 1, "K : %x-%x: %x-%x-%x-%u\n", [ebx + SOCKET.PID]:2, [ebx + SOCKET.Number]:2, [ebx + SOCKET.LocalPort]:4, [ebx + SOCKET.RemoteIP], [ebx + SOCKET.RemotePort]:4, [ebx + SOCKET.TCBState]
struct TCP_queue_entry
 
cmp [ebx + SOCKET.TCBTimer], 0
jne .decrement_tcb
cmp [ebx + SOCKET.wndsizeTimer], 0
jne .decrement_wnd
jmp .next_socket
ip_ptr dd ?
segment_ptr dd ?
segment_size dd ?
device_ptr dd ?
 
.decrement_tcb:
; decrement it, delete socket if TCB timer = 0 & socket in timewait state
dec [ebx + SOCKET.TCBTimer]
jnz .next_socket
buffer_ptr dd ?
timestamp dd ?
 
cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
jne .next_socket
ends
 
push [ebx + SOCKET.PrevPtr]
stdcall net_socket_free, ebx
pop ebx
jmp .next_socket
align 4
uglobal
TCP_segments_tx rd MAX_NET_DEVICES
TCP_segments_rx rd MAX_NET_DEVICES
TCP_segments_missed rd MAX_NET_DEVICES
TCP_segments_dumped rd MAX_NET_DEVICES
; TCP_bytes_rx rq MAX_NET_DEVICES
; TCP_bytes_tx rq MAX_NET_DEVICES
TCP_sequence_num dd ?
TCP_queue rd TCP_QUEUE_SIZE*sizeof.TCP_queue_entry/4
TCP_input_event dd ?
endg
 
.decrement_wnd:
; TODO - prove it works!
dec [ebx + SOCKET.wndsizeTimer]
jmp .next_socket
 
.exit:
ret
endp
 
 
;***************************************************************************
; Function
; tcp_tx_handler
;-----------------------------------------------------------------
;
; Description
; Handles queued TCP data
; This is a kernel function, called by stack_handler
; TCP_init
;
;***************************************************************************
; This function resets all TCP variables
;
;-----------------------------------------------------------------
macro TCP_init {
 
proc tcp_tx_handler stdcall
; decrement all resend buffers timers. If they
; expire, queue them for sending, and restart the timer.
; If the retries counter reach 0, delete the entry
xor eax, eax
mov edi, TCP_segments_tx
mov ecx, (6*MAX_NET_DEVICES)
rep stosd
 
mov esi, resendQ
mov ecx, 0
pseudo_random eax
mov [TCP_sequence_num], eax
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .exit ; None left
cmp dword[esi + 4], 0
jne @f ; found one
inc ecx
add esi, 8
jmp .next_resendq
init_queue TCP_queue
 
@@: ; we have one. decrement it's timer by 1
dec word[esi + 2]
jz @f
inc ecx
add esi, 8
jmp .next_resendq ; Timer not zero, so move on
 
@@:
xor ebx, ebx
; restart timer, and decrement retries
; After the first resend, back of on next, by a factor of 5
mov [esi + 2], word TCP_TIMEOUT * 5
dec byte[esi + 1]
jnz @f
 
; retries now 0, so delete from queue
xchg [esi + 4], ebx
 
@@: ; resend packet
pushad
 
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
jne .tth004z
 
; TODO - try again in 10ms.
test ebx, ebx
jnz @f
mov [esi + 4], ebx
 
@@: ; Mark it to expire in 10ms - 1 tick
mov byte[esi + 1], 1
mov word[esi + 2], 1
jmp .tth005
 
.tth004z:
; we have a buffer # in ax
push eax ecx
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
 
; we have the buffer address in eax
mov edi, eax
pop ecx
; Now get buffer location, and copy buffer across. argh! more copying,,
imul esi, ecx, IPBUFFSIZE
add esi, resendBuffer
 
; we have resend buffer location in esi
mov ecx, IPBUFFSIZE
 
; copy data across
push edi
cld
rep movsb
pop edi
 
; queue packet
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
cmp edx, [edi + IP_PACKET.DestinationAddress]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
push 1
pop ebx
call queue
mov ecx, TCP_process_input
call new_sys_threads
 
.tth005:
popad
}
 
inc ecx
add esi, 8
jmp .next_resendq
 
.exit:
ret
endp
include 'tcp_timer.inc'
include 'tcp_subr.inc'
include 'tcp_usreq.inc'
include 'tcp_input.inc'
include 'tcp_output.inc'
 
 
;***************************************************************************
; Function
; tcp_rx
;---------------------------------------------------------------------------
;
; Description
; TCP protocol handler
; This is a kernel function, called by ip_rx
; IP buffer address given in edx
; IP buffer number in eax
; Free up (or re-use) IP buffer when finished
; TCP_API
;
;***************************************************************************
 
proc tcp_rx stdcall uses ebx
; The process is as follows.
; Look for a socket with matching remote IP, remote port, local port
; if not found, then
; look for remote IP + local port match ( where sockets remote port = 0)
; if not found, then
; look for a socket where local socket port == IP packets remote port
; where sockets remote port, remote IP = 0
; discard if not found
; Call sockets tcbStateMachine, with pointer to packet.
; the state machine will not delete the packet, so do that here.
 
push eax
 
; Look for a socket where
; IP Packet TCP Destination Port = local Port
; IP Packet SA = Remote IP
; IP Packet TCP Source Port = remote Port
 
mov ebx, net_sockets
 
.next_socket.1:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .next_socket.1.exit
 
; DEBUGF 1, "K : tcp_rx - 1.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
 
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr
cmp [ebx + SOCKET.LocalPort], ax ; get the dest. port from the TCP hdr
jne .next_socket.1 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 1.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP]
 
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP
jne .next_socket.1 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 1.sport: %x - %x\n", [edx + 20 + TCP_PACKET.SourcePort]:4, [ebx + SOCKET.RemotePort]:4
 
mov ax, [edx + 20 + TCP_PACKET.SourcePort] ; get the source port from the TCP hdr
cmp [ebx + SOCKET.RemotePort], ax ; compare with socket's remote port
jne .next_socket.1 ; different - try next socket
 
; We have a complete match - use this socket
jmp .change_state
 
.next_socket.1.exit:
 
; If we got here, there was no match
; Look for a socket where
; IP Packet TCP Destination Port = local Port
; IP Packet SA = Remote IP
; socket remote Port = 0
 
mov ebx, net_sockets
 
.next_socket.2:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .next_socket.2.exit
 
; DEBUGF 1, "K : tcp_rx - 2.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
 
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get the dest. port from the TCP hdr
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port
jne .next_socket.2 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 2.addr: %x - %x\n", [edx + IP_PACKET.SourceAddress], [ebx + SOCKET.RemoteIP]
 
mov eax, [edx + IP_PACKET.SourceAddress] ; get the source IP Addr from the IP hdr
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP
jne .next_socket.2 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 2.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4
 
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0
jne .next_socket.2 ; different - try next socket
 
; We have a complete match - use this socket
jmp .change_state
 
.next_socket.2.exit:
 
; If we got here, there was no match
; Look for a socket where
; IP Packet TCP Destination Port = local Port
; socket Remote IP = 0
; socket remote Port = 0
 
mov ebx, net_sockets
 
.next_socket.3:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .next_socket.3.exit
 
; DEBUGF 1, "K : tcp_rx - 3.dport: %x - %x\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4
 
mov ax, [edx + 20 + TCP_PACKET.DestinationPort] ; get destination port from the TCP hdr
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port
jne .next_socket.3 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 3.addr: 00000000 - %x\n", [ebx + SOCKET.RemoteIP]
 
cmp [ebx + SOCKET.RemoteIP], 0 ; only match a socket remote IP of 0
jne .next_socket.3 ; different - try next socket
 
; DEBUGF 1, "K : tcp_rx - 3.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4
 
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0
jne .next_socket.3 ; different - try next socket
 
; We have a complete match - use this socket
jmp .change_state
 
.next_socket.3.exit:
 
; If we got here, we need to reject the packet
 
DEBUGF 1, "K : tcp_rx - dumped\n"
DEBUGF 1, "K : --------: %x-%x-%x (flags: %x)\n", [edx + 20 + TCP_PACKET.DestinationPort]:4, [edx + IP_PACKET.SourceAddress], [edx + 20 + TCP_PACKET.SourcePort]:4, [edx + 20 + TCP_PACKET.Flags]:2
 
inc [dumped_rx_count]
jmp .exit
 
.change_state:
 
; We have a valid socket/TCB, so call the TCB State Machine for that skt.
; socket is pointed to by ebx
; IP packet is pointed to by edx
; IP buffer number is on stack ( it will be popped at the end)
 
stdcall tcpStateMachine, ebx
 
.exit:
pop eax
call freeBuff
ret
endp
 
 
;***************************************************************************
; Function
; buildTCPPacket
; This function is called by system function 76
;
; Description
; builds an IP Packet with TCP data fully populated for transmission
; You may destroy any and all registers
; TCP control flags specified in bl
; This TCB is in [sktAddr]
; User data pointed to by esi
; Data length in ecx
; Transmit buffer number in eax
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
;***************************************************************************
 
proc build_tcp_packet stdcall, sockAddr:DWORD
push ecx ; Save data length
 
; convert buffer pointer eax to the absolute address
mov ecx, IPBUFFSIZE
mul ecx
add eax, IPbuffs
 
mov edx, eax
 
mov [edx + 20 + TCP_PACKET.Flags], bl ; TCP flags
 
mov ebx, [sockAddr]
 
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
 
; Fill in the IP header ( some data is in the socket descriptor)
mov eax, [ebx + SOCKET.LocalIP]
mov [edx + IP_PACKET.SourceAddress], eax
mov eax, [ebx + SOCKET.RemoteIP]
mov [edx + IP_PACKET.DestinationAddress], eax
 
mov [edx + IP_PACKET.VersionAndIHL], 0x45
mov [edx + IP_PACKET.TypeOfService], 0
 
pop eax ; Get the TCP data length
push eax
 
add eax, 20 + 20 ; add IP header and TCP header lengths
rol ax, 8
mov [edx + IP_PACKET.TotalLength], ax
mov [edx + IP_PACKET.Identification], 0
mov [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
mov [edx + IP_PACKET.TimeToLive], 0x20
mov [edx + IP_PACKET.Protocol], PROTOCOL_TCP
 
; Checksum left unfilled
mov [edx + IP_PACKET.HeaderChecksum], 0
 
; Fill in the TCP header (some data is in the socket descriptor)
mov ax, [ebx + SOCKET.LocalPort]
mov [edx + 20 + TCP_PACKET.SourcePort], ax ; Local Port
 
mov ax, [ebx + SOCKET.RemotePort]
mov [edx + 20 + TCP_PACKET.DestinationPort], ax ; desitination Port
 
; Checksum left unfilled
mov [edx + 20 + TCP_PACKET.Checksum], 0
 
; sequence number
mov eax, [ebx + SOCKET.SND_NXT]
mov [edx + 20 + TCP_PACKET.SequenceNumber], eax
 
; ack number
mov eax, [ebx + SOCKET.RCV_NXT]
mov [edx + 20 + TCP_PACKET.AckNumber], eax
 
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size)
; 768 bytes seems better
mov [edx + 20 + TCP_PACKET.Window], 0x0003
 
; Urgent pointer (0)
mov [edx + 20 + TCP_PACKET.UrgentPointer], 0
 
; data offset ( 0x50 )
mov [edx + 20 + TCP_PACKET.DataOffset], 0x50
 
pop ecx ; count of bytes to send
mov ebx, ecx ; need the length later
 
cmp ebx, 0
jz @f
 
mov edi, edx
add edi, 40
cld
rep movsb ; copy the data across
 
@@: ; we have edx as IPbuffer ptr.
; Fill in the TCP checksum
; First, fill in pseudoheader
mov eax, [edx + IP_PACKET.SourceAddress]
mov [pseudoHeader], eax
mov eax, [edx + IP_PACKET.DestinationAddress]
mov [pseudoHeader + 4], eax
mov word[pseudoHeader + 8], PROTOCOL_TCP shl 8 + 0
add ebx, 20
mov [pseudoHeader + 10], bh
mov [pseudoHeader + 11], bl
 
mov eax, pseudoHeader
mov [checkAdd1], eax
mov word[checkSize1], 12
mov eax, edx
add eax, 20
mov [checkAdd2], eax
mov eax, ebx
mov [checkSize2], ax
 
call checksum
 
; store it in the TCP checksum ( in the correct order! )
mov ax, [checkResult]
rol ax, 8
mov [edx + 20 + TCP_PACKET.Checksum], ax
 
; Fill in the IP header checksum
GET_IHL eax, edx ; get IP-Header length
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size
rol ax, 8
mov [edx + IP_PACKET.HeaderChecksum], ax
 
ret
endp
 
 
; Increments the 32 bit value pointed to by esi in internet order
proc inc_inet_esi stdcall
push eax
mov eax, [esi]
bswap eax
inc eax
bswap eax
mov [esi], eax
pop eax
ret
endp
 
 
; Increments the 32 bit value pointed to by esi in internet order
; by the value in ecx
proc add_inet_esi stdcall
push eax
mov eax, [esi]
bswap eax
add eax, ecx
bswap eax
mov [esi], eax
pop eax
ret
endp
 
 
iglobal
TCBStateHandler dd \
stateTCB_LISTEN, \
stateTCB_SYN_SENT, \
stateTCB_SYN_RECEIVED, \
stateTCB_ESTABLISHED, \
stateTCB_FIN_WAIT_1, \
stateTCB_FIN_WAIT_2, \
stateTCB_CLOSE_WAIT, \
stateTCB_CLOSING, \
stateTCB_LAST_ACK, \
stateTCB_TIME_WAIT, \
stateTCB_CLOSED
endg
 
 
;***************************************************************************
; Function
; tcpStateMachine
; OUT:
;
; Description
; TCP state machine
; This is a kernel function, called by tcp_rx
;
; IP buffer address given in edx
; Socket/TCB address in ebx
;
; The IP buffer will be released by the caller
;***************************************************************************
;---------------------------------------------------------------------------
align 4
TCP_api:
 
proc tcpStateMachine stdcall, sockAddr:DWORD
; as a packet has been received, update the TCB timer
mov [ebx + SOCKET.TCBTimer], TWOMSL
movzx eax, bh
shl eax, 2
 
; If the received packet has an ACK bit set,
; remove any packets in the resend queue that this
; received packet acknowledges
pushad
test [edx + 20 + TCP_PACKET.Flags], TH_ACK
jz .call_handler ; No ACK, so no data yet
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
dec bl
jz .packets_missed ; 2
dec bl
jz .packets_dumped ; 3
 
; get skt number in eax
stdcall net_socket_addr_to_num, ebx
 
; The ack number is in [edx + 28], inet format
; skt in eax
 
mov esi, resendQ
xor ecx, ecx
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .call_handler ; None left
cmp [esi + 4], eax
je @f ; found one
inc ecx
add esi, 8
jmp .next_resendq
 
@@: ; Can we delete this buffer?
 
; If yes, goto @@. No, goto .next_resendq
; Get packet data address
 
push ecx
; Now get buffer location, and copy buffer across. argh! more copying,,
imul edi, ecx, IPBUFFSIZE
add edi, resendBuffer
 
; we have dest buffer location in edi. incoming packet in edx.
; Get this packets sequence number
; preserve al, ecx, esi, edx
mov ecx, [edi + 20 + TCP_PACKET.SequenceNumber]
bswap ecx
movzx ebx, word[edi + 2]
xchg bl, bh
sub ebx, 40
add ecx, ebx ; ecx is now seq# of last byte +1, intel format
 
; get recievd ack #, in intel format
mov ebx, [edx + 20 + TCP_PACKET.AckNumber]
bswap ebx
 
cmp ebx, ecx ; Finally. ecx = rx'ed ack. ebx = last byte in que
; DANGER! need to handle case that we have just
; passed the 2**32, and wrapped round!
pop ecx
jae @f ; if rx > old, delete old
 
inc ecx
add esi, 8
jmp .next_resendq
 
@@:
mov dword[esi + 4], 0
inc ecx
add esi, 8
jmp .next_resendq
 
.call_handler:
popad
 
; Call handler for given TCB state
 
mov eax, [ebx + SOCKET.TCBState]
cmp eax, TCB_LISTEN
jb .exit
cmp eax, TCB_CLOSED
ja .exit
 
stdcall [TCBStateHandler + (eax - 1) * 4], [sockAddr]
 
.exit:
.error:
mov eax, -1
ret
endp
 
;***************************************************************************
; Function
; signal_network_event
;
; Description
; Signals about network event to socket owner
; This is a kernel function, called from TCP handler
;
; Socket/TCB address in ebx
;***************************************************************************
proc signal_network_event
push ecx esi eax
mov eax, [ebx + SOCKET.PID]
mov ecx, 1
mov esi, TASK_DATA + TASKDATA.pid
 
.next_pid:
cmp [esi], eax
je .found_pid
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next_pid
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
pop eax esi ecx
.packets_tx:
mov eax, [TCP_segments_tx + eax]
ret
endp
 
proc stateTCB_LISTEN stdcall, sockAddr:DWORD
; In this case, we are expecting a SYN packet
; For now, if the packet is a SYN, process it, and send a response
; If not, ignore it
 
; Look at control flags
test [edx + 20 + TCP_PACKET.Flags], TH_SYN
jz .exit
 
; We have a SYN. update the socket with this IP packets details,
; And send a response
 
mov eax, [edx + IP_PACKET.SourceAddress]
mov [ebx + SOCKET.RemoteIP], eax
mov ax, [edx + 20 + TCP_PACKET.SourcePort]
mov [ebx + SOCKET.RemotePort], ax
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber]
mov [ebx + SOCKET.IRS], eax
mov [ebx + SOCKET.RCV_NXT], eax
lea esi, [ebx + SOCKET.RCV_NXT]
call inc_inet_esi ; RCV.NXT
mov eax, [ebx + SOCKET.ISS]
mov [ebx + SOCKET.SND_NXT], eax
 
; Now construct the response, and queue for sending by IP
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .exit
 
push ebx
push eax
mov bl, TH_SYN + TH_ACK
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
 
pop ebx
mov esi, [sockAddr]
mov [esi + SOCKET.TCBState], TCB_SYN_RECEIVED
call signal_network_event
 
; increment SND.NXT in socket
add esi, SOCKET.SND_NXT
call inc_inet_esi
 
.exit:
.packets_rx:
mov eax, [TCP_segments_rx + eax]
ret
endp
 
 
proc stateTCB_SYN_SENT stdcall, sockAddr:DWORD
; We are awaiting an ACK to our SYN, with a SYM
; Look at control flags - expecting an ACK
 
mov al, [edx + 20 + TCP_PACKET.Flags]
and al, TH_SYN + TH_ACK
cmp al, TH_SYN + TH_ACK
je .syn_ack
 
test al, TH_SYN
jz .exit
 
mov [ebx + SOCKET.TCBState], TCB_SYN_RECEIVED
push TH_SYN + TH_ACK
jmp .send
 
.syn_ack:
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED
push TH_ACK
 
.send:
call signal_network_event
; Store the recv.nxt field
mov eax, [edx + 20 + TCP_PACKET.SequenceNumber]
 
; Update our recv.nxt field
mov [ebx + SOCKET.RCV_NXT], eax
lea esi, [ebx + SOCKET.RCV_NXT]
call inc_inet_esi
 
; Send an ACK
; Now construct the response, and queue for sending by IP
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
pop ebx
je .exit
 
push eax
 
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
 
.exit:
.packets_missed:
mov eax, [TCP_segments_missed + eax]
ret
endp
 
 
proc stateTCB_SYN_RECEIVED stdcall, sockAddr:DWORD
; In this case, we are expecting an ACK packet
; For now, if the packet is an ACK, process it,
; If not, ignore it
 
test [edx + 20 + TCP_PACKET.Flags], TH_RST
jz .check_ack
 
push [ebx + SOCKET.OrigRemotePort] [ebx + SOCKET.OrigRemoteIP]
pop [ebx + SOCKET.RemoteIP] [ebx + SOCKET.RemotePort]
 
mov [ebx + SOCKET.TCBState], TCB_LISTEN
jmp .signal
 
.check_ack:
; Look at control flags - expecting an ACK
test [edx + 20 + TCP_PACKET.Flags], TH_ACK
jz .exit
 
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED
.signal:
call signal_network_event
 
.exit:
.packets_dumped:
mov eax, [TCP_segments_dumped + eax]
ret
endp
 
 
proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD
; Here we are expecting data, or a request to close
; OR both...
 
; Ignore all packets with sequnce number other than next expected
 
; recv.nxt is in dword [edx+24], in inet format
; recv seq is in [sktAddr]+56, in inet format
; just do a comparision
mov eax, [ebx + SOCKET.RCV_NXT]
cmp eax, [edx + 20 + TCP_PACKET.SequenceNumber]
jne .exit
 
; Did we receive a FIN or RST?
test [edx + 20 + TCP_PACKET.Flags], TH_FIN+TH_RST
jz .check_ack
 
; It was a fin or reset.
 
; Remove resend entries from the queue - I dont want to send any more data
pushad
 
; get skt #
stdcall net_socket_addr_to_num, ebx
 
mov esi, resendQ
mov ecx, 0
 
.next_resendq:
cmp ecx, NUMRESENDENTRIES
je .last_resendq ; None left
cmp [esi + 4], eax
je @f ; found one
inc ecx
add esi, 8
jmp .next_resendq
 
@@:
mov dword[esi + 4], 0
inc ecx
add esi, 8
jmp .next_resendq
 
.last_resendq:
popad
 
@@: ; Send an ACK to that fin, and enter closewait state
 
mov [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT
test [edx + 20 + TCP_PACKET.Flags], TH_RST
je @f
mov [ebx + SOCKET.TCBState], TCB_CLOSED
@@:
call signal_network_event
lea esi, [ebx + SOCKET.RCV_NXT]
mov eax, [esi] ; save original
call inc_inet_esi
;; jmp ste_ack - NO, there may be data
 
.check_ack:
; Check that we received an ACK
test [edx + 20 + TCP_PACKET.Flags], TH_ACK
jz .exit
 
; TODO - done, I think!
; First, look at the incoming window. If this is less than or equal to 1024,
; Set the socket window timer to 1. This will stop an additional packets being queued.
; ** I may need to tweak this value, since I do not know how many packets are already queued
mov cx, [edx + 20 + TCP_PACKET.Window]
xchg cl, ch
cmp cx, 1024
ja @f
 
mov [ebx + SOCKET.wndsizeTimer], 1
 
@@: ; OK, here is the deal
 
 
; Read the data bytes, store in socket buffer
movzx ecx, [edx + IP_PACKET.TotalLength]
xchg cl, ch
sub ecx, 40 ; Discard 40 bytes of header
ja .data ; Read data, if any
 
; If we had received a fin, we need to ACK it.
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT
je .ack
jmp .exit
 
.data:
push ecx
push ecx edx
lea ecx, [ebx+SOCKET.mutex]
call mutex_lock
pop edx ecx
 
push ebx
mov eax, [ebx + SOCKET.rxDataCount]
add eax, ecx
cmp eax, SOCKETBUFFSIZE - SOCKETHEADERSIZE
ja .overflow
 
mov [ebx + SOCKET.rxDataCount], eax ; increment the count of bytes in buffer
 
; point to the location to store the data
lea edi, [ebx + eax + SOCKETHEADERSIZE]
sub edi, ecx
 
add edx, 40 ; edx now points to the data
mov esi, edx
 
cld
rep movsb ; copy the data across
 
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
 
; flag an event to the application
pop ebx
call signal_network_event
 
pop ecx
 
; Update our recv.nxt field
lea esi, [ebx + SOCKET.RCV_NXT]
call add_inet_esi
 
.ack:
; Send an ACK
; Now construct the response, and queue for sending by IP
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .exit
 
push eax
 
mov bl, TH_ACK
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
 
mov eax, NET1OUT_QUEUE
 
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
 
.exit:
ret
.overflow:
; no place in buffer
; so simply restore stack and exit
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax ecx
ret
endp
 
 
proc stateTCB_FIN_WAIT_1 stdcall, sockAddr:DWORD
; We can either receive an ACK of a fin, or a fin
mov al, [edx + 20 + TCP_PACKET.Flags]
and al, TH_FIN + TH_ACK
 
cmp al, TH_ACK
jne @f
 
; It was an ACK
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_2
jmp .exit
 
@@:
mov [ebx + SOCKET.TCBState], TCB_CLOSING
cmp al, TH_FIN
je @f
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
 
@@:
lea esi, [ebx + SOCKET.RCV_NXT]
call inc_inet_esi
 
; Send an ACK
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .exit
 
push eax
 
mov bl, TH_ACK
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
 
.exit:
ret
endp
 
 
proc stateTCB_FIN_WAIT_2 stdcall, sockAddr:DWORD
test [edx + 20 + TCP_PACKET.Flags], TH_FIN
jz .exit
 
; Change state, as we have a fin
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
 
lea esi, [ebx + SOCKET.RCV_NXT]
call inc_inet_esi
 
; Send an ACK
mov eax, EMPTY_QUEUE
call dequeue
cmp ax, NO_BUFFER
je .exit
 
push eax
 
mov bl, TH_ACK
xor ecx, ecx
xor esi, esi
stdcall build_tcp_packet, [sockAddr]
 
mov eax, NET1OUT_QUEUE
mov edx, [stack_ip]
mov ecx, [sockAddr]
cmp edx, [ecx + SOCKET.RemoteIP]
jne .not_local
mov eax, IPIN_QUEUE
 
.not_local:
; Send it.
pop ebx
call queue
 
.exit:
ret
endp
 
 
proc stateTCB_CLOSE_WAIT stdcall, sockAddr:DWORD
; Intentionally left empty
; socket_close_tcp handles this
ret
endp
 
 
proc stateTCB_CLOSING stdcall, sockAddr:DWORD
; We can either receive an ACK of a fin, or a fin
test [edx + 20 + TCP_PACKET.Flags], TH_ACK
jz .exit
 
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT
 
.exit:
ret
endp
 
 
proc stateTCB_LAST_ACK stdcall, sockAddr:DWORD
; Look at control flags - expecting an ACK
test [edx + 20 + TCP_PACKET.Flags], TH_ACK
jz .exit
 
; delete the socket
stdcall net_socket_free, ebx
 
.exit:
ret
endp
 
 
proc stateTCB_TIME_WAIT stdcall, sockAddr:DWORD
ret
endp
 
 
proc stateTCB_CLOSED stdcall, sockAddr:DWORD
ret
endp
/kernel/branches/Kolibri-acpi/network/tcp_input.inc
0,0 → 1,1663
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3407 $
 
;-----------------------------------------------------------------
;
; TCP_input:
;
; Add a segment to the incoming TCP queue
;
; IN: [esp] = ptr to buffer
; [esp+4] = buffer size (dont care)
; ebx = ptr to device struct
; ecx = segment size
; esi = ptr to TCP segment
; edi = ptr to ipv4 source address, followed by ipv4 dest address
;
; OUT: /
;
;-----------------------------------------------------------------
 
align 4
TCP_input:
 
; record the current time
mov eax, [timer_ticks] ; in 1/100 seconds
mov [esp + 4], eax
 
push ebx ecx esi edi ; mind the order
mov esi, esp
 
pushf
cli
add_to_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .fail
popf
 
add esp, sizeof.TCP_queue_entry
 
xor edx, edx
mov eax, [TCP_input_event]
mov ebx, [eax + EVENT.id]
xor esi, esi
call raise_event
 
ret
 
.fail:
popf
DEBUGF 2, "TCP incoming queue is full, discarding packet!\n"
 
inc [TCP_segments_missed] ; FIXME: use correct interface
 
add esp, sizeof.TCP_queue_entry - 8
call kernel_free
add esp, 4
 
ret
 
 
 
 
align 4
TCP_process_input:
 
xor esi, esi
mov ecx, MANUAL_DESTROY
call create_event
mov [TCP_input_event], eax
 
.wait:
mov eax, [TCP_input_event]
mov ebx, [eax + EVENT.id]
call wait_event
 
.loop:
get_from_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .wait
 
push [esi + TCP_queue_entry.timestamp]
push [esi + TCP_queue_entry.buffer_ptr]
 
mov ebx, [esi + TCP_queue_entry.device_ptr]
mov ecx, [esi + TCP_queue_entry.segment_size]
mov edi, [esi + TCP_queue_entry.ip_ptr] ; ptr to ipv4 source address, followed by ipv4 destination address
mov esi, [esi + TCP_queue_entry.segment_ptr] ; change esi last
 
DEBUGF 1,"TCP_input: size=%u time=%d\n", ecx, [timer_ticks]
 
mov edx, esi
 
cmp ebx, LOOPBACK_DEVICE
je .checksum_ok
 
; re-calculate the checksum (if not already done by hw)
; test [ebx + NET_DEVICE.hwacc], HWACC_TCP_IPv4_IN
; jnz .checksum_ok
 
push ecx esi
pushw [esi + TCP_header.Checksum]
mov [esi + TCP_header.Checksum], 0
TCP_checksum (edi), (edi+4)
pop cx ; previous checksum
cmp cx, dx
pop edx ecx
jne .drop_no_socket
.checksum_ok:
 
; Verify the data offset
and [edx + TCP_header.DataOffset], 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header)
shr [edx + TCP_header.DataOffset], 2
cmp [edx + TCP_header.DataOffset], sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header
jb .drop_no_socket ; If not, drop the packet
 
movzx eax, [edx + TCP_header.DataOffset]
sub ecx, eax ; substract TCP header size from total segment size
jb .drop_no_socket ; If total segment size is less then the advertised header size, drop packet
DEBUGF 1,"TCP_input: %u bytes of data\n", ecx
 
;-------------------------------------------
; Convert Big-endian values to little endian
 
ntohd [edx + TCP_header.SequenceNumber]
ntohd [edx + TCP_header.AckNumber]
 
ntohw [edx + TCP_header.Window]
ntohw [edx + TCP_header.UrgentPointer]
 
;------------------------
; Find the socket pointer
 
; IP Packet TCP Destination Port = local Port
; (IP Packet SenderAddress = Remote IP) OR (Remote IP = 0)
; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0)
 
.findpcb:
mov ebx, net_sockets
mov si, [edx + TCP_header.DestinationPort]
 
.socket_loop:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .respond_seg_reset
 
cmp [ebx + SOCKET.Domain], AF_INET4
jne .socket_loop
 
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP
jne .socket_loop
 
cmp [ebx + TCP_SOCKET.LocalPort], si
jne .socket_loop
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
cmp eax, [edi] ; Ipv4 source address
je @f
test eax, eax
jnz .socket_loop
@@:
 
mov ax, [ebx + TCP_SOCKET.RemotePort]
cmp [edx + TCP_header.SourcePort], ax
je .found_socket
test ax, ax
jnz .socket_loop
.found_socket: ; ebx now contains the socketpointer
DEBUGF 1,"TCP_input: socket ptr=%x state=%u flags=%x\n", ebx, [ebx + TCP_SOCKET.t_state], [edx + TCP_header.Flags]:2
 
;-------------
; update stats
 
inc [TCP_segments_rx] ; FIXME: correct interface?
 
;----------------------------
; Check if socket isnt closed
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
je .drop_no_socket
 
;----------------
; Lock the socket
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_lock
popa
 
DEBUGF 1,"TCP_input: socket locked\n"
 
;---------------------------
; disable all temporary bits
 
mov [ebx + TCP_SOCKET.temp_bits], 0
 
;---------------------------------------
; unscale the window into a 32 bit value
 
movzx eax, [edx + TCP_header.Window]
push ecx
mov cl, [ebx + TCP_SOCKET.SND_SCALE]
shl eax, cl
mov dword [edx + TCP_header.Window], eax ; word after window is checksum, we dont need checksum anymore
pop ecx
 
;---------------------------------------
; Are we accepting incoming connections?
 
test [ebx + SOCKET.options], SO_ACCEPTCON
jz .no_accept
 
DEBUGF 1,"TCP_input: Accepting new connection\n"
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
push ecx edx esi edi ;;;
call SOCKET_fork
pop edi esi edx ecx
 
test eax, eax
jz .drop_no_socket
 
mov ebx, eax
 
mov [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET ;;; FIXME: should we take over bits from previous socket?
 
push dword [edi + 4] ; Ipv4 destination addres
pop [ebx + IP_SOCKET.LocalIP]
 
push [edx + TCP_header.DestinationPort]
pop [ebx + TCP_SOCKET.LocalPort]
 
mov [ebx + TCP_SOCKET.t_state], TCPS_LISTEN
.no_accept:
 
 
;-------------------------------------
; Reset idle timer and keepalive timer
 
mov [ebx + TCP_SOCKET.t_idle], 0
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle
 
;--------------------
; Process TCP options
 
push ecx
 
movzx ecx, [edx + TCP_header.DataOffset]
cmp ecx, sizeof.TCP_header ; Does header contain any options?
je .no_options
 
DEBUGF 1,"TCP_input: Segment has options\n"
 
;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS
;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state
;;; jz .not_uni_xfer ; also no header prediction
 
add ecx, edx
lea esi, [edx + sizeof.TCP_header]
 
.opt_loop:
cmp esi, ecx ; are we scanning outside of header?
jae .no_options
lodsb
cmp al, TCP_OPT_EOL ; end of option list?
je .no_options
cmp al, TCP_OPT_NOP
je .opt_loop
cmp al, TCP_OPT_MAXSEG
je .opt_maxseg
cmp al, TCP_OPT_WINDOW
je .opt_window
cmp al, TCP_OPT_SACK_PERMIT
je .opt_sack_permit
; cmp al, TCP_OPT_SACK
; je .opt_sack
cmp al, TCP_OPT_TIMESTAMP
je .opt_timestamp
DEBUGF 1,"TCP_input: unknown option:%u\n", al
jmp .no_options ; If we reach here, some unknown options were received, skip them all!
 
.opt_maxseg:
lodsb
cmp al, 4
jne .no_options ; error occured, ignore all options!
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
 
lodsw
rol ax, 8
DEBUGF 1,"TCP_input: Maxseg=%u\n", ax
call TCP_mss
@@:
jmp .opt_loop
 
 
.opt_window:
lodsb
cmp al, 3
jne .no_options
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
 
DEBUGF 1,"TCP_input: Got window scale option\n"
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
 
lodsb
mov [ebx + TCP_SOCKET.SND_SCALE], al
;;;;; TODO
 
@@:
jmp .opt_loop
 
 
.opt_sack_permit:
lodsb
cmp al, 2
jne .no_options
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
 
DEBUGF 1,"TCP_input: Selective Acknowledgement permitted\n"
or [ebx + TCP_SOCKET.t_flags], TF_SACK_PERMIT
 
@@:
jmp .opt_loop
 
 
.opt_timestamp:
lodsb
cmp al, 10 ; length must be 10
jne .no_options
 
DEBUGF 1,"TCP_input: Got timestamp option\n"
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
@@:
 
lodsd
mov [ebx + TCP_SOCKET.ts_val], eax
lodsd ; timestamp echo reply
mov [ebx + TCP_SOCKET.ts_ecr], eax
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
 
; Since we have a timestamp, lets do the paws test right away!
 
test [edx + TCP_header.Flags], TH_RST
jnz .no_paws
 
mov eax, [ebx + TCP_SOCKET.ts_recent]
test eax, eax
jz .no_paws
cmp eax, [ebx + TCP_SOCKET.ts_val]
jge .no_paws
 
DEBUGF 1,"TCP_input: PAWS: detected an old segment\n"
 
mov eax, [esp+4+4] ; tcp_now
sub eax, [ebx + TCP_SOCKET.ts_recent_age]
 
pop ecx
cmp eax, TCP_PAWS_IDLE
jle .drop_after_ack ; TODO: update stats
push ecx
 
mov [ebx + TCP_SOCKET.ts_recent], 0 ; timestamp was invalid, fix it.
.no_paws:
jmp .opt_loop
 
.no_options:
 
pop ecx
 
;-----------------------------------------------------------------------
; Time to do some header prediction (Original Principle by Van Jacobson)
 
; There are two common cases for an uni-directional data transfer.
;
; General rule: the packets has no control flags, is in-sequence,
; window width didnt change and we're not retransmitting.
;
; Second rules:
; - If the length is 0 and the ACK moved forward, we're the sender side of the transfer.
; In this case we'll free the ACK'ed data and notify higher levels that we have free space in buffer
;
; - If the length is not 0 and the ACK didn't move, we're the receiver side of the transfer.
; If the packets are in order (data queue is empty), add the data to the socket buffer and request a delayed ACK
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jnz .not_uni_xfer
 
test [edx + TCP_header.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
test [edx + TCP_header.Flags], TH_ACK
jz .not_uni_xfer
 
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
mov eax, dword [edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .not_uni_xfer
 
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jne .not_uni_xfer
 
;---------------------------------------
; check if we are sender in the uni-xfer
 
; If the following 4 conditions are all true, this segment is a pure ACK.
;
; - The segment contains no data.
test ecx, ecx
jnz .not_sender
 
; - The congestion window is greater than or equal to the current send window.
; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance.
mov eax, [ebx + TCP_SOCKET.SND_CWND]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jb .not_uni_xfer
 
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent.
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .not_uni_xfer
 
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number.
sub eax, [ebx + TCP_SOCKET.SND_UNA]
jbe .not_uni_xfer
 
DEBUGF 1,"TCP_input: Header prediction: we are sender\n"
 
;---------------------------------
; Packet is a pure ACK, process it
 
; Delete acknowledged bytes from send buffer
pusha
mov ecx, eax
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_free
popa
 
; Update RTT estimators
 
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp_rtt
mov eax, [esp + 4] ; timestamp when this segment was received
sub eax, [ebx + TCP_SOCKET.ts_ecr]
inc eax
call TCP_xmit_timer
jmp .rtt_done
 
.no_timestamp_rtt:
cmp [ebx + TCP_SOCKET.t_rtt], 0
je .rtt_done
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done
mov eax, [ebx + TCP_SOCKET.t_rtt]
call TCP_xmit_timer
 
.rtt_done:
 
; update window pointers
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
; Stop retransmit timer
mov [ebx + TCP_SOCKET.timer_retransmission], 0
 
; Unlock the socket
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
; Awaken waiting processes
mov eax, ebx
call SOCKET_notify
 
; Generate more output
call TCP_output
 
jmp .drop_no_socket
 
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
.not_sender:
; - The amount of data in the segment is greater than 0 (data count is in ecx)
 
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jne .not_uni_xfer
 
; - The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp).
 
;;; TODO
 
; jnz .not_uni_xfer
 
; Complete processing of received data
 
DEBUGF 1,"TCP_input: Header prediction: we are receiving %u bytes\n", ecx
 
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied
 
movzx esi, [edx + TCP_header.DataOffset]
add esi, edx
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_write ; Add the data to the socket buffer
 
mov eax, ebx
call SOCKET_notify
 
or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag
 
jmp .drop
 
;--------------------------------------------------
; Header prediction failed, do it the slow way
 
.not_uni_xfer:
 
DEBUGF 1,"TCP_input: Header prediction failed\n"
 
; Calculate receive window size
 
push edx
mov eax, SOCKETBUFFSIZE
sub eax, [ebx + STREAM_SOCKET.rcv.size]
mov edx, [ebx + TCP_SOCKET.RCV_ADV]
sub edx, [ebx + TCP_SOCKET.RCV_NXT]
cmp eax, edx
jg @f
mov eax, edx
@@:
DEBUGF 1,"Receive window size=%d\n", eax
mov [ebx + TCP_SOCKET.RCV_WND], eax
pop edx
 
; If we are in listen or syn_sent state, go to that specific code right away
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN
je .LISTEN
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_SENT
je .SYN_SENT
 
;----------------------------
; trim any data not in window
 
; check for duplicate data at beginning of segment (635)
 
mov eax, [ebx + TCP_SOCKET.RCV_NXT]
sub eax, [edx + TCP_header.SequenceNumber]
jle .no_duplicate
 
DEBUGF 1,"TCP_input: %u bytes duplicate data!\n", eax
 
test [edx + TCP_header.Flags], TH_SYN
jz .no_dup_syn
 
DEBUGF 1,"TCP_input: got duplicate syn\n"
 
and [edx + TCP_header.Flags], not (TH_SYN)
inc [edx + TCP_header.SequenceNumber]
 
cmp [edx + TCP_header.UrgentPointer], 1
jbe @f
dec [edx + TCP_header.UrgentPointer]
jmp .dup_syn
@@:
and [edx + TCP_header.Flags], not (TH_URG)
.dup_syn:
dec eax
.no_dup_syn:
 
; Check for entire duplicate segment (646)
cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size
jb .duplicate
jnz @f
test [edx + TCP_header.Flags], TH_FIN
jnz .duplicate
@@:
 
; Any valid FIN must be to the left of the window.
; At this point the FIN must be out of sequence or a duplicate, drop it
and [edx + TCP_header.Flags], not TH_FIN
 
; send an ACK and resynchronize and drop any data.
; But keep on processing for RST or ACK
DEBUGF 1, "616\n"
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
mov eax, ecx
;TODO: update stats
 
;-----------------------------------------------
; Remove duplicate data and update urgent offset
 
.duplicate:
;;; TODO: 677
add [edx + TCP_header.SequenceNumber], eax
sub ecx, eax
 
sub [edx + TCP_header.UrgentPointer], ax
jg @f
and [edx + TCP_header.Flags], not (TH_URG)
mov [edx + TCP_header.UrgentPointer], 0
@@:
 
;--------------------------------------------------
; Handle data that arrives after process terminates (687)
 
.no_duplicate:
cmp [ebx + SOCKET.PID], 0
jne .not_terminated
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT
jbe .not_terminated
test ecx, ecx
jz .not_terminated
 
mov eax, ebx
call TCP_close
;;;TODO: update stats
jmp .respond_seg_reset
 
;----------------------------------------
; Remove data beyond right edge of window (700-736)
 
.not_terminated:
mov eax, [edx + TCP_header.SequenceNumber]
add eax, ecx
sub eax, [ebx + TCP_SOCKET.RCV_NXT]
sub eax, [ebx + TCP_SOCKET.RCV_WND] ; eax now holds the number of bytes to drop
jle .no_excess_data
 
DEBUGF 1,"%d bytes beyond right edge of window\n", eax
 
;;; TODO: update stats
cmp eax, ecx
jl .dont_drop_all
; If a new connection request is received while in TIME_WAIT, drop the old connection and start over,
; if the sequence numbers are above the previous ones
 
test [edx + TCP_header.Flags], TH_SYN
jz .no_new_request
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
jne .no_new_request
; mov edx, [ebx + TCP_SOCKET.RCV_NXT]
; cmp edx, [edx + TCP_header.SequenceNumber]
; add edx, 64000 ; TCP_ISSINCR FIXME
mov eax, ebx
call TCP_close
jmp .findpcb ; FIXME: skip code for unscaling window, ...
.no_new_request:
 
; If window is closed can only take segments at window edge, and have to drop data and PUSH from
; incoming segments. Continue processing, but remember to ACK. Otherwise drop segment and ACK
 
cmp [ebx + TCP_SOCKET.RCV_WND], 0
jne .drop_after_ack
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .drop_after_ack
 
DEBUGF 1, "690\n"
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
;;; TODO: update stats
jmp .no_excess_data
.dont_drop_all:
;;; TODO: update stats
;;; TODO: 733
 
sub ecx, eax
and [ebx + TCP_SOCKET.t_flags], not (TH_PUSH or TH_FIN)
.no_excess_data:
 
;-----------------
; Record timestamp (737-746)
 
; If last ACK falls within this segments sequence numbers, record its timestamp
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp
mov eax, [ebx + TCP_SOCKET.last_ack_sent]
sub eax, [edx + TCP_header.SequenceNumber]
jb .no_timestamp
test [ebx + TCP_header.Flags], TH_SYN or TH_FIN ; syn and fin occupy one byte
jz @f
dec eax
@@:
sub eax, ecx
jae .no_timestamp
 
DEBUGF 1,"Recording timestamp\n"
 
mov eax, [esp + 4] ; tcp_now
mov [ebx + TCP_SOCKET.ts_recent_age], eax
mov eax, [ebx + TCP_SOCKET.ts_val]
mov [ebx + TCP_SOCKET.ts_recent], eax
.no_timestamp:
 
;------------------
; Process RST flags
 
test [edx + TCP_header.Flags], TH_RST
jz .no_rst
 
DEBUGF 1,"TCP_input: Got an RST flag\n"
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .rst_sw_list]
 
.rst_sw_list:
dd .no_rst ; TCPS_CLOSED
dd .no_rst ; TCPS_LISTEN
dd .no_rst ; TCPS_SYN_SENT
dd .econnrefused ; TCPS_SYN_RECEIVED
dd .econnreset ; TCPS_ESTABLISHED
dd .econnreset ; TCPS_CLOSE_WAIT
dd .econnreset ; TCPS_FIN_WAIT_1
dd .rst_close ; TCPS_CLOSING
dd .rst_close ; TCPS_LAST_ACK
dd .econnreset ; TCPS_FIN_WAIT_2
dd .rst_close ; TCPS_TIMED_WAIT
 
.econnrefused:
DEBUGF 1,"TCP_input: Connection refused\n"
 
mov [ebx + SOCKET.errorcode], ECONNREFUSED
jmp .close
 
.econnreset:
DEBUGF 1,"TCP_input: Connection reset\n"
 
mov [ebx + SOCKET.errorcode], ECONNRESET
 
.close:
DEBUGF 1,"TCP_input: Closing connection\n"
 
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
;;; TODO: update stats (tcp drops)
mov eax, ebx
call TCP_close
jmp .drop_no_socket
 
.rst_close:
DEBUGF 1,"TCP_input: Closing with reset\n"
 
mov eax, ebx
call TCP_close
jmp .drop_no_socket
 
.no_rst:
 
;--------------------------------------
; handle SYN-full and ACK-less segments
 
test [edx + TCP_header.Flags], TH_SYN
jz .not_syn_full
 
mov eax, ebx
mov ebx, ECONNRESET
call TCP_drop
jmp .drop_with_reset
.not_syn_full:
 
;---------------
; ACK processing
 
test [edx + TCP_header.Flags], TH_ACK
jz .drop
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
jb .ack_processed ; states: closed, listen, syn_sent
ja .no_syn_rcv ; established, fin_wait_1, fin_wait_2, close_wait, closing, last_ack, time_wait
 
DEBUGF 1,"TCP_input: state=syn_received\n"
 
mov eax, [edx + TCP_header.AckNumber]
cmp [ebx + TCP_SOCKET.SND_UNA], eax
ja .drop_with_reset
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .drop_with_reset
 
;;; TODO: update stats
 
mov eax, ebx
call SOCKET_is_connected
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
 
; Do window scaling?
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz @f
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz @f
 
push word [ebx + TCP_SOCKET.requested_s_scale] ; Set send and receive scale factors to the received values
pop word [ebx + TCP_SOCKET.SND_SCALE]
@@:
 
;;; TODO: call TCP_reassemble
 
mov eax, [edx + TCP_header.SequenceNumber]
dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
.no_syn_rcv:
 
;-------------------------
; check for duplicate ACKs
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
ja .not_dup_ack
 
test ecx, ecx
jnz .reset_dupacks
 
mov eax, dword [edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .reset_dupacks
 
DEBUGF 1,"TCP_input: Processing duplicate ACK\n"
 
; If we have outstanding data, other than a window probe, this is a completely duplicate ACK
; (window info didnt change) The ACK is the biggest we've seen and we've seen exactly our rexmt threshold of them,
; assume a packet has been dropped and retransmit it. Kludge snd_nxt & the congestion window so we send only this one packet.
 
cmp [ebx + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME
jg @f
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
je .dup_ack
 
@@:
mov [ebx + TCP_SOCKET.t_dupacks], 0
jmp .not_dup_ack
 
.dup_ack:
inc [ebx + TCP_SOCKET.t_dupacks]
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh
jne .no_re_xmit
 
push [ebx + TCP_SOCKET.SND_NXT] ; >>>>
 
mov eax, [ebx + TCP_SOCKET.SND_WND]
cmp eax, [ebx + TCP_SOCKET.SND_CWND]
cmova eax, [ebx + TCP_SOCKET.SND_CWND]
shr eax, 1
push edx
xor edx, edx
div [ebx + TCP_SOCKET.t_maxseg]
cmp eax, 2
ja @f
xor eax, eax
mov al, 2
@@:
mul [ebx + TCP_SOCKET.t_maxseg]
pop edx
mov [ebx + TCP_SOCKET.SND_SSTHRESH], eax
 
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; turn off retransmission timer
mov [ebx + TCP_SOCKET.t_rtt], 0
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_NXT], eax
mov eax, [ebx + TCP_SOCKET.t_maxseg]
mov [ebx + TCP_SOCKET.SND_CWND], eax
 
; Unlock the socket
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
 
; retransmit missing segment
mov eax, [esp]
call TCP_output
 
; Lock the socket again
mov ecx, [esp]
add ecx, SOCKET.mutex
call mutex_lock
pop ebx
 
; Continue processing
xor edx, edx
mov eax, [ebx + TCP_SOCKET.t_maxseg]
mul [ebx + TCP_SOCKET.t_dupacks]
add eax, [ebx + TCP_SOCKET.SND_SSTHRESH]
mov [ebx + TCP_SOCKET.SND_CWND], eax
 
pop eax ; <<<<
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jb @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
jmp .drop
 
 
.no_re_xmit:
jbe .not_dup_ack
 
DEBUGF 1,"TCP_input: Increasing congestion window\n"
 
mov eax, [ebx + TCP_SOCKET.t_maxseg]
add [ebx + TCP_SOCKET.SND_CWND], eax
 
; Unlock the socket
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
 
; retransmit missing segment
mov eax, [esp]
call TCP_output
 
; Lock the socket again
mov ecx, [esp]
add ecx, SOCKET.mutex
call mutex_lock
pop ebx
 
jmp .drop
 
 
.not_dup_ack:
 
;-------------------------------------------------
; If the congestion window was inflated to account
; for the other side's cached packets, retract it
 
mov eax, [ebx + TCP_SOCKET.SND_SSTHRESH]
cmp eax, [ebx + TCP_SOCKET.SND_CWND]
ja @f
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh
jbe @f
mov [ebx + TCP_SOCKET.SND_CWND], eax
@@:
 
mov [ebx + TCP_SOCKET.t_dupacks], 0
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jbe @f
 
;;; TODO: update stats
jmp .drop_after_ack
 
@@:
 
mov edi, [edx + TCP_header.AckNumber]
sub edi, [ebx + TCP_SOCKET.SND_UNA] ; now we got the number of acked bytes in edi
 
;;; TODO: update stats
 
DEBUGF 1,"TCP_input: acceptable ACK for %u bytes\n", edi
 
;------------------------------------------
; RTT measurements and retransmission timer (912-926)
 
; If we have a timestamp, update smoothed RTT
 
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP
jz .timestamp_not_present
mov eax, [esp+4]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
inc eax
call TCP_xmit_timer
jmp .rtt_done_
 
; If no timestamp but transmit timer is running and timed sequence number was acked,
; update smoothed RTT. Since we now have an RTT measurement, cancel the timer backoff
; (Phil Karn's retransmit algo)
; Recompute the initial retransmit timer
 
.timestamp_not_present:
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done_
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
jz .rtt_done_
call TCP_xmit_timer
 
.rtt_done_:
 
; If all outstanding data is acked, stop retransmit timer and remember to restart (more output or persist)
; If there is more data to be acked, restart retransmit timer, using current (possible backed-off) value.
 
mov eax, [ebx + TCP_SOCKET.SND_MAX]
cmp eax, [edx + TCP_header.AckNumber]
jne .more_data
mov [ebx + TCP_SOCKET.timer_retransmission], 0
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
jmp .no_restart
.more_data:
cmp [ebx + TCP_SOCKET.timer_persist], 0
jne .no_restart
 
mov eax, [ebx + TCP_SOCKET.t_rxtcur]
mov [ebx + TCP_SOCKET.timer_retransmission], eax
 
.no_restart:
 
 
;-------------------------------------------
; Open congestion window in response to ACKs
 
mov esi, [ebx + TCP_SOCKET.SND_CWND]
mov eax, [ebx + TCP_SOCKET.t_maxseg]
 
cmp esi, [ebx + TCP_SOCKET.SND_SSTHRESH]
jbe @f
push edx
push eax
mul eax
div esi
pop edx
shr edx, 3
add eax, edx
pop edx
@@:
 
add esi, eax
 
push ecx
mov cl, [ebx + TCP_SOCKET.SND_SCALE]
mov eax, TCP_max_win
shl eax, cl
pop ecx
 
cmp esi, eax
cmova esi, eax
mov [ebx + TCP_SOCKET.SND_CWND], esi
 
;------------------------------------------
; Remove acknowledged data from send buffer
 
cmp edi, [ebx + STREAM_SOCKET.snd.size]
jbe .finiacked
 
push ecx edx ebx
mov ecx, [ebx + STREAM_SOCKET.snd.size]
lea eax, [ebx + STREAM_SOCKET.snd]
sub [ebx + TCP_SOCKET.SND_WND], ecx
call SOCKET_ring_free
pop ebx edx ecx
 
DEBUGF 1,"TCP_input: our FIN is acked\n"
stc
 
jmp .wakeup
 
.finiacked:
 
push ecx edx ebx
mov ecx, edi
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_free
pop ebx
sub [ebx + TCP_SOCKET.SND_WND], ecx
pop edx ecx
 
DEBUGF 1,"TCP_input: our FIN is not acked\n"
clc
 
;----------------------------------------
; Wake up process waiting on send buffer
 
.wakeup:
 
pushf ; Keep the flags (Carry flag)
mov eax, ebx
call SOCKET_notify
 
; Update TCPS
 
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jb @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
popf
 
; General ACK handling complete
; Now do the state-specific ones
; Carry flag is set when our FIN is acked
 
mov eax, [ebx + TCP_SOCKET.t_state]
jmp dword [eax*4 + .ACK_sw_list]
 
.ACK_sw_list:
dd .ack_processed ; TCPS_CLOSED
dd .ack_processed ; TCPS_LISTEN
dd .ack_processed ; TCPS_SYN_SENT
dd .ack_processed ; TCPS_SYN_RECEIVED
dd .ack_processed ; TCPS_ESTABLISHED
dd .ack_processed ; TCPS_CLOSE_WAIT
dd .ack_fw1 ; TCPS_FIN_WAIT_1
dd .ack_c ; TCPS_CLOSING
dd .ack_la ; TCPS_LAST_ACK
dd .ack_processed ; TCPS_FIN_WAIT_2
dd .ack_tw ; TCPS_TIMED_WAIT
 
 
.ack_fw1:
jnc .ack_processed
 
test [ebx + SOCKET.state], SS_CANTRCVMORE
jnz @f
mov eax, ebx
call SOCKET_is_disconnected
mov [ebx + TCP_SOCKET.timer_timed_wait], TCP_time_max_idle
@@:
mov [ebx + TCP_SOCKET.t_state], TCPS_FIN_WAIT_2
jmp .ack_processed
 
.ack_c:
jnc .ack_processed
 
mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
mov eax, ebx
call TCP_cancel_timers
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL
mov eax, ebx
call SOCKET_is_disconnected
jmp .ack_processed
 
.ack_la:
jnc .ack_processed
 
mov eax, ebx
call TCP_disconnect
jmp .drop
 
.ack_tw:
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL
jmp .drop_after_ack
 
.reset_dupacks: ; We got a new ACK, reset duplicate ACK counter
mov [ebx + TCP_SOCKET.t_dupacks], 0
jmp .ack_processed
 
;-------
; LISTEN
 
align 4
.LISTEN:
 
DEBUGF 1,"TCP_input: state=listen\n"
 
test [edx + TCP_header.Flags], TH_RST
jnz .drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .drop_with_reset
 
test [edx + TCP_header.Flags], TH_SYN
jz .drop
 
;;; TODO: check if it's a broadcast or multicast, and drop if so
 
push dword [edi] ; Ipv4 source addres
pop [ebx + IP_SOCKET.RemoteIP]
 
push [edx + TCP_header.SourcePort]
pop [ebx + TCP_SOCKET.RemotePort]
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.IRS]
 
mov eax, [TCP_sequence_num]
add [TCP_sequence_num], 64000 / 2
mov [ebx + TCP_SOCKET.ISS], eax
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
TCP_sendseqinit ebx
TCP_rcvseqinit ebx
 
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval ;;;; macro
 
lea eax, [ebx + STREAM_SOCKET.snd]
call SOCKET_ring_create
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_create
 
and [ebx + TCP_SOCKET.temp_bits], not TCP_BIT_DROPSOCKET
 
;;; call SOCKET_notify_owner
 
jmp .trim_then_step6
 
;------------
; Active Open
 
align 4
.SYN_SENT:
 
DEBUGF 1,"TCP_input: state=syn_sent\n"
 
test [edx + TCP_header.Flags], TH_ACK
jz @f
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.ISS]
jbe .drop_with_reset
 
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .drop_with_reset
@@:
 
test [edx + TCP_header.Flags], TH_RST
jz @f
 
test [edx + TCP_header.Flags], TH_ACK
jz .drop
 
mov eax, ebx
mov ebx, ECONNREFUSED
call TCP_drop
 
jmp .drop
@@:
 
test [edx + TCP_header.Flags], TH_SYN
jz .drop
 
; at this point, segment seems to be valid
 
test [edx + TCP_header.Flags], TH_ACK
jz .no_syn_ack
 
; now, process received SYN in response to an active open
 
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jbe @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
.no_syn_ack:
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.IRS]
 
TCP_rcvseqinit ebx
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.ISS]
jbe .simultaneous_open
 
test [edx + TCP_header.Flags], TH_ACK
jz .simultaneous_open
 
DEBUGF 1,"TCP_input: active open\n"
 
;;; TODO: update stats
 
; set socket state to connected
mov [ebx + SOCKET.state], SS_ISCONNECTED
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
 
; Do window scaling on this connection ?
mov eax, [ebx + TCP_SOCKET.t_flags]
and eax, TF_REQ_SCALE or TF_RCVD_SCALE
cmp eax, TF_REQ_SCALE or TF_RCVD_SCALE
jne .no_scaling
 
mov ax, word [ebx + TCP_SOCKET.requested_s_scale]
mov word [ebx + TCP_SOCKET.SND_SCALE], ax
.no_scaling:
 
;;; TODO: reassemble packets queue
 
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
je .trim_then_step6
call TCP_xmit_timer
jmp .trim_then_step6
 
.simultaneous_open:
 
DEBUGF 1,"TCP_input: simultaneous open\n"
; We have received a syn but no ACK, so we are having a simultaneous open..
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
 
;-------------------------------------
; Common processing for receipt of SYN
 
.trim_then_step6:
 
inc [edx + TCP_header.SequenceNumber]
 
;;; TODO: Drop any received data that follows receive window (590)
 
mov eax, [edx + TCP_header.SequenceNumber]
mov [ebx + TCP_SOCKET.RCV_UP], eax
dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
;-------
; step 6
 
.ack_processed:
 
DEBUGF 1,"TCP_input: ACK processed\n"
 
;----------------------------------------------
; check if we need to update window information
 
test [edx + TCP_header.Flags], TH_ACK
jz .no_window_update
 
mov eax, [ebx + TCP_SOCKET.SND_WL1]
cmp eax, [edx + TCP_header.SequenceNumber]
jb .update_window
ja @f
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_header.AckNumber]
jb .update_window
ja .no_window_update
@@:
 
mov eax, dword [edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jbe .no_window_update
 
.update_window:
 
;;; TODO: update stats (Keep track of pure window updates)
 
mov eax, dword [edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.max_sndwnd]
jbe @f
mov [ebx + TCP_SOCKET.max_sndwnd], eax
@@:
mov [ebx + TCP_SOCKET.SND_WND], eax
 
DEBUGF 1,"TCP_input: Updating window to %u\n", eax
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.SND_WL1]
 
push [edx + TCP_header.AckNumber]
pop [ebx + TCP_SOCKET.SND_WL2]
 
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
 
.no_window_update:
 
;-----------------
; process URG flag
 
test [edx + TCP_header.Flags], TH_URG
jz .not_urgent
 
cmp [edx + TCP_header.UrgentPointer], 0
jz .not_urgent
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
je .not_urgent
 
; Ignore bogus urgent offsets
 
movzx eax, [edx + TCP_header.UrgentPointer]
add eax, [ebx + STREAM_SOCKET.rcv.size]
cmp eax, SOCKET_MAXDATA
jbe .not_urgent
 
mov [edx + TCP_header.UrgentPointer], 0
and [edx + TCP_header.Flags], not (TH_URG)
jmp .do_data
 
.not_urgent:
 
; processing of received urgent pointer
 
;;; TODO (1051-1093)
 
 
;---------------------------------------
; process the data in the segment (1094)
 
.do_data:
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
jae .final_processing
 
test [edx + TCP_header.Flags], TH_FIN
jnz @f
 
test ecx, ecx
jnz .final_processing
@@:
 
 
; The segment is in order?
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .out_of_order
 
; The reassembly queue is empty?
cmp [ebx + TCP_SOCKET.seg_next], 0
jne .out_of_order
 
; The connection is established?
cmp [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jne .out_of_order
 
; Ok, lets do this.. Set delayed ACK flag and copy data into socket buffer
or [ebx + TCP_SOCKET.t_flags], TF_DELACK
 
pusha
movzx esi, [edx + TCP_header.DataOffset]
add esi, edx
lea eax, [ebx + STREAM_SOCKET.rcv]
call SOCKET_ring_write ; Add the data to the socket buffer
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied
popa
 
; Wake up the sleeping process
mov eax, ebx
call SOCKET_notify
 
jmp .data_done
 
.out_of_order:
 
; Uh-oh, some data is out of order, lets call TCP reassemble for help
 
call TCP_reassemble
 
DEBUGF 1, "1470\n"
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
.data_done:
 
;---------------
; FIN processing
 
test [edx + TCP_header.Flags], TH_FIN
jz .final_processing
 
DEBUGF 1,"TCP_input: Processing FIN\n"
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
jae .not_first_fin
 
DEBUGF 1,"TCP_input: First FIN for this connection\n"
 
mov eax, ebx
call SOCKET_cant_recv_more
 
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
inc [ebx + TCP_SOCKET.RCV_NXT]
 
.not_first_fin:
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .FIN_sw_list]
 
.FIN_sw_list:
dd .final_processing ; TCPS_CLOSED
dd .final_processing ; TCPS_LISTEN
dd .final_processing ; TCPS_SYN_SENT
dd .fin_syn_est ; TCPS_SYN_RECEIVED
dd .fin_syn_est ; TCPS_ESTABLISHED
dd .final_processing ; TCPS_CLOSE_WAIT
dd .fin_wait1 ; TCPS_FIN_WAIT_1
dd .final_processing ; TCPS_CLOSING
dd .final_processing ; TCPS_LAST_ACK
dd .fin_wait2 ; TCPS_FIN_WAIT_2
dd .fin_timed ; TCPS_TIMED_WAIT
 
.fin_syn_est:
 
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT
jmp .final_processing
 
.fin_wait1:
 
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSING
jmp .final_processing
 
.fin_wait2:
 
mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT
mov eax, ebx
call TCP_cancel_timers
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL
call SOCKET_is_disconnected
jmp .final_processing
 
.fin_timed:
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL
jmp .final_processing
 
 
.drop_after_ack:
DEBUGF 1,"TCP_input: Drop after ACK\n"
 
push edx ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax edx
 
test [edx + TCP_header.Flags], TH_RST
jnz .dumpit
 
or [eax + TCP_SOCKET.t_flags], TF_ACKNOW
jmp .need_output
 
.drop_with_reset:
DEBUGF 1,"TCP_input: Drop with reset\n"
 
push ebx edx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop edx ebx
 
test [edx + TCP_header.Flags], TH_RST
jnz .dumpit
 
;;; if its a multicast/broadcast, also drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .respond_ack
 
test [edx + TCP_header.Flags], TH_SYN
jnz .respond_syn
jmp .dumpit
 
;-----------------
; Final processing
 
.final_processing:
DEBUGF 1,"TCP_input: Final processing\n"
 
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax
 
test [eax + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT
jnz .need_output
 
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW
jz .dumpit
DEBUGF 1,"TCP_input: ACK now!\n"
 
.need_output:
DEBUGF 1,"TCP_input: need output\n"
call TCP_output
 
.dumpit:
DEBUGF 1,"TCP_input: dumping\n"
 
call kernel_free
add esp, 4
jmp .loop
 
;---------
; Respond
 
.respond_ack:
push ebx
mov cl, TH_RST
call TCP_respond
pop ebx
jmp .destroy_new_socket
 
.respond_syn:
push ebx
mov cl, TH_RST + TH_ACK
call TCP_respond
pop ebx
jmp .destroy_new_socket
 
.respond_seg_reset:
test [edx + TCP_header.Flags], TH_RST
jnz .drop_no_socket
 
;;; TODO: if its a multicast/broadcast, also drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .respond_seg_ack
 
test [edx + TCP_header.Flags], TH_SYN
jnz .respond_seg_syn
 
jmp .drop_no_socket
 
.respond_seg_ack:
mov cl, TH_RST
call TCP_respond_segment
jmp .drop_no_socket
 
.respond_seg_syn:
mov cl, TH_RST + TH_ACK
call TCP_respond_segment
jmp .drop_no_socket
 
;-----
; Drop
 
.drop:
DEBUGF 1,"TCP_input: Dropping segment\n"
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
.destroy_new_socket:
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET
jz .drop_no_socket
 
mov eax, ebx
call SOCKET_free
 
.drop_no_socket:
DEBUGF 1,"TCP_input: Drop (no socket)\n"
 
call kernel_free
add esp, 4
jmp .loop
/kernel/branches/Kolibri-acpi/network/tcp_output.inc
0,0 → 1,621
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3289 $
 
;-----------------------------------------------------------------
;
; TCP_output
;
; IN: eax = socket pointer
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_output:
 
DEBUGF 1,"TCP_output: socket=%x\n", eax
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop eax
 
; We'll detect the length of the data to be transmitted, and flags to be used
; If there is some data, or any critical controls to send (SYN / RST), then transmit
; Otherwise, investigate further
 
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
jbe .not_idle
 
mov ebx, [eax + TCP_SOCKET.t_idle]
cmp ebx, [eax + TCP_SOCKET.t_rxtcur]
jbe .not_idle
 
; We have been idle for a while and no ACKS are expected to clock out any data we send..
; Slow start to get ack "clock" running again.
 
mov ebx, [eax + TCP_SOCKET.t_maxseg]
mov [eax + TCP_SOCKET.SND_CWND], ebx
 
.not_idle:
.again:
mov [eax + TCP_SOCKET.temp_bits], 0
 
mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71)
sub ebx, [eax + TCP_SOCKET.SND_UNA] ;
 
mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window
cmp ecx, [eax + TCP_SOCKET.SND_CWND] ;
jb @f ;
mov ecx, [eax + TCP_SOCKET.SND_CWND] ;
@@: ;
 
call TCP_outflags ; flags in dl
 
;------------------------
; data being forced out ?
 
; If in persist timeout with window of 0, send 1 byte.
; Otherwise, if window is small but nonzero, and timer expired,
; we will send what we can and go to transmit state
 
cmp [eax + TCP_SOCKET.t_force], 0
je .no_force
 
DEBUGF 1,"TCP_output: forcing data out\n"
 
test ecx, ecx
jnz .no_zero_window
 
cmp ebx, [eax + STREAM_SOCKET.snd.size]
jae @f
 
and dl, not (TH_FIN)
 
@@:
inc ecx
jmp .no_force
 
.no_zero_window:
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
.no_force:
 
;--------------------------------
; Calculate how much data to send (106)
 
mov esi, [eax + STREAM_SOCKET.snd.size]
cmp esi, ecx
jb @f
mov esi, ecx
@@:
sub esi, ebx
 
 
;------------------------
; check for window shrink (107)
 
; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1
; Otherwise, window shrank after we sent into it.
 
jae .not_persist
 
; enter persist state
xor esi, esi
 
; If window shrank to 0
test ecx, ecx
jnz @f
 
; cancel pending retransmit
mov [eax + TCP_SOCKET.timer_retransmission], 0
 
; pull SND_NXT back to (closed) window, We will enter persist state below.
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
@@:
 
; If window didn't close completely, just wait for an ACK
 
.not_persist:
 
;---------------------------
; Send one segment at a time (124)
 
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jbe @f
 
mov esi, [eax + TCP_SOCKET.t_maxseg]
or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
@@:
 
;--------------------------------------------
; Turn of FIN flag if send buffer not emptied (128)
 
mov edi, [eax + TCP_SOCKET.SND_NXT]
add edi, esi
sub edi, [eax + TCP_SOCKET.SND_UNA]
cmp edi, [eax + STREAM_SOCKET.snd.size]
jae @f
and dl, not (TH_FIN)
 
@@:
 
;-------------------------------
; calculate window advertisement (130)
 
mov ecx, SOCKET_MAXDATA
sub ecx, [eax + STREAM_SOCKET.rcv.size]
 
;------------------------------
; Sender silly window avoidance (131)
 
test esi, esi
jz .len_zero
 
cmp esi, [eax + TCP_SOCKET.t_maxseg]
je TCP_send
 
add ebx, esi ; offset + length
cmp ebx, [eax + STREAM_SOCKET.snd.size]
jb @f
 
test [eax + TCP_SOCKET.t_flags], TF_NODELAY
jnz TCP_send
 
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
je TCP_send
@@:
 
test [eax + TCP_SOCKET.t_force], -1 ;;;
jnz TCP_send
 
mov ebx, [eax + TCP_SOCKET.max_sndwnd]
shr ebx, 1
cmp esi, ebx
jae TCP_send
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
jb TCP_send
 
.len_zero:
 
;----------------------------------------
; Check if a window update should be sent (154)
 
DEBUGF 1,"TCP_output: window=%d\n", ecx
 
; Compare available window to amount of window known to peer (as advertised window less next expected input)
; If the difference is at least two max size segments, or at least 50% of the maximum possible window,
; Then we want to send a window update to the peer.
 
test ecx, ecx
jz .no_window
 
push ecx
mov cl, [eax + TCP_SOCKET.RCV_SCALE]
mov ebx, TCP_max_win
shl ebx, cl
pop ecx
cmp ebx, ecx
jb @f
mov ebx, ecx
@@:
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
 
mov edi, [eax + TCP_SOCKET.t_maxseg]
shl edi, 1
 
; cmp ebx, edi
; jae TCP_send
 
; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark
; jae TCP_send
 
.no_window:
 
;--------------------------
; Should a segment be sent? (174)
 
DEBUGF 1,"TCP_output: 174\n"
 
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK
jnz TCP_send
 
test dl, TH_SYN + TH_RST ; we need to send a SYN or RST
jnz TCP_send
 
mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
ja TCP_send
 
test dl, TH_FIN
jz .enter_persist ; no reason to send, enter persist state
 
; FIN was set, only send if not already sent, or on retransmit
 
test [eax + TCP_SOCKET.t_flags], TF_SENTFIN
jz TCP_send
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
je TCP_send
 
;--------------------
; Enter persist state (191)
 
.enter_persist:
 
cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send?
jne @f
cmp [eax + TCP_SOCKET.timer_retransmission], 0
jne @f
cmp [eax + TCP_SOCKET.timer_persist], 0 ; Persist timer already expired?
jne @f
 
DEBUGF 1,"TCP_output: Entering persist state\n"
 
mov [eax + TCP_SOCKET.t_rxtshift], 0
call TCP_set_persist
@@:
 
;----------------------------
; No reason to send a segment (219)
 
DEBUGF 1,"TCP_output: No reason to send a segment\n"
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
; Fixme: returnvalue?
 
ret
 
 
 
 
 
 
 
 
 
;-----------------------------------------------
;
; Send a segment (222)
;
; eax = socket pointer
; esi = data len
; dl = flags
;
;-----------------------------------------------
align 4
TCP_send:
 
DEBUGF 1,"TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl
 
push eax ; save socket ptr
push esi ; and data length too
mov edi, sizeof.TCP_header ; edi will contain headersize
 
;------------------------------------
; Send options with first SYN segment
 
test dl, TH_SYN
jz .options_done
 
push [eax + TCP_SOCKET.ISS]
pop [eax + TCP_SOCKET.SND_NXT]
 
test [eax + TCP_SOCKET.t_flags], TF_NOOPT
jnz .options_done
 
mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
bswap ecx
push ecx
add di, 4
 
DEBUGF 1,"TCP_send: added maxseg option\n"
 
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz .no_scale
 
test dl, TH_ACK
jz .scale_opt
 
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz .no_scale
 
.scale_opt:
mov cl, [eax + TCP_SOCKET.request_r_scale]
mov ch, TCP_OPT_NOP
pushw cx
pushw TCP_OPT_WINDOW + 3 shl 8
add di, 4
 
DEBUGF 1,"TCP_send: added scale option\n"
 
.no_scale:
.no_syn:
 
;------------------------------------
; Make the timestamp option if needed
 
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
jz .no_timestamp
 
test dl, TH_RST
jnz .no_timestamp
 
test dl, TH_ACK
jz .timestamp
 
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
jz .no_timestamp
 
.timestamp:
pushd 0
pushd [timer_ticks]
pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24
add di, 12
 
DEBUGF 1,"TCP_send: added timestamp\n"
 
.no_timestamp:
 
; <Add additional options here>
 
.options_done:
 
; eax = socket ptr
; edx = flags
; edi = header size
; esi = data len
 
;---------------------------------------------
; check if we dont exceed the max segment size (270)
 
add esi, edi ; total TCP segment size
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jbe .no_overflow
 
mov esi, [eax + TCP_SOCKET.t_maxseg]
or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
.no_overflow:
 
;-----------------------------------------------------------------
; Start by pushing all TCP header values in reverse order on stack
; (essentially, creating the tcp header on the stack!)
 
pushw 0 ; .UrgentPointer dw ?
pushw 0 ; .Checksum dw ?
pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370)
shl edi, 2 ; .DataOffset db ? only 4 left-most bits
shl dx, 8
or dx, di ; .Flags db ?
pushw dx
shr edi, 2 ; .DataOffset db ?
 
push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ?
ntohd [esp]
 
push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ?
ntohd [esp]
 
push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ?
push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ?
 
push edi ; header size
 
;---------------------
; Create the IP packet
 
mov ecx, esi
 
mov ebx, [eax + SOCKET.device]
mov edx, [eax + IP_SOCKET.LocalIP] ; source ip
mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
mov di, IP_PROTO_TCP shl 8 + 128
call IPv4_output
jz .ip_error
 
;-----------------------------------------
; Move TCP header from stack to TCP packet
 
push ecx
mov ecx, [esp + 4]
lea esi, [esp + 8]
shr ecx, 2 ; count is in bytes, we will work with dwords
rep movsd
pop ecx ; full TCP packet size
 
pop esi ; headersize
add esp, esi ; remove it from stack
 
push edx ; packet size for send proc
push eax ; packet ptr for send proc
 
mov edx, edi ; begin of data
sub edx, esi ; begin of packet (edi = begin of data)
push ecx
sub ecx, esi ; data size
 
;--------------
; Copy the data
 
; eax = ptr to ring struct
; ecx = buffer size
; edi = ptr to buffer
 
mov eax, [esp + 16] ; get socket ptr
 
push edx
push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission
test ecx, ecx
jz .nodata
mov edx, [eax + TCP_SOCKET.SND_NXT]
add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME
sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset
add eax, STREAM_SOCKET.snd
call SOCKET_ring_read
.nodata:
pop edi
pop esi ; begin of data
pop ecx ; full packet size
mov eax, [esp + 12] ; socket ptr
 
;----------------------------------
; initialize retransmit timer (400)
 
;TODO: check t_force and persist
 
test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number
jz @f
inc [eax + TCP_SOCKET.SND_NXT]
test [esi + TCP_header.Flags], TH_FIN
jz @f
or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag
@@:
 
mov edx, [eax + TCP_SOCKET.SND_NXT]
cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission?
jbe @f
mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it
 
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything?
je @f
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer
mov [eax + TCP_SOCKET.t_rtseq], edi
;TODO: update stats
@@:
 
; set retransmission timer if not already set, and not doing an ACK or keepalive probe
 
cmp [eax + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME
ja .retransmit_set
 
cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT]
je .retransmit_set
 
mov edx, [eax + TCP_SOCKET.t_rxtcur]
mov [eax + TCP_SOCKET.timer_retransmission], edx
 
cmp [eax + TCP_SOCKET.timer_persist], 0
jne .retransmit_set
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
.retransmit_set:
 
;--------------------
; Create the checksum
 
TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
mov [esi + TCP_header.Checksum], dx
 
;----------------
; Send the packet
 
DEBUGF 1,"TCP_send: Sending with device %x\n", ebx
call [ebx + NET_DEVICE.transmit]
jnz .send_error
 
;---------------
; Ok, data sent!
 
pop ecx
pop eax
 
inc [TCP_segments_tx] ; FIXME: correct interface?
 
; update advertised receive window
test ecx, ecx
jz @f
add ecx, [eax + TCP_SOCKET.RCV_NXT]
cmp ecx, [eax + TCP_SOCKET.RCV_ADV]
jbe @f
mov [eax + TCP_SOCKET.RCV_ADV], ecx
@@:
 
; update last ack sent
push [eax + TCP_SOCKET.RCV_NXT]
pop [eax + TCP_SOCKET.last_ack_sent]
 
; and flags
and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK)
 
;--------------
; unlock socket
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
pop eax
 
;-----------------------------
; Check if we need more output
 
test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT
jnz TCP_output.again
 
DEBUGF 1,"TCP_send: success!\n"
 
xor eax, eax
ret
 
 
.ip_error:
pop ecx
add esp, ecx
add esp, 4
pop eax
 
mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
DEBUGF 1,"TCP_send: IP error\n"
 
or eax, -1
ret
 
 
.send_error:
add esp, 8
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
DEBUGF 1,"TCP_send: sending failed\n"
 
or eax, -2
ret
 
 
 
 
 
 
/kernel/branches/Kolibri-acpi/network/tcp_subr.inc
0,0 → 1,546
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3514 $
 
align 4
iglobal
TCP_backoff db 0,1,2,3,4,5,6,6,6,6,6,6,6
endg
 
macro TCP_checksum IP1, IP2 {
 
;-------------
; Pseudoheader
 
; protocol type
mov edx, IP_PROTO_TCP
 
; source address
add dl, byte [IP1+1]
adc dh, byte [IP1+0]
adc dl, byte [IP1+3]
adc dh, byte [IP1+2]
 
; destination address
adc dl, byte [IP2+1]
adc dh, byte [IP2+0]
adc dl, byte [IP2+3]
adc dh, byte [IP2+2]
 
; size
adc dl, cl
adc dh, ch
 
adc edx, 0
 
;---------------------
; Real header and data
 
push esi
call checksum_1
call checksum_2
pop esi
 
} ; returns in dx only
 
 
 
 
macro TCP_sendseqinit ptr {
 
push edi ;;;; i dont like this static use of edi
mov edi, [ptr + TCP_SOCKET.ISS]
mov [ptr + TCP_SOCKET.SND_UP], edi
mov [ptr + TCP_SOCKET.SND_MAX], edi
mov [ptr + TCP_SOCKET.SND_NXT], edi
mov [ptr + TCP_SOCKET.SND_UNA], edi
pop edi
 
}
 
 
 
macro TCP_rcvseqinit ptr {
 
push edi
mov edi, [ptr + TCP_SOCKET.IRS]
inc edi
mov [ptr + TCP_SOCKET.RCV_NXT], edi
mov [ptr + TCP_SOCKET.RCV_ADV], edi
pop edi
 
}
 
 
 
macro TCP_init_socket socket {
 
mov [socket + TCP_SOCKET.t_maxseg], TCP_mss_default
mov [socket + TCP_SOCKET.t_flags], TF_REQ_SCALE or TF_REQ_TSTMP
 
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min
;;; TODO: TCP_time_rangeset
 
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift
 
 
}
 
 
;---------------------------
;
; TCP_pull_out_of_band
;
; IN: eax =
; ebx = socket ptr
; edx = tcp packet ptr
;
; OUT: /
;
;---------------------------
 
align 4
TCP_pull_out_of_band:
 
DEBUGF 1,"TCP_pull_out_of_band\n"
 
;;;; 1282-1305
 
ret
 
 
 
 
 
 
 
 
;-------------------------
;
; TCP_drop
;
; IN: eax = socket ptr
; ebx = error number
;
; OUT: eax = socket ptr
;
;-------------------------
align 4
TCP_drop:
 
DEBUGF 1,"TCP_drop: %x\n", eax
 
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
jb .no_syn_received
 
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED
 
call TCP_output
 
;;; TODO: update stats
 
jmp TCP_close
 
.no_syn_received:
 
;;; TODO: update stats
 
;;; TODO: check if error code is "Connection timed out' and handle accordingly
 
mov [eax + SOCKET.errorcode], ebx
 
 
 
 
 
 
 
 
;-------------------------
;
; TCP_close
;
; IN: eax = socket ptr
; OUT: eax = socket ptr
;
;-------------------------
align 4
TCP_close:
 
DEBUGF 1,"TCP_close: %x\n", eax
 
;;; TODO: update RTT and mean deviation
;;; TODO: update slow start threshold
 
call SOCKET_is_disconnected
call SOCKET_free
 
ret
 
 
 
 
;-------------------------
;
; TCP_outflags
;
; IN: eax = socket ptr
;
; OUT: edx = flags
;
;-------------------------
align 4
TCP_outflags:
 
mov edx, [eax + TCP_SOCKET.t_state]
movzx edx, byte [edx + .flaglist]
 
DEBUGF 1,"TCP_outflags: socket=%x flags=%x\n", eax, dl
 
ret
 
.flaglist:
 
db TH_RST + TH_ACK ; TCPS_CLOSED
db 0 ; TCPS_LISTEN
db TH_SYN ; TCPS_SYN_SENT
db TH_SYN + TH_ACK ; TCPS_SYN_RECEIVED
db TH_ACK ; TCPS_ESTABLISHED
db TH_ACK ; TCPS_CLOSE_WAIT
db TH_FIN + TH_ACK ; TCPS_FIN_WAIT_1
db TH_FIN + TH_ACK ; TCPS_CLOSING
db TH_FIN + TH_ACK ; TCPS_LAST_ACK
db TH_ACK ; TCPS_FIN_WAIT_2
db TH_ACK ; TCPS_TIMED_WAIT
 
 
 
 
 
 
;---------------------------------------
;
; The fast way to send an ACK/RST/keepalive segment
;
; TCP_respond
;
; IN: ebx = socket ptr
; cl = flags
;
;--------------------------------------
align 4
TCP_respond:
 
DEBUGF 1,"TCP_respond_socket: socket=%x flags=%x\n", ebx, cl
 
;---------------------
; Create the IP packet
 
push cx ebx
mov eax, [ebx + IP_SOCKET.RemoteIP]
mov edx, [ebx + IP_SOCKET.LocalIP]
mov ecx, sizeof.TCP_header
mov di, IP_PROTO_TCP shl 8 + 128
call IPv4_output
test edi, edi
jz .error
pop esi cx
push edx eax
 
;-----------------------------------------------
; Fill in the TCP header by using the socket ptr
 
mov ax, [esi + TCP_SOCKET.LocalPort]
stosw
mov ax, [esi + TCP_SOCKET.RemotePort]
stosw
mov eax, [esi + TCP_SOCKET.SND_NXT]
bswap eax
stosd
mov eax, [esi + TCP_SOCKET.RCV_NXT]
bswap eax
stosd
mov al, 0x50 ; Dataoffset: 20 bytes (TCP_header.DataOffset)
stosb
mov al, cl
stosb
; mov ax, [esi + TCP_SOCKET.RCV_WND]
; rol ax, 8
mov ax, 0x00a0 ;;;;;;; FIXME
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
 
;---------------------
; Fill in the checksum
 
.checksum:
sub edi, sizeof.TCP_header
mov ecx, sizeof.TCP_header
xchg esi, edi
TCP_checksum (edi + IP_SOCKET.LocalIP), (edi + IP_SOCKET.RemoteIP)
mov [esi+TCP_header.Checksum], dx
 
;--------------------
; And send the segment
 
call [ebx + NET_DEVICE.transmit]
ret
 
.error:
DEBUGF 1,"TCP_respond_socket: failed\n"
add esp, 2 + 4
 
ret
 
 
 
 
 
 
 
 
;-------------------------
; TCP_respond_segment:
;
; IN: edx = segment ptr (a previously received segment)
; edi = ptr to dest and src IPv4 addresses
; cl = flags
 
align 4
TCP_respond_segment:
 
DEBUGF 1,"TCP_respond_segment: frame=%x flags=%x\n", edx, cl
 
;---------------------
; Create the IP packet
 
push cx edx
mov ebx, [edi + 4]
mov eax, [edi]
mov ecx, sizeof.TCP_header
mov di, IP_PROTO_TCP shl 8 + 128
call IPv4_output
jz .error
pop esi cx
 
push edx eax
 
;---------------------------------------------------
; Fill in the TCP header by using a received segment
 
mov ax, [esi + TCP_header.DestinationPort]
stosw
mov ax, [esi + TCP_header.SourcePort]
stosw
mov eax, [esi + TCP_header.AckNumber]
bswap eax
stosd
xor eax, eax
stosd
mov al, 0x50 ; Dataoffset: 20 bytes (sizeof.TCP_header/4 shl 4)
stosb
mov al, cl
stosb
mov ax, 1280
rol ax, 8
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
 
;---------------------
; Fill in the checksum
 
lea esi, [edi - sizeof.TCP_header]
mov ecx, sizeof.TCP_header
TCP_checksum (esi - sizeof.IPv4_header + IPv4_header.DestinationAddress),\ ; FIXME
(esi - sizeof.IPv4_header + IPv4_header.SourceAddress)
mov [esi + TCP_header.Checksum], dx
 
;--------------------
; And send the segment
 
call [ebx + NET_DEVICE.transmit]
ret
 
.error:
DEBUGF 1,"TCP_respond_segment: failed\n"
add esp, 2+4
 
ret
 
 
macro TCPT_RANGESET timer, value, min, max {
 
local .min
local .max
local .done
 
cmp value, min
jb .min
cmp value, max
ja .max
 
mov timer, value
jmp .done
 
.min:
mov timer, value
jmp .done
 
.max:
mov timer, value
jmp .done
 
.done:
}
 
 
align 4
TCP_set_persist:
 
DEBUGF 1,"TCP_set_persist\n"
 
; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive
 
cmp [eax + TCP_SOCKET.timer_retransmission], 0
ja @f
 
; calculate RTO
push ebx
mov ebx, [eax + TCP_SOCKET.t_srtt]
shr ebx, 2
add ebx, [eax + TCP_SOCKET.t_rttvar]
shr ebx, 1
 
mov cl, [eax + TCP_SOCKET.t_rxtshift]
shl ebx, cl
 
; Start/restart persistance timer.
 
TCPT_RANGESET [eax + TCP_SOCKET.timer_persist], ebx, TCP_time_pers_min, TCP_time_pers_max
 
pop ebx
 
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift
jae @f
inc [eax + TCP_SOCKET.t_rxtshift]
@@:
 
ret
 
 
 
; eax = rtt
; ebx = socket ptr
 
align 4
TCP_xmit_timer:
 
DEBUGF 1,"TCP_xmit_timer: socket=%x rtt=%d0ms\n", ebx, eax
 
;TODO: update stats
 
cmp [ebx + TCP_SOCKET.t_rtt], 0
je .no_rtt_yet
 
; srtt is stored as a fixed point with 3 bits after the binary point.
; The following magic is equivalent of the smoothing algorithm in rfc793 with an alpha of .875
; (srtt = rtt/8 + srtt*7/8 in fixed point)
; Adjust rtt to origin 0.
 
push ecx
mov ecx, [ebx + TCP_SOCKET.t_srtt]
shr ecx, TCP_RTT_SHIFT
sub eax, ecx
dec eax
pop ecx
 
add [ebx + TCP_SOCKET.t_srtt], eax
ja @f
mov [ebx + TCP_SOCKET.t_srtt], 1
@@:
 
; We accumulate a smoothed rtt variance (actually, a smoothed mean difference),
; then set the retransmit timer to smoothed rtt + 4 times the smoothed variance.
; rttvar is stored as fixed point with 2 bits after the binary point.
; The following is equivalent to rfc793 smoothing with an alpha of .75
; (rttvar = rttvar*3/4 + delta/4) (delta = eax)
 
; get abs(eax)
push edx
cdq
xor eax, edx
sub eax, edx
 
mov edx, [ebx + TCP_SOCKET.t_rttvar]
shr edx, TCP_RTTVAR_SHIFT
sub eax, edx
pop edx
 
add [ebx + TCP_SOCKET.t_rttvar], eax
ja @f
mov [ebx + TCP_SOCKET.t_rttvar], 1
@@:
ret
 
 
.no_rtt_yet:
 
push ecx
mov ecx, eax
shl ecx, TCP_RTT_SHIFT
mov [ebx + TCP_SOCKET.t_srtt], ecx
 
shl eax, TCP_RTTVAR_SHIFT - 1
mov [ebx + TCP_SOCKET.t_rttvar], eax
pop ecx
 
ret
 
 
 
 
; eax = max segment size
; ebx = socket ptr
align 4
TCP_mss:
 
cmp eax, 1420 ; FIXME
jbe @f
mov eax, 1420
@@:
mov [ebx + TCP_SOCKET.t_maxseg], eax
 
 
ret
 
 
 
 
; ebx = socket ptr
; edx = segment ptr
align 4
TCP_reassemble:
 
 
 
ret
 
/kernel/branches/Kolibri-acpi/network/tcp_timer.inc
0,0 → 1,168
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3143 $
 
;----------------------
; 160 ms timer
;----------------------
macro TCP_timer_160ms {
 
local .loop
local .exit
 
mov ebx, net_sockets
.loop:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .exit
 
cmp [ebx + SOCKET.Domain], AF_INET4
jne .loop
 
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP
jne .loop
 
test [ebx + TCP_SOCKET.t_flags], TF_DELACK
jz .loop
and [ebx + TCP_SOCKET.t_flags], not (TF_DELACK)
 
push ebx
mov cl, TH_ACK
call TCP_respond
; and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW ;;
; mov eax, ebx ;;
; call TCP_output ;;
pop ebx
 
jmp .loop
 
.exit:
 
}
 
 
;----------------------
; 640 ms timer
;----------------------
macro TCP_timer_640ms {
 
local .loop
local .exit
 
; Update TCP sequence number
 
add [TCP_sequence_num], 64000
 
; scan through all the active TCP sockets, decrementing ALL timers
; timers do not have the chance to wrap because the keepalive timer will kill the socket when it expires
 
mov eax, net_sockets
.loop:
mov eax, [eax + SOCKET.NextPtr]
.check_only:
or eax, eax
jz .exit
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .loop
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne .loop
 
inc [eax + TCP_SOCKET.t_idle]
dec [eax + TCP_SOCKET.timer_retransmission]
jnz .check_more2
 
DEBUGF 1,"socket %x: Retransmission timer expired\n", eax
 
push eax
call TCP_output
pop eax
 
.check_more2:
dec [eax + TCP_SOCKET.timer_keepalive]
jnz .check_more3
 
DEBUGF 1,"socket %x: Keepalive expired\n", eax
 
cmp [eax + TCP_SOCKET.state], TCPS_ESTABLISHED
ja .dont_kill
 
push eax
call TCP_disconnect
pop eax
jmp .loop
 
.dont_kill:
test [eax + SOCKET.options], SO_KEEPALIVE
jz .reset_keepalive
 
push eax
mov ebx, eax
xor cl, cl
call TCP_respond ; send keepalive
pop eax
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
jmp .check_more3
 
.reset_keepalive:
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle
 
.check_more3:
dec [eax + TCP_SOCKET.timer_timed_wait]
jnz .check_more5
 
DEBUGF 1,"socket %x: 2MSL timer expired\n", eax
 
.check_more5:
dec [eax + TCP_SOCKET.timer_persist]
jnz .loop
 
DEBUGF 1,"socket %x: persist timer expired\n", eax
 
call TCP_set_persist
mov [eax + TCP_SOCKET.t_force], 1
push eax
call TCP_output
pop eax
mov [eax + TCP_SOCKET.t_force], 0
 
jmp .loop
.exit:
 
}
 
 
 
; eax = socket
 
TCP_cancel_timers:
 
push eax edi
 
lea edi, [eax + TCP_SOCKET.timer_retransmission]
xor eax, eax
stosd
stosd
stosd
stosd
stosd
 
pop edi eax
 
 
ret
/kernel/branches/Kolibri-acpi/network/tcp_usreq.inc
0,0 → 1,102
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
;-------------------------
;
; TCP_usrclose
;
; Move connection to next state, based on process close.
;
; IN: eax = socket ptr
;
;-------------------------
align 4
TCP_usrclosed:
 
DEBUGF 1,"TCP_usrclosed: %x\n", eax
 
push ebx
mov ebx, [eax + TCP_SOCKET.t_state]
mov ebx, dword [.switch + ebx*4]
jmp ebx
 
.switch:
 
dd .close ; TCPS_CLOSED
dd .close ; TCPS_LISTEN
dd .close ; TCPS_SYN_SENT
dd .wait1 ; TCPS_SYN_RECEIVED
dd .wait1 ; TCPS_ESTABLISHED
dd .last_ack ; TCPS_CLOSE_WAIT
dd .ret ; TCPS_FIN_WAIT_1
dd .ret ; TCPS_CLOSING
dd .ret ; TCPS_LAST_ACK
dd .disc ; TCPS_FIN_WAIT_2
dd .disc ; TCPS_TIMED_WAIT
 
 
.close:
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED
call TCP_close
pop ebx
ret
 
.wait1:
mov [eax + TCP_SOCKET.t_state], TCPS_FIN_WAIT_1
; TODO: set timer?
pop ebx
ret
 
.last_ack:
mov [eax + TCP_SOCKET.t_state], TCPS_LAST_ACK
pop ebx
ret
 
.disc:
call SOCKET_is_disconnected
; TODO: set timer?
.ret:
pop ebx
ret
 
 
 
 
;-------------------------
;
; TCP_disconnect
;
; IN: eax = socket ptr
; OUT: eax = socket ptr
;
;-------------------------
align 4
TCP_disconnect:
 
DEBUGF 1,"TCP_disconnect: %x\n", eax
 
cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jb TCP_close
 
 
; TODO: implement LINGER ?
 
call SOCKET_is_disconnecting
call TCP_usrclosed
call TCP_output
 
ret
/kernel/branches/Kolibri-acpi/network/udp.inc
1,155 → 1,325
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ;;
;; UDP.INC ;;
;; ;;
;; UDP Processes for Menuet OS TCP/IP stack ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; See file COPYING for details ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
;*******************************************************************
; Interface
;
; udp_rx Handles received IP packets with the UDP protocol
;
;*******************************************************************
struct UDP_header
 
SourcePort dw ?
DestinationPort dw ?
Length dw ? ; Length of (UDP Header + Data)
Checksum dw ?
 
ends
 
 
align 4
uglobal
UDP_PACKETS_TX rd MAX_NET_DEVICES
UDP_PACKETS_RX rd MAX_NET_DEVICES
endg
 
 
;-----------------------------------------------------------------
;
; UDP Payload ( Data field in IP datagram )
; UDP_init
;
; 0 1 2 3
; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
; This function resets all UDP variables
;
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | Source Port | Destination Port |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | Length ( UDP Header + Data ) | Checksum |
; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; | UDP Data |
; +-+-+-.......... -+
;
;-----------------------------------------------------------------
macro UDP_init {
 
struc UDP_PACKET
{ .SourcePort dw ? ;+00
.DestinationPort dw ? ;+02
.Length dw ? ;+04 - Length of (UDP Header + Data)
.Checksum dw ? ;+06
.Data db ? ;+08
xor eax, eax
mov edi, UDP_PACKETS_TX
mov ecx, 2*MAX_NET_DEVICES
rep stosd
}
 
virtual at 0
UDP_PACKET UDP_PACKET
end virtual
 
macro UDP_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx
 
;***************************************************************************
; Function
; udp_rx [by Johnny_B]
; Pseudoheader
mov edx, IP_PROTO_UDP
 
add dl, [IP1+1]
adc dh, [IP1+0]
adc dl, [IP1+3]
adc dh, [IP1+2]
 
adc dl, [IP2+1]
adc dh, [IP2+0]
adc dl, [IP2+3]
adc dh, [IP2+2]
 
adc dl, cl ; byte[esi+UDP_header.Length+1]
adc dh, ch ; byte[esi+UDP_header.Length+0]
 
; Done with pseudoheader, now do real header
adc dl, byte[esi+UDP_header.SourcePort+1]
adc dh, byte[esi+UDP_header.SourcePort+0]
 
adc dl, byte[esi+UDP_header.DestinationPort+1]
adc dh, byte[esi+UDP_header.DestinationPort+0]
 
adc dl, byte[esi+UDP_header.Length+1]
adc dh, byte[esi+UDP_header.Length+0]
 
adc edx, 0
 
; Done with header, now do data
push esi
movzx ecx, [esi+UDP_header.Length]
rol cx , 8
sub cx , sizeof.UDP_header
add esi, sizeof.UDP_header
 
call checksum_1
call checksum_2
pop esi
 
add [esi+UDP_header.Checksum], dx ; this final instruction will set or clear ZF :)
 
}
 
 
;-----------------------------------------------------------------
;
; Description
; UDP protocol handler
; This is a kernel function, called by ip_rx
; IP buffer address given in edx
; IP buffer number in eax
; Free up (or re-use) IP buffer when finished
; UDP_input:
;
;***************************************************************************
; Called by IPv4_input,
; this procedure will inject the udp data diagrams in the application sockets.
;
; IN: [esp] = Pointer to buffer
; [esp+4] = size of buffer
; ebx = ptr to device struct
; ecx = UDP Packet size
; esi = ptr to UDP header
; edi = ptr to ipv4 source and dest address
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
UDP_input:
 
proc udp_rx stdcall
push eax
DEBUGF 1,"UDP_input: size=%u\n", ecx
 
; First validate the header & checksum. Discard buffer if error
; First validate, checksum
 
neg [esi + UDP_header.Checksum] ; substract checksum from 0
jz .no_checksum ; if checksum is zero, it is considered valid
 
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct
 
UDP_checksum (edi), (edi+4)
jnz .checksum_mismatch
 
.no_checksum:
DEBUGF 1,"UDP_input: checksum ok\n"
 
; Convert length to little endian
 
rol [esi + UDP_header.Length], 8
 
; Look for a socket where
; IP Packet UDP Destination Port = local Port
; IP Packet SA = Remote IP
 
mov ax, [edx + 20 + UDP_PACKET.DestinationPort] ; get the local port from
; the IP packet's UDP header
mov cx, [esi + UDP_header.SourcePort]
mov dx, [esi + UDP_header.DestinationPort]
mov edi, [edi + 4] ; ipv4 source address
mov eax, net_sockets
 
mov ebx, net_sockets
 
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .exit ; No match, so exit
cmp [ebx + SOCKET.LocalPort], ax ; ax will hold the 'wrong' value,
; but the comparision is correct
jne .next_socket ; Return back if no match
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump
 
; For dhcp, we must allow any remote server to respond.
; I will accept the first incoming response to be the one
; I bind to, if the socket is opened with a destination IP address of
; 255.255.255.255
cmp [ebx + SOCKET.RemoteIP], 0xffffffff
je @f
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
 
mov eax, [edx + IP_PACKET.SourceAddress] ; get the Source address from the IP packet
cmp [ebx + SOCKET.RemoteIP], eax
jne .exit ; Quit if the source IP is not valid
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
jne .next_socket
 
@@: ; OK - we have a valid UDP packet for this socket.
; First, update the sockets remote port number with the incoming msg
; - it will have changed
; from the original ( 69 normally ) to allow further connects
mov ax, [edx + 20 + UDP_PACKET.SourcePort] ; get the UDP source port
; ( was 69, now new )
mov [ebx + SOCKET.RemotePort], ax
cmp [eax + UDP_SOCKET.LocalPort], dx
jne .next_socket
 
; Now, copy data to socket. We have socket address as [eax + sockets].
; We have IP packet in edx
DEBUGF 1,"UDP_input: socket=%x\n", eax
 
; get # of bytes in ecx
movzx ecx, [edx + IP_PACKET.TotalLength] ; total length of IP packet. Subtract
xchg cl, ch ; 20 + 8 gives data length
sub ecx, 28
;;; TODO: when packet is processed, check more sockets!
 
mov eax, [ebx + SOCKET.rxDataCount] ; get # of bytes already in buffer
add [ebx + SOCKET.rxDataCount], ecx ; increment the count of bytes in buffer
; cmp [eax + IP_SOCKET.RemoteIP], 0xffffffff
; je @f
; cmp [eax + IP_SOCKET.RemoteIP], edi
; jne .next_socket
; @@:
;
; FIXME: UDP should check remote IP, but not under all circumstances!
 
; ecx has count, edx points to data
cmp [eax + UDP_SOCKET.firstpacket], 0
je .updateport
 
add edx, 28 ; edx now points to the data
lea edi, [ebx + eax + SOCKETHEADERSIZE]
mov esi, edx
cmp [eax + UDP_SOCKET.RemotePort], cx
jne .dump
 
cld
rep movsb ; copy the data across
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
; flag an event to the application
mov eax, [ebx + SOCKET.PID] ; get socket owner PID
mov ecx, 1
mov esi, TASK_DATA + TASKDATA.pid
.updatesock:
inc [UDP_PACKETS_RX] ; Fixme: correct interface?
 
.next_pid:
cmp [esi], eax
je .found_pid
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next_pid
movzx ecx, [esi + UDP_header.Length]
sub ecx, sizeof.UDP_header
add esi, sizeof.UDP_header
 
jmp .exit
jmp SOCKET_input
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
.updateport:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
mov [check_idle_semaphore], 200
DEBUGF 1,"UDP_input: new remote port=%x\n", cx ; FIXME: find a way to print big endian values with debugf
mov [eax + UDP_SOCKET.RemotePort], cx
inc [eax + UDP_SOCKET.firstpacket]
 
.exit:
pop eax
call freeBuff ; Discard the packet
jmp .updatesock
 
 
.checksum_mismatch:
DEBUGF 2,"UDP_input: checksum mismatch\n"
 
.dump:
call kernel_free
add esp, 4 ; pop (balance stack)
DEBUGF 2,"UDP_input: dumping\n"
 
ret
endp
 
 
 
 
;-----------------------------------------------------------------
;
; UDP_output
;
; IN: eax = socket pointer
; ecx = number of bytes to send
; esi = pointer to data
;
;-----------------------------------------------------------------
 
align 4
UDP_output:
 
DEBUGF 1,"UDP_output: socket=%x bytes=%u data_ptr=%x\n", eax, ecx, esi
 
mov dx, [eax + UDP_SOCKET.RemotePort]
DEBUGF 1,"UDP_output: remote port=%x, ", dx ; FIXME: find a way to print big endian values with debugf
rol edx, 16
mov dx, [eax + UDP_SOCKET.LocalPort]
DEBUGF 1,"local port=%x\n", dx
 
sub esp, 8 ; Data ptr and data size will be placed here
push edx esi
mov ebx, [eax + SOCKET.device]
mov edx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
mov di, IP_PROTO_UDP shl 8 + 128
add ecx, sizeof.UDP_header
call IPv4_output
jz .fail
mov [esp + 8], eax ; pointer to buffer start
mov [esp + 8 + 4], edx ; buffer size
 
mov [edi + UDP_header.Length], cx
rol [edi + UDP_header.Length], 8
 
pop esi
push edi ecx
sub ecx, sizeof.UDP_header
add edi, sizeof.UDP_header
shr ecx, 2
rep movsd
mov ecx, [esp]
and ecx, 3
rep movsb
pop ecx edi
 
pop dword [edi + UDP_header.SourcePort]
 
; Checksum
mov esi, edi
mov [edi + UDP_header.Checksum], 0
UDP_checksum (edi-4), (edi-8) ; FIXME: IPv4 packet could have options..
 
DEBUGF 1,"UDP_output: sending with device %x\n", ebx
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
inc [UDP_PACKETS_TX] ; FIXME: correct device?
@@:
 
ret
 
.fail:
DEBUGF 1,"UDP_output: failed\n"
add esp, 4+4+8
or eax, -1
ret
 
 
 
;---------------------------------------------------------------------------
;
; UDP_API
;
; This function is called by system function 75
;
; IN: subfunction number in bl
; device number in bh
; ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
 
align 4
UDP_api:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [UDP_PACKETS_TX + eax]
ret
 
.packets_rx:
mov eax, [UDP_PACKETS_RX + eax]
ret
/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/PrimaryLoader.txt
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 не будут модифицированы ядром.
/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/after_win/kordldr.win.txt
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. Добавляет сектор в конец списка (самый новый вход).
/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/cdfs/bootsect.txt
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. Если же нет, то считываются все сектора, кроме последнего, после чего
последний сектор считывается отдельно во временную область, и уже
оттуда нужная часть данных копируется в буфер.
/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat1x/bootsect.txt
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.
/kernel/branches/Kolibri-acpi/sec_loader/trunk/boot/fat32/bootsect.txt
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.
/kernel/branches/Kolibri-acpi/sec_loader/trunk/debug_msg.inc
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
/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.asm
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_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
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
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
 
 
; 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
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
 
 
}
;Òóò áóäóò ðàñïîëîãàòñüÿ äàííûå, êîòîðûå çàòðóäíèòåëüíî ðàñïîëîãàòü â ñòåêîâîé îáëàñòè....
;Тут будут распологатсья данные, которые затруднительно распологать в стековой области....
;;;
;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
 
/kernel/branches/Kolibri-acpi/sec_loader/trunk/loader.lst
905,7 → 905,7
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, 0xC4
0E61: B9 3D 00 mov cx, 61
0E64: F3 rep
0E65: AB stosw
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse.inc
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,7 → 63,7
 
.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
70,7 → 70,7
cmp al, '['
jz .parse_loader
jmp .start
;////// ïðîâåðêà íà íàëè÷åå ñåêöèè loader
;////// проверка на наличее секции loader
use_parse_loader
;pause
if DEBUG
77,9 → 77,9
xor ax, ax
int 16h
end if
;////// âûâîä ãðàôè÷åñêîãî ýêðàíà, âûáîð, ñåêöèè ïîä äåôîëòó
;////// вывод графического экрана, выбор, секции под дефолту
use_any_sec
;ïàðñèíã âûáðàíîé èëè äåôîëòíîé ñåêöèè ò.å. ðàçáîð ïàðàìåòðîâ âûïîëíåíèå ñöåíàðèÿ
;парсинг выбраной или дефолтной секции т.е. разбор параметров выполнение сценария
use_parse_def_sect
 
;//////////////////
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_any.inc
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
 
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
test ax, ax
jz .parse_run_only
 
;îòîáðàçèì ïîëíûé ñïèñîê âñåõ íàéäåíûõ ñåêöèé.
;отобразим полный список всех найденых секций.
if DEBUG
pusha
mov si, show_all_sect
70,7 → 70,7
popa
end if
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov al, 0xf6 ; Ñáðîñ êëàâèàòóðû, ðàçðåøèòü ñêàíèðîâàíèå
mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование
out 0x60, al
xor cx, cx
.wait_loop: ; variant 2
102,7 → 102,7
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
pop dword [es:8*4]
sti
 
;ïðîöåäóðà ôîðìèðîâàíèÿ áóôåðà äëÿ ñêðîëèíãà ñåêöèé
;процедура формирования буфера для скролинга секций
;if DEBUG
; pusha
; mov ax,point_default
130,20 → 130,20
; 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_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
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
@@:
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
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
 
 
.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
 
 
 
; òóò ìû áóäåì ïàðñèòü òîëüêî äåôîëòíóþ ñåêöèþ è âûïîëíÿòü åå íè÷åãî íå ïðåäëàãàÿ ïîëüçîâàòåëþ èç äèàëîãîâ.
; тут мы будем парсить только дефолтную секцию и выполнять ее ничего не предлагая пользователю из диалогов.
.parse_run_only:
if DEBUG
pusha
286,7 → 286,7
macro show_bl_sc
{
;;;;;;;;;;;;;;;
;î÷èñòèì ýêðàí è âûâåäåì ìåíþ
;очистим экран и выведем меню
; draw frames
xor ax, ax
if DEBUG
324,7 → 324,7
;;;;;;;;;;;;;;;;;;;;;;; show '__________________________'
mov di, 480
mov ah, color_sym_yellow
mov al, 'Ä'
mov al, 0xC4 ; '─'
mov cx, 61
rep stosw
;;;;;;;;;;;;;;;;;;;;;;; show 'Select section'
418,7 → 418,7
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
 
.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
 
 
.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
 
.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 show_act_cursor
{
;îòîáðàæåíèå êóðñîðà ïî óìîë÷àíèþ
;отображение курсора по умолчанию
lea si, point_to_hframe
mov di, 962-160
mov ax, point_default
554,7 → 554,7
}
 
macro show_descript
;Ýòîò ìàêðîñ ïîêàçûâàåò êðàòêîå îïèñàíèå, åñëè îíî åñòü ó ñåêöèè â ïóíêòå
;Этот макрос показывает краткое описание, если оно есть у секции в пункте
;Section description
{
local .start_p_sh_d
568,14 → 568,14
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
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
repe scasb ;cut ' '
inc cx
dec di
;;;;;;;;;;;;;;;;;;;;di óêàçûâàåò íà ñòðî÷êó, êîòîðóþ íàì íóæíî âûâîäèòü.
;ñòðî÷êà áóäåò âûâîäèòüñÿ áëîêàìè ïî 37 ñèìâîëîâ.
;íàñòðîèì êóäà áóäåì âûâîäèòü ò.å. íà÷àëî
;es:di - óêàçûâàþò íà ñòðî÷êó èç êîòîðîé ìû áåðåì ñèìâîë, ds:si êóäà áóäåì âûâîäèòü
;;;;;;;;;;;;;;;;;;;;di указывает на строчку, которую нам нужно выводить.
;строчка будет выводиться блоками по 37 символов.
;настроим куда будем выводить т.е. начало
;es:di - указывают на строчку из которой мы берем символ, ds:si куда будем выводить
push di
pop si
 
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_dat.inc
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'
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_def_sect.inc
24,10 → 24,10
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;*****************************************************************************
 
; â ýòîé ñåêöèè èäåò ðàçáîð ïàðàìåòðîâ óêàçàòåëü íà ñåêöèþ õðàíèòüñÿ â point_default
;òèïû îøèáîê ïðè îáðàáîòêå ìàêðîñà
;Ìàêðîñ RamdiskFS
;/îïðåäåëåíèå ôëàãîâ â çàïèñè êîðíåâîé äèðåêòîðèè
; в этой секции идет разбор параметров указатель на секцию храниться в point_default
;типы ошибок при обработке макроса
;Макрос RamdiskFS
;/определение флагов в записи корневой директории
ATTR_READ_ONLY equ 0x01
ATTR_HIDDEN equ 0x02
ATTR_SYSTEM equ 0x04
37,9 → 37,9
 
 
 
show_error_1 equ 0x1 ;êîí÷èëèñü äàííûå - íå çàïëàíèðîâàííûé êîíåö ñåêöèè
show_error_2 equ 0x2 ;íåò çàâåðøàþùåãî ñèìâîëà â ðàçìåðå ðàì äèñêà.
show_error_3 equ 0x4 ; ðàì äèñê áóäåò èìåòü ðàçìåð =64 êá.
show_error_1 equ 0x1 ;кончились данные - не запланированный конец секции
show_error_2 equ 0x2 ;нет завершающего символа в размере рам диска.
show_error_3 equ 0x4 ; рам диск будет иметь размер =64 кб.
show_error_4 equ 0x8 ;
 
macro use_parse_def_sect
49,41 → 49,41
pop es
mov si, point_to_point_def
sub si, 2
mov cx, [si] ;çàãðóçèì óêàçàòåëü íàñëåäóþùèþ ñåêöèþ
mov cx, [si] ;загрузим указатель наследующию секцию
 
xor ax, ax ;îáíóëèì àx äëÿ î÷èñòêè ôëàãîâ
xor ax, ax ;обнулим аx для очистки флагов
 
sub cx, di ;âîò òåïåðü èìååì èñòèíûé ðàçìåð
mov save_cx_d, cx ;ñîõðàíèì çíà÷åíèå cx ñâîåé ïåðåìåííîé
;îáíóëèì ïåðåìåííóþ ôëàãîâ, ýòî íåîáõîäèìî, äëÿ òîãî, ÷òî áû èçáåæàòü îáðàáîòêó ïîâòîðÿþùèõñÿ çíà÷åíèé
sub cx, di ;вот теперь имеем истиный размер
mov save_cx_d, cx ;сохраним значение cx своей переменной
;обнулим переменную флагов, это необходимо, для того, что бы избежать обработку повторяющихся значений
 
mov status_flag, ax
;;;;
;ÂÕîä â îáðàáîòêó ïàðñèíãà çíà÷åíèé ñåêöèé. es:di - óêàçàòåëü íà íà÷àëî ñåêöèè cx ðàçìåð ñåêöèè äîñòóïíîé äëÿ ïàðñèíãà
;ВХод в обработку парсинга значений секций. es:di - указатель на начало секции cx размер секции доступной для парсинга
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ñîãëàøåíèå íå ðàçðóøàåì bp, es, cs, sp
;use_Loader_Image ;çàãðóçèòü îáðàç âûøå 1 ìá
;соглашение не разрушаем bp, es, cs, sp
;use_Loader_Image ;загрузить образ выше 1 мб
use_RamdiskFS
;ïðîâåðÿåòñÿ ñàìûé ïîñëåäíèé.
use_LoaderModule ;îñîáåííîñòü - ïåðåäàåò óïðàâëåíèå íà çàãðóæåííûé ìîäóëü.
;проверяется самый последний.
use_LoaderModule ;особенность - передает управление на загруженный модуль.
}
 
macro use_LoaderModule
;êàê âàðèàíò ñåé÷àñ èñïîëüçóåòñÿ ìîäåëü, ïðè çàãðóçêå ìîäóëÿ íà íåãî ïåðåäàåòñÿ óïðàâëåíèå, ðåøåíèå âðåìåíîå
;óïðàâëåíèå áóäåò ïåðåäàâàòüñÿ òîëüêî ïîñëå îáðàáîòêè âñåé ñåêöèè
;как вариант сейчас используется модель, при загрузке модуля на него передается управление, решение временое
;управление будет передаваться только после обработки всей секции
{
local .found_end_str
 
mov di, point_default ;restore value
mov cx, save_cx_d
;îáðàáîòêà êîíñòðóêöèè òèïà LoaderModule=kord/kolibri.ldm
;обработка конструкции типа LoaderModule=kord/kolibri.ldm
.start_p_LM:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz ._afterLoaderModule ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz ._afterLoaderModule ;нету? ну ладно - следующее значение тогда )
cmp al, 'L'
jnz .start_p_LM
;ïðîâåðêà íà çíà÷åíèå LoaderModule
;проверка на значение LoaderModule
; parse_LoaderModule
mov bx, cx
mov ax, di
97,10 → 97,10
add bx, cx
mov cx, bx
 
test status_flag, flag_found_LM ;îöåíêà ôëàãîâ
test status_flag, flag_found_LM ;оценка флагов
jz .correct_is_not_set_LM
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
115,26 → 115,26
repe scasb ;cut ' '
inc cx
dec di
;di óêàçûâàåò íà íà÷àëî áëîêà èíôîðìàöèè, â cx äëèííà äî êîíöà ñåêöèè.
;ïîñëå çàãðóçêè çàíîñèòüñÿ çíà÷åíèå çàíÿòîé ïàìÿòè.
;äëÿ òîãî ÷òî áû çàãðóçèòü ìîäóëü, âîñïîëüçóåìñÿ callback ñåðâèñîì
;îðèãèíàëüíîå ðåøåíèå - ðàçìåñòèì dd ïåðåä ñòðî÷êîé è ïîñëå ñòðî÷êè ðàçìåñòèì byte =0
;ýòî âûãëÿäèò òàê: â ini ôàéëå ñóùåñòâóåò ñòðî÷êà LoaderModule = kord/kernel.loader
;ìû åå ìîäèôèöèðóåì äî òàêîãî ñîñòîÿíèÿ dw,dw,db'kord/kernel.loader',0 êîíå÷íî ñîõðàíèâ òå çíà÷åíèÿ êîòîðûå ìû çàìåíÿåì
;ñîõðàíèëè ïåâûå 2 word
;di указывает на начало блока информации, в cx длинна до конца секции.
;после загрузки заноситься значение занятой памяти.
;для того что бы загрузить модуль, воспользуемся callback сервисом
;оригинальное решение - разместим dd перед строчкой и после строчки разместим byte =0
;это выглядит так: в ini файле существует строчка LoaderModule = kord/kernel.loader
;мы ее модифицируем до такого состояния dw,dw,db'kord/kernel.loader',0 конечно сохранив те значения которые мы заменяем
;сохранили певые 2 word
push dword [es:di-6]
lea si, [di-6]
 
push word [es:di-2]
xor ax, ax
mov word [es:di-6], ax ;âíîñèì íóæíûå çíà÷åíèÿ
;info_real_mode_size ðàçìåð è óêàçàòåëü íà îáëàñòü â êîòîðóþ ìîæíî çàãðóçèòüñÿ
mov ax, info_real_mode_size ;0x3000 ;ñëåäóþùèé ñåãìåíò çà äàííûìè
mov word [es:di-6], ax ;вносим нужные значения
;info_real_mode_size размер и указатель на область в которую можно загрузиться
mov ax, info_real_mode_size ;0x3000 ;следующий сегмент за данными
 
 
mov word [es:di-4], ax
mov word [es:di-2], 16 ;êîë-âî áëîêîâ ïî 4 êá =64 êá ò.å. áîëüøå íå ñ÷èòàåì
;;;;;; ïîèñê êîíöà ñòðî÷êè
mov word [es:di-2], 16 ;кол-во блоков по 4 кб =64 кб т.е. больше не считаем
;;;;;; поиск конца строчки
@@:
mov al, byte [es:di]
cmp al, ' '
146,7 → 146,7
inc di
dec cx
jnz @b
;;;not found äîïóñòèì,÷òî ýòî êîíåö ôàéëà è îí íå èìååò ïðèâû÷íîãî çàâåðåøíèÿ ñòðîêè
;;;not found допустим,что это конец файла и он не имеет привычного заверешния строки
.found_end_str:
 
push word [es:di]
189,7 → 189,7
}
 
macro use_RamdiskFS
; ôîðìèðîâàíèå ðàì äèñêà, + îáðàáîòêà âñåãî ñâÿçàííîãî.
; формирование рам диска, + обработка всего связанного.
{
if DEBUG
local ._not_memory_in_sys
200,19 → 200,19
mov si, ramdiskFS_st
call printplain
end if
; îáíóëèì ðåãèñòð ñîñòîÿíèÿ îøèáîê
; обнулим регистр состояния ошибок
xor ax, ax
mov show_errors_sect, ax
use_free_memory ; óçíàåì êàêîãî îáúåìà ó íàñ äîñòóïíà ïàìÿòü. çíà÷åíèå âîçàðàùàåòñÿ â ax
;óçíàåì ñêîëüêî ó íàñ åñòü ïàìÿòè è ñìîæåì ëè ìû ñôîðìèðîâàòü íóæíîãî ðàçìåðà ðàì äèñê.
use_RamdiskSize ;çíà÷åíèå âîçâðàùàåòñÿ â bx
cmp free_ad_memory, bx ; ðàçìåðíîñòü â êá.
use_free_memory ; узнаем какого объема у нас доступна память. значение возаращается в ax
;узнаем сколько у нас есть памяти и сможем ли мы сформировать нужного размера рам диск.
use_RamdiskSize ;значение возвращается в bx
cmp free_ad_memory, bx ; размерность в кб.
jbe ._not_memory_in_sys
movzx eax, bx
shl eax, 10 ;*1024 = get size in byte
mov save_ramdisksize, eax ; ñîðõàíèì ðàçìåð â byte
mov save_ramdisksize, eax ; сорханим размер в byte
 
get_type_FS ;ïîëó÷èì òèï ôàéëîâîé ñèñòåìû + ñîçäàäèì åå
get_type_FS ;получим тип файловой системы + создадим ее
 
._not_memory_in_sys:
234,17 → 234,17
local .end_get_RS_ERROR_1
local .end_get_RS_ERROR_2
local ._end_parse_RS
;îáðàáàòûâàåòñÿ ðàçìåð ôîðìèðóåìîãî ðàì äèñêà
;çàãðóçèì íà÷àëî ñåêöèè, ò.ê. áóäåì ïðîñìàòðèâàòü ñ íà÷àëà è âñþ ñåêöèþ
;обрабатывается размер формируемого рам диска
;загрузим начало секции, т.к. будем просматривать с начала и всю секцию
mov di, point_default ;restore value
mov cx, save_cx_d
.start_p_RS:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz ._end_parse_RS ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz ._end_parse_RS ;нету? ну ладно - следующее значение тогда )
cmp al, 'R'
jnz .start_p_RS
;ïðîâåðêà íà çíà÷åíèÿ RamdiskSize
;проверка на значения RamdiskSize
; parse_RamdiskSize
mov bx, cx
mov ax, di
258,10 → 258,10
add bx, cx
mov cx, bx
 
test status_flag, flag_found_RS ;îöåíêà ôëàãîâ
test status_flag, flag_found_RS ;оценка флагов
jz .correct_is_not_set_RS
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
271,13 → 271,13
jcxz .end_get_RS_ERROR_1 ;not found param
cmp ah, byte [es:di-1] ;find '='
jnz .start_p_RS ; ïåðåéäåì íà íà÷àëî è ïîïðîáóåì íàéòè åùå ñåêöèþ
jnz .start_p_RS ; перейдем на начало и попробуем найти еще секцию
repe scasb ;cut ' '
inc cx
dec di
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Òóò íóæíî ïðåîáðàçîâûâàòü ñòðî÷êó â öèôðîâîå çíà÷åíèå.
;Тут нужно преобразовывать строчку в цифровое значение.
;;;;;;;;;;;;;;;;;;;;;;;;;;
xor bx, bx
mov cx, 5
299,12 → 299,12
loop @b
 
.correct_size_RS:
;âîçìîæåí 1 âàðèàíò, êîãäà ðàçìåð çàäàí â K êèëëîáàéòàõ
;âíóòðåííûé ôîðìàò äàííûõ ýòî êîë-âî çàïðîùåíîé ïàìÿòè â êá.
;возможен 1 вариант, когда размер задан в K киллобайтах
;внутренный формат данных это кол-во запрощеной памяти в кб.
test bx, bx
jnz @f ;åñëè çíà÷åíèå îòëè÷íî îò 0
;;;;;ñîîáùåíèå îá îøèáêå, ðàçìåð "íàéäåíîãî" áëîêà =0 ìèíèìàëüíî ìû äîëæíû
;óñòàíîâèòü 64 êá ðàçìåð ðàì äèñêà.
jnz @f ;если значение отлично от 0
;;;;;сообщение об ошибке, размер "найденого" блока =0 минимально мы должны
;установить 64 кб размер рам диска.
or show_errors_sect, show_error_3
mov bx, 64
@@:
319,7 → 319,7
.end_get_RS_ERROR_1:
;ñîîáùåíèå îá îøèáêå - äàííûé ó÷àñòîê êîäà íå áûë êîððåêòíî îáðàáîòàí :(
;сообщение об ошибке - данный участок кода не был корректно обработан :(
or show_errors_sect, show_error_1
jmp ._end_parse_RS
.end_get_RS_ERROR_2:
346,16 → 346,16
macro use_free_memory
{
local _support_function_use_free_memory
;ìàêðîñ äëÿ ïîëó÷åíèÿ îáùåãî ÷èñëà äîñòóïíîé ïàìÿòè â êá, äëÿ ôîðìèðîâàíèÿ ðàì äèñêà çà ïðåäåëàìè 1 ìá.
;èñïîëüçóåòñÿ 0õ88 ôóíêöèÿ 0õ15 ïðåðûâàíèÿ
; åñëè ïîääåðæèâàåòñÿ ôóíêöèÿ, òî â ax çíà÷åíèå â êá, åñëè íåò, òî â ax=0
;макрос для получения общего числа доступной памяти в кб, для формирования рам диска за пределами 1 мб.
;используется 0х88 функция 0х15 прерывания
; если поддерживается функция, то в ax значение в кб, если нет, то в ax=0
mov ah, 0x88 ;ah,0x88
int 0x15
jnc ._support_function_use_free_memory
xor ax, ax
;âîçâðàùàåò â ax ÷èñëî â êá
;возвращает в ax число в кб
._support_function_use_free_memory:
mov free_ad_memory, ax ; åñëè íå ïîääåðæèâàåòñÿ áèîñîì, òî â ax=0
mov free_ad_memory, ax ; если не поддерживается биосом, то в ax=0
if DEBUG
pushad
movzx eax, ax
380,7 → 380,7
 
}
 
macro get_type_FS ;ïîëó÷èòü è ñîçäàòü îáðàç äëÿ çàäàííîé RFS.
macro get_type_FS ;получить и создать образ для заданной RFS.
{
mov di, point_default ;restore value
mov cx, save_cx_d
387,10 → 387,10
.start_g_tpe_RFS:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz ._end_parse_FRS ;._end_get_type_RFS ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz ._end_parse_FRS ;._end_get_type_RFS ;нету? ну ладно - следующее значение тогда )
cmp al, 'R'
jnz .start_g_tpe_RFS
;ïðîâåðêà íà çíà÷åíèÿ RamdiskSize
;проверка на значения RamdiskSize
; parse_RamdiskSize
mov bx, cx
mov ax, di
404,10 → 404,10
add bx, cx
mov cx, bx
 
test status_flag, flag_found_GTRFMS ;îöåíêà ôëàãîâ
test status_flag, flag_found_GTRFMS ;оценка флагов
jz .correct_is_not_set_FRS
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
418,13 → 418,13
jz .end_get_FRS_ERROR_1 ;not found param
cmp ah, byte [es:di-1] ;find '='
jnz .start_g_tpe_RFS ; ïåðåéäåì íà íà÷àëî è ïîïðîáóåì íàéòè åùå ñåêöèþ
jnz .start_g_tpe_RFS ; перейдем на начало и попробуем найти еще секцию
repe scasb ;cut ' '
inc cx
dec di
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Òóò íóæíî ïðåîáðàçîâûâàòü ñòðî÷êó â öèôðîâîå çíà÷åíèå.
;Тут нужно преобразовывать строчку в цифровое значение.
;;;;;;;;;;;;;;;;;;;;;;;;;;
mov bx, cx
mov ax, di
434,7 → 434,7
repe cmpsb
jnz .krfs_cmp ;is not compare
 
make_FAT_RamFS ;ñäåëàòü
make_FAT_RamFS ;сделать
 
if DEBUG
pusha
464,7 → 464,7
.end_get_FRS_ERROR_1:
;ñîîáùåíèå îá îøèáêå - äàííûé ó÷àñòîê êîäà íå áûë êîððåêòíî îáðàáîòàí :(
;сообщение об ошибке - данный участок кода не был корректно обработан :(
or show_errors_sect, show_error_1
jmp ._end_parse_FRS
.end_get_FRS_ERROR_2:
486,27 → 486,27
local .RS1
local .fat12
local .fat16
; ìû äîëæíû ñôîðìèðîâàòü â íà÷àëüíûé îáðàç Ram FS, à ïîòîì çàïèñàòü åãî çà îáëàñòü âûøå 1 ìá..
;äëÿ ñëó÷àÿ ñ FAT12
; mov di,fat12_buffer ;ds äîëæåí áûòü = cs
;es:di - óêàçûâàþò íà íà÷àëî áëîêà äëÿ ôîðìèðîâàíèÿ ðàì ôñ.
use_RamdiskSector ;âîçðàùàåìîå çíà÷åíèå â ax ðàçìåð ñåêòîðà â áàéòàõ
cmp ax, 4096;ïî ñïåöèôèêàöèè çíà÷åíèå äîëæíî áûòü â ïðåäåëàõ îò 1 äî 4096
; мы должны сформировать в начальный образ Ram FS, а потом записать его за область выше 1 мб..
;для случая с FAT12
; mov di,fat12_buffer ;ds должен быть = cs
;es:di - указывают на начало блока для формирования рам фс.
use_RamdiskSector ;возращаемое значение в ax размер сектора в байтах
cmp ax, 4096;по спецификации значение должно быть в пределах от 1 до 4096
ja .RS1
test ax, ax
jnz @f ;îøèáêà åñëè ñþäà ïðûãíóëè âñå òàêè ...
jnz @f ;ошибка если сюда прыгнули все таки ...
 
.RS1:
mov word [fat12_buffer.BPB_BytsPerSec], 512
;;;;;;;;;;ñêàæåì ÷òî ïî äåôîëòó áóäåì þçàòü çíà÷åíèå...
;;;;;;;;;;скажем что по дефолту будем юзать значение...
@@:
mov word [fat12_buffer.BPB_BytsPerSec], ax;òóò âñå îê
mov word [fat12_buffer.BPB_BytsPerSec], ax;тут все ок
 
;BPB_SecPerClus êîë-âî ñåêòîðîâ â êëàñòåðå
use_RamdiskCluster ;âîçðàùàåìîå çíà÷åíèå â al
;BPB_SecPerClus кол-во секторов в кластере
use_RamdiskCluster ;возращаемое значение в al
cmp al, 128
ja @f
; test al,0x1 ;ïðîâåðêà íà êðàòíîñòü )
; test al,0x1 ;проверка на кратность )
; jnz @f
 
mov byte [fat12_buffer.BPB_SecPerClus], al
513,28 → 513,28
 
;incorrect value will be set dafault
 
;íèæå íåêîððåêòíîå çíà÷åíèå â ò.ê. ðàçìåð êðàòåí 2 è â äèàïàçîíå îò 1 äî 128 âêëþ÷èòåëüíî
; ìû äîëæíû ðóãíóòüñÿ íà ýòî
;ниже некорректное значение в т.к. размер кратен 2 и в диапазоне от 1 до 128 включительно
; мы должны ругнуться на это
;@@: ;mov byte [fat12_buffer.BPB_SecPerClus],1
 
;;;;; îïðåäåëåèì êàêàÿ ó íàñ áóäåò èñïîëüçîâàòüñÿ FAT
;ïî óñëîâèþ, fat12<4085<=fat16<65525<=fat32
; fat12_buffer.BPB_BytsPerSec*fat12_buffer.BPB_SecPerClus = êîë-âî ñåêòîðîâ
;;;;; определеим какая у нас будет использоваться FAT
;по условию, fat12<4085<=fat16<65525<=fat32
; fat12_buffer.BPB_BytsPerSec*fat12_buffer.BPB_SecPerClus = кол-во секторов
movzx eax, word [fat12_buffer.BPB_BytsPerSec]
movzx ebx, byte [fat12_buffer.BPB_SecPerClus]
 
imul ebx, eax;òóò ðàçìåðíîñòü ñåêòîðà
mov eax, save_ramdisksize ;ðàçìåð çàïðîøåííîãî ðàì äèñêà â áàéòàõ
imul ebx, eax;тут размерность сектора
mov eax, save_ramdisksize ;размер запрошенного рам диска в байтах
cdq
idiv ebx
;;;;;;;; ñåé÷àñ ÷àñòíîå â eax, à îñòàòîê â edx
;ïîëó÷èì êîë-âî ñåêòîðîâ, è ìîæåì óæå îïðåäåëèòü òèï FAT êîòîðóþ íóæíî äåëàòü.
;;;;;;;; сейчас частное в eax, а остаток в edx
;получим кол-во секторов, и можем уже определить тип FAT которую нужно делать.
cmp eax, 4085
jb .fat12
cmp eax, 65525
jb .fat16
;;;;;;;;;;;;;;;;;;;;;;;; òóò fat32
mov set_ramfs, 32 ;óñòàíîâèì òèï ôàéëîâîé ñèñòåìû
;;;;;;;;;;;;;;;;;;;;;;;; тут fat32
mov set_ramfs, 32 ;установим тип файловой системы
mov word [fat12_buffer.BPB_RsvdSecCnt], 32
xor eax, eax
mov word [fat12_buffer.BPB_RootEntCnt], ax
543,9 → 543,9
 
 
.fat16: ;fat16
;Äëÿ FAT12 è FAT16 äèñêîâ ýòî ïîëå ñîäåðæèò êîëè÷åñòâî ñåêòîðîâ, à BPB_TotSec32 ðàâíî 0, åñëè çíà÷åíèå <óìåùàåòñÿ> (ìåíüøå 0x10000).
;Для FAT12 и FAT16 дисков это поле содержит количество секторов, а BPB_TotSec32 равно 0, если значение <умещается> (меньше 0x10000).
jmp $
mov set_ramfs, 16 ;óñòàíîâèì òèï ôàéëîâîé ñèñòåìû
mov set_ramfs, 16 ;установим тип файловой системы
movzx ebx, byte [fat12_buffer.BPB_SecPerClus]
imul eax, ebx
 
554,17 → 554,17
mov word [fat12_buffer.BPB_TotSec16], ax
mov dword [fat12_buffer.BPB_TotSec32], 0
@@:
;êîëè÷åñòâî ñåêòîðîâ çàíèìàåìîå îäíîé êîïèåé ôàò
; mov word [fat12_buffer.BPB_FATSz16],0x9 ;Äëÿ FAT12/FAT16 ýòî êîëè÷åñòâî ñåêòîðîâ îäíîé FAT. ??
;;;; çàïîëíèì BPB_RootEntCnt Äëÿ FAT12 è FAT16 äèñêîâ, ýòî ïîëå ñîäåðæèò ÷èñëî
;32-áàéòíûõ ýëåìåíòîâ êîðíåâîé äèðåêòîðèè. Äëÿ FAT32 äèñêîâ, ýòî ïîëå äîëæíî
;áûòü 0. Ïîêà êîíñòàíòà, íóæíî áóäåò ïîçæå äîäåëàòü.
;количество секторов занимаемое одной копией фат
; mov word [fat12_buffer.BPB_FATSz16],0x9 ;Для FAT12/FAT16 это количество секторов одной FAT. ??
;;;; заполним BPB_RootEntCnt Для FAT12 и FAT16 дисков, это поле содержит число
;32-байтных элементов корневой директории. Для FAT32 дисков, это поле должно
;быть 0. Пока константа, нужно будет позже доделать.
mov eax, root_dir_entry_count
mov word [fat12_buffer.BPB_RootEntCnt], ax ; count of 32-byte dir. entries (224*32 = 14 sectors= 7 kb)
;ïî äîêóìåíòàöèè ðåêîìåíäóþò îòðåçàòü 16 êá äëÿ ðóò äèð íî ýòî î÷ ìíîãî, äàæå äëÿ êîîñ. èìõî äëÿ íà÷àëà õâàòèò è 7 êá
;по документации рекомендуют отрезать 16 кб для рут дир но это оч много, даже для коос. имхо для начала хватит и 7 кб
;;;;;;;
;Äëÿ FAT16 ýòî êîëè÷åñòâî ñåêòîðîâ îäíîé FAT. Äëÿ FAT32 ýòî çíà÷åíèå
;ðàâíî 0, à êîëè÷åñòâî ñåêòîðîâ îäíîé FAT ñîäåðæèòñÿ â BPB_FATSz32.
;Для FAT16 это количество секторов одной FAT. Для FAT32 это значение
;равно 0, а количество секторов одной FAT содержится в BPB_FATSz32.
;RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec;
 
;TmpVal1 = DskSize - (BPB_ResvdSecCnt + RootDirSectors);
588,13 → 588,13
 
cdq
idiv ebx
;;;;;;;; ñåé÷àñ ÷àñòíîå â eax, à îñòàòîê â edx äëÿ äèñêåòû 1.44 ó íàñ äîëæíî áûòü çíà÷åíèå =14
;;;;;;;; сейчас частное в eax, а остаток в edx для дискеты 1.44 у нас должно быть значение =14
;BPB_ResvdSecCnt + RootDirSectors
movzx ebx, word [fat12_buffer.BPB_RsvdSecCnt]
add ebx, eax
;DskSize ó íàñ ýòî çíà÷åíèå óæå ïîëó÷åíî è äîñòóïíî
movzx eax, word [fat12_buffer.BPB_TotSec16] ;äîëæåí áûòü â ñåêòîðàõ
;DskSize у нас это значение уже получено и доступно
movzx eax, word [fat12_buffer.BPB_TotSec16] ;должен быть в секторах
sub eax, ebx
 
 
607,7 → 607,7
dec eax
cdq
idiv edi
;FATSz = ñåé÷àñ ÷àñòíîå â eax, à îñòàòîê â edx
;FATSz = сейчас частное в eax, а остаток в edx
mov word [fat12_buffer.BPB_FATSz16], ax
 
 
619,7 → 619,7
 
.fat12: ;fat12
if DEBUG
; âûâåäåì â îòëàäêå, ÷òî ñîáèðàåìñÿ äåëàòü îáðàç äèñêà c FS=fat12
; выведем в отладке, что собираемся делать образ диска c FS=fat12
pushad
mov si, start_making_FAT12_msg
call printplain
628,8 → 628,8
 
 
 
;Äëÿ FAT12 è FAT16 äèñêîâ ýòî ïîëå ñîäåðæèò êîëè÷åñòâî ñåêòîðîâ, à BPB_TotSec32 ðàâíî 0, åñëè çíà÷åíèå <óìåùàåòñÿ> (ìåíüøå 0x10000).
mov set_ramfs, 12 ;óñòàíîâèì òèï ôàéëîâîé ñèñòåìû
;Для FAT12 и FAT16 дисков это поле содержит количество секторов, а BPB_TotSec32 равно 0, если значение <умещается> (меньше 0x10000).
mov set_ramfs, 12 ;установим тип файловой системы
movzx ebx, byte [fat12_buffer.BPB_SecPerClus]
imul eax, ebx
 
638,54 → 638,54
mov word [fat12_buffer.BPB_TotSec16], ax
mov dword [fat12_buffer.BPB_TotSec32], 0
@@:
;êîëè÷åñòâî ñåêòîðîâ çàíèìàåìîå îäíîé êîïèåé ôàò
; mov word [fat12_buffer.BPB_FATSz16],0x9 ;Äëÿ FAT12/FAT16 ýòî êîëè÷åñòâî ñåêòîðîâ îäíîé FAT. ??
;;;; çàïîëíèì BPB_RootEntCnt Äëÿ FAT12 è FAT16 äèñêîâ, ýòî ïîëå ñîäåðæèò ÷èñëî
;32-áàéòíûõ ýëåìåíòîâ êîðíåâîé äèðåêòîðèè. Äëÿ FAT32 äèñêîâ, ýòî ïîëå äîëæíî
;áûòü 0. Ïîêà êîíñòàíòà, íóæíî áóäåò ïîçæå äîäåëàòü.
;количество секторов занимаемое одной копией фат
; mov word [fat12_buffer.BPB_FATSz16],0x9 ;Для FAT12/FAT16 это количество секторов одной FAT. ??
;;;; заполним BPB_RootEntCnt Для FAT12 и FAT16 дисков, это поле содержит число
;32-байтных элементов корневой директории. Для FAT32 дисков, это поле должно
;быть 0. Пока константа, нужно будет позже доделать.
mov eax, root_dir_entry_count
mov word [fat12_buffer.BPB_RootEntCnt], ax ; count of 32-byte dir. entries (224*32 = 14 sectors= 7 kb)
;ïî äîêóìåíòàöèè ðåêîìåíäóþò îòðåçàòü 16 êá äëÿ ðóò äèð íî ýòî î÷ ìíîãî, äàæå äëÿ êîîñ. èìõî äëÿ íà÷àëà õâàòèò è 7 êá
;по документации рекомендуют отрезать 16 кб для рут дир но это оч много, даже для коос. имхо для начала хватит и 7 кб
;;;;;;;
;DskSize(â ñåêòîðàõ)*12 (ðàçìåðíîñòü ôàéëîâîé ñèñòåìû, ò.å ïðåäïîëîæèì ñêîëüêî áèòîâ ïîòðåáóåòñÿ äëÿ àäðåñàöèè ýòîãî îáúåìà) /8 (÷òî ïîëó÷èòü ðàçìåð â áàéòàõ)
;ïîëó÷åííîå ÷èñëî îêðóãëÿåì â áîëüøóþ ñòîðîíó êðàòíîå ñåêòîðó ò.å. 512 áàéò Òàêîé ïîäõîä íå óíèâåðñàëåí, íî ïîêà ïîéäåò
;âîîáùå ó ìåëêîñîôò ýòî âñå ñ÷èòàåòñÿ ðó÷êàìè, íî ìû áóäåì þçàòü òîëüêî ïîä êîîñ ðàì äèñê ñ ôàò12
;DskSize(в секторах)*12 (размерность файловой системы, т.е предположим сколько битов потребуется для адресации этого объема) /8 (что получить размер в байтах)
;полученное число округляем в большую сторону кратное сектору т.е. 512 байт Такой подход не универсален, но пока пойдет
;вообще у мелкософт это все считается ручками, но мы будем юзать только под коос рам диск с фат12
movzx eax, word [fat12_buffer.BPB_TotSec16]
imul eax, 12
shr eax, 3 ;äåëèì íà 8 íî ò.å. íàì íóæíî äåëèòü åùå è íà 512 èëè áîëåå â çàâèñèìîñòè îò ðàçìåðîâ êëàñòåðà
movzx ebx, word [fat12_buffer.BPB_BytsPerSec] ;ðàçìåð ñåêòîðà
shr eax, 3 ;делим на 8 но т.е. нам нужно делить еще и на 512 или более в зависимости от размеров кластера
movzx ebx, word [fat12_buffer.BPB_BytsPerSec] ;размер сектора
cdq
idiv ebx ;ðàçäåëèì íà ðàçìåð êëàñòåðà
;ñåé÷àñ ó íàñ â eax çíà÷åíèå åãî íóæíî îêðóãëèòü â áîëüøóþ ñòîðîíó êðàòíîìó 512 áàéòàì
;ïðèìåíèì ñëåäóþùåå î÷èñòèì and è äîáàâèì 512 áàéò. òàêèì îáðàçîì âûðàâíèì íà 512 áàéò
;íî ò.ê. âñå ðàâíî äåëèòü íèæíèé êîä íàì íå íóæåí
idiv ebx ;разделим на размер кластера
;сейчас у нас в eax значение его нужно округлить в большую сторону кратному 512 байтам
;применим следующее очистим and и добавим 512 байт. таким образом выравним на 512 байт
;но т.к. все равно делить нижний код нам не нужен
; and eax,0xfff200
; add eax,0x200 ;äîáàâèì 512 áàéò äëÿ 1.44 äèñêåòû èäåàëüíî ïîäõîäèò ))
; add eax,0x200 ;добавим 512 байт для 1.44 дискеты идеально подходит ))
 
inc ax
;ïî èäåå äîëæíî íà êàæäóþ ôàò òàáëèöó
;ðåçåðâèðîâàòüñÿ 9 ñåêòîðîâ ò.å. ïîëó÷àåòñÿ 2*9=18+1 =19 ñåêòîðîâ ò.å. ðóò äèð íàõîäèòüñÿ íà ñ 20 ñåòîðà ò.å. ñ àäðåñà 0õ2600
;ñåé÷àñ íóæíî âû÷èñëèòü ñêîëüêî áóäåò ñåêòîðîâ çàíèìàòü ôàò ) íóæíî ðàçäåëèòü íà 512
;FATSz = ñåé÷àñ ÷àñòíîå â eax
;по идее должно на каждую фат таблицу
;резервироваться 9 секторов т.е. получается 2*9=18+1 =19 секторов т.е. рут дир находиться на с 20 сетора т.е. с адреса 0х2600
;сейчас нужно вычислить сколько будет секторов занимать фат ) нужно разделить на 512
;FATSz = сейчас частное в eax
mov word [fat12_buffer.BPB_FATSz16], ax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_firstDataSector ;ïîëó÷èòü ñìåùåíèå äî äàííûõ
;ñîçäàäèì ïåâóþ çàïèñü â ôàò ïî îïðåäåëåííîìó àäðåñó.
get_firstDataSector ;получить смещение до данных
;создадим певую запись в фат по определенному адресу.
first_create_fat_table
;çàêèíèì BPB ôàéëîâîé ñèñòåìû çà 1 ìá.
;закиним BPB файловой системы за 1 мб.
use_BPB_RAM
;
;êîïèðîâàíèå ôàéëà.
;копирование файла.
use_RamdiskFile
 
;;;; âû÷èñëÿåì óêàçàòåëü íà êîðíåâóþ äèð FirstRootDirSecNum = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16);
;;;; вычисляем указатель на корневую дир FirstRootDirSecNum = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16);
; movzx ebx, [fat12_buffer.BPB_NumFATs]
; movzx eax,ax
; imul eax,ebx
;eax=(BPB_NumFATs * BPB_FATSz16)
; inc eax
; BPB_ResvdSecCnt çíà÷åíèå òîëüêî 1 äëÿ fat12/16
;â eax óêàçàòåëü íà root dir. äëÿ äèñêåòû fat12 äîëæíî ïîëó÷èòüñÿ ïðè êîë-âî êîïèé fat 1 = 1+ (1*1) =2 èëè 3
; BPB_ResvdSecCnt значение только 1 для fat12/16
;в eax указатель на root dir. для дискеты fat12 должно получиться при кол-во копий fat 1 = 1+ (1*1) =2 или 3
 
if DEBUG
pusha
715,7 → 715,7
 
macro use_RamdiskSector
{
;äëÿ íåêîòîðûõ FS áóäåò èãíîðèðîâàòüñÿ
;для некоторых FS будет игнорироваться
mov di, point_default ;restore value
mov cx, save_cx_d
 
722,11 → 722,11
.start_RamdiskSector:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz .end_RamdiskSector ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz .end_RamdiskSector ;нету? ну ладно - следующее значение тогда )
 
cmp al, 'R'
jnz .start_RamdiskSector
;ïðîâåðêà íà çíà÷åíèÿ RamdiskSize
;проверка на значения RamdiskSize
; parse_RamdiskSize
 
mov bx, cx
741,10 → 741,10
add bx, cx
mov cx, bx
 
test status_flag, flag_found_RamdiskSector ;îöåíêà ôëàãîâ
test status_flag, flag_found_RamdiskSector ;оценка флагов
jz .correct_is_not_set_RamdiskSector
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
754,7 → 754,7
jcxz .end_get_RamS_ERROR_1 ;not found param
cmp ah, byte [es:di-1] ;find '='
jnz .start_RamdiskSector ; ïåðåéäåì íà íà÷àëî è ïîïðîáóåì íàéòè åùå ñåêöèþ
jnz .start_RamdiskSector ; перейдем на начало и попробуем найти еще секцию
repe scasb ;cut ' '
inc cx
810,7 → 810,7
 
macro use_RamdiskCluster
{
;äëÿ íåêîòîðûõ FS áóäåò èãíîðèðîâàòüñÿ
;для некоторых FS будет игнорироваться
; push es
; push di
mov di, point_default ;restore value
820,10 → 820,10
.start_RamdiskCluster:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz .end_RamdiskCluster ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz .end_RamdiskCluster ;нету? ну ладно - следующее значение тогда )
cmp al, 'R'
jnz .start_RamdiskCluster
;ïðîâåðêà íà çíà÷åíèÿ RamdiskSize
;проверка на значения RamdiskSize
; parse_RamdiskSize
 
mov bx, cx
838,10 → 838,10
add bx, cx
mov cx, bx
 
test status_flag, flag_found_RamdiskCluster ;îöåíêà ôëàãîâ
test status_flag, flag_found_RamdiskCluster ;оценка флагов
jz .correct_is_not_set_RamdiskCluster
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
851,7 → 851,7
jcxz .end_get_RamSC_ERROR_1 ;not found param
cmp ah, byte [es:di-1] ;find '='
jnz .start_RamdiskCluster ; ïåðåéäåì íà íà÷àëî è ïîïðîáóåì íàéòè åùå ñåêöèþ
jnz .start_RamdiskCluster ; перейдем на начало и попробуем найти еще секцию
repe scasb ;cut ' '
inc cx
892,8 → 892,8
}
 
macro use_Loader_Image
;ïðåäíàçíà÷åí äëÿ çàãðóçêè îáðàçîâ âûøå 1 Ìá.
;ïåðâîíà÷àëüíàÿ âåðñèÿ çàãðóæàåò îáðàç äèñêåòû 1.44 ìá
;предназначен для загрузки образов выше 1 Мб.
;первоначальная версия загружает образ дискеты 1.44 мб
{
local .start_p_LI
local .exit
902,14 → 902,14
local .found_end_str
mov di, point_default ;restore value
mov cx, save_cx_d
;îáðàáîòêà êîíñòðóêöèè òèïà LoaderModule=kord/kolibri.ldm
;обработка конструкции типа LoaderModule=kord/kolibri.ldm
.start_p_LI:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz .exit ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz .exit ;нету? ну ладно - следующее значение тогда )
cmp al, 'L'
jnz .start_p_LI
;ïðîâåðêà íà çíà÷åíèå LoaderModule
;проверка на значение LoaderModule
; parse_LoaderModule
mov bx, cx
mov ax, di
923,10 → 923,10
add bx, cx
mov cx, bx
 
; test status_flag,flag_found_LM ;îöåíêà ôëàãîâ
; test status_flag,flag_found_LM ;оценка флагов
; jz .correct_is_not_set_LI
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
941,26 → 941,26
repe scasb ;cut ' '
inc cx
dec di
;di óêàçûâàåò íà íà÷àëî áëîêà èíôîðìàöèè, â cx äëèííà äî êîíöà ñåêöèè.
;ïîñëå çàãðóçêè çàíîñèòüñÿ çíà÷åíèå çàíÿòîé ïàìÿòè.
;äëÿ òîãî ÷òî áû çàãðóçèòü ìîäóëü, âîñïîëüçóåìñÿ callback ñåðâèñîì
;îðèãèíàëüíîå ðåøåíèå - ðàçìåñòèì dd ïåðåä ñòðî÷êîé è ïîñëå ñòðî÷êè ðàçìåñòèì byte =0
;ýòî âûãëÿäèò òàê: â ini ôàéëå ñóùåñòâóåò ñòðî÷êà LoaderModule = kord/kernel.loader
;ìû åå ìîäèôèöèðóåì äî òàêîãî ñîñòîÿíèÿ dw,dw,db'kord/kernel.loader',0 êîíå÷íî ñîõðàíèâ òå çíà÷åíèÿ êîòîðûå ìû çàìåíÿåì
;ñîõðàíèëè ïåâûå 2 word
;di указывает на начало блока информации, в cx длинна до конца секции.
;после загрузки заноситься значение занятой памяти.
;для того что бы загрузить модуль, воспользуемся callback сервисом
;оригинальное решение - разместим dd перед строчкой и после строчки разместим byte =0
;это выглядит так: в ini файле существует строчка LoaderModule = kord/kernel.loader
;мы ее модифицируем до такого состояния dw,dw,db'kord/kernel.loader',0 конечно сохранив те значения которые мы заменяем
;сохранили певые 2 word
push dword [es:di-6]
lea si, [di-6]
 
push word [es:di-2]
xor ax, ax
mov word [es:di-6], ax ;âíîñèì íóæíûå çíà÷åíèÿ
;info_real_mode_size ðàçìåð è óêàçàòåëü íà îáëàñòü â êîòîðóþ ìîæíî çàãðóçèòüñÿ
mov ax, info_real_mode_size ;0x3000 ;ñëåäóþùèé ñåãìåíò çà äàííûìè
mov word [es:di-6], ax ;вносим нужные значения
;info_real_mode_size размер и указатель на область в которую можно загрузиться
mov ax, info_real_mode_size ;0x3000 ;следующий сегмент за данными
 
 
mov word [es:di-4], ax
mov word [es:di-2], 16 ;êîë-âî áëîêîâ ïî 4 êá =64 êá ò.å. áîëüøå íå ñ÷èòàåì
;;;;;; ïîèñê êîíöà ñòðî÷êè
mov word [es:di-2], 16 ;кол-во блоков по 4 кб =64 кб т.е. больше не считаем
;;;;;; поиск конца строчки
@@:
mov al, byte [es:di]
cmp al, ' '
972,9 → 972,9
inc di
dec cx
jnz @b
;;;not found äîïóñòèì,÷òî ýòî êîíåö ôàéëà è îí íå èìååò ïðèâû÷íîãî çàâåðåøíèÿ ñòðîêè
;;;not found допустим,что это конец файла и он не имеет привычного заверешния строки
.found_end_str:
; ÷òåíèå áëîêà ïî 64 êá â ñåãìåíò è çàáðàñûâàíèå åãî âûøå 1 ìá.
; чтение блока по 64 кб в сегмент и забрасывание его выше 1 мб.
push word [es:di]
xor ax, ax
mov word [es:di], ax
994,7 → 994,7
jnz .error_LM
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; çàáðàñûâàíèå áëîêà â 64 êá âûøå 1 ìá.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; забрасывание блока в 64 кб выше 1 мб.
mov si, table_15_87
push es
push ds
1027,7 → 1027,7
 
 
macro name_in_root_fat
;ìàêðîñ, êîòîðûé çàïèñûâàåò èíôîðìàöèþ î çàãðóæåííîì ôàéëå â êîðíåâóþ ôàò òàáëèöó
;макрос, который записывает информацию о загруженном файле в корневую фат таблицу
{
 
}
1036,9 → 1036,9
 
macro use_RamdiskFile
{
;çàãðóçêà ôàéëîâ ñ èñïîëüçîâàíèå callback ñåðâèñà ïåðâè÷íîãî çàãðóç÷èêà
;èñïîëüçóåòñÿ òîëüêî äëÿ çàãðóçêè íåîáõîäèìûõ è íåáîëüøèõ ôàéëîâ, ò.ê. äîñòàòî÷íî ìåäëåííî ðàáîòàåò
;äëÿ çàãðóçêè èñïîëüçóåò 0õ87 ôóíêöèþ int 0x15 ïðåðûâàíèÿ - çàãðóçêà áëîêîâ äàííûõ äî 64 êá âûøå 1 ìá
;загрузка файлов с использование callback сервиса первичного загрузчика
;используется только для загрузки необходимых и небольших файлов, т.к. достаточно медленно работает
;для загрузки использует 0х87 функцию int 0x15 прерывания - загрузка блоков данных до 64 кб выше 1 мб
local .start_loop
local ._end
local .rest_value_loop
1046,14 → 1046,14
mov di, point_default ;restore value
mov cx, save_cx_d
mov data_offset, 0 ;clean offset
;îáðàáîòêà êîíñòðóêöèè òèïà LoaderModule=kord/kolibri.ldm
;обработка конструкции типа LoaderModule=kord/kolibri.ldm
.start_loop:
call get_firs_sym ;get first symbol on new line
test cx, cx
jz ._end ;íåòó? íó ëàäíî - ñëåäóþùåå çíà÷åíèå òîãäà )
jz ._end ;нету? ну ладно - следующее значение тогда )
cmp al, 'R'
jnz .start_loop
;ïðîâåðêà íà çíà÷åíèå RamdiskFile
;проверка на значение RamdiskFile
mov bx, cx
mov ax, di
 
1065,10 → 1065,10
sub bx, parse_RamdiskFile_e - parse_RamdiskFile;correct cx
add bx, cx
mov cx, bx
; test status_flag,flag_found_LM ;îöåíêà ôëàãîâ
; test status_flag,flag_found_LM ;оценка флагов
; jz .correct_is_not_set_LM
 
; mov si,found_equal_timeout ;ìû íàøëè ÷òî ôëàã óæå óñòàíîâëåí, èíôîðìèðóåì
; mov si,found_equal_timeout ;мы нашли что флаг уже установлен, информируем
; call printplain
; jmp .get_next_str
 
1087,26 → 1087,26
 
mov save_di_RAMDISK, di
mov save_cx_RAMDISK, cx
;di óêàçûâàåò íà íà÷àëî áëîêà èíôîðìàöèè, â cx äëèííà äî êîíöà ñåêöèè.
;ïîñëå çàãðóçêè çàíîñèòüñÿ çíà÷åíèå çàíÿòîé ïàìÿòè.
;äëÿ òîãî ÷òî áû çàãðóçèòü ìîäóëü, âîñïîëüçóåìñÿ callback ñåðâèñîì
;îðèãèíàëüíîå ðåøåíèå - ðàçìåñòèì dd ïåðåä ñòðî÷êîé è ïîñëå ñòðî÷êè ðàçìåñòèì byte =0
;ýòî âûãëÿäèò òàê: â ini ôàéëå ñóùåñòâóåò ñòðî÷êà RamdiskFile = @menu,@menu
;ìû åå ìîäèôèöèðóåì äî òàêîãî ñîñòîÿíèÿ dw,dw,db'@menu',0 êîíå÷íî ñîõðàíèâ òå çíà÷åíèÿ êîòîðûå ìû çàìåíÿåì
;ñîõðàíèëè ïåâûå 2 word
;di указывает на начало блока информации, в cx длинна до конца секции.
;после загрузки заноситься значение занятой памяти.
;для того что бы загрузить модуль, воспользуемся callback сервисом
;оригинальное решение - разместим dd перед строчкой и после строчки разместим byte =0
;это выглядит так: в ini файле существует строчка RamdiskFile = @menu,@menu
;мы ее модифицируем до такого состояния dw,dw,db'@menu',0 конечно сохранив те значения которые мы заменяем
;сохранили певые 2 word
 
;
@@:
mov al, byte [es:di]
cmp al, ',' ; ò.å. èùåì ðàçäåëèòåëü
cmp al, ',' ; т.е. ищем разделитель
jz .found_end_str
inc di
dec cx
jnz @b
;;;not found äîïóñòèì,÷òî ýòî êîíåö ôàéëà è îí íå èìååò ïðèâû÷íîãî çàâåðøåíèÿ ñòðîêè
;;;not found допустим,что это конец файла и он не имеет привычного завершения строки
.found_end_str:
; mov al,byte [es:di]
; cmp al,' ' ; óáèðàåì ïðîáåëû, åñëè îíè åñòü
; cmp al,' ' ; убираем пробелы, если они есть
; jnz @f
; inc di
; dec cx
1115,7 → 1115,7
;@@:
mov point_to_dest_file_name, di
inc di
;ïðîâåðêà èíäèâèäóàëüíîñòè èìåíè ôàéëà
;проверка индивидуальности имени файла
check_name_file
;/restore di - point and cx -size section
mov di, save_di_RAMDISK
1122,7 → 1122,7
mov cx, save_cx_RAMDISK
 
test al, al
jnz .start_loop ;åñëè â al çíà÷åíèå íå =0, òî òàêîå èìÿ óæå ñóùåñòâóåò â ñèñòåìå.
jnz .start_loop ;если в al значение не =0, то такое имя уже существует в системе.
 
 
 
1132,13 → 1132,13
push word [es:di-2]
push di
xor ax, ax
mov word [es:di-6], ax ;âíîñèì íóæíûå çíà÷åíèÿ
;info_real_mode_size ðàçìåð è óêàçàòåëü íà îáëàñòü â êîòîðóþ ìîæíî çàãðóçèòüñÿ
mov ax, info_real_mode_size ;0x3000 ;ñëåäóþùèé ñåãìåíò çà äàííûìè
mov word [es:di-6], ax ;вносим нужные значения
;info_real_mode_size размер и указатель на область в которую можно загрузиться
mov ax, info_real_mode_size ;0x3000 ;следующий сегмент за данными
 
 
mov word [es:di-4], ax
mov word [es:di-2], 16 ;êîë-âî áëîêîâ ïî 4 êá =64 êá ò.å. áîëüøå íå ÷èòàåì
mov word [es:di-2], 16 ;кол-во блоков по 4 кб =64 кб т.е. больше не читаем
 
mov di, point_to_dest_file_name
 
1186,19 → 1186,19
 
cmp bx, 2
ja .error
; ñåé÷àñ ó íàñ â dx:ax ðàçìåð ôàéëà, êîòîðûé ìû çàãðóçèëè.
; âîçìîæíà ñèòóàöèÿ, êîãäà â bx=1 ò.å. åñòü åùå äàííûå íà äèñêå
; сейчас у нас в dx:ax размер файла, который мы загрузили.
; возможна ситуация, когда в bx=1 т.е. есть еще данные на диске
mov status_flag_loader_f, bx
 
shl edx, 16
mov dx, ax
; shr edx,10 ;ðàçìåð ôàéëà â êá.
;;â edx ðàçìåð â áàéòàõ.
; shr edx,10 ;размер файла в кб.
;;в edx размер в байтах.
mov save_file_size, edx
mov eax, edx
;âîññòàíîâèì ïîëíîñòüþ ôàéë ñöåíàðèÿ
;восстановим полностью файл сценария
pop di
pop cx ;äëèííà îñòàòêà ñ 2-îé ÷àñòüþ èìåíè ò.å. ñ èìåíåì íàçíà÷åíèåì.
pop cx ;длинна остатка с 2-ой частью имени т.е. с именем назначением.
pop word [es:di]
pop di
pop word [es:di-2]
1227,24 → 1227,24
 
 
 
; çàãðóçèì ÷åìó ó íàñ ðàâåí êëàñòåð
; mov ax,word [fat12_buffer.BPB_BytsPerSec] ;êîë-âî áàéòîâ â ñåêòîðå ìîæåò áûòü ëþáîå 512 1024 2048 4096 2 áàéòà
; movzx bx,byte [fat12_buffer.BPB_SecPerClus] ;êîë-âî ñåêòîðîâ â êëàñòåðå
; загрузим чему у нас равен кластер
; mov ax,word [fat12_buffer.BPB_BytsPerSec] ;кол-во байтов в секторе может быть любое 512 1024 2048 4096 2 байта
; movzx bx,byte [fat12_buffer.BPB_SecPerClus] ;кол-во секторов в кластере
; imul ax,bx
;ñåé÷àñ â eax ðàçìåð êëàñòåðà (512) áàéò
;â edx äëèíà ôàéëà â áàéòàõ äî 64 êá
;çàêèíèì ôàéë çà 1 ìá
;1 íàì íóæíî ñîñòàâèòü ôàò òàáëèöó ò.å. ïðîèçâåñòè ðàçìåòêó ðàìäèñêà, çàòåì ïåðåíåñåì ïî àäðåñó ôàéë
;сейчас в eax размер кластера (512) байт
;в edx длина файла в байтах до 64 кб
;закиним файл за 1 мб
;1 нам нужно составить фат таблицу т.е. произвести разметку рамдиска, затем перенесем по адресу файл
 
;çàïèñàòü èíôîðàìàöèþ î ôàéëå â êîðíåâóþ äèðåêòîðèþ
;записать инфорамацию о файле в корневую директорию
register_file_in_fat
;ïåðåíåñòè çà 1 ìá ñîäåðæèìîå ôàéëà
;перенести за 1 мб содержимое файла
move_file_up
 
;ïðîâåðèì, çàãðóæåí ëè äî êîíöà ôàéë? ò.å. åñëè ðàçìåð ôàéëà áîëüøå ÷åì 64 êá, òî áóäåò ïîäãðóæàòü îñòàâøèåñÿ áëîêè
;проверим, загружен ли до конца файл? т.е. если размер файла больше чем 64 кб, то будет подгружать оставшиеся блоки
cmp status_flag_loader_f, 0x1
jnz @f
;íóæíî äîçàãóçèòü äàííûå ôàéëà è ïåðåíåñòè èõ çà 1-ûé ìá ñîãëàñíî ôàò ñòðóêòóðå
;нужно дозагузить данные файла и перенести их за 1-ый мб согласно фат структуре
 
 
 
1255,7 → 1255,7
 
 
@@:
;òóò îðãàíèçîâàí öèêë ïî çàãðóçêå ôàéëîâ â êîðíåâóþ äèðåêòîðèþ
;тут организован цикл по загрузке файлов в корневую директорию
mov di, save_di_RAMDISK
mov cx, save_cx_RAMDISK
if DEBUG
1278,7 → 1278,7
jmp .start_loop
 
._end:
;ïåðåíåñåì çà 1-ûé ìá ôàò è ðóò äèð
;перенесем за 1-ый мб фат и рут дир
move_up_fat_and_root_d
 
 
1286,7 → 1286,7
 
 
 
;çàãðóçêà áëîêà
;загрузка блока
; mov ah,0x87
; mov cx, ;size in byte
1296,8 → 1296,8
 
}
 
macro use_BPB_RAM ;çàêèíóòü ñàìûå ïåðâûå 512 áàéò çà 1-é ìá
;äàííûé ìàêðîñ çàêèäûâàåò BPB ñòðóêòóðó ò.å. ïåðâûå 512 áàéò, ïîêà òîëüêî ôàò12 çà 1 ìá
macro use_BPB_RAM ;закинуть самые первые 512 байт за 1-й мб
;данный макрос закидывает BPB структуру т.е. первые 512 байт, пока только фат12 за 1 мб
{
mov ax, fat12_buffer
mov si, table_15_87
1305,7 → 1305,7
push es
push ds
pop es
mov cx, 256 ;áóò ñåêòîð óêëàäûâàåòñÿ â 512 áàéò 512/2=256
mov cx, 256 ;бут сектор укладывается в 512 байт 512/2=256
mov ah, 0x87
int 0x15
pop es
1326,8 → 1326,8
end if
}
macro first_create_fat_table
;äàííûé ìàêðîñ ñîçäàåò îôîðìëÿåò 3 ïåðâûõ áàéòà fat òàáëèöû, è óñòàíàâëèâàåò óêàçàòåëü íà ñëåäóþùèé áëîê, è âíîñèò 0 çíà÷åíèå
;äëÿ ñìåùåíèÿ â êîðíåâîé òàáëèöå.
;данный макрос создает оформляет 3 первых байта fat таблицы, и устанавливает указатель на следующий блок, и вносит 0 значение
;для смещения в корневой таблице.
{
mov al, byte [fat12_buffer.BPB_Media]
 
1361,12 → 1361,12
end if
 
 
push di ; push word info_real_mode_size+0x1000 ;cëåäóþùèé ñåãìåíò çà çàãðóæåííûì ó÷àñòêîì
push di ; push word info_real_mode_size+0x1000 ;cледующий сегмент за загруженным участком
xor di, di
mov point_to_free_root, di ;çíà÷åíèå ñìåùåíèÿ =0 â êîðíåâîé ôàò òàáëèöå îïèñàíèÿ
mov point_to_free_root, di ;значение смещения =0 в корневой фат таблице описания
 
pop ds ; çàãðóæåí ñëåäóþùèé ñåãìåíò ò.å. ïóñòîé ñåãìåíò
pop ds ; загружен следующий сегмент т.е. пустой сегмент
 
mov byte [di], al
or ax, -1
1390,9 → 1390,9
 
}
macro register_file_in_fat
;ìàêðîñ ðåãèñòðàöèè ôàéëà â ôàéëîâîé ñòðóêòóðå Fat
;ïîêà ïîääåðæèâàåòñÿ òîëüêî ôàò12, ïîêà ))
;âû÷èñëåíèå ñìåæíûõ êëàñòåðîâ è çàíåñåíèå èíôû â fat/
;макрос регистрации файла в файловой структуре Fat
;пока поддерживается только фат12, пока ))
;вычисление смежных кластеров и занесение инфы в fat/
{
local .step2
local .step3
1399,21 → 1399,21
local .end
local .eof_file
 
;di point on root dir íà ôðè ñåêöèþ.
;di point on root dir на фри секцию.
push es
 
mov ax, info_real_mode_size
add ax, 0x1000
mov es, ax ; push word info_real_mode_size+0x1000 ;ñåãìåíò ñëåäóþùèé çà çàãðóæåííûì áëîêîì â 64 êá
mov es, ax ; push word info_real_mode_size+0x1000 ;сегмент следующий за загруженным блоком в 64 кб
 
; îïðåäåëÿåì òèï ôàò ïîêà íå îïðåäåëÿåì, ïîêà òîëüêî ôàò 12
; 12 áèò, äëÿ âû÷åñëåíèÿ ñîñåäíèõ êàëàñòåðîâ.
mov di, firstDataSect ;â ñåêòîðàõ
; определяем тип фат пока не определяем, пока только фат 12
; 12 бит, для вычесления соседних каластеров.
mov di, firstDataSect ;в секторах
sub di, size_root_dir
;òåïåðü â ax ðàçìåð â ñåêòîðàõ íà÷àëà ðóò äèð
;теперь в ax размер в секторах начала рут дир
shl di, 9;imul 512
add di, point_to_free_root ;ñìåùåíèå â óæå çàïèñàííûõ 32-õ ñòðóêòóðàõ.
;íåîáõîäèìî âíåñòè çíà÷åíèå â ðóò äèð ò.å. 32 áàéòà
add di, point_to_free_root ;смещение в уже записанных 32-х структурах.
;необходимо внести значение в рут дир т.е. 32 байта
if DEBUG
pushad
; mov ax,point_default
1434,16 → 1434,16
 
 
 
;gs:di - óêàçàòåëü äëÿ âíåñåíèÿ èíôîðàöèè â ðóò îáëàñòü ôàò òàáëèöû èíîðìàöèè î ôàéëå.
;gs:di - указатель для внесения инфорации в рут область фат таблицы инормации о файле.
mov si, shot_name_fat
mov cx, 11
;çàïèøåì â ñòðóêòóðó èìÿ
;запишем в структуру имя
@@:
lodsb
stosb
loop @b
 
;çàïèøåì àòðèáóòû ôàéëà è DIR_NTRes - çàðåçåâðèðîâàííûé áàéò =0
;запишем атрибуты файла и DIR_NTRes - зарезеврированный байт =0
xor ax, ax
mov ah, ATTR_VOLUME_ID
mov word [es:di], ax
1452,19 → 1452,19
mov byte [es:di], 100
inc di
;DIR_CrtTime
mov word [es:di], 0x032b ;äàòà
mov word [es:di], 0x032b ;дата
add di, 2
;DIR_CrtDate
mov word [es:di], 0x0 ;âðåìÿ ><
mov word [es:di], 0x0 ;время ><
add di, 2
;DIR_LstAccDate
mov word [es:di], 0x032b ;äàòà ìîåãî
mov word [es:di], 0x032b ;дата моего
add di, 2
;DIR_FstClusHI
mov word [es:di], 0x0 ;âðåìÿ äëÿ ôàò12 /16 âñåãäà 0
mov word [es:di], 0x0 ;время для фат12 /16 всегда 0
add di, 2
;DIR_WrtTime
mov word [es:di], 0x0 ;âðåìÿ ><
mov word [es:di], 0x0 ;время ><
add di, 2
;DIR_WrtDate
mov word [es:di], 0x032b
1475,28 → 1475,28
add di, 2
 
push di
;DIR_FstClusLO Ìëàäøåå ñëîâî íîìåðà ïåðâîãî êëàñòåðà.
; mov ax,point_next_fat_str ;çàãðóçèì óêàçàòåëü íà ýëåìåíò ôàò òàáëèöû ò.å. ýòî íîìåð ôàò çàïèñè
;FATOffset = N + (N / 2) ò.å. ýòî óæå ó íàñ ñìåùåíèå ìû çíàåì ÷òî -íà÷èíàåòñÿ âñå ñ 3-ãî ýëåìåíòà çàïèñè ôàò
;DIR_FstClusLO Младшее слово номера первого кластера.
; mov ax,point_next_fat_str ;загрузим указатель на элемент фат таблицы т.е. это номер фат записи
;FATOffset = N + (N / 2) т.е. это уже у нас смещение мы знаем что -начинается все с 3-го элемента записи фат
mov bx, ax
shr bx, 1
add ax, bx
;â àõ ñåé÷àñ FATOffset
;в ах сейчас FATOffset
;ThisFATEntOffset = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
mov bx, word [fat12_buffer.BPB_BytsPerSec]
cwd
idiv bx
;ax=ThisFATEntOffset= rem (FATOffset / BPB_BytsPerSec) ÷åòíûé èëè íå÷åòíûé óêàçàòåëü.
;ax=ThisFATEntOffset= rem (FATOffset / BPB_BytsPerSec) четный или нечетный указатель.
mov si, ax
;íàì íóæíî â öèêëå çàïèñàòü âñå êëàñòåðû êîòîðûå áóäóò èñïîëüçîâàíû äëÿ ðàçìåùåíèÿ ôàéëà.
;óçíàåì ðàçìåð êëàñòåðà.
;нам нужно в цикле записать все кластеры которые будут использованы для размещения файла.
;узнаем размер кластера.
movzx eax, word [fat12_buffer.BPB_BytsPerSec]
movzx ebx, byte [fat12_buffer.BPB_SecPerClus]
imul eax, ebx
;ax - ðàçìåð êëàñòåðà.
;ñåé÷àñ áóäåì çàïèñûâàòü âî âðåìåííûé áóôåð ôàò òàáëèöó äëÿ âûáðàííîãî ôàéëà. Ïîñêîëüêó ìû åãî çàãðóçèëè âîçìîæíî íå ïîëíîñòüþ
;ìû îáðàáîòàåì çàïèñü äëÿ ôàò ïîëíîñòüþ, â íå çàâèñèìîñòè îò ïðåäåëà áóôåðà ãäå âîçìîæíà ÷àñòü ôàéëà.
mov ebx, save_file_size ;ðàçìåð ôàéëà â áàéòàõ
;ax - размер кластера.
;сейчас будем записывать во временный буфер фат таблицу для выбранного файла. Поскольку мы его загрузили возможно не полностью
;мы обработаем запись для фат полностью, в не зависимости от предела буфера где возможна часть файла.
mov ebx, save_file_size ;размер файла в байтах
@@:
sub ebx, eax
1504,8 → 1504,8
jbe .eof_file
 
inc point_next_fat_str
mov cx, point_next_fat_str ;çàãðóçèì óêàçàòåëü íà ýëåìåíò ôàò òàáëèöû ò.å. ýòî íîìåð ôàò çàïèñè
;FATOffset = N + (N / 2) ò.å. ýòî óæå ó íàñ ñìåùåíèå ìû çíàåì ÷òî -íà÷èíàåòñÿ âñå ñ 3-ãî ýëåìåíòà çàïèñè ôàò
mov cx, point_next_fat_str ;загрузим указатель на элемент фат таблицы т.е. это номер фат записи
;FATOffset = N + (N / 2) т.е. это уже у нас смещение мы знаем что -начинается все с 3-го элемента записи фат
mov dx, ax
shr dx, 1
add cx, dx
1543,7 → 1543,7
inc point_next_fat_str
 
pop di
;DIR_FileSize 32-áèòíûé DWORD ñîäåðæèò ðàçìåð ôàéëà â áàéòàõ.
;DIR_FileSize 32-битный DWORD содержит размер файла в байтах.
mov eax, save_file_size
mov dword [es:di], eax
 
1550,11 → 1550,11
if DEBUG
pushad
 
mov di, firstDataSect ;â ñåêòîðàõ
mov di, firstDataSect ;в секторах
sub di, size_root_dir
;òåïåðü â ax ðàçìåð â ñåêòîðàõ íà÷àëà ðóò äèð
;теперь в ax размер в секторах начала рут дир
shl di, 9;imul 512
add di, point_to_free_root ;ñìåùåíèå â óæå çàïèñàííûõ 32-õ ñòðóêòóðàõ.
add di, point_to_free_root ;смещение в уже записанных 32-х структурах.
 
push di
 
1561,7 → 1561,7
mov si, dest_name_fat
mov cx, 11
 
;çàïèøåì â ñòðóêòóðó èìÿ
;запишем в структуру имя
@@:
mov al, byte [es:di]
inc di
1585,7 → 1585,7
 
 
 
add point_to_free_root, 32 ;óâåëèöèì ñìåùåíèå äî ñëåäóþùåãî çíà÷åíèÿ.
add point_to_free_root, 32 ;увелицим смещение до следующего значения.
pop es
 
}
1595,8 → 1595,8
 
 
macro get_firstDataSector
;ìàêðîñ äëÿ âû÷èñëåíèÿ ïåâîãî ñåêòîðà äàííûõ ò.å. äàííûõ ôàéëîâ â ôàòå
;âû÷èñëèì FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;
;макрос для вычисления певого сектора данных т.е. данных файлов в фате
;вычислим FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;
{
mov ax, word [fat12_buffer.BPB_FATSz16]
movzx bx, byte [fat12_buffer.BPB_NumFATs]
1608,9 → 1608,9
mov size_root_dir, bx
movzx bx, byte [fat12_buffer.BPB_RsvdSecCnt] ;add 1 for fat 16/12
add ax, bx
;ax=firstDataSector - ãäå íà÷èíàåòñÿ ïåðâûé ñåêòîðî îò 0 ñåêòîðà â ñåêòîðàõ. - ôàêòè÷åñêè = 24 ñåêòîð
mov firstDataSect, ax ;ñîõðàíèì äëÿ âû÷èñëåíèÿ
; ïîëó÷èìçíà÷åíèå êëàñòåðîâ, ýòî îáúåì â êîòîðûé ìû ìîæåì çàïèñàòü äàííûå
;ax=firstDataSector - где начинается первый секторо от 0 сектора в секторах. - фактически = 24 сектор
mov firstDataSect, ax ;сохраним для вычисления
; получимзначение кластеров, это объем в который мы можем записать данные
mov bx, word [fat12_buffer.BPB_TotSec16]
sub bx, ax
mov ax, bx
1621,7 → 1621,7
 
if DEBUG
pushad
mov ax, firstDataSect ;ïåðâûé ñåêòîð äàííûõ
mov ax, firstDataSect ;первый сектор данных
mov cx, 0x0a
mov di, firstDataSect_msg
call decode
1629,7 → 1629,7
mov si, firstDataSect_msg
call printplain
;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, size_root_dir ;ðàçìåð ðóò äèð â ñåòîêòîðàõ
mov ax, size_root_dir ;размер рут дир в сетокторах
mov cx, 0x0a
mov di, size_root_dir_msg
call decode
1637,7 → 1637,7
mov si, size_root_dir_msg
call printplain
;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, DataClasters;êëàñòåðû
mov ax, DataClasters;кластеры
mov cx, 0x0a
mov di, DataClasters_msg
call decode
1651,40 → 1651,40
}
 
macro use_RamdiskPATHS
;ïàðñèíã ïóòè èñòî÷íèêà ôàéëîâ.
;парсинг пути источника файлов.
{
 
}
 
macro use_RamdiskPATHD
;ïàðñèíã ïóòè íàçíà÷åíèÿ ôàéëîâ.
;парсинг пути назначения файлов.
{
 
}
macro check_name_file
;ìàêðîñ ïðîâåðêè èìåíè íà ïîâòîð, èìÿ äîëæíî áûòü óíèêàëüíûì.
;âõîäíûå äàííûå: es- ñåãìåíò ãäå ëåæèò ôàéë äëÿ ïàðñèíãà ò.å. startos.ini
;di - óêàçàòåëü íà èìÿ ôàéëà ò.å. es:di óêàçûâàåò íà èìÿ ôàéëà íàçíà÷åíèÿ
;âûõîäíûå äàííûå eax =-1 èìÿ ñîâïàëî, eax=0 èìÿ íå ñîâïàëî.
;макрос проверки имени на повтор, имя должно быть уникальным.
;входные данные: es- сегмент где лежит файл для парсинга т.е. startos.ini
;di - указатель на имя файла т.е. es:di указывает на имя файла назначения
;выходные данные eax =-1 имя совпало, eax=0 имя не совпало.
{
local .no_equal
local .exit
local .loop_size_root_dir
;âû÷èñëèì äëèííó ñòðî÷êè èìåíè íàçíà÷åíèÿ, êîòîðóþ áóäåì ñðàâíèâàòü ñ óæå çàïèñàííûìè äàííûìè.
;ïðåîáðàçóåì â àíàëîã ôàò çàïèñè ñòî÷êó ñ èìåíåì íàçíà÷åíèÿ
convertion_file_name ; ïðåîáðàçîâàëè èìÿ ïî íóæíûì ïðàâèëàì
;вычислим длинну строчки имени назначения, которую будем сравнивать с уже записанными данными.
;преобразуем в аналог фат записи сточку с именем назначения
convertion_file_name ; преобразовали имя по нужным правилам
test ax, ax
jnz .exit
 
lea si, [shot_name_fat] ; desination name of file
 
;âû÷èñëèì óêàçàòåëü íà êîðíåâóþ äèðåêòîðèþ
;вычислим указатель на корневую директорию
mov di, firstDataSect
sub di, size_root_dir
;òåïåðü â ax ðàçìåð â ñåêòîðàõ íà÷àëà ðóò äèð
;теперь в ax размер в секторах начала рут дир
shl di, 9;imul 512
;di= Ýòî ñìåùåíèå îò íà÷àëà áóôåðà äî ðóò äèðåêòîðèè. â ïðåäåëàõ 64 êá.
;çàãðóçèì çíà÷åíèå - ò.å. êîë-âî ýëåìåíòîâ, êîòîðûå ìû ìîæåì ïðîñìàòðèâàòü.
;di= Это смещение от начала буфера до рут директории. в пределах 64 кб.
;загрузим значение - т.е. кол-во элементов, которые мы можем просматривать.
mov dx, root_dir_entry_count
mov ax, info_real_mode_size
1766,7 → 1766,7
loop @b
 
;.succesfuly:
;ïå÷àëüíî, òàêîå èìÿ óæå èìååòñÿ :(
;печально, такое имя уже имеется :(
or ax, -1
jmp .exit
 
1799,9 → 1799,9
 
 
macro convertion_file_name
;ìàêðîñ êîíâåðòàöèè èìåíè, ýòî íóæíî ïîñêîëüêó ôîðìàò ïðåäñòàâëåííûé íå ñîîòâåòñâóåò ôàò è íàïðÿìóþ ðåäêî ìîæíî êîãäà èñïîëüçîâàòü
;ïðåîáðàçîâàíèå èìåíè òèïà hello.asm â 'HELLO ASM', â ñîîòâåòñòâèè ñ ïðàâèëàìè fat.
;âõîäíûå ïàðàìåòðû es:di óêàçàòåëü íà èìÿ ôàéëà êîòîðîå íóæíî ïðåîáðàçîâàòü, êîíå÷íûé áóôåð shot_name_fat
;макрос конвертации имени, это нужно поскольку формат представленный не соответсвует фат и напрямую редко можно когда использовать
;преобразование имени типа hello.asm в 'HELLO ASM', в соответствии с правилами fat.
;входные параметры es:di указатель на имя файла которое нужно преобразовать, конечный буфер shot_name_fat
{
local .next_step
local .error
1813,11 → 1813,11
local .st4
local .st5
 
;âû÷èñëèì äëèííó ñòðî÷êè èìåíè íàçíà÷åíèÿ, êîòîðóþ áóäåì ñðàâíèâàòü ñ óæå çàïèñàííûìè äàííûìè.
; mov di,point_to_dest_file_name âõîäíîé ïàðàìåòð
;вычислим длинну строчки имени назначения, которую будем сравнивать с уже записанными данными.
; mov di,point_to_dest_file_name входной параметр
mov si, shot_name_fat
or first_input, -1 ;ïðè ïåðâîì âõîäå óñòàíàâëèâàåì ôëàã
mov cx, 11 ;äëèííà èìåíè â ñòóêòóðå ôàò òàáëèöû
or first_input, -1 ;при первом входе устанавливаем флаг
mov cx, 11 ;длинна имени в стуктуре фат таблицы
 
@@:
mov al, byte [es:di]
1867,19 → 1867,19
cmp first_input, -1
jnz .next_step
and first_input, 0 ;ñáîðîñèì ôëàã.
and first_input, 0 ;сборосим флаг.
cmp al, '.'
jz .error ;îáðàáîòêà òî÷êè, ôàéë íå ìîæåò íà÷èíàòüñÿ ñ òî÷êè
jz .error ;обработка точки, файл не может начинаться с точки
 
.next_step:
cmp al, 0x2e
jnz .st2 ;îáðàáîòêà òî÷êè, â ñåðåäèíå ôàéëà
;òóò ó íàñ óñòàíîâëåí ðàçäåëèòåëü
;âñå îñòàëüíåî ìåñòî çàéìóò ïðîáåëû
jnz .st2 ;обработка точки, в середине файла
;тут у нас установлен разделитель
;все остальнео место займут пробелы
mov al, ' '
 
;!fixme îáðàáîòàíû íå âñå èñêëþ÷åíèÿ :(
cmp cl, 3 ;ôîðìàò ôàéëà òàêîé GIDGIDIIASM ò.å. gidgidii.asm
;!fixme обработаны не все исключения :(
cmp cl, 3 ;формат файла такой GIDGIDIIASM т.е. gidgidii.asm
jbe .st2
 
 
1897,7 → 1897,7
cmp al, 0x60
jbe .st2_l
xor al, 0x20;ñäåëàåì çàãëàâíûå áóêâû
xor al, 0x20;сделаем заглавные буквы
.st2_l:
mov byte [si], al
inc di
1909,7 → 1909,7
xor ax, ax
jmp @f
 
;;;;;;;;ôàéë çàêîí÷èëñÿ, è íóæíî âíåñòè â êîíåö ïðîáåëû
;;;;;;;;файл закончился, и нужно внести в конец пробелы
.st4_s:
mov al, ' '
.st4:
1941,18 → 1941,18
}
 
macro move_file_up
;ìàêðîñ êîòîðûé ïåðåìåùàåò çà 1 ìá ñ ïðàâèëàìè ôàò äàííûå ôàéëà.
;макрос который перемещает за 1 мб с правилами фат данные файла.
{
local .st1
local .correct_on_byte
;ñåé÷àñ èìååò áûòü ñèòóàöèÿ, êîãäà BPB óæå ïåðåìåùåí çà 1 ìá, ôàò, è ðóò äèð áóäóò ïîçæå ïåðåìåùåíû,
;à íàì íóæíî âû÷èñëèòü ìåñòî, è ïåðåíåñòè òóäà ñîäåðæèìîå ôàéëà
;ïîëó÷åíîå çíà÷åíèå óêàçûâàåò â áàéòàõ íà íà÷àëî äàííûõ
;сейчас имеет быть ситуация, когда BPB уже перемещен за 1 мб, фат, и рут дир будут позже перемещены,
;а нам нужно вычислить место, и перенести туда содержимое файла
;полученое значение указывает в байтах на начало данных
 
mov ax, info_real_mode_size ; ñåãìåíò ãäå ðàñïîëîæåíû äàííûå
mov ax, info_real_mode_size ; сегмент где расположены данные
mov si, table_15_87
mov word [si+8*2+2], ax
;ñìåùåíèå äî äàííûõ óæå çà 1-ì ìá
;смещение до данных уже за 1-м мб
movzx eax, firstDataSect
movzx edx, data_offset
add eax, edx
1959,7 → 1959,7
 
movzx ebx, word [fat12_buffer.BPB_BytsPerSec]
movzx edx, byte [fat12_buffer.BPB_SecPerClus]
imul bx, dx ;ïîëó÷èì ðàçìåð êëàñòåðà
imul bx, dx ;получим размер кластера
 
 
 
1966,7 → 1966,7
push ebx ;save bx
 
imul eax, ebx
; shl eax,9 ;óìíîæèì íà 512
; shl eax,9 ;умножим на 512
if DEBUG
pushad
2002,25 → 2002,25
 
 
@@:
mov byte [si+8*3+3], dl ;êóäà ïèñàòü
mov byte [si+8*3+3], dl ;куда писать
mov word [si+8*3+2], ax
 
mov ecx, save_file_size ;ðàçìåð ôàéëà â áàéòàõ.
cmp ecx, 0x0000ffff ;ðàçìåð áëîêà ò.å. 64 êá
jbe .correct_on_byte ;êîððåêòèðîâêà íà áàéò çíà÷åíèÿ
mov ecx, save_file_size ;размер файла в байтах.
cmp ecx, 0x0000ffff ;размер блока т.е. 64 кб
jbe .correct_on_byte ;корректировка на байт значения
 
 
 
mov ecx, 0x00010000 ;65536
sub save_file_size, ecx ;îòíèìèì
; jmp .st1 ;ïîëó÷èì 0õ8000
sub save_file_size, ecx ;отнимим
; jmp .st1 ;получим 0х8000
 
 
 
 
;êîððåêòèðîâêà çíà÷åíèÿ äîëæíà áûòü âûïîëåíåíà íà ðàçìåð êëàñòåðà
;корректировка значения должна быть выполенена на размер кластера
.correct_on_byte:
;/óçíàåì ðàçìåð êëàñòåðà
;/узнаем размер кластера
pop eax ;restore size of claster
push ecx
@@:
2040,9 → 2040,9
jz .st1
inc ecx
.st1:
shr ecx, 1 ; ïðåîáðàçîâàòü çíà÷åíèå äëÿ 0x87 function
shr ecx, 1 ; преобразовать значение для 0x87 function
 
;ïåðåíåñåì áëîê çà 1 ìá
;перенесем блок за 1 мб
push es
push ds
pop es
2068,7 → 2068,7
 
 
macro move_up_fat_and_root_d
;ìàêðîñ, êîòîðûé ïîçâîëÿåò ïåðåíåñòè âûøå 1 ìá â ñòðóêòóðó îáðàçà ôàò òàáëèöó è ðóò äèðåêòîðèþ
;макрос, который позволяет перенести выше 1 мб в структуру образа фат таблицу и рут директорию
{
local .st1
 
2077,20 → 2077,20
 
mov si, table_15_87
mov word [si+8*2+2], ax
;ñìåùåíèå äî äàííûõ
;смещение до данных
mov ax, 512
mov word [si+8*3+2], ax
;fixme! òóò íåîáõîäèìî ñäåëàòü ïîäåðæêó ò.å. ôîðìèðîâàòü ñìåùåíèå ôàéëà â óæå çàïèñàííûõ äàííûõ.
;fixme! тут необходимо сделать подержку т.е. формировать смещение файла в уже записанных данных.
 
movzx ecx, word [fat12_buffer.BPB_FATSz16]
movzx bx, byte [fat12_buffer.BPB_NumFATs]
imul cx, bx ;9x1=9
 
add cx, size_root_dir ;ðàçìåð êîðíåâîé äèððåêòîðèè
add cx, size_root_dir ;размер корневой дирректории
shl ecx, 9 ;imul 512
 
 
;êîððåêòèðîâêà çíà÷åíèÿ
;корректировка значения
test ecx, 0x1
jz .st1
inc ecx
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_err.inc
30,7 → 30,7
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
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
/kernel/branches/Kolibri-acpi/sec_loader/trunk/parse_loader.inc
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,7 → 35,7
;//////////////////
;/ parse [loader]
;//////////////////
mov bx, cx ;cîõðàíèì â ðåãèñòðû çíà÷åíèÿ ñ÷åò÷èêà è óêàçàòåëÿ
mov bx, cx ;cохраним в регистры значения счетчика и указателя
mov ax, di
 
; mov word [bp-4],.start ;is alredy set, see up
42,9 → 42,9
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
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
;//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
 
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
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
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
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
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
;
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
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
 
.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
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
jmp .start_d
 
.correct_exit:
pop cx ;âîññòàíîâèì çíà÷åíèå ñ÷åò÷èêà
pop cx ;восстановим значение счетчика
pop di
 
 
272,7 → 272,7
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
inc cx
dec di
;get timeout value
;2 çíàêa ìîæåò áûòü îáðàáîòàíî ò.å. çíà÷åíèå îò 0 äî 99 ñåêóíä
;2 знакa может быть обработано т.е. значение от 0 до 99 секунд
push cx
xor bx, bx
mov cx, 2
/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_equ.inc
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_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
 
 
 
; òóò ðàñïîëîæåíî âðåìåííîå õðàíèëèùå äëÿ 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
/kernel/branches/Kolibri-acpi/sec_loader/trunk/sl_proc.inc
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_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
dec di
 
 
;âñå âûðåçàëè è âñå ãîòîâî äëÿ âûâîäà èìåíè ñåêöèè ))
;все вырезали и все готово для вывода имени секции ))
push es
pop ds
 
249,7 → 249,7
pop si
ret
 
.not_name_sec_fb: ;íåò èìåíè â íàçâàíèè ñåêöèè - çíà÷èò òàê è ñêàæåì îá ýòîì
.not_name_sec_fb: ;нет имени в названии секции - значит так и скажем об этом
push cs
pop ds
mov di, default_section_name
256,45 → 256,45
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
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
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
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
 
 
 
;íàñòðîèëè óêàçàòåëü íà äåôàóëò ñåêöèþ
;áóäåì èñêàòü âíèç
;настроили указатель на дефаулт секцию
;будем искать вниз
.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
;;;;;;;;;;;;;;;;;;;;;;;;;;
;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
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;óñòàíîâêà òàéìåðà è îòîáðàæåíèå ñ÷åò÷èêà âðåìåíè
;установка таймера и отображение счетчика времени
gettime:
mov ah, 0
int 1Ah
462,13 → 462,13
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
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
/kernel/branches/Kolibri-acpi/sec_loader/trunk/startos.ini
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"
/kernel/branches/Kolibri-acpi/video/cursors.inc
846,7 → 846,7
add eax, ebx
mov ebx, eax
shl eax, 2
cmp [ScreenBPP], byte 32
cmp byte [_display.bpp], 32
je @f
sub eax, ebx
;--------------------------------------
921,7 → 921,7
add ecx, ebx
mov ebx, ecx
shl ecx, 2
cmp [ScreenBPP], byte 24
cmp byte [_display.bpp], 24
je .24
and eax, 0xFFFFFF
mov [ecx + cur_saved_data], eax ;store new color to
991,21 → 991,15
 
test word [SCR_MODE], 0x4000
jz .fail
; jmp .fail
 
mov ebx, restore_32
mov ecx, move_cursor_32
movzx eax, byte [ScreenBPP]
cmp eax, 32
je @F
mov edx, Vesa20_putpixel32_new
mov eax, [_display.bpp]
cmp al, 32
jne .24
 
mov ebx, restore_24
mov ecx, move_cursor_24
cmp eax, 24
jne .fail
;--------------------------------------
align 4
@@:
.set:
mov [_display.select_cursor], select_cursor
mov [_display.move_cursor], ecx
mov [_display.restore_cursor], ebx
1014,16 → 1008,7
 
cmp [PUTPIXEL], dword VGA_putpixel
je @f
cmp [ScreenBPP], byte 32
je .32
mov [PUTPIXEL], dword Vesa20_putpixel24_new
jmp @f
;--------------------------------------
align 4
.32:
mov [PUTPIXEL], dword Vesa20_putpixel32_new
;--------------------------------------
align 4
mov [PUTPIXEL], edx
@@:
stdcall load_cursor, clock_arrow, dword LOAD_FROM_MEM
mov [def_cursor_clock], eax
1030,8 → 1015,14
stdcall load_cursor, def_arrow, dword LOAD_FROM_MEM
mov [def_cursor], eax
ret
;--------------------------------------
align 4
 
.24:
mov ebx, restore_24
mov ecx, move_cursor_24
mov edx, Vesa20_putpixel24_new
cmp al, 24
je .set
 
.fail:
xor eax, eax
mov [_display.select_cursor], eax
/kernel/branches/Kolibri-acpi/video/vesa20.inc
231,7 → 231,7
mov [putimg.winmap_newline], eax
; screen new line increment
mov eax, [BytesPerScanLine]
movzx ebx, byte [ScreenBPP]
mov ebx, [_display.bpp]
shr ebx, 3
imul ecx, ebx
sub eax, ecx
266,7 → 266,7
;--------------------------------------
; get process number
mov ebx, [CURRENT_TASK]
cmp byte [ScreenBPP], 32
cmp byte [_display.bpp], 32
je put_image_end_32
;--------------------------------------
put_image_end_24:
534,7 → 534,7
.finish:
add esp, putimg.stack_data
popad
cmp [SCR_MODE], dword 0x12
cmp [SCR_MODE], 0x12
jne @f
call VGA__putimage
;--------------------------------------
1229,7 → 1229,7
mov [drbar.line_inc_map], eax
; line_inc_scr
mov eax, [drbar.real_sx]
movzx ebx, byte [ScreenBPP]
mov ebx, [_display.bpp]
shr ebx, 3
imul eax, ebx
neg eax
1268,7 → 1268,7
rol eax, 8
mov bh, al ; 0x80 drawing gradient bars
ror eax, 8
cmp byte [ScreenBPP], 24
cmp byte [_display.bpp], 24
jne draw_bar_end_32
;--------------------------------------
align 4
1508,7 → 1508,7
.end:
add esp, drbar.stack_data
popad
cmp [SCR_MODE], dword 0x12
cmp [SCR_MODE], 0x12
jne @f
call VGA_draw_bar
;--------------------------------------
1653,7 → 1653,7
add ebp, eax
add ebp, eax
add ebp, eax
cmp [ScreenBPP], byte 24 ; 24 or 32 bpp ? - x size
cmp byte [_display.bpp], 24 ; 24 or 32 bpp ? - x size
jz @f
add ebp, eax
;--------------------------------------
1733,7 → 1733,7
;--------------------------------------
align 4
@@:
cmp [ScreenBPP], byte 25 ; 24 or 32 bpp?
cmp byte [_display.bpp], 25 ; 24 or 32 bpp?
sbb edi, -1 ; +1 for 32 bpp
; I do not use 'inc eax' because this is slightly slower then 'add eax,1'
add ebp, edx
1757,7 → 1757,7
jbe dp2
popad
mov [EGA_counter], 1
cmp [SCR_MODE], dword 0x12
cmp [SCR_MODE], 0x12
jne @f
call VGA_drawbackground
;--------------------------------------
1799,7 → 1799,7
add ebp, eax
add ebp, eax
add ebp, eax
cmp [ScreenBPP], byte 24 ; 24 or 32 bpp ? - x size
cmp byte [_display.bpp], 24 ; 24 or 32 bpp ? - x size
jz @f
add ebp, eax
;--------------------------------------
1918,7 → 1918,7
;--------------------------------------
align 4
snbgp:
cmp [ScreenBPP], byte 25
cmp byte [_display.bpp], 25
sbb edi, -4
add ebp, 1
mov eax, [esp+20]
1944,7 → 1944,7
sub edi, eax
sub edi, eax
sub edi, eax
cmp [ScreenBPP], byte 24
cmp byte [_display.bpp], 24
jz @f
sub edi, eax
;--------------------------------------
1981,7 → 1981,7
add esp, 44
popad
mov [EGA_counter], 1
cmp [SCR_MODE], dword 0x12
cmp [SCR_MODE], 0x12
jne @f
call VGA_drawbackground
;--------------------------------------
/kernel/branches/Kolibri-acpi/video/vga.inc
150,7 → 150,7
;------------------------------------------------------------------------------
align 4
checkVga_N13:
cmp [SCR_MODE], dword 0x13
cmp [SCR_MODE], 0x13
jne @f
 
pushad
403,7 → 403,7
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
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