/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 3116 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 150 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 |